Dev#1823
Conversation
fix:修改硬编码温度
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
Walkthrough该 PR 发布 1.0.5/1.4.5 版本,重构 Dashboard 主题与样式配置,新增插件嵌入页面与详情弹窗流程,扩展行为学习和人物画像配置,并调整后端工具记录、数据库迁移、统计与表达方式查询逻辑。 Changes主发布改动
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
src/services/image_path_maintenance_service.py (1)
10-24:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winimport 顺序不符合项目规范。
根据
AGENTS.md的 import 规范,标准库(asyncio)应该放在第三方库(rich、sqlalchemy)之前。当前import asyncio在第三方库导入之后,需要调整顺序。📦 建议的 import 顺序
+import asyncio + from rich.console import Console from rich.progress import ( BarColumn, MofNCompleteColumn, Progress, ProgressColumn, Task, TimeElapsedColumn, TimeRemainingColumn, ) from rich.text import Text from sqlalchemy import text -import asyncio - from src.common.database.database import get_db_session根据项目编码规范,标准库和第三方库的导入应该放在本地模块导入的前面,各个导入块之间应该使用一个空行进行分隔。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/services/image_path_maintenance_service.py` around lines 10 - 24, The import statement order in the file does not follow the project's import specification. Currently, the standard library import (asyncio) is placed after third-party library imports (rich and sqlalchemy), but according to the project coding standards, standard library imports must come before third-party library imports. Reorganize the imports by moving the asyncio import to the beginning of the import section, before the rich and sqlalchemy imports, and ensure there is one blank line separating the standard library imports from the third-party library imports.Source: Coding guidelines
dashboard/src/routes/resource/behavior/index.tsx (1)
1620-1650:⚠️ Potential issue | 🟠 Major | ⚡ Quick win补齐取消捕获场景的清理逻辑,避免节点拖拽状态“卡死”
Line 1620 仅在
onPointerUp中清理pointerRef和node.fixed。当出现pointercancel/ 丢失捕获时,这段清理不会执行,节点可能长期保持fixed=true,后续布局与交互会异常。建议修复
+ const resetPointerState = useCallback( + (target?: HTMLCanvasElement, pointerId?: number) => { + const pointer = pointerRef.current + if (pointer.nodeId) { + const node = nodesRef.current.find((item) => item.id === pointer.nodeId) + if (node) node.fixed = false + heatRef.current = Math.max(heatRef.current, 0.55) + } + pointerRef.current = { mode: null, nodeId: null, lastX: 0, lastY: 0 } + if (target && pointerId !== undefined) { + try { + target.releasePointerCapture(pointerId) + } catch { + // ignore capture-release race + } + } + }, + [] + ) + - const handlePointerUp = useCallback((event: PointerEvent<HTMLCanvasElement>) => { - const pointer = pointerRef.current - if (pointer.nodeId) { - const node = nodesRef.current.find((item) => item.id === pointer.nodeId) - if (node) node.fixed = false - heatRef.current = Math.max(heatRef.current, 0.55) - } - pointerRef.current = { mode: null, nodeId: null, lastX: 0, lastY: 0 } - event.currentTarget.releasePointerCapture(event.pointerId) - }, []) + const handlePointerUp = useCallback((event: PointerEvent<HTMLCanvasElement>) => { + resetPointerState(event.currentTarget, event.pointerId) + }, [resetPointerState])<canvas ref={canvasRef} className="h-full w-full touch-none" onDoubleClick={handleDoubleClick} onPointerDown={handlePointerDown} onPointerMove={handlePointerMove} onPointerUp={handlePointerUp} + onPointerCancel={handlePointerUp} + onLostPointerCapture={() => resetPointerState()} />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/resource/behavior/index.tsx` around lines 1620 - 1650, The handlePointerUp callback cleans up the pointer state and resets node.fixed to false, but this cleanup only executes on the onPointerUp event. When a pointercancel event occurs or pointer capture is lost, the cleanup logic does not run, leaving nodes stuck in a fixed state. Create a shared cleanup function that resets the pointerRef and unfixes the node, then call this function from both handlePointerUp and a new handlePointerCancel callback. Add the onPointerCancel handler to the canvas element alongside the existing pointer event handlers to ensure cleanup occurs in all pointer termination scenarios.dashboard/src/routes/settings/AppearanceTab.tsx (2)
1080-1082:⚠️ Potential issue | 🟠 Major | ⚡ Quick win继承态判断要覆盖所有非 page 背景层。
useBackground对任意非page层只要inherit为 true 都会返回 page 背景;这里仅禁用sidebar/header,会导致card/dialog开启继承后仍可编辑实际不会生效的资源、效果和 CSS。🐛 建议修复继承层判断
- const isInheritedLayer = - (layerId === 'sidebar' || layerId === 'header') && - (bgConfig[layerId]?.inherit ?? false) + const isInheritedLayer = + layerId !== 'page' && (bgConfig[layerId]?.inherit ?? false)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/settings/AppearanceTab.tsx` around lines 1080 - 1082, The inheritance state check in the isInheritedLayer condition is too restrictive, only covering sidebar and header layers. Since useBackground returns the page background for any non-page layer when inherit is true, the condition should be expanded to cover all non-page layers including card and dialog. Replace the explicit layer name checks (layerId === 'sidebar' || layerId === 'header') with a more general check that covers any layer that is not 'page', so that all layers with inherit enabled are properly marked as inherited and prevented from being edited.
256-272:⚠️ Potential issue | 🟠 Major应该存储 sanitizeCSS 返回的净化后 CSS,而不是原始输入
代码调用
sanitizeCSS(val)获取了净化结果,但存储到styleCustomCSS的仍是原始的val。虽然applyThemePipeline在应用前会再次调用sanitizeCSS进行净化(pipeline.ts第 204 行),但应该在保存时就使用净化后的 CSS (result.css),而不是依赖后续的再次净化。请改为:[dashboardStyle]: result.css,这样可以避免在配置中积累未净化的 CSS,确保存储层的安全性和清晰度。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/settings/AppearanceTab.tsx` around lines 256 - 272, The handleCSSChange function calls sanitizeCSS(val) to obtain sanitized CSS content but stores the original unsanitized val in the styleCustomCSS configuration instead of using the sanitized result.css from the sanitizeCSS return value. Update the updateThemeConfig call to store result.css in the [dashboardStyle] key instead of val. This ensures that only sanitized CSS is persisted in the configuration, improving security and clarity at the storage layer rather than relying on subsequent re-sanitization during theme application.dashboard/src/routes/config/modelProvider/ProviderList.tsx (1)
289-301:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win“测试连接”按钮也应提供可访问名称。
该按钮当前只渲染图标,建议与编辑/删除保持一致补充
aria-label。建议修复
<Button variant="outline" size="sm" onClick={() => onTest(provider.name)} disabled={testingProviders.has(provider.name)} title="测试连接" + aria-label={`测试厂商 ${provider.name} 连接`} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/config/modelProvider/ProviderList.tsx` around lines 289 - 301, The test connection button (the one with onClick={() => onTest(provider.name)} that renders a Loader2 or Zap icon) is missing an aria-label attribute. Add an aria-label attribute to the Button component with an appropriate accessible label (e.g., "测试连接") to provide screen reader support, following the same pattern as the edit/delete buttons in this component.dashboard/src/routes/auth.tsx (1)
268-274:⚠️ Potential issue | 🟡 Minor | ⚡ Quick win为主题切换按钮补充
aria-label。当前是纯图标按钮,只有
title;建议补充aria-label,避免读屏器无法稳定读出控件名称。建议修复
<button type="button" data-auth-theme-toggle="true" onClick={toggleTheme} className="absolute right-4 top-4 rounded-lg p-2 hover:bg-accent transition-colors z-10 text-foreground" title={actualTheme === 'dark' ? t('auth.switchToLight') : t('auth.switchToDark')} + aria-label={actualTheme === 'dark' ? t('auth.switchToLight') : t('auth.switchToDark')} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/auth.tsx` around lines 268 - 274, The theme toggle button in the auth route has a title attribute for accessibility but is missing an aria-label attribute, which is needed for screen readers to reliably announce the button's purpose. Add an aria-label attribute to the button element with the same conditional logic as the title attribute, using the translation keys t('auth.switchToLight') or t('auth.switchToDark') based on the current theme, to ensure both visual tooltips and screen reader users have proper access to the button's function.dashboard/src/routes/config/model/components/Pagination.tsx (1)
85-96:⚠️ Potential issue | 🟠 Major | ⚡ Quick win首尾分页按钮缺少可访问名称(仅图标)。
Line [85]-Line [96] 与 Line [136]-Line [147] 的按钮都没有可读标签,读屏用户无法识别按钮语义(首页/末页)。
建议修复(示例)
<Button variant="outline" size="sm" onClick={() => onPageChange(1)} disabled={page === 1} className="hidden sm:flex" + aria-label="第一页" + title="第一页" > ... <Button variant="outline" size="sm" onClick={() => onPageChange(totalPages)} disabled={page >= totalPages} className="hidden sm:flex" + aria-label="最后一页" + title="最后一页" >Also applies to: 136-147
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/config/model/components/Pagination.tsx` around lines 85 - 96, The pagination buttons at lines 85-96 (first page button calling onPageChange(1)) and lines 136-147 (last page button) lack accessible labels, preventing screen reader users from understanding their purpose. Add an aria-label attribute to both Button components: use "Go to first page" for the button at lines 85-96 and "Go to last page" for the button at lines 136-147. This will ensure assistive technology users can identify the pagination controls and their semantics.dashboard/src/routes/config/model/components/ModelTable.tsx (1)
90-98:⚠️ Potential issue | 🟠 Major | ⚡ Quick win避免用对象引用计算
actualIndex,会导致错位编辑/删除风险。在 Line [90] 使用
m === model做索引定位过于脆弱;一旦分页/过滤过程返回了新对象实例,actualIndex会变成-1,随后 Line [141]-Line [154] 的编辑、删除、勾选操作都会落到错误索引。dashboard/src/routes/config/model/components/ModelCardList.tsxLine [46] 也有同样问题,建议一并修复。建议修复(示例)
- const actualIndex = allModels.findIndex((m) => m === model) + const actualIndex = allModels.findIndex( + (m) => + m.name === model.name && + m.model_identifier === model.model_identifier && + m.api_provider === model.api_provider + ) + const canOperate = actualIndex >= 0 ... - onClick={() => onEdit(model, actualIndex)} + onClick={() => canOperate && onEdit(model, actualIndex)} + disabled={!canOperate} ... - onClick={() => onDelete(actualIndex)} + onClick={() => canOperate && onDelete(actualIndex)} + disabled={!canOperate}Also applies to: 141-154
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/routes/config/model/components/ModelTable.tsx` around lines 90 - 98, The code uses object reference equality (m === model) to find actualIndex in ModelTable.tsx, which fails when models are recreated during pagination or filtering, resulting in actualIndex becoming -1. This causes subsequent edit, delete, and selection operations at lines 141-154 to target wrong indices. Replace the object reference comparison with a unique identifier comparison (such as model.id or model.name) in the findIndex call. Apply the same fix to ModelCardList.tsx at line 46 where the same pattern exists.
🧹 Nitpick comments (7)
AGENTS.md (1)
50-50: ⚡ Quick win句子不完整。
Line 50 的规则描述不完整:"禁止改动 legacy_migration,此文件以固定" 缺少后半句。建议补充为"此文件以固定形式存在"或类似的完整表述。
✏️ 建议的修正
-禁止改动 legacy_migration,此文件以固定 +禁止改动 legacy_migration,此文件以固定形式存在🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@AGENTS.md` at line 50, The sentence on line 50 in AGENTS.md describing the legacy_migration rule is incomplete and cuts off mid-thought. Complete the sentence by adding the missing second half after "此文件以固定" to make it grammatically complete and convey the full intended meaning, such as by appending descriptive text like "形式存在" or another appropriate completion that clarifies the constraint on the legacy_migration file.src/common/version.py (1)
8-8: 💤 Low value可选:为
PROJECT_ROOT添加注释说明。
PROJECT_ROOT的计算依赖于当前文件位置(src/common/version.py),使用parents[2]向上两级到达项目根目录。建议添加注释说明这一假设,以便未来移动文件时能及时调整。💡 建议的注释
+# 项目根目录:从当前文件 src/common/version.py 向上两级 PROJECT_ROOT: Path = Path(__file__).resolve().parents[2]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/common/version.py` at line 8, The PROJECT_ROOT variable calculation in src/common/version.py relies on a hardcoded parent directory depth of 2, which assumes the current file (version.py) remains at src/common/. Add a comment above the PROJECT_ROOT assignment explaining this directory structure dependency and noting that the parents[2] index must be adjusted if the file location changes in the future.src/services/image_path_maintenance_service.py (1)
384-455: ⚖️ Poor tradeoff评估移除批次间节流的影响。
重构后的实现移除了原先每批处理后的
asyncio.sleep(_BATCH_SLEEP_SECONDS)节流逻辑。虽然这能加快处理速度,但可能会在短时间内产生较高的数据库 I/O 负载。建议:
- 在低峰期测试移除节流后的系统负载表现
- 如果观察到明显的性能影响(如阻塞其他数据库操作),可以考虑保留一个较短的 sleep(如 0.01 秒)作为"呼吸时间"
当前实现添加了丰富的进度显示,用户体验得到提升 ✓
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/services/image_path_maintenance_service.py` around lines 384 - 455, The refactored `run_image_path_maintenance_background()` function removed the batch-to-batch throttling (asyncio.sleep call that previously occurred between batch processing iterations). To mitigate potential high database I/O load spikes, add back a brief async sleep interval after each batch is processed in the while loop. Insert an `await asyncio.sleep()` call with a short duration (such as 0.01 seconds) after the progress update operations complete and before the next batch iteration begins. This provides a "breathing time" for the system to handle other database operations without causing excessive load.src/maisaka/utils/tool_post_execution.py (1)
56-59: ⚡ Quick win避免在关键依赖缺失时静默跳过反馈入队。
Line [56]-[59] 在
saved_record或chat_stream缺失时直接return,会让“应创建的反馈任务”悄然丢失且缺少排障线索。建议至少记录warning(含tool_call_id与缺失字段)。Based on learnings: “Avoid excessive fallback logic; when errors occur, ensure they are exposed completely and immediately rather than being masked by fallback mechanisms.”♻️ 建议改动
async def _enqueue_memory_feedback_task( @@ - if saved_record is None: - return - if chat_stream is None: - return + if saved_record is None: + logger.warning( + f"{log_prefix} 跳过反馈纠错任务入队: 缺少 saved_record, tool_call_id={invocation.call_id}" + ) + return + if chat_stream is None: + logger.warning( + f"{log_prefix} 跳过反馈纠错任务入队: 缺少 chat_stream, tool_call_id={invocation.call_id}" + ) + return🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/maisaka/utils/tool_post_execution.py` around lines 56 - 59, The early return statements when `saved_record` or `chat_stream` are None in the tool_post_execution function silently skip critical feedback enqueueing without any logging, making it difficult to debug when these dependencies are missing. Instead of returning silently, add warning-level log messages before each return that include diagnostic context such as the `tool_call_id` and the specific field that is missing (either "saved_record" or "chat_stream"). This ensures that missing critical dependencies are properly exposed in logs rather than being masked.Source: Learnings
src/maisaka/reasoning_engine.py (1)
27-28: ⚡ Quick win按项目规范整理这次触达的本地导入块。
新增的
src.maisaka.utils导入应和其他from src.maisaka...跨目录本地导入放在同一组,并避免被同级相对导入.chat_loop_service打断;否则 Ruff/项目导入规范可能继续报错。As per coding guidelines, “本地同级模块使用相对导入,跨目录使用以from src开头的绝对导入”,且本地导入需按项目规范分组。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/maisaka/reasoning_engine.py` around lines 27 - 28, The newly added imports from src.maisaka.utils (handle_tool_post_execution_effects, build_tool_record_payload, and normalize_tool_record_value) are not organized according to project conventions. Reorganize the import block in reasoning_engine.py so that all absolute imports starting with "from src.maisaka..." are grouped together, separate from relative imports like ".chat_loop_service". The two utils imports should be placed with other cross-directory absolute imports from src.maisaka, not intermixed with same-level relative imports, to comply with the project's import grouping standards and avoid Ruff linting errors.Source: Coding guidelines
dashboard/src/lib/theme/storage.ts (1)
136-143: 💤 Low value
parseJSONStorage异常未在调用点外处理时可能导致问题。
parseJSONStorage在 JSON 格式无效时会抛出异常,调用点已用 try/catch 包裹。但函数本身返回undefined表示键不存在,与抛异常表示格式错误的语义不一致。当前实现可行,但建议统一返回
undefined处理所有无效情况以简化调用点。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@dashboard/src/lib/theme/storage.ts` around lines 136 - 143, The parseJSONStorage function has inconsistent error handling: it returns undefined when a key doesn't exist, but throws an exception when JSON.parse fails due to invalid format. Wrap the JSON.parse(value) call in a try/catch block to catch any JSON parsing errors, and return undefined when parsing fails instead of allowing the exception to propagate. This unifies the function's behavior to always return undefined for any invalid case (missing key or invalid JSON format), eliminating the need for callers to handle exceptions from this function.src/common/database/migrations/v27_to_v28.py (1)
135-155: 💤 Low value
_count_tool_records_for_rebuild重复调用。该函数在
migrate_v27_to_v28(line 22) 和_replace_tool_records_with_empty_v28_table(line 140) 中被调用两次,执行相同的 COUNT 查询。考虑将第一次调用的结果传递给
_replace_tool_records_with_empty_v28_table以避免重复查询。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/common/database/migrations/v27_to_v28.py` around lines 135 - 155, The function `_count_tool_records_for_rebuild` is being called redundantly in both `migrate_v27_to_v28` and `_replace_tool_records_with_empty_v28_table`, causing the same COUNT query to execute twice. Refactor by removing the call to `_count_tool_records_for_rebuild` from within `_replace_tool_records_with_empty_v28_table` and instead add it as a parameter to that function. Call `_count_tool_records_for_rebuild` once in `migrate_v27_to_v28`, capture the result, and pass it to `_replace_tool_records_with_empty_v28_table` so the function uses the passed value instead of executing the query again.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@changelogs/changelog.md`:
- Line 9: On line 9 of the changelog.md file, adjust the Chinese wording of the
changelog entry to improve its naturalness and clarity. The current phrasing
needs to be refined to ensure it reads more naturally in Chinese while
maintaining the same meaning about completing configuration item names and
descriptions.
In `@dashboard/src/components/layout/Header.tsx`:
- Around line 154-157: The aria-label for the mobile menu toggle button in
Header.tsx is hardcoded to display "close menu" regardless of the actual menu
state, which creates accessibility confusion. Replace the fixed aria-label value
with a conditional expression that dynamically selects between "open menu" and
"close menu" translation keys based on the mobileMenuOpen boolean state. When
mobileMenuOpen is false, use the open menu translation; when true, use the close
menu translation. This ensures the aria-label accurately describes the action
the button will perform.
- Around line 264-273: The window.open() call in the Button component's onClick
handler is missing security parameters that prevent reverse tabnapping attacks.
Add the 'noopener,noreferrer' parameter as the third argument to the
window.open() call when opening the documentation link
'https://docs.mai-mai.org' in a new tab. This protects against the opened page
from accessing and controlling the current window via window.opener.
In `@dashboard/src/index.css`:
- Line 143: Fix multiple Stylelint violations in dashboard/src/index.css to
unblock the CI pipeline. At lines 143, 153, 165, and 176, correct the
font-family property quotes to use the appropriate quote style expected by
Stylelint. At lines 573, 575, 583, and 593, fix the currentColor keyword
capitalization to match Stylelint requirements. At lines 942 and 1628, add or
remove the required blank line before declarations as specified by Stylelint
rules. These fixes address all static linting errors preventing merge.
In `@dashboard/src/lib/theme/storage.ts`:
- Around line 21-30: The storage key names in THEME_STORAGE_KEYS have been
changed from maibot-theme-overrides, maibot-theme-custom-css, and
maibot-theme-background to maibot-theme-style-overrides,
maibot-theme-style-custom-css, and maibot-theme-style-background, but existing
user data stored under the old keys will be lost. Add a one-time migration
function in loadThemeConfig that checks for the presence of old storage keys,
retrieves and converts their values to match the new key structure, stores them
under the new keys, and then removes the old keys from localStorage to prevent
data loss during upgrades.
In `@dashboard/src/routes/config/model/components/TaskConfigCard.tsx`:
- Around line 175-180: The TooltipTrigger component in TaskConfigCard.tsx is
wrapping a non-focusable StreamlineIcon element, which prevents keyboard users
from accessing the tooltip. Replace the icon as the direct child of
TooltipTrigger with a button element that wraps the StreamlineIcon, and add an
appropriate aria-label attribute to the button to provide an accessible label
for screen readers. This ensures keyboard users can focus on and interact with
the tooltip trigger.
In `@dashboard/src/routes/plugin-config-embed.tsx`:
- Around line 5-9: The PluginConfigPage component does not support the embedded
parameter, causing navigation inconsistency in embedded mode. First, modify the
PluginConfigEmbedPage function to pass the embedded prop to PluginConfigPage
(similar to how PluginDetailPage receives it). Then, update the PluginConfigPage
component signature to accept an embedded parameter with a default value of
false. Finally, update the closePluginConfig callback function to use this
embedded parameter to determine the correct return route: return to
/plugin-config/embed when embedded is true, otherwise return to /plugin-config.
In `@dashboard/src/routes/plugins/PluginMarketplacePage.tsx`:
- Around line 103-129: The readPluginMarketplaceViewState function directly
calls JSON.parse on the savedState without error handling, which will throw an
exception if sessionStorage contains corrupted or invalid JSON data, causing
page initialization to fail. Wrap the JSON.parse call and the subsequent parsing
logic in a try/catch block, ensuring that if any parsing error occurs, the
function returns DEFAULT_PLUGIN_MARKET_VIEW_STATE as a safe fallback instead of
crashing the application.
In `@dashboard/src/routes/settings/AppearanceTab.tsx`:
- Around line 459-499: The theme mode selector currently uses tablist and tab
ARIA roles, but this component implements a single-selection radio button
pattern without tabpanel children or keyboard navigation, which is semantically
incorrect. Change the outer container's role from "tablist" to "radiogroup" and
the button elements' role from "tab" to "radio". Additionally, replace the
aria-selected attribute with aria-checked on each button to correctly represent
the radio button state.
In `@src/config/config.py`:
- Around line 641-645: The call to mark_legacy_config_migration_completed() at
line 642 performs a database write operation that is not wrapped in exception
handling. If the database becomes temporarily unavailable, this will cause the
entire configuration loading to fail even though the configuration itself is
already available. Wrap the mark_legacy_config_migration_completed() call in a
try-except block to isolate any database-related exceptions, log the error for
debugging purposes, but allow the configuration loading to continue regardless
of whether the migration completion marking succeeds or fails.
In `@src/config/official_configs.py`:
- Around line 4011-4023: The docstring for the record_tool_structured_content
field only mentions the database size impact but fails to highlight privacy
risks. Update the docstring to include a warning that the structured content
saved may contain sensitive information such as user data, external API
responses, or debug information. The privacy warning should be prominently
featured in addition to or prioritized over the existing note about database
volume to ensure users understand the security and privacy implications when
enabling this setting.
- Around line 4147-4159: When the api_server_host is configured to listen
externally (0.0.0.0), the system must enforce that api_server_allowed_api_keys
is not empty to prevent exposing unauthenticated APIs. Add validation in the
model_post_init method to check that if api_server_host is "0.0.0.0", the
api_server_allowed_api_keys list must be non-empty; if validation fails, raise
an error. Alternatively, change the default value of api_server_host from
"0.0.0.0" to "127.0.0.1" to restrict to localhost by default. This validation
applies at the location around line 4147 and also at the related configuration
site around line 4217-4229.
- Around line 5284-5294: The render field in the config currently uses
default_factory=PluginRuntimeRenderConfig, which enables browser rendering by
default with unsandboxed parameters (--no-sandbox/--disable-setuid-sandbox).
This creates a security risk when rendering untrusted content. Change the render
field to not enable the rendering capability by default—either remove the
default_factory parameter to make render optional/null by default, or modify
PluginRuntimeRenderConfig's default initialization to exclude the no-sandbox
parameters and require users to explicitly enable them when needed in container
environments.
In `@src/maisaka/reasoning_engine.py`:
- Around line 539-546: The direct await calls to
handle_tool_post_execution_effects in the tool execution flow are not protected
against exceptions, meaning that failures in memory feedback queuing or other
post-execution effects will interrupt the main tool chain. Create a safe wrapper
function (or use try-except blocks) around each invocation of
handle_tool_post_execution_effects that catches any exceptions, logs them for
debugging, and allows the main execution flow to continue uninterrupted. This
wrapper should not re-raise exceptions. Apply this safe wrapping to all four
affected locations: in src/maisaka/reasoning_engine.py around lines 539-546 (in
the first await handle_tool_post_execution_effects call), lines 556-563 (in the
second call), lines 2272-2279 (in the third call), and lines 2306-2317 (in the
fourth call).
In `@src/maisaka/utils/tool_record_payload.py`:
- Around line 12-38: The normalize_tool_record_value function lacks circular
reference protection, causing RecursionError when processing self-referencing
objects from external tool return values. Add a visited set parameter to track
processed objects by their id, pass this set through all recursive calls, and
check if an object's id is already in the visited set before processing dict,
list, tuple, set, model_dump(), or __dict__ recursively. When a circular
reference is detected, return a placeholder string like "<circular_reference>"
instead of attempting further recursion.
- Around line 137-148: The payload construction in the tool record payload
builder currently only applies large media omission (via
build_tool_record_structured_content) to the structured_content field, but the
arguments and metadata fields can also contain large inline media like
image_base64, audio_base64, or data that should be omitted. Modify the payload
dictionary to apply large media omission to both the arguments field
(invocation.arguments) and the metadata field (result.metadata) using the same
omission strategy applied to structured_content, ensuring that large binary data
is consistently excluded from the database write across all relevant fields.
- Around line 122-127: The code unconditionally calls
normalize_tool_record_value on the input value before checking if
record_tool_structured_content is disabled, causing unnecessary memory and CPU
consumption for large structures even when the feature is off. Reorder the logic
to check global_config.debug.record_tool_structured_content first: if disabled,
immediately call _build_omitted_structured_content_marker without any
normalization; only call normalize_tool_record_value when the feature is
actually enabled, then pass the normalized value to either
_build_omitted_structured_content_marker (if now disabled) or
_omit_tool_record_large_media (if enabled).
---
Outside diff comments:
In `@dashboard/src/routes/auth.tsx`:
- Around line 268-274: The theme toggle button in the auth route has a title
attribute for accessibility but is missing an aria-label attribute, which is
needed for screen readers to reliably announce the button's purpose. Add an
aria-label attribute to the button element with the same conditional logic as
the title attribute, using the translation keys t('auth.switchToLight') or
t('auth.switchToDark') based on the current theme, to ensure both visual
tooltips and screen reader users have proper access to the button's function.
In `@dashboard/src/routes/config/model/components/ModelTable.tsx`:
- Around line 90-98: The code uses object reference equality (m === model) to
find actualIndex in ModelTable.tsx, which fails when models are recreated during
pagination or filtering, resulting in actualIndex becoming -1. This causes
subsequent edit, delete, and selection operations at lines 141-154 to target
wrong indices. Replace the object reference comparison with a unique identifier
comparison (such as model.id or model.name) in the findIndex call. Apply the
same fix to ModelCardList.tsx at line 46 where the same pattern exists.
In `@dashboard/src/routes/config/model/components/Pagination.tsx`:
- Around line 85-96: The pagination buttons at lines 85-96 (first page button
calling onPageChange(1)) and lines 136-147 (last page button) lack accessible
labels, preventing screen reader users from understanding their purpose. Add an
aria-label attribute to both Button components: use "Go to first page" for the
button at lines 85-96 and "Go to last page" for the button at lines 136-147.
This will ensure assistive technology users can identify the pagination controls
and their semantics.
In `@dashboard/src/routes/config/modelProvider/ProviderList.tsx`:
- Around line 289-301: The test connection button (the one with onClick={() =>
onTest(provider.name)} that renders a Loader2 or Zap icon) is missing an
aria-label attribute. Add an aria-label attribute to the Button component with
an appropriate accessible label (e.g., "测试连接") to provide screen reader support,
following the same pattern as the edit/delete buttons in this component.
In `@dashboard/src/routes/resource/behavior/index.tsx`:
- Around line 1620-1650: The handlePointerUp callback cleans up the pointer
state and resets node.fixed to false, but this cleanup only executes on the
onPointerUp event. When a pointercancel event occurs or pointer capture is lost,
the cleanup logic does not run, leaving nodes stuck in a fixed state. Create a
shared cleanup function that resets the pointerRef and unfixes the node, then
call this function from both handlePointerUp and a new handlePointerCancel
callback. Add the onPointerCancel handler to the canvas element alongside the
existing pointer event handlers to ensure cleanup occurs in all pointer
termination scenarios.
In `@dashboard/src/routes/settings/AppearanceTab.tsx`:
- Around line 1080-1082: The inheritance state check in the isInheritedLayer
condition is too restrictive, only covering sidebar and header layers. Since
useBackground returns the page background for any non-page layer when inherit is
true, the condition should be expanded to cover all non-page layers including
card and dialog. Replace the explicit layer name checks (layerId === 'sidebar'
|| layerId === 'header') with a more general check that covers any layer that is
not 'page', so that all layers with inherit enabled are properly marked as
inherited and prevented from being edited.
- Around line 256-272: The handleCSSChange function calls sanitizeCSS(val) to
obtain sanitized CSS content but stores the original unsanitized val in the
styleCustomCSS configuration instead of using the sanitized result.css from the
sanitizeCSS return value. Update the updateThemeConfig call to store result.css
in the [dashboardStyle] key instead of val. This ensures that only sanitized CSS
is persisted in the configuration, improving security and clarity at the storage
layer rather than relying on subsequent re-sanitization during theme
application.
In `@src/services/image_path_maintenance_service.py`:
- Around line 10-24: The import statement order in the file does not follow the
project's import specification. Currently, the standard library import (asyncio)
is placed after third-party library imports (rich and sqlalchemy), but according
to the project coding standards, standard library imports must come before
third-party library imports. Reorganize the imports by moving the asyncio import
to the beginning of the import section, before the rich and sqlalchemy imports,
and ensure there is one blank line separating the standard library imports from
the third-party library imports.
---
Nitpick comments:
In `@AGENTS.md`:
- Line 50: The sentence on line 50 in AGENTS.md describing the legacy_migration
rule is incomplete and cuts off mid-thought. Complete the sentence by adding the
missing second half after "此文件以固定" to make it grammatically complete and convey
the full intended meaning, such as by appending descriptive text like "形式存在" or
another appropriate completion that clarifies the constraint on the
legacy_migration file.
In `@dashboard/src/lib/theme/storage.ts`:
- Around line 136-143: The parseJSONStorage function has inconsistent error
handling: it returns undefined when a key doesn't exist, but throws an exception
when JSON.parse fails due to invalid format. Wrap the JSON.parse(value) call in
a try/catch block to catch any JSON parsing errors, and return undefined when
parsing fails instead of allowing the exception to propagate. This unifies the
function's behavior to always return undefined for any invalid case (missing key
or invalid JSON format), eliminating the need for callers to handle exceptions
from this function.
In `@src/common/database/migrations/v27_to_v28.py`:
- Around line 135-155: The function `_count_tool_records_for_rebuild` is being
called redundantly in both `migrate_v27_to_v28` and
`_replace_tool_records_with_empty_v28_table`, causing the same COUNT query to
execute twice. Refactor by removing the call to
`_count_tool_records_for_rebuild` from within
`_replace_tool_records_with_empty_v28_table` and instead add it as a parameter
to that function. Call `_count_tool_records_for_rebuild` once in
`migrate_v27_to_v28`, capture the result, and pass it to
`_replace_tool_records_with_empty_v28_table` so the function uses the passed
value instead of executing the query again.
In `@src/common/version.py`:
- Line 8: The PROJECT_ROOT variable calculation in src/common/version.py relies
on a hardcoded parent directory depth of 2, which assumes the current file
(version.py) remains at src/common/. Add a comment above the PROJECT_ROOT
assignment explaining this directory structure dependency and noting that the
parents[2] index must be adjusted if the file location changes in the future.
In `@src/maisaka/reasoning_engine.py`:
- Around line 27-28: The newly added imports from src.maisaka.utils
(handle_tool_post_execution_effects, build_tool_record_payload, and
normalize_tool_record_value) are not organized according to project conventions.
Reorganize the import block in reasoning_engine.py so that all absolute imports
starting with "from src.maisaka..." are grouped together, separate from relative
imports like ".chat_loop_service". The two utils imports should be placed with
other cross-directory absolute imports from src.maisaka, not intermixed with
same-level relative imports, to comply with the project's import grouping
standards and avoid Ruff linting errors.
In `@src/maisaka/utils/tool_post_execution.py`:
- Around line 56-59: The early return statements when `saved_record` or
`chat_stream` are None in the tool_post_execution function silently skip
critical feedback enqueueing without any logging, making it difficult to debug
when these dependencies are missing. Instead of returning silently, add
warning-level log messages before each return that include diagnostic context
such as the `tool_call_id` and the specific field that is missing (either
"saved_record" or "chat_stream"). This ensures that missing critical
dependencies are properly exposed in logs rather than being masked.
In `@src/services/image_path_maintenance_service.py`:
- Around line 384-455: The refactored `run_image_path_maintenance_background()`
function removed the batch-to-batch throttling (asyncio.sleep call that
previously occurred between batch processing iterations). To mitigate potential
high database I/O load spikes, add back a brief async sleep interval after each
batch is processed in the while loop. Insert an `await asyncio.sleep()` call
with a short duration (such as 0.01 seconds) after the progress update
operations complete and before the next batch iteration begins. This provides a
"breathing time" for the system to handle other database operations without
causing excessive load.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 376c8a3f-da8f-461c-8679-d32a78954572
⛔ Files ignored due to path filters (2)
dashboard/package-lock.jsonis excluded by!**/package-lock.jsonuv.lockis excluded by!**/*.lock
📒 Files selected for processing (109)
AGENTS.mdchangelogs/changelog.mddashboard/app-version.tsdashboard/electron.vite.config.tsdashboard/package.jsondashboard/src/components/animation-provider.tsxdashboard/src/components/chat-scope-filter-panel.tsxdashboard/src/components/dynamic-form/DynamicConfigForm.tsxdashboard/src/components/dynamic-form/DynamicField.tsxdashboard/src/components/embed-page-shell.tsxdashboard/src/components/layout/Header.tsxdashboard/src/components/layout/Layout.tsxdashboard/src/components/layout/LogoArea.tsxdashboard/src/components/layout/NavItem.tsxdashboard/src/components/layout/Sidebar.tsxdashboard/src/components/layout/constants.tsdashboard/src/components/layout/types.tsdashboard/src/components/memory/MemoryEpisodeManager.tsxdashboard/src/components/memory/MemoryMaintenanceManager.tsxdashboard/src/components/memory/MemoryMiniTabs.tsxdashboard/src/components/memory/MemoryProfileManager.tsxdashboard/src/components/search-dialog.tsxdashboard/src/components/theme-provider.tsxdashboard/src/components/ui/accent-panel.tsxdashboard/src/components/ui/alert.tsxdashboard/src/components/ui/card.tsxdashboard/src/components/ui/checkbox.tsxdashboard/src/components/ui/radio-group.tsxdashboard/src/components/ui/slider.tsxdashboard/src/components/ui/streamline-icon.tsxdashboard/src/components/ui/streamline-menu-icon.tsxdashboard/src/components/ui/table.tsxdashboard/src/components/ui/toast.tsxdashboard/src/components/ui/tooltip.tsxdashboard/src/components/waves-background.tsxdashboard/src/hooks/use-background.tsdashboard/src/i18n/locales/en.jsondashboard/src/i18n/locales/ja.jsondashboard/src/i18n/locales/ko.jsondashboard/src/i18n/locales/zh.jsondashboard/src/index.cssdashboard/src/lib/animation-context.tsdashboard/src/lib/settings-manager.tsdashboard/src/lib/theme-context.tsdashboard/src/lib/theme/palette.tsdashboard/src/lib/theme/pipeline.tsdashboard/src/lib/theme/storage.tsdashboard/src/lib/theme/tokens.tsdashboard/src/lib/version.tsdashboard/src/router.tsxdashboard/src/routes/auth.tsxdashboard/src/routes/config/bot.tsxdashboard/src/routes/config/bot/hooks/ListItemEditorHookFactory.tsxdashboard/src/routes/config/bot/hooks/complexFieldHooks.tsxdashboard/src/routes/config/bot/hooks/index.tsdashboard/src/routes/config/bot/types.tsdashboard/src/routes/config/model/components/ModelCardList.tsxdashboard/src/routes/config/model/components/ModelTable.tsxdashboard/src/routes/config/model/components/Pagination.tsxdashboard/src/routes/config/model/components/TaskConfigCard.tsxdashboard/src/routes/config/modelProvider/ProviderCard.tsxdashboard/src/routes/config/modelProvider/ProviderList.tsxdashboard/src/routes/config/prompts.tsxdashboard/src/routes/index.tsxdashboard/src/routes/plugin-config-embed.tsxdashboard/src/routes/plugin-detail-embed.tsxdashboard/src/routes/plugin-detail.tsxdashboard/src/routes/plugin-mirrors-embed.tsxdashboard/src/routes/plugin-mirrors.tsxdashboard/src/routes/plugins/MarketplaceTab.tsxdashboard/src/routes/plugins/PluginCard.tsxdashboard/src/routes/plugins/PluginMarketplacePage.tsxdashboard/src/routes/plugins/embed.tsxdashboard/src/routes/resource/behavior/index.tsxdashboard/src/routes/resource/expression/ExpressionList.tsxdashboard/src/routes/resource/expression/index.tsxdashboard/src/routes/resource/jargon/JargonList.tsxdashboard/src/routes/resource/jargon/index.tsxdashboard/src/routes/resource/knowledge-base.tsxdashboard/src/routes/settings/AboutTab.tsxdashboard/src/routes/settings/AppearanceTab.tsxdashboard/src/routes/settings/index.tsxdashboard/vite.config.tsdashboard/vitest.config.tspyproject.tomlrequirements.txtsrc/A_memorix/CONFIG_REFERENCE.mdsrc/A_memorix/QUICK_START.mdsrc/A_memorix/config_schema.jsonsrc/A_memorix/core/utils/person_profile_service.pysrc/common/database/migrations/v27_to_v28.pysrc/common/database/tool_record_payload_cleanup.pysrc/common/utils/utils_config.pysrc/common/version.pysrc/config/config.pysrc/config/legacy_migration.pysrc/config/model_configs.pysrc/config/official_configs.pysrc/learners/behavior_selector.pysrc/llm_models/utils_model.pysrc/maisaka/builtin_tool/query_memory.pysrc/maisaka/reasoning_engine.pysrc/maisaka/utils/__init__.pysrc/maisaka/utils/tool_post_execution.pysrc/maisaka/utils/tool_record_payload.pysrc/plugin_runtime/__init__.pysrc/services/image_path_maintenance_service.pysrc/services/statistics_service.pysrc/webui/routers/expression.py
💤 Files with no reviewable changes (6)
- dashboard/src/lib/animation-context.ts
- dashboard/src/components/waves-background.tsx
- dashboard/src/components/theme-provider.tsx
- dashboard/src/components/animation-provider.tsx
- src/common/database/tool_record_payload_cleanup.py
- dashboard/src/lib/settings-manager.ts
|
|
||
| ## WebUI | ||
|
|
||
| - 补全部分配置项的名称和说明 |
There was a problem hiding this comment.
修正文案“补全部分”为“补全部分”以避免语义不顺。
Line 9 当前表达不够自然,建议改为“补全部分配置项的名称和说明”。
✏️ 建议修改
-- 补全部分配置项的名称和说明
+- 补全部分配置项的名称和说明🧰 Tools
🪛 LanguageTool
[uncategorized] ~9-~9: 您的意思是“"不"全”?
Context: ...d)。 # [1.0.5] - 2026-6-16 ## WebUI - 补全部分配置项的名称和说明 - 移除启动页特效以适配低性能设备 - 优化多个页面的...
(BU)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@changelogs/changelog.md` at line 9, On line 9 of the changelog.md file,
adjust the Chinese wording of the changelog entry to improve its naturalness and
clarity. The current phrasing needs to be refined to ensure it reads more
naturally in Chinese while maintaining the same meaning about completing
configuration item names and descriptions.
Source: Linters/SAST tools
| <button | ||
| onClick={onMobileMenuToggle} | ||
| aria-label={t('a11y.closeMenu')} | ||
| aria-expanded={mobileMenuOpen} |
There was a problem hiding this comment.
移动端菜单按钮的 aria-label 与实际状态不一致。
Line 156 当前固定为“关闭菜单”,在菜单未展开时语义错误,建议按 mobileMenuOpen 动态切换“打开/关闭”。
♿ 建议修改
- aria-label={t('a11y.closeMenu')}
+ aria-label={mobileMenuOpen ? t('a11y.closeMenu') : t('a11y.openMenu')}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| onClick={onMobileMenuToggle} | |
| aria-label={t('a11y.closeMenu')} | |
| aria-expanded={mobileMenuOpen} | |
| <button | |
| onClick={onMobileMenuToggle} | |
| aria-label={mobileMenuOpen ? t('a11y.closeMenu') : t('a11y.openMenu')} | |
| aria-expanded={mobileMenuOpen} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/components/layout/Header.tsx` around lines 154 - 157, The
aria-label for the mobile menu toggle button in Header.tsx is hardcoded to
display "close menu" regardless of the actual menu state, which creates
accessibility confusion. Replace the fixed aria-label value with a conditional
expression that dynamically selects between "open menu" and "close menu"
translation keys based on the mobileMenuOpen boolean state. When mobileMenuOpen
is false, use the open menu translation; when true, use the close menu
translation. This ensures the aria-label accurately describes the action the
button will perform.
| <Button | ||
| variant="ghost" | ||
| size="icon" | ||
| onClick={() => window.open('https://docs.mai-mai.org', '_blank')} | ||
| className="hidden sm:inline-flex" | ||
| title={t('header.viewDocs')} | ||
| aria-label={t('header.viewDocs')} | ||
| > | ||
| <BookOpen className="h-4 w-4" /> | ||
| </Button> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 只读检查:定位项目中所有 target=_blank 或 window.open 用法,确认是否统一添加防护
rg -nP "window\.open\s*\(" dashboard/src
rg -nP "target\s*=\s*['\"]_blank['\"]" dashboard/srcRepository: Mai-with-u/MaiBot
Length of output: 1895
window.open() 缺少安全参数,存在反向标签劫持风险。
第 267 行直接调用 window.open('https://docs.mai-mai.org', '_blank') 会暴露 window.opener,允许外部文档反向控制当前页面。需要添加 'noopener,noreferrer' 参数以防止此风险。
修改建议
- onClick={() => window.open('https://docs.mai-mai.org', '_blank')}
+ onClick={() => window.open('https://docs.mai-mai.org', '_blank', 'noopener,noreferrer')}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => window.open('https://docs.mai-mai.org', '_blank')} | |
| className="hidden sm:inline-flex" | |
| title={t('header.viewDocs')} | |
| aria-label={t('header.viewDocs')} | |
| > | |
| <BookOpen className="h-4 w-4" /> | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="icon" | |
| onClick={() => window.open('https://docs.mai-mai.org', '_blank', 'noopener,noreferrer')} | |
| className="hidden sm:inline-flex" | |
| title={t('header.viewDocs')} | |
| aria-label={t('header.viewDocs')} | |
| > | |
| <BookOpen className="h-4 w-4" /> | |
| </Button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/components/layout/Header.tsx` around lines 264 - 273, The
window.open() call in the Button component's onClick handler is missing security
parameters that prevent reverse tabnapping attacks. Add the
'noopener,noreferrer' parameter as the third argument to the window.open() call
when opening the documentation link 'https://docs.mai-mai.org' in a new tab.
This protects against the opened page from accessing and controlling the current
window via window.opener.
| } | ||
|
|
||
| @font-face { | ||
| font-family: 'MaiRetroText'; |
There was a problem hiding this comment.
修复 Stylelint 阻塞项,避免 CI 失败。
当前有明确的静态检查错误:Line 143/153/165/176 的 font-family 名称引号、Line 573/575/583/593 的 currentColor 大小写、以及 Line 942 与 Line 1628 的声明前空行。建议在本 PR 内一并修复,否则会持续阻塞合并流水线。
Also applies to: 153-153, 165-165, 176-176, 573-573, 575-575, 583-583, 593-593, 942-942, 1628-1628
🧰 Tools
🪛 Stylelint (17.13.0)
[error] 143-143: Expected no quotes around "MaiRetroText" (font-family-name-quotes)
(font-family-name-quotes)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/index.css` at line 143, Fix multiple Stylelint violations in
dashboard/src/index.css to unblock the CI pipeline. At lines 143, 153, 165, and
176, correct the font-family property quotes to use the appropriate quote style
expected by Stylelint. At lines 573, 575, 583, and 593, fix the currentColor
keyword capitalization to match Stylelint requirements. At lines 942 and 1628,
add or remove the required blank line before declarations as specified by
Stylelint rules. These fixes address all static linting errors preventing merge.
Source: Linters/SAST tools
| export const THEME_STORAGE_KEYS = { | ||
| MODE: 'maibot-theme-mode', | ||
| PRESET: 'maibot-theme-preset', | ||
| ACCENT: 'maibot-theme-accent', | ||
| OVERRIDES: 'maibot-theme-overrides', | ||
| CUSTOM_CSS: 'maibot-theme-custom-css', | ||
| BACKGROUND_CONFIG: 'maibot-theme-background', | ||
| STYLE_OVERRIDES: 'maibot-theme-style-overrides', | ||
| STYLE_CUSTOM_CSS: 'maibot-theme-style-custom-css', | ||
| STYLE_BACKGROUND_CONFIG: 'maibot-theme-style-background', | ||
| DASHBOARD_STYLE: 'maibot-theme-dashboard-style', | ||
| STYLE_CONFIG: 'maibot-theme-style-config', | ||
| } as const |
There was a problem hiding this comment.
现有用户主题配置将丢失:缺少 localStorage 迁移逻辑。
存储键从 maibot-theme-overrides/maibot-theme-custom-css/maibot-theme-background 更名为 maibot-theme-style-*,但未提供从旧键到新键的迁移逻辑。已有自定义主题的用户升级后将丢失所有配置。
建议在 loadThemeConfig 中添加一次性迁移:检测旧键存在时读取并转换为新结构,然后删除旧键。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dashboard/src/lib/theme/storage.ts` around lines 21 - 30, The storage key
names in THEME_STORAGE_KEYS have been changed from maibot-theme-overrides,
maibot-theme-custom-css, and maibot-theme-background to
maibot-theme-style-overrides, maibot-theme-style-custom-css, and
maibot-theme-style-background, but existing user data stored under the old keys
will be lost. Add a one-time migration function in loadThemeConfig that checks
for the presence of old storage keys, retrieves and converts their values to
match the new key structure, stores them under the new keys, and then removes
the old keys from localStorage to prevent data loss during upgrades.
| render: PluginRuntimeRenderConfig = Field( | ||
| default_factory=PluginRuntimeRenderConfig, | ||
| json_schema_extra={ | ||
| "label": { | ||
| "zh_CN": "浏览器渲染", | ||
| "en_US": "Browser rendering", | ||
| "ja_JP": "ブラウザ描画", | ||
| }, | ||
| }, | ||
| ) | ||
| """插件需要网页截图或渲染时使用的浏览器配置。""" |
There was a problem hiding this comment.
避免默认启用无沙箱浏览器渲染。
新增 render 配置后会默认启用 PluginRuntimeRenderConfig,而其默认启动参数包含 --no-sandbox/--disable-setuid-sandbox。如果插件渲染不可信页面或插件内容,默认无沙箱会放大浏览器漏洞或恶意页面的影响面;建议默认关闭渲染能力,或移除无沙箱参数并要求需要兼容容器环境的用户显式配置。
建议修复
class PluginRuntimeRenderConfig(ConfigBase):
enabled: bool = Field(
- default=True,
+ default=False,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/config/official_configs.py` around lines 5284 - 5294, The render field in
the config currently uses default_factory=PluginRuntimeRenderConfig, which
enables browser rendering by default with unsandboxed parameters
(--no-sandbox/--disable-setuid-sandbox). This creates a security risk when
rendering untrusted content. Change the render field to not enable the rendering
capability by default—either remove the default_factory parameter to make render
optional/null by default, or modify PluginRuntimeRenderConfig's default
initialization to exclude the no-sandbox parameters and require users to
explicitly enable them when needed in container environments.
| saved_record = await self._store_tool_execution_record(invocation, result, None) | ||
| await handle_tool_post_execution_effects( | ||
| invocation=invocation, | ||
| result=result, | ||
| saved_record=saved_record, | ||
| chat_stream=self._runtime.chat_stream, | ||
| log_prefix=self._runtime.log_prefix, | ||
| ) |
There was a problem hiding this comment.
隔离工具后置副作用失败,避免中断主工具链路。
这些新增调用直接 await handle_tool_post_execution_effects(...);如果记忆反馈入队或其他后置效果异常,当前工具已经执行/可能已落库,但历史写回、监控结果和后续工具都会被异常打断。建议用一个安全包装只记录异常,不让非关键后置副作用影响主流程。Based on relevant context, handle_tool_post_execution_effects 会根据 result.metadata 触发记忆反馈任务入队。
建议修复
+ async def _handle_tool_post_execution_effects_safely(
+ self,
+ *,
+ invocation: ToolInvocation,
+ result: ToolExecutionResult,
+ saved_record: Optional[dict[str, Any]],
+ ) -> None:
+ try:
+ await handle_tool_post_execution_effects(
+ invocation=invocation,
+ result=result,
+ saved_record=saved_record,
+ chat_stream=self._runtime.chat_stream,
+ log_prefix=self._runtime.log_prefix,
+ )
+ except Exception:
+ logger.exception(
+ f"{self._runtime.log_prefix} 处理工具后置副作用失败: "
+ f"工具={invocation.tool_name} 调用编号={invocation.call_id}"
+ )然后将各处直接调用替换为:
- await handle_tool_post_execution_effects(
- invocation=invocation,
- result=result,
- saved_record=saved_record,
- chat_stream=self._runtime.chat_stream,
- log_prefix=self._runtime.log_prefix,
- )
+ await self._handle_tool_post_execution_effects_safely(
+ invocation=invocation,
+ result=result,
+ saved_record=saved_record,
+ )Also applies to: 556-563, 2272-2279, 2306-2317
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/maisaka/reasoning_engine.py` around lines 539 - 546, The direct await
calls to handle_tool_post_execution_effects in the tool execution flow are not
protected against exceptions, meaning that failures in memory feedback queuing
or other post-execution effects will interrupt the main tool chain. Create a
safe wrapper function (or use try-except blocks) around each invocation of
handle_tool_post_execution_effects that catches any exceptions, logs them for
debugging, and allows the main execution flow to continue uninterrupted. This
wrapper should not re-raise exceptions. Apply this safe wrapping to all four
affected locations: in src/maisaka/reasoning_engine.py around lines 539-546 (in
the first await handle_tool_post_execution_effects call), lines 556-563 (in the
second call), lines 2272-2279 (in the third call), and lines 2306-2317 (in the
fourth call).
| def normalize_tool_record_value(value: Any) -> Any: | ||
| """将工具记录值规范化为可 JSON 序列化的结构。""" | ||
|
|
||
| if value is None or isinstance(value, (str, int, float, bool)): | ||
| return value | ||
| if isinstance(value, datetime): | ||
| return value.isoformat() | ||
| if isinstance(value, dict): | ||
| normalized_dict: dict[str, Any] = {} | ||
| for key, item in value.items(): | ||
| normalized_dict[str(key)] = normalize_tool_record_value(item) | ||
| return normalized_dict | ||
| if isinstance(value, (list, tuple, set)): | ||
| return [normalize_tool_record_value(item) for item in value] | ||
| if isinstance(value, bytes): | ||
| return f"<bytes:{len(value)}>" | ||
| if hasattr(value, "model_dump"): | ||
| try: | ||
| return normalize_tool_record_value(value.model_dump()) | ||
| except Exception: | ||
| return str(value) | ||
| if hasattr(value, "__dict__"): | ||
| try: | ||
| return normalize_tool_record_value(dict(value.__dict__)) | ||
| except Exception: | ||
| return str(value) | ||
| return str(value) |
There was a problem hiding this comment.
给递归规范化增加循环引用保护。
工具返回值/metadata 是外部扩展可控结构;当前递归处理 dict/list/set/__dict__ 时没有 visited 集合,遇到自引用对象会 RecursionError,中断工具记录落库流程。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/maisaka/utils/tool_record_payload.py` around lines 12 - 38, The
normalize_tool_record_value function lacks circular reference protection,
causing RecursionError when processing self-referencing objects from external
tool return values. Add a visited set parameter to track processed objects by
their id, pass this set through all recursive calls, and check if an object's id
is already in the visited set before processing dict, list, tuple, set,
model_dump(), or __dict__ recursively. When a circular reference is detected,
return a placeholder string like "<circular_reference>" instead of attempting
further recursion.
| if value is None: | ||
| return None | ||
| normalized_value = normalize_tool_record_value(value) | ||
| if not global_config.debug.record_tool_structured_content: | ||
| return _build_omitted_structured_content_marker(normalized_value) | ||
| return _omit_tool_record_large_media(normalized_value) |
There was a problem hiding this comment.
关闭 structured_content 记录时不要先完整规范化。
record_tool_structured_content=false 本应避免大结构落库成本,但这里仍先递归规范化整个 value,大 base64/深层对象仍会消耗内存和 CPU,循环结构也会先崩溃。先判断开关,再构造轻量 marker。
🐛 建议先按配置短路
if value is None:
return None
- normalized_value = normalize_tool_record_value(value)
if not global_config.debug.record_tool_structured_content:
- return _build_omitted_structured_content_marker(normalized_value)
+ return _build_omitted_structured_content_marker(value)
+ normalized_value = normalize_tool_record_value(value)
return _omit_tool_record_large_media(normalized_value)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if value is None: | |
| return None | |
| normalized_value = normalize_tool_record_value(value) | |
| if not global_config.debug.record_tool_structured_content: | |
| return _build_omitted_structured_content_marker(normalized_value) | |
| return _omit_tool_record_large_media(normalized_value) | |
| if value is None: | |
| return None | |
| if not global_config.debug.record_tool_structured_content: | |
| return _build_omitted_structured_content_marker(value) | |
| normalized_value = normalize_tool_record_value(value) | |
| return _omit_tool_record_large_media(normalized_value) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/maisaka/utils/tool_record_payload.py` around lines 122 - 127, The code
unconditionally calls normalize_tool_record_value on the input value before
checking if record_tool_structured_content is disabled, causing unnecessary
memory and CPU consumption for large structures even when the feature is off.
Reorder the logic to check global_config.debug.record_tool_structured_content
first: if disabled, immediately call _build_omitted_structured_content_marker
without any normalization; only call normalize_tool_record_value when the
feature is actually enabled, then pass the normalized value to either
_build_omitted_structured_content_marker (if now disabled) or
_omit_tool_record_large_media (if enabled).
| payload: dict[str, Any] = { | ||
| "call_id": invocation.call_id, | ||
| "session_id": invocation.session_id, | ||
| "stream_id": invocation.stream_id, | ||
| "arguments": normalize_tool_record_value(invocation.arguments), | ||
| "success": result.success, | ||
| "content": result.content, | ||
| "error_message": result.error_message, | ||
| "history_content": result.get_history_content(), | ||
| "structured_content": build_tool_record_structured_content(result.structured_content), | ||
| "metadata": normalize_tool_record_value(result.metadata), | ||
| } |
There was a problem hiding this comment.
arguments 和 metadata 也需要省略内联大媒体。
当前只有 structured_content 经过 _omit_tool_record_large_media;如果工具参数或 metadata 携带 image_base64/audio_base64/data,仍会完整写入数据库,绕过本模块的大字段省略策略。
🐛 建议统一套用大媒体省略
- "arguments": normalize_tool_record_value(invocation.arguments),
+ "arguments": _omit_tool_record_large_media(normalize_tool_record_value(invocation.arguments)),
@@
- "metadata": normalize_tool_record_value(result.metadata),
+ "metadata": _omit_tool_record_large_media(normalize_tool_record_value(result.metadata)),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/maisaka/utils/tool_record_payload.py` around lines 137 - 148, The payload
construction in the tool record payload builder currently only applies large
media omission (via build_tool_record_structured_content) to the
structured_content field, but the arguments and metadata fields can also contain
large inline media like image_base64, audio_base64, or data that should be
omitted. Modify the payload dictionary to apply large media omission to both the
arguments field (invocation.arguments) and the metadata field (result.metadata)
using the same omission strategy applied to structured_content, ensuring that
large binary data is consistently excluded from the database write across all
relevant fields.
zh-CN目标翻译作为常规 GitHub 编辑面;常规翻译以 Crowdin ->l10n_*PR 回流为准,详见docs/i18n.md请填写以下内容
(删除掉中括号内的空格,并替换为小写的x)
main分支 禁止修改,请确认本次提交的分支 不是main分支src/A_memorix,我确认已阅读src/A_memorix/MODIFICATION_POLICY.md,不涉及则无需勾选其他信息
Summary by CodeRabbit
版本 1.0.5 发布说明
新功能
优化
样式