feat(btw): move the dual-loop config into AI capabilities - #199
Conversation
The BTW dual-loop group is not a plugin setting, yet its WebUI metadata lived under plugin_group.metadata.btw, so the plugin page showed it and the AI capabilities tab never did. Host the group on ai_group.metadata.btw and list it with the other capabilities, keeping the root-level btw config shape and the absolute btw.* item paths unchanged. The plugin group keeps only its selector. Label btw.work_loop.computer_use_runtime so the inherit option no longer renders as raw text. Move the bilingual config-metadata entries from plugin_group.btw to ai_group.btw and update the docs navigation paths. Fixes #183 AI-Generated: true Generated-At: 2026-09-15T09:14:29Z
BegoniaHe
left a comment
There was a problem hiding this comment.
AI-assisted review
This is the separate AI-assisted review required by AI_POLICY.md. It is not a maintainer approval and does not authorize merge.
Checked against issue #183, origin/master...HEAD (da2b0d2fa), CONFIG_METADATA_3 / AiConfigPanel.vue / plugin-page rendering, i18n conversion, and GitHub Checks for this PR (all required jobs green as of this review).
Verdict
The placement change itself matches #183 and should land. One pre-existing Dashboard regression becomes user-visible the moment this group is shown; restore that wiring before merge, or land this PR only with an immediately following fix.
What is correct
CONFIG_METADATA_3["ai_group"]["metadata"]["btw"]is the right host. Item keys stay absolutebtw.*, soDEFAULT_CONFIG["btw"]and existing profiles are untouched.convert_to_i18n_keysnow emitsai_group.btw.*, which is covered byCONFIG_METADATA_I18N_PREFIXES.AiConfigPanel.vueonly enumerates capabilities for the local Agent tab. Adding'btw'afterproactive_capabilityis the only list that needed to change.- Plugin Config still hard-codes
PluginSetSelector.test_btw_section_stays_out_of_the_plugin_groupnow also keepsplugin_group.metadatato{plugin}. btw.work_loop.computer_use_runtimelabels align with optionsinherit/none/local/sandbox(Computer Use itself has noinherit, which is correct).- Locale trees were moved, not duplicated. Docs breadcrumbs now match the UI: 配置文件 → AI 配置 → 能力 / Config → AI → Capabilities.
- Tests cover the metadata contract and the capabilities-tab membership. Commit message and Agent note follow policy.
Required follow-up (pre-existing, now user-visible)
PluginLoopSelector and CapabilityLoopSelector exist, and the three _special values are still on the BTW items:
select_plugin_loop_routesselect_mcp_loop_routesselect_skill_loop_routes
They are not handled in dashboard/src/components/shared/ConfigItemRenderer.vue. Those branches were present after b70e3ee7c / 656969a47 / e7b698e32 and were dropped by the upstream cherry-pick c2ef5d968 (feat: enhance slider robustness (#10037)), which rewrote the special-handler block.
Until that wiring is restored, enabling BTW on AI → Capabilities will render the three route fields through generic ListConfigItem (string chips / a single text field). route_is_available_in_loop ignores non-dict entries, so a typed-in plugin name is silently dropped. The docs this PR updates tell operators they can assign conversation / work / both from that screen.
This is not introduced by the diff, but this PR is what makes those controls appear. Restoring the three _special branches (and their imports) is a small, in-scope companion change. A Vitest that mounts ConfigItemRenderer for those _special values would have caught the drop.
Nits (non-blocking)
tests/unit/test_config_metadata_i18n.pydropsfrom __future__ import annotationswith no related need. Harmless on 3.14; keep the file focused.dashboard/tests/aiConfigCapabilities.vitest.tsstubsAstrBotConfigV4, so it asserts tab membership, not the ten field widgets. That is acceptable if the ConfigItemRenderer wiring test above is added.btw.work_loop.max_concurrentstill requiresbtw.work_loop.enabled, so “all ten fields after enabling BTW” is 9 until the work loop is also on. Pre-existing metadata.- MCP / Skill sections in
astrbot-config.mdstill name the controls without the new breadcrumb. Only the plugin-assignment sentence had the old path; updating the other two would keep the page consistent.
I will not merge this PR.
The BTW plugin, MCP, and Skill route fields already declare _special renderers, but ConfigItemRenderer dropped those branches in the slider robustness cherry-pick. They fell through to ListConfigItem and showed [object Object] for stored route maps. Wire PluginLoopSelector and CapabilityLoopSelector back and cover the three specials so they cannot fall through again. Related: #183 AI-Generated: true Generated-At: 2026-09-15T15:23:37Z
Summary
Moves the BTW dual-loop configuration group from the plugin page to AI → Capabilities, so the ten
btw.*settings are reachable where the other agent capabilities live. The root-levelbtwconfig shape is untouched.Related issue
Fixes #183
Behavior
btw.enabledreveals all ten field controls.btw.work_loop.computer_use_runtimenow shipslabels, so the select shows translated options instead of the rawinherit/none/local/sandboxvalues.ai_group.btw.*in both locales; no key falls back to raw text.Non-goals
btw.pluginplugin_groupmetadata. The group now holds onlyplugin, so the hard-coded plugin section is still exact; a catch-all renderer stays a separate change.Implementation notes
CONFIG_METADATA_3["plugin_group"]["metadata"]["btw"]becomesCONFIG_METADATA_3["ai_group"]["metadata"]["btw"]. This is the placement that7630c7c76worked around: a top-levelbtwtab entry has nometadatakey, soConfigMetadataI18n.convert_to_i18n_keysemptied its controls.btw.*paths, soAstrBotConfigV4keeps reading and writing the root-levelbtwobject.DEFAULT_CONFIGis unchanged and no profile migration is needed.AiConfigPanel.vueaddsbtwto thecapabilitiesgroup list, which is the only place the tab's contents are enumerated.config-metadataentries move fromplugin_group.btw.*toai_group.btw.*(the section key isbtwand the item keys arebtw.*, so the converted keys readai_group.btw.btw.*).ai_group.is already inCONFIG_METADATA_I18N_PREFIXES, so a future missing translation warns at runtime instead of silently rendering the key —plugin_group.was not in that list.docs/zh|en/dev/astrbot-config.mdnow read Config → AI → Capabilities → BTW dual loops → ….Validation
Notes on the above:
uv runis unusable in this environment (No interpreter found for Python 3.14.6), somake checkanduv run pytest --test-profile blockingwere not run as written; the underlyingpython -m pytest,ruff, and node checks were run directly against the checked-out venv.marketPluginKey,pluginPageHost,extensionRuntimeSmokes,coverageMounts) fail to collect withTypeError: ... Received 'file:///favicon.svg'. Reproduced on a clean tree viagit stash -u, so it is pre-existing and unrelated to this change.docs:buildonly passes after moving the untracked local scratch directoriesdocs/backend-architecture/anddocs/btw-architecture-design/aside; they carry dead.drawiolinks and are not part of this change set.dashboard/tests/aiConfigCapabilities.vitest.tswas verified to fail when theAiConfigPanel.vueline is reverted.Compatibility and risk
btwstays at the config root with the same JSON shape; reading and saving an existing profile neither migrates nor drops fields.plugin_grouploses one key andai_groupgains one. No route or schema changes, so the OpenAPI document and generated client are untouched.Checklist
docs/zh/anddocs/en/.docs/public/openapi.json, and tests change together when routes or schemas change.pyproject.toml,requirements.txt, anduv.locktogether.!and aBREAKING CHANGE:footer.Agent note
Goal: implement issue #183 so the BTW dual-loop group is configured from AI → Capabilities instead of the plugin page.
Paths touched:
astrbot/core/config/default.py(group relocation pluscomputer_use_runtimelabels),dashboard/src/components/config/AiConfigPanel.vue(capabilities list),dashboard/src/i18n/locales/{zh-CN,en-US}/features/config-metadata.json(entry migration and labels),tests/unit/test_config_metadata_i18n.py,dashboard/tests/aiConfigCapabilities.vitest.ts(new),docs/{zh,en}/dev/astrbot-config.md.Checks run: the commands listed under Validation, all executed locally. Full
tests/unitsuite,i18n:check, prettier, markdownlint, ruff, anddocs:buildpass; the four dashboard collection failures are pre-existing and were reproduced on a clean tree. The new frontend test was confirmed to fail without the panel change.Residual risk: the capability-tab ordering is asserted only through the new Vitest, which stubs
AstrBotConfigV4; the ten field controls themselves are covered at the metadata level bytest_every_btw_profile_field_reaches_dashboard_controlsrather than by a rendered-page test.make check/uv run pytest --test-profile blockingwere not run becauseuvcannot resolve Python 3.14.6 here, so CI remains the authority on those targets.Tools used: Claude Code (Opus 5) with file edits and local test execution; issue text authored by OpenCode per the issue's Agent note.