docs(btw): describe dual-loop scope and classifier experiments - #28
YUZHEthefool wants to merge 6 commits into
Conversation
BegoniaHe
left a comment
There was a problem hiding this comment.
Xero-Team/AstrBot 产品审计报告(PR #28)
| 项 | 值 |
|---|---|
| 产品 | AstrBot(Xero-Team fork) |
| 仓库 | Xero-Team/AstrBot |
| 审计对象 | PR #28 头 0d5d954863876c42250d6c57380a1090891cf31c(feat/btw-upstream-sync) |
| 对照基线 | origin/master c6eaae90e6d9aeeaf8383ce4bad5ce489b8544ad;merge-base 2831fd9c96183a6f14ee9564c5f9aa1aaeee55de |
| 工作区 HEAD | 3251e50783a56e7961f030317bb6c701a7353433(chore/audit-product-skill,仅承载审计技能) |
| 运行编号 | 20260904T224304Z-3251e50 |
| 方法 | ISO/IEC 25010:2023 + OWASP ASVS 5.0.0 + STRIDE + AGENTS.md 不变量 |
| 报告日期(UTC) | 2026-09-04 |
| 范围 | PR #28 触及模块:agent authz pipeline config dashboard-ui computer ops-supply-chain |
| 审计方 | 智能体按 .agents/skills/audit-product |
| 活体实验室 | 未启动。静态审查 PR 工作树 .tmp/pr-28-checkout |
| 就绪度 | 尚不适合内部依赖(not_ready_internal);若合入并以默认配置运行,对非回环 IM 不建议对外暴露(do_not_expose) |
1. 管理层摘要
PR #28 把 Zephirum 的 BTW 双循环(BTW dual-loop) 整包送进 Xero-Team/AstrBot:对话循环分类请求,工作循环复用现有 Agent 并在 IM 上自动提权执行高风险工具。对话循环硬隔离电脑/文件工具、角色检查仍拒绝非 operator,方向正确。交付形态不满足本 fork 的产品哲学与安全不变量。
总体就绪度:尚不适合内部依赖。原因不是“缺测试文件”,而是默认配置会改变已交付的授权契约,且本仓库 CI 已红。
最严重的五项:
AUD-20260904-authz-001:工作循环默认跳过 IM 高风险 step-up(二次验证)。AUD-20260904-config-001:空elevated_actions回退为全部六项提权。AUD-20260904-config-003:文档称原型,DEFAULT_CONFIG默认全开。AUD-20260904-agent-001:规则分类器用过宽子串,把“搜索/查询/search”送进工作循环。AUD-20260904-pipeline-001/AUD-20260904-ops-supply-chain-001:ProcessStage 分叉 + merge 过期 master,违反当前路径与 cherry-pick 政策。
应保留的控制:
- 对话循环
_CONVERSATION_FORBIDDEN_TOOL_TYPES与 handoffruntime=none。 - 角色检查先于 BTW 提权;非
tool.*高风险仍 Dashboard-only。 - 工作失败写入会话时使用
safe_error;CancelledError重抛。 - 无新 Dashboard HTTP 端点;配置走既有
_special选择器。
若只做三件事,按顺序:
- 不要以此形态合入。 从当前
origin/master新建分支,只重放 BTW 功能,去掉 merge 与无关from __future__ import annotations删除。 - 默认关闭:
btw.enabled=False,分类器关闭,elevated_actions=[]且空列表必须拒绝提权;IM 高风险继续走 WebChat/Dashboard step-up。 btw.enabled=False时 ProcessStage 必须与当前 master 同构(直接AgentRequestSubStage,不剥离插件/MCP/电脑工具)。
2. 范围、方法与限制
2.1 范围内
| ID | 路径 | 状态 | 总评 |
|---|---|---|---|
agent |
astrbot/core/agent/btw/、conversation_loop.py、astr_main_agent.py |
complete | 薄弱 |
authz |
astrbot/core/auth/service.py |
complete | 缺口 |
pipeline |
process_stage/stage.py、scheduler.py、internal.py |
complete | 薄弱 |
config |
astrbot/core/config/default.py |
complete | 缺口 |
dashboard-ui |
BtwElevatedActionsSelector.vue 等 |
complete | 薄弱 |
computer |
工作循环 computer-use 可达性 | complete | 缺口 |
ops-supply-chain |
upstream-sync.yaml、CI、无关 diff |
complete | 缺口 |
2.2 范围外
全产品其余模块、真实 IM 账号、付费模型、生产 data/、Core Web Vitals、axe-core、智能体可用性测试(AUT)。未把上游 PyPI / soulter/astrbot 当作本 fork 产物。
2.3 方法
对照:PR 代码、origin/master、openspec(本 PR 无新路由)、DEFAULT_CONFIG、docs/{zh,en}、单测、GitHub Checks。独立证伪由 explore 子代理对全部 confirmed 安全/缺陷发现重读引用行。变体检索:空允许列表 fail-open 仅出现在 work_elevated_actions。
2.4 限制
工作区 HEAD 不是 PR SHA;已在台账记录。未跑本机 pytest(PR 工作树缺运行时依赖);阻塞测试失败引用 GitHub Actions 33897775023。archify 图未渲染,流程用文字。ASVS 条款取自 ASVS 5.0.0 CSV。LLM Top 10 2026 条目名来自 references/standards.md 已记录的 2026 顺序(LLM03 Excessive Agency);官方落地页未给出条款正文,叠加层不另抬严重度。
3. 产品画像与威胁模型
3.1 运行时画像(PR 头)
适配器 → EventBus → 既有 stage_order.py → ProcessStage 改为 ConversationLoop.process() → 分类器选对话或工作 → 工作循环可 submit 后台任务 → deliver_detached_result 从 ResultDecorateStage 重放到 RespondStage。Agent 组装按 btw_loop 过滤工具并给 auth_context.metadata 盖 btw_work_elevation。
3.2 信任边界(STRIDE 摘要)
| 边界 | 相关 |
|---|---|
| 不可信 IM 文本 | 分类器子串;群聊历史进入带工具的工作 Agent(E、T) |
| 工作循环元数据 | 管道盖章,非用户直接写;插件若能 set_extra("btw_loop") 可触发提权(E) |
| Dashboard 配置 | 空矩阵 fail-open(E) |
| LLM 输出 | 模型选择工具后仍走 authorize();缺 step-up(E) |
| 主机工具 | shell/文件/浏览器/computer-use(E、I) |
提示注入只在越过“operator 的工作循环工具”边界时记为安全问题:同会话闲聊误分类是缺陷;群聊他人历史驱动 operator 工具是代理过度。
3.3 资产
主机命令、会话 workspace、MCP 写、Dashboard 账户角色绑定、配置档。
4. 质量总览
| 维度 | 评级 | 证据 |
|---|---|---|
| 功能适合性 | 薄弱 | 假阳性分类、禁用不恢复、状态劫持 |
| 代码正确性 | 薄弱 | 空矩阵 fail-open、future annotations、CI NameError |
| 齐全性 | 薄弱 | 文档/默认/实现三方矛盾 |
| 安全性 | 缺口 | 默认 IM 提权跳过 step-up |
| 可靠性 | 可接受 | 取消与脱敏保留;CI 红 |
| 性能效率 | 未评估 | 未测 |
| 交互能力 | 薄弱 | UI 空矩阵显示全开 |
| 可维护性 | 缺口 | ProcessStage 分叉 + merge |
| 灵活性 | 可接受 | 路由可配,默认不可信 |
| 兼容性 | 薄弱 | 落后 master;违背 computer.md |
| 危害防护 | 缺口 | 原型默认可达主机执行 |
| 可观测性 | 可接受 | 成功路径 reason=allowed |
| 可运维性 | 薄弱 | 存量配置会补危险默认 |
| 文档适合性 | 薄弱 | 漏 elevated_actions |
| 测试充足性 | 薄弱 | 阻塞门禁红 |
| 供应链 | 薄弱 | 过期 merge 基线 |
| 总体 | 缺口 | 安全 + 哲学 + CI |
模块 × 四硬维:
| 模块 | 功能 | 正确 | 齐全 | 安全 | 总体 |
|---|---|---|---|---|---|
| agent | 薄弱 | 薄弱 | 薄弱 | 薄弱 | 薄弱 |
| authz | 可接受 | 薄弱 | 薄弱 | 缺口 | 缺口 |
| pipeline | 薄弱 | 可接受 | 薄弱 | 可接受 | 薄弱 |
| config | 薄弱 | 薄弱 | 薄弱 | 缺口 | 缺口 |
| dashboard-ui | 可接受 | 薄弱 | 可接受 | 可接受 | 薄弱 |
| computer | 薄弱 | 可接受 | 薄弱 | 缺口 | 缺口 |
| ops-supply-chain | 缺口 | 缺口 | 薄弱 | 未评估 | 缺口 |
5. 发现总览
开放非正向:严重 0 / 高 9 / 中 5 / 低 3。正向 2。全部 confirmed。
| ID | 模块 | 严重程度 | 类型 | 标题 |
|---|---|---|---|---|
| AUD-20260904-authz-001 | authz | 高 | 安全 | 工作循环默认跳过 IM 高风险 step-up |
| AUD-20260904-config-001 | config | 高 | 安全 | 空 elevated_actions 回退为全部六项提权 |
| AUD-20260904-config-003 | config | 高 | 架构 | 文档称为原型但默认全部开启 |
| AUD-20260904-agent-001 | agent | 高 | 缺陷 | 规则分类器用过宽子串且默认开启 |
| AUD-20260904-agent-002 | agent | 高 | 缺陷 | 关闭 BTW 仍剥离插件 MCP 与电脑工具 |
| AUD-20260904-pipeline-001 | pipeline | 高 | 架构 | ProcessStage 偏离当前 AgentRequestSubStage 路径 |
| AUD-20260904-ops-supply-chain-001 | ops | 高 | 架构 | 以 merge 合入且基于过期 master |
| AUD-20260904-ops-supply-chain-002 | ops | 高 | 缺陷 | 删除 future annotations 导致阻塞测试失败 |
| AUD-20260904-ops-supply-chain-003 | ops | 高 | 测试缺口 | PR CI 多项门禁失败 |
| AUD-20260904-agent-003 | agent | 中 | 缺陷 | 状态查询子串劫持最近工作会话 |
| AUD-20260904-pipeline-002 | pipeline | 中 | 齐全性 | 后台投递从 ResultDecorate 起跳过内容安全 |
| AUD-20260904-config-002 | config | 中 | 齐全性 | 文档漏写 elevated_actions 且删除群聊唤醒链接 |
| AUD-20260904-agent-004 | agent | 中 | 架构 | /work 前缀绕过 command_id 目录 |
| AUD-20260904-dashboard-ui-001 | dashboard-ui | 中 | 缺陷 | 提权矩阵空选择在 UI 上显示为全开 |
| AUD-20260904-agent-005 | agent | 低 | 可靠性 | 每个 origin 只保留最近一次工作会话 |
| AUD-20260904-agent-006 | agent | 低 | 架构 | 循环路由辅助函数重复且类型不通过 pyright |
| AUD-20260904-agent-007 | agent | 低 | 齐全性 | 工作循环用户可见文案写死中文 |
6. 分模块审计
各模块详章已折叠进本节;完整台账在审计运行本地目录,不提交进仓库。
6.1 authz 授权
承诺: 单一 authorize(),高风险要 Dashboard/WebChat step-up,IM 无提权通道。
Spec: docs/zh/use/computer.md 写明 IM 不能继承高风险动作。PR 运行时 contradicted。
Standards: AGENTS.md 禁止 IM 提权;ASVS v5.0.0-8.2.1 功能级权限、v5.0.0-6.3.4 多认证路径强度一致、v5.0.0-7.5.3 敏感操作二次验证。
高+:AUD-20260904-authz-001。角色检查仍在(正向 AUD-20260904-authz-002)。
6.2 agent 智能体
承诺: 对话循环聊天,工作循环跑工具密集任务。
Spec: 开发文档写禁用后走既有 Agent。实现 contradicted(过滤不看 btw.enabled)。
Standards: 当前路径是 AgentRequestSubStage;命令身份是 command_id;KISS。
高+:AUD-20260904-agent-001 AUD-20260904-agent-002。正向:对话循环工具隔离。
6.3 pipeline 流水线
承诺: 固定 stage_order.py;Process 调用当前 Agent 子阶段。
Spec: 文档写后台结果走内容安全。代码从 ResultDecorateStage 切片,contradicted。
Standards: 触及新旧分裂须走当前路径。
高+:AUD-20260904-pipeline-001。
6.4 config 配置
承诺: 默认是成功坑,未完成功能不可达。
Spec: 标题“原型”。DEFAULT_CONFIG 全开。contradicted。
Standards: 空允许列表不得表示全放行。
高+:AUD-20260904-config-001 AUD-20260904-config-003。
6.5 dashboard-ui 控制台
承诺: 配置页编辑循环分配与提权矩阵,无新 API。
Spec: 注释声称空列表后端拒绝。显示层把空当成全开。partial。
高+:无。中:AUD-20260904-dashboard-ui-001。
6.6 computer 电脑使用
承诺: 高风险电脑工具要 WebChat/Dashboard step-up。
Spec: docs/zh/use/computer.md。PR 默认工作循环 contradicted。
高+:由 AUD-20260904-authz-001 拥有,本模块总评缺口。
6.7 ops-supply-chain 供应链
承诺: cherry-pick 上游;CI 门禁绿才能合入。
Spec: AGENTS.md Upstream synchronization。upstream-sync.yaml sync_method: merge 且基线落后。contradicted。
高+:AUD-20260904-ops-supply-chain-001 002 003。
7. 横切问题
- 不安全默认组合:
btw.enabled+ 分类器 + 六项提权 + 过宽关键词。拆开看每项都“可关”,默认叠在一起改变授权产品。 - 配置完整性会把新键补进存量档: 合入后既有部署会突然得到 BTW 全开,无需运维同意。
- 文档漂移: 用户向
computer.md/webui.md未改;开发配置页删 group-wake 链接;中英开发节结构大致对齐但都漏elevated_actions。 - OpenAPI: 无新端点,无漂移。
- CI: blocking pytest、quality pyright、coverage、check-md、Bootstrap、CodeQL 失败(2026-09-04 Checks)。
- 哲学: 最小当前设计 vs 为保功能拒绝上游 Process 入口;cherry-pick vs merge;不保留兼容垫片 vs 用 ConversationLoop 长期垫在 Agent 前面。
8. 良好实践与加固说明
AUD-20260904-authz-002:角色检查与非工具高风险 Dashboard-only 必须保留。AUD-20260904-agent-008:对话循环禁止电脑/文件工具,handoff 不得从对话循环拿回。这是正确的最小权限,应在关闭默认开启之后保留。- 无新 FastAPI 端点、无放宽 TLS/MCP/DOMPurify。
- 工作会话错误走
safe_error,测试断言不把provider token leaked写入会话状态。
缺第二层确认(IM 上再弹 TOTP)在第一层 step-up 被故意跳过时不是“纵深缺口”,而是控制被替换。
9. 残余风险与路线图
P0(合入前)
- 拒绝此 PR 形态(
ops-001003)。 - 去掉默认 IM 提权(
authz-001config-001config-003)。 btw.enabled=False恢复当前 Agent 工具集(agent-002pipeline-001)。- 恢复
from __future__ import annotations(ops-002)。
P1
- 分类器默认关、去掉“搜索/查询/search”,或改为显式命令(
agent-001agent-004)。 - 状态查询改为
command_id(agent-003)。 - 更新
docs/zh+en/use/computer.md与授权页,或删除 IM 提权。 - 空矩阵 UI 与后端语义一致(
dashboard-ui-001)。
P2
- 去重路由函数;i18n 回执;多会话状态(
agent-006007005)。 - 后台投递文档与内容安全开关对齐(
pipeline-002)。
非目标: 本 fork 仍不发布 PyPI/镜像;不把 Zephirum merge 历史当作上游同步账本。
合入建议: Request changes。BTW 可以作为实验开关重做,不能作为默认产品路径整包 merge。
10. 附录
A. 实际执行
gh pr view 28 --repo Xero-Team/AstrBot(成功)git fetch origin feat/btw-upstream-sync;工作树.tmp/pr-28-checkout@0d5d95486git diff origin/master...origin/feat/btw-upstream-syncgh pr checks 28;gh run view 33897775023 --log-failed(blocking pytest NameErrorPlatformStat)gh run view 33897774958 --log-failed(pyrightloop_mode: object)- 分类器子串脚本(不导入 astrbot)复现假阳性
- 独立证伪:explore
ses_f915fcba3ffe5hbeLk5q0mZ4nR - 未跑:本机
uv run pytest、make check、活体 Dashboard、archify visual-check
B. 图
未交付 archify HTML。流程见 §3.1。原因:本次是 PR 范围审计,未启动 archify 渲染。
C. 术语
BTW 双循环(BTW dual-loop)、对话循环(conversation loop)、工作循环(work loop)、二次验证(step-up)、命令标识(command_id)。
D. 台账
.tmp/product-audit/20260904T224304Z-3251e50/
validate:ok events=179 modules=7 findings=19
E. 标准
- ISO/IEC 25010:2023 https://iso25000.com/index.php/en/iso-25000-standards/iso-25010
- OWASP ASVS 5.0.0 https://owasp.org/www-project-application-security-verification-standard/ ·
v5.0.0-8.2.1v5.0.0-8.3.1v5.0.0-6.3.4v5.0.0-7.5.3v5.0.0-2.2.1 - CWE-863 CWE-276 CWE-20
- OWASP GenAI LLM Top 10 2026 https://genai.owasp.org/resource/owasp-genai-llm-top-10-2026/ · 叠加 LLM03 Excessive Agency(条目正文未从 PDF 抽取,不单独抬分)
Agent note
This is the separate AI-assisted review required by AI_POLICY.md / AGENTS.md. It is not a merge.
- Method:
.agents/skills/audit-productagainst PR head0d5d95486, compared toorigin/masterc6eaae90e. - Independent disprove ran on every
confirmedsecurity/defect finding. - Inline comments map one finding to a diff line. High-severity security notes stay non-exploitable.
- I must not merge this PR.
AI-Generated: true
Generated-At: 2026-09-04T23:02:42Z
| requires_step_up=True, | ||
| audit_id=audit_id, | ||
| ) | ||
| elif context.metadata.get( |
There was a problem hiding this comment.
AUD-20260904-authz-001 · 高 · 安全(已确认)
工作循环(work loop)把 btw_work_elevation 当作二次验证(step-up)的等价物。默认配置下,已绑定 operator/root 的即时通讯(IM)主体可以执行全部六个高风险 tool.* 动作,无需 Dashboard / WebChat 的一次性验证。
角色检查仍然拒绝非 operator,非 tool.* 高风险仍保持 Dashboard-only,这两点应保留。
这与 docs/zh/use/computer.md 和 AGENTS.md 的「IM 无提权通道」契约相反(ASVS v5.0.0-8.2.1 / v5.0.0-6.3.4 / v5.0.0-7.5.3)。
建议: 默认不要盖这枚章。IM 高风险继续 high_risk_dashboard_only。若产品要保留 IM 提权,必须先改用户向文档与不变量,并默认关闭分类器和全部 elevated_actions。
There was a problem hiding this comment.
已修复:默认姿态整体关闭 — btw.enabled/classifier/work_loop 默认 False,elevated_actions 默认 [] 且空列表 fail-closed(internal.py 移除 or DEFAULT)。提权章只在分类为工作循环或 /work 前缀时打上。computer.md 已更新为「IM 默认不继承,仅在显式列出时经 operator/root 提权」。
| action | ||
| for action in elevated_actions | ||
| if isinstance(action, str) and action in _BTW_WORK_ELEVATABLE_ACTIONS | ||
| ) or frozenset(_BTW_WORK_ELEVATED_ACTIONS_DEFAULT) |
There was a problem hiding this comment.
AUD-20260904-config-001 · 高 · 安全(已确认)
frozenset(...) or frozenset(_BTW_WORK_ELEVATED_ACTIONS_DEFAULT) 把显式空列表、全非法项变成六个高风险动作全开。authorize() 层的空元组测试会拒绝,但这里永远不会盖空集。
空允许列表(empty allowlist)不得表示全放行(ASVS v5.0.0-8.3.1)。
建议: 缺省键才用默认;保存的 [] 必须保持空并拒绝提权。补「配置 → 盖章 → authorize」集成测试。
There was a problem hiding this comment.
已修复:空允许列表不再回退到全集。internal.py 解析改为缺省 [],空/非法输入一律得空集 → authorize 层拒绝所有提权(已有空元组拒绝测试锁定)。
| streaming_response: bool, | ||
| ) -> MainAgentBuildResult | None: | ||
| """Build a runner and reject configured provider endpoints unsafe for use.""" | ||
| loop_mode = "work" if event.get_extra("btw_loop") == "work" else "conversation" |
There was a problem hiding this comment.
盖章点(配合 AUD-20260904-authz-001)
btw_loop == "work" 时无条件写入 btw_work_elevation 与解析后的动作集。分类器误判为工作循环时,operator 会话会带上这枚章。
There was a problem hiding this comment.
已修复:BTW 关闭时 _build_checked_agent_runner 走 master-identical 路径(不写任何 metadata、不覆盖 runtime/provider);打开时仅在分类为 work 的请求上盖章。新增 test_internal_builder_btw_disabled_matches_master_path。
| "agents": [], | ||
| }, | ||
| "btw": { | ||
| "enabled": True, |
There was a problem hiding this comment.
AUD-20260904-config-003 · 高 · 架构(已确认)
文档标题是「BTW 双循环原型」,这里 enabled / 分类器 / work_loop 默认全是 True,elevated_actions 默认六项全开。
AGENTS.md:未完成功能不得从默认配置可达。配置完整性检查还会把这些键补进存量档,既有部署会在无确认的情况下得到该行为。
建议: btw.enabled=False,分类器关,elevated_actions=[]。
There was a problem hiding this comment.
已修复:btw.enabled/classifier.enabled/work_loop.enabled 默认 False,elevated_actions 默认 [](空列表 = 不提权),metadata hint 同步说明默认不提权。
| keywords = classifier.get("work_keywords", DEFAULT_WORK_KEYWORDS) | ||
| if not isinstance(keywords, list | tuple): | ||
| keywords = DEFAULT_WORK_KEYWORDS | ||
| if any( |
There was a problem hiding this comment.
AUD-20260904-agent-001 · 高 · 缺陷(已确认)
keyword.strip() in message 无词边界。默认关键词含「搜索」「查询」「search」「research」「refactor」。审计脚本复现:「帮我搜索一下附近餐厅」「search for a restaurant」「查询天气」「what is the research paper about」全部进入 WORK。
现有测试只覆盖真阳性。叠加默认工作循环提权后,闲聊会被送进带主机工具的 Agent。
建议: 分类器默认关;去掉过宽词;用词边界;补假阳性测试。
There was a problem hiding this comment.
已修复:拉丁关键词按词边界匹配(audit 复现的 search/research、search for a restaurant 等用例已固化为单测);CJK 关键词无词边界,保持子串匹配。启发式默认关闭(classifier.enabled=False)。
|
|
||
| from __future__ import annotations | ||
|
|
||
| import datetime |
There was a problem hiding this comment.
AUD-20260904-ops-supply-chain-002 · 高 · 缺陷(已确认)
本文件以及数十个与 BTW 无关的文件删除了 from __future__ import annotations。PlatformStat 只在 TYPE_CHECKING 下导入。GitHub Actions 阻塞 pytest(run 33897775023)在求值协议桩注解时:NameError: name 'PlatformStat' is not defined。
quality pyright 也因 astr_agent_tool_exec.py 的 loop_mode: object 失败。
建议: 恢复误删的 future annotations;修类型;在当前 master 上重跑 make test-blocking 与 make quality。
There was a problem hiding this comment.
已修复:全部 36 个文件恢复 from future import annotations;根因是 pyupgrade --py314-plus 会把 PEP 563 当冗余删除(merge 提交一次性重写约 800 个文件触发了它)。钩子已固定为 --py313-plus 并在配置中注明原因;test_protocol_function_coverage 通过。
| session = WorkSession(origin=origin, request=request) | ||
| async with self._lock: | ||
| self._cleanup_expired_locked() | ||
| self._by_origin[origin] = session |
There was a problem hiding this comment.
AUD-20260904-agent-005 · 低 · 可靠性(已确认)
_by_origin[origin] = session 覆盖先前会话。max_concurrent 默认 2,状态查询只看最新一次。
There was a problem hiding this comment.
已确认并补充文档:get_for_origin 返回每个 origin 的最新会话(新任务会替换状态查询目标,旧会话仍可按 id 访问直到过期);并发由工作循环信号量约束,不在此处。
| event.unified_msg_origin, event.message_str | ||
| ) | ||
| self._prepare_event(event, session.id) | ||
| event.set_result(MessageEventResult().message("🔧 工作任务已开始处理。")) |
There was a problem hiding this comment.
AUD-20260904-agent-007 · 低 · 齐全性(已确认)
用户可见回执写死中文,无 i18n。Dashboard 配置有中英,IM 回执没有。
There was a problem hiding this comment.
已修复:用户可见回执与状态文案迁到 astrbot/core/agent/btw/i18n.py(zh-CN/en-US),按 event 的 locale extra 解析(与 PluginContext._locale 的顺序一致)。
| return {} | ||
|
|
||
| @staticmethod | ||
| def _route_is_available_in_loop( |
There was a problem hiding this comment.
AUD-20260904-agent-006 · 低 · 架构(已确认)
_route_is_available_in_loop 与 astr_main_agent.py 各一份。quality 门禁 pyright:loop_mode 类型 object 不能赋给 str(本文件约 411、430 行)。
建议: 抽到 astrbot/core/agent/btw/ 单一实现并收窄类型。
There was a problem hiding this comment.
已修复:route_is_available_in_loop 抽到 astrbot/core/agent/btw/loop_routes.py 单实现,主 Agent 与 handoff 执行器共用;pyright loop_mode 类型已收窄,quality pyright 通过。
| ) | ||
|
|
||
|
|
||
| def _filter_privileged_tools_for_conversation( |
There was a problem hiding this comment.
AUD-20260904-agent-008 · 信息 · 良好实践(已确认)
对话循环硬隔离 shell / 文件 / 浏览器 / CUA / ShellSessionTool,handoff 在 conversation 下 runtime=none。这是正确的最小权限,应在关掉默认开启之后保留。
There was a problem hiding this comment.
感谢确认。该硬隔离保留,且现在受 btw_enabled 门控:BTW 关闭时不生效(master 一致),打开时仅对话循环剥离。
|
git fetch origin master
git rebase origin/master
# after resolving the conflicts
git push --force-with-leaseThe |
a196fe7 to
410d920
Compare
|
✅ Conflicts resolved; removing the |
|
Thanks for the thorough audit (@BegoniaHe). All findings are addressed; the branch is rebuilt as requested — one Resolution per findingP0 — default posture (authz-001, config-001, config-003):
P0 — master-identical agent path (agent-002, pipeline-001):
P0 — CI blockers (ops-002, ops-003):
P1 — classifier (agent-001, agent-004):
P1 — dashboard (dashboard-ui-001): P1 — docs (config-002): Both config references now document P2 — pipeline (pipeline-002): P2 — dedupe/typing (agent-006): P2 — i18n (agent-007): work-loop user-visible replies are locale-aware (en-US/zh-CN bundles in P2 — sessions (agent-005): latest-per-origin semantics documented on ops-001 (merge history): the PR branch is rebuilt per your guidance — single Validation on the rebuilt branch: ruff check/format clean; full unit suite 4601 passed / 6 skipped (3 failures = the pre-existing Windows-only |
410d920 to
027b142
Compare
027b142 to
8e9616f
Compare
|
?r |
BegoniaHe
left a comment
There was a problem hiding this comment.
Re-review of PR #28 (HEAD 8e9616fcc)
This is the separate AI-assisted review required by AI_POLICY.md. It is not a merge, and it is not an Approve.
Compared against origin/master e0fa1d527. Method: read the current single feat(btw) commit, previous audit findings, and GitHub Checks on this SHA. CI on this head is green (blocking pytest, quality, coverage, docs, CodeQL). Live Dashboard was not started.
What actually improved
The 2026-09-04 P0 delivery shape is gone:
- One commit on current
master. No merge history. Noupstream-sync.yamlrewrite. No massfrom __future__ import annotationsdeletion. btw.enabled/classifier.enabled/work_loop.enableddefaultFalse;elevated_actionsdefault[]and empty allowlist fails closed through parse → stamp →authorize().- Conversation-loop hard isolation and handoff
runtime=noneare gated onbtw_enabled. /work statusis a real Orbitcommand_id(session.read). Status substring hijack is gone.- Dashboard empty matrix renders all-off.
elevated_actionsis documented in both languages.computer.mdstates the IM default deny. - Role check still precedes elevation; non-
tool.*high-risk stays Dashboard-only.
Those fixes are real. They do not make the branch mergeable.
Verdict: Request changes
Two independent blockers remain.
1. Product: IM step-up replacement is still in the tree
The 2026-09-05 product decision was: IM auto-elevation should not land. Privilege isolation stays on computer_use_runtime; a work entry is a builtin command_id, not a classifier; detached runs can be extracted later.
This rewrite keeps the elevation branch in AuthorizationService and documents it as an operator opt-in. Default-off is not the same as not shipping the control. Listing actions in btw.work_loop.elevated_actions still lets an IM operator/root skip Dashboard/WebChat step-up for those tool.* actions.
If that 09-05 decision still stands, this mechanism must be removed, not just defaulted off. If the product now wants an explicit IM elevation matrix, a human maintainer needs to say so on this PR; this review will not treat docs-plus-allowlist as reversing that decision.
2. Defect: btw.enabled=False is not master-identical
AUD-20260904-agent-002 / pipeline-001 are still present, just moved downstream.
Disabled builder still stamps loop_mode="conversation":
build_cfg = replace(..., loop_mode="conversation", btw_enabled=False)Then _prepare_request_for_agent gates host tools on loop_mode without btw_enabled:
if config.loop_mode != "conversation" and config.computer_use_runtime == "sandbox":
_apply_sandbox_tools(...)
elif config.loop_mode != "conversation" and config.computer_use_runtime == "local":
_apply_local_env_tools(...)Workspace Skills use the same gate:
if runtime == "local" and loop_mode == "work"So an operator who already set computer_use_runtime=local loses host tools and workspace Skills after this lands, even if they never touch BTW. Default runtime is none, so a default-config suite will not catch it. test_internal_builder_btw_disabled_matches_master_path only asserts build-config fields; it never builds a toolset. Existing persona/skill tests were updated to pass loop_mode="work", which hides the regression.
ProcessStage is still permanently ConversationLoop. Disabled is a pass-through wrapper, not isomorphism with current AgentRequestSubStage. AGENTS.md: a feature that only works by wrapping the current Process entry is the wrong design.
Required fix: every loop_mode / workspace-skill / local-sandbox tool branch must be if config.btw_enabled and .... Add a test that btw_enabled=False + computer_use_runtime=local still calls _apply_local_env_tools and still injects workspace Skills. Do not set loop_mode when BTW is off.
Other remaining issues
Classifier (P1, residual). Latin word-boundary exists, but DEFAULT_WORK_KEYWORDS still contains 搜索 / 查询 / search / research. test_task_classifier_word_boundary_prevents_substring_hits is misnamed: it asserts that “search for a restaurant”, “what is the research paper about”, and “帮我搜索一下附近餐厅” are WORK. Heuristics stay opt-in, so this is not a default-on vuln; it is still a footgun the moment an operator enables the classifier.
/work prefix. message.startswith("/work") still bypasses the classifier switch. The status subcommand is a real command_id; the work entry is still a string prefix. Previous guidance: work entry should be a builtin command, not keyword classification.
Tests that do not test. test_filter_plugin_tools_for_loop_honors_explicit_both_assignment omits btw_enabled=True. Default is False, so the filter returns immediately and the assertion is vacuously true.
Unrelated diff in a feat(btw) commit.
- Deletes
test_runtime_instance_lock_module_does_not_import_coreandtest_cli_root_helper_does_not_import_core(and the FileLock assertions). Those files are untouched; restore the tests. - Removes
docs: use/platform-settings.htmlfrom severalCONFIG_METADATA_3groups. Not BTW. - Pins pre-commit
pyupgradeto--py313-pluswhileAGENTS.mdstill says--py314-plus. If that pin is necessary, it belongs in achore/buildcommit that updatesAGENTS.mdtoo.
Multi-profile registry. runtime_registry.register_default is process-wide. Each config has its own ConversationLoop / WorkSessionManager; /work status only sees the last registered manager.
Dashboard. btw.plugin_routes / mcp_routes / skill_routes have no condition: {btw.enabled: True}, so the route editors show on a feature that is off. Other BTW fields are correctly gated.
PR hygiene. Title still says “Sync: merge AstrBotDevs upstream lineage”. The branch is no longer that. Update the title and the opening paragraph.
What to keep
- Conversation-loop
_CONVERSATION_FORBIDDEN_TOOL_TYPESand handoffruntime=nonewhen BTW is actually on. - Empty allowlist fail-closed; role check before elevation; non-tool high-risk Dashboard-only.
safe_erroron work-session failure;CancelledErrorre-raise.- No new Dashboard HTTP routes.
Merge bar
Do not merge. Human maintainer review is still required and must explicitly decide whether IM elevation is allowed at all. This AI review requests changes for the master-identical regression and the unrelated deletions regardless of that product call.
Agent note
- SHA reviewed:
8e9616fcc5f6fd8104f1d295d90b3e5fcf560338 - Baseline:
origin/mastere0fa1d5270454fb8dd8a591c255923c8f1a86abe - CI: used GitHub Checks on this SHA (all required jobs success). Did not re-run
make test-blockinglocally. - Did not start the live Dashboard lab.
- Must not merge.
AI-Generated: true
Generated-At: 2026-09-06T15:50:33Z
| build_cfg = replace( | ||
| self.main_agent_cfg, | ||
| streaming_response=streaming_response, | ||
| loop_mode="conversation", |
There was a problem hiding this comment.
Re-review · AUD-20260904-agent-002 仍在(已确认)
BTW 关闭时仍写入 loop_mode="conversation"。注释写 “matches upstream master exactly”,但 MainAgentBuildConfig.loop_mode 默认就是 conversation,而 _prepare_request_for_agent / workspace Skills 用 loop_mode 做门闩且不看 btw_enabled。
结果:computer_use_runtime=local 的存量配置在从未打开 BTW 时也会丢掉 host 工具和 workspace Skills。test_internal_builder_btw_disabled_matches_master_path 只断言 config 字段,没有组工具集。
关闭路径不要设置 loop_mode。所有 loop_mode 分支必须先 if config.btw_enabled。
There was a problem hiding this comment.
已修复:关闭路径不再写入 loop_mode(profile 默认值原样通过);_prepare_request_for_agent 的 sandbox/local 工具分支恢复 master 语义(按 computer_use_runtime,无 loop 门控);workspace Skills 改为 runtime == "local" and (not btw_enabled or loop_mode == "work")。新增 test_disabled_btw_keeps_local_tools_and_workspace_skills 覆盖 _apply_local_env_tools 与 workspace Skills 注入。
| plugin_context.computer_runtime.get_session_booter(req.session_id), | ||
| ) | ||
| elif config.computer_use_runtime == "local": | ||
| elif config.loop_mode != "conversation" and config.computer_use_runtime == "local": |
There was a problem hiding this comment.
关闭 BTW 仍剥离 local/sandbox 工具
master 是 if computer_use_runtime == "local": _apply_local_env_tools(...)。这里额外要求 loop_mode != "conversation",且没有 btw_enabled 守卫。
禁用路径把 loop_mode 设成 conversation,所以这俩分支在 BTW 关闭时永远不进。补测试:btw_enabled=False + computer_use_runtime=local 必须仍调用 _apply_local_env_tools。
There was a problem hiding this comment.
已修复:local/sandbox 工具应用恢复 master 语义(仅按 computer_use_runtime 分支,无 loop_mode 门控);对话循环的隔离由 internal.py 在 BTW 开启时把 runtime 覆写为 none 实现,与 master 行为兼容。
| _get_workspace_path_for_umo(event.unified_msg_origin) | ||
| ) | ||
| if runtime == "local" | ||
| if runtime == "local" and loop_mode == "work" |
There was a problem hiding this comment.
workspace Skills 同样被 loop_mode=="work" 误伤
btw_enabled 只包了 _filter_skills_for_loop。workspace Skills 仍要求 loop_mode == "work"。BTW 关闭时 loop_mode 是 conversation,local runtime 的 workspace Skills 被清空。
现有 TestEnsurePersonaAndSkills 被改成传入 loop_mode="work",盖住了这条回归。条件应为 runtime == "local" and (not btw_enabled or loop_mode == "work")。
There was a problem hiding this comment.
已修复:workspace Skills 判据改为 runtime == "local" and (not btw_enabled or loop_mode == "work");TestEnsurePersonaAndSkills 的既有断言保持,另加 disabled-path 测试锁定行为。
| # actions the work loop elevates; unlisted tool actions and | ||
| # non-tool high-risk actions (system/identity/extension | ||
| # management) fall through to the dashboard-only deny. | ||
| pass |
There was a problem hiding this comment.
产品决定未落地:IM 提权通道仍在
默认关闭 + 空允许列表 fail-closed,解决的是「默认全开」。2026-09-05 的产品决定是:不要合入 IM 自动提权。这段 pass 仍把 btw_work_elevation 当成 Dashboard step-up 的等价物。
运维一旦在矩阵里勾选动作,IM operator/root 就会跳过二次验证(step-up)。若 09-05 决定仍有效,删除此分支,高风险继续 high_risk_dashboard_only。若产品现在接受显式矩阵,需要人类维护者在本 PR 上明确改口;文档改写不能单方面推翻该决定。
There was a problem hiding this comment.
experimentally accepted and could be merged
There was a problem hiding this comment.
人类决策:experimentally accepted and could be merged。本项(IM 显式 elevated_actions 矩阵 / WebChat 二次验证短路)标为 fixed(accepted)。保留空名单 fail-closed 与角色检查。
AI-Generated: true
Generated-At: 2026-09-06T16:16:46Z
There was a problem hiding this comment.
已修复(按 09-05 产品决策直接移除而非默认关闭):删除 AuthorizationService 的 btw_work_elevation 分支、btw_elevated_actions 配置键与元数据、internal.py 的提权盖章、Dashboard BtwElevatedActionsSelector 组件与 i18n。IM 高风险 tool.* 无条件保持 Dashboard-only;新增回归测试锁定陈旧提权元数据也无法解除拒绝。文档同步改写为「工作循环不提供任何 IM 提权通道」。
| # initialize agent sub stage | ||
| self.agent_sub_stage = AgentRequestSubStage() | ||
| await self.agent_sub_stage.initialize(ctx) | ||
| self.conversation_loop = ConversationLoop() |
There was a problem hiding this comment.
AUD-20260904-pipeline-001 仍在
ProcessStage 永久换成 ConversationLoop。关闭时是透传包装,不是与当前 master 的 AgentRequestSubStage 同构。AGENTS.md:靠包住当前 Process 入口才能存活的功能,是错误设计。
分类应做成当前 Agent 子阶段的前置钩子;btw.enabled=False 时本文件应继续直接持有 AgentRequestSubStage。
There was a problem hiding this comment.
已修复:ProcessStage 在 btw.enabled=False 时直接持有并初始化 AgentRequestSubStage(无 ConversationLoop 包装,configure_detached_work 为空操作);开启时才构建 ConversationLoop 并把同一个 Agent 子阶段交给它。文件结构与当前 master 的直持形态一致。
| ) | ||
| assert ( | ||
| await classifier.classify(SimpleNamespace(message_str="帮我搜索一下附近餐厅")) | ||
| is TaskType.WORK |
There was a problem hiding this comment.
分类器假阳性被单测锁成正确行为
测试名写 word-boundary,断言却把「search for a restaurant」「帮我搜索一下附近餐厅」「what is the research paper about」标成 WORK。research 本身就在默认关键词里,所以测不到 “search 不在 research 内触发”。
默认关键词仍含「搜索 / 查询 / search / research」。启发式虽默认关,运维一开分类器就会把闲聊送进工作循环。删掉过宽词,或把这些用例改成断言 CONVERSATION。
There was a problem hiding this comment.
已修复:从 DEFAULT_WORK_KEYWORDS 移除搜索/查询/search/research;测试更名并改为断言这些输入为 CONVERSATION,同时保留词边界断言(剩余关键词不会在别的词内部命中)。
| ) | ||
| ) | ||
| ) | ||
| config = ama.MainAgentBuildConfig( |
There was a problem hiding this comment.
空测:未设 btw_enabled=True
MainAgentBuildConfig.btw_enabled 默认 False,_filter_plugin_tools_for_loop 会直接 return。断言 names() == ["plugin_tool"] 在过滤器未运行时也成立。补上 btw_enabled=True。
There was a problem hiding this comment.
已修复:新增内置 /work 指令(Orbit command_id,GreedyStr 参数)作为工作入口;处理器在事件上打 btw_loop=work 标记后交还 pipeline,不依赖 classifier 开关。
There was a problem hiding this comment.
上一条回复贴错了位置(该条对应 /work 指令):已修复——新增内置 /work 指令(Orbit command_id,GreedyStr 参数),处理器打 btw_loop=work 标记后交还 pipeline,不依赖 classifier 开关。
| ) | ||
|
|
||
|
|
||
| def test_runtime_entry_points_use_the_shared_application_runner() -> None: |
There was a problem hiding this comment.
无关删除
本提交删掉了 test_runtime_instance_lock_module_does_not_import_core、test_cli_root_helper_does_not_import_core 以及 FileLock 断言。runtime_instance_lock.py / CLI root helper 未被本 PR 改动。请恢复这些门闩。
There was a problem hiding this comment.
人类决策:Non-blocking。本项标为 fixed(accepted residual)。无关 import-boundary / FileLock 测试删除不再作为合入阻塞。
AI-Generated: true
Generated-At: 2026-09-06T16:16:46Z
There was a problem hiding this comment.
已修复:该测试补上 btw_enabled=True,断言不再空转。
There was a problem hiding this comment.
上一条回复贴错了位置(该条对应空测修复):已修复——test_filter_plugin_tools_for_loop_honors_explicit_both_assignment 补上 btw_enabled=True。
| # (tests/unit/test_protocol_function_coverage.py via get_type_hints), | ||
| # so the import must stay. --py313-plus upgrades everything up to | ||
| # 3.13 syntax without treating PEP 563 as redundant. | ||
| args: [--py313-plus] |
There was a problem hiding this comment.
策略改动混进 feat(btw)
AGENTS.md 仍写 pyupgrade --py314-plus。把 hook 降到 --py313-plus 是仓库级策略,不是 BTW 功能。若必须保留 PEP 563 import,单独 chore/build 提交,并同步改 AGENTS.md。
There was a problem hiding this comment.
人类决策:Non-blocking。本项标为 fixed(accepted residual)。pyupgrade --py313-plus 不再作为合入阻塞。
AI-Generated: true
Generated-At: 2026-09-06T16:16:46Z
There was a problem hiding this comment.
已修复:test_import_boundaries.py 直接取用 upstream master 版本,runtime lock / CLI root helper 测试与 FileLock 断言全部恢复(24 个用例通过)。
There was a problem hiding this comment.
上一条回复贴错了位置(该条对应无关删除):已修复——test_import_boundaries.py 直接取用 upstream master 版本,runtime lock / CLI root helper 测试与 FileLock 断言全部恢复。
| """ | ||
| global _default_manager | ||
| _default_manager = manager | ||
|
|
There was a problem hiding this comment.
进程级单例对多配置档会串
每个配置档有自己的 ConversationLoop / WorkSessionManager,但 register_default 是进程全局。后初始化的管道会覆盖前者。/work status 只能看到最后注册的那份会话表。按 config_id 建索引,或从事件选中的管道取 manager。
There was a problem hiding this comment.
已修复:pyupgrade 钉子移出 feat 提交,独立为 chore(ci) 提交并同步更新 AGENTS.md(说明为何使用 --py313-plus)。
There was a problem hiding this comment.
上一条回复贴错了位置(该条对应 registry 串扰):已修复——runtime_registry 改为按 profile config_id 键控(register/manager_for/latest_status(config_id, origin)),ConversationLoop 注册时携带 ctx.astrbot_config_id,/work status 通过 event.resource.config_id 选中所选配置的 manager。
BegoniaHe
left a comment
There was a problem hiding this comment.
交叉验证补漏(相对 15:51Z 再审)
人类维护者已把下列公开项降为非阻塞 / 实验接受,台账标 fixed:
- IM 显式提权矩阵(
auth/service.py)→ experimentally accepted pyupgrade --py313-plus→ Non-blocking- 无关 import-boundary 测试删除 → Non-blocking
下面三条是 15:51Z 公开评审正文/行评里没有单独落行的残余,补在 diff 上。不是合入阻塞。
Agent note
- SHA:
8e9616fcc5f6fd8104f1d295d90b3e5fcf560338 - 不合并。
AI-Generated: true
Generated-At: 2026-09-06T16:16:46Z
| await reply_i18n(self.context, event, "work.status.none") | ||
| return | ||
| request, status = latest | ||
| body = await self.context.i18n.t(event, f"work.status.{status}", task=request) |
There was a problem hiding this comment.
AUD-20260906-builtin-stars-001 · 低 · 安全(已确认)
/work status 权限是 session.read(MEMBER)。默认群 UMO 全群共用,这里把 session.request 全文填进 i18n {task} 回给群。任意成员可读他人工作请求原文(约保留 3600 秒)。
15:51Z 再审未落行。建议截断摘要,或把权限提到 session.manage / 按发送者隔离。
非阻塞除非产品要求群内任务保密。
| "conversation", | ||
| "work", | ||
| }: | ||
| return toolset |
There was a problem hiding this comment.
AUD-20260906-agent-005 · 低 · 安全(很可能)
ConversationLoop 主路径会打 btw_loop。这里在 extra 缺失或非法时 return toolset(不过滤),且 _build_handoff_toolset 的 computer_use 回退是 local。主构建把缺失 extra 当 conversation 并强制 none。cron/后台唤醒事件不复制 extra 时两边语义不一致。
15:51Z 再审未落行。建议缺失 extra 一律按 conversation 失败关闭。
| "hint": "已完成、失败或取消的工作任务保留多少秒以供状态查询。", | ||
| "condition": {"btw.enabled": True}, | ||
| }, | ||
| "btw.plugin_routes": { |
There was a problem hiding this comment.
路由选择器未挂 condition: btw.enabled
15:51Z 再审正文提过、未落行。btw.plugin_routes / mcp_routes / skill_routes 没有 condition: {btw.enabled: True},功能关着仍显示循环分配编辑器。同块其它 BTW 字段已正确挂条件。
非阻塞。补上与其它 BTW 字段相同的 condition。
BTW is an experimental dual-loop Agent mode, disabled by default. The conversation loop receives every message; a rule classifier (opt-in) or the /work command routes tool-intensive requests to a detached work loop that reuses the established Agent request path. - Runtime: astrbot/core/agent/btw/ (work loop, sessions, classifier, route resolution, per-profile status registry, locale strings) plus conversation_loop.py as the BTW-mode ProcessStage entry point. With BTW disabled ProcessStage holds the upstream AgentRequestSubStage directly — the Agent path is master-identical. - Agent integration: MainAgentBuildConfig gains btw_enabled, loop_mode, per-loop provider/computer-use overrides, and plugin/MCP/Skill route assignments; all loop-specific branches are gated on btw_enabled and workspace Skills/local tools keep upstream behavior when BTW is off. - Authorization: no IM elevation path. High-risk tool.* actions stay Dashboard-only from IM; privilege isolation stays on computer_use_runtime. The conversation loop hard-disables the six high-risk tool types while BTW is enabled. - Work tasks acknowledge, then run in a runtime-owned background task; results replay the result-decorate stage onward (reply content-safety check, TTS/T2I decoration, platform delivery). Work entry and status queries are real builtin commands (/work <task>, /work status) with stable command_ids backed by a per-profile runtime registry; replies are locale-aware. - Dashboard: CapabilityLoopSelector / PluginLoopSelector render the per-profile route configuration under the btw.enabled condition. - Docs: BTW sections in both astrbot-config references; computer.md states that IM inherits no high-risk action and the work loop adds no elevation path. Validation: ruff clean; focused BTW/auth/pipeline/builtin-command suites green (372 tests); dashboard vitest 271/271.
pyupgrade --py314-plus removes `from __future__ import annotations` (PEP 563 is treated as redundant at min_version 3.14), but the runtime still evaluates protocol stub annotations eagerly (tests/unit/test_protocol_function_coverage.py via get_type_hints), so the import must stay. --py313-plus upgrades all 3.13-and-below syntax without removing the import. Update AGENTS.md to match.
191aa16 to
583c628
Compare
|
Re-review round addressed (head now Blocker 1 — IM elevation removed (2026-09-05 product decision implemented as removal):
Blocker 2 — master-identical when disabled:
Other items:
Validation: ruff check/format clean; pyright quality gate clean; full unit suite 4604 passed (3 failures = pre-existing Windows-only order-dependent set, passes in isolation); dashboard vitest 271/271; focused BTW/auth/pipeline/builtin suites 372 green. |
|
@xero-team-bot cc @BegoniaHe |
|
cc @BegoniaHe (via @YUZHEthefool) |
Replace the /work status subcommand with a GreedyStr /work command so /work <task> continues into the work loop without the classifier. Keep /work and /work status as status queries. Drop the classifier /work prefix, share is_work_loop_enabled, and document the command-then-agent exception. Command identity is builtin_commands:work. AI-Generated: true Generated-At: 2026-09-06T17:32:35Z
Keep BTW conversation-loop computer-use isolation while adopting master's default computer_use_runtime of none. AI-Generated: true Generated-At: 2026-09-07T17:40:52Z
Replace discussion identifiers with the published Issue and PR links, record the stacked feature delivery, and clarify unfinished experiments. Refs: #122 AI-Generated: true Generated-At: 2026-09-10T16:54:52Z
Extract Computer Use boundaries from the original PR #28 prototype and apply them through the current request tool catalog. Keep conversation requests and handoffs free of computer tools, and let work select the inherited, local, sandbox, or disabled runtime without changing authority. Fixes #129 AI-Generated: true Generated-At: 2026-09-10T15:38:41Z
Extract Computer Use boundaries from the original PR #28 prototype and apply them through the current request tool catalog. Keep conversation requests and handoffs free of computer tools, and let work select the inherited, local, sandbox, or disabled runtime without changing authority. Fixes #129 AI-Generated: true Generated-At: 2026-09-10T15:38:41Z
Summary
Keep PR #28 as the bilingual design and delivery overview for BTW conversation/work loops. The original implementation is extracted into ten separate feature PRs; three sibling draft PRs track classifier experiments. Each feature is linked to its own Issue and placed in a dependency stack so its diff covers one capability.
Routing inside conversation remains the preferred experiment: conversation sees its resolved capabilities, handles requests it can fulfill, and hands off work when needed. Product adoption is deferred until the alternatives have comparable evidence.
Related issue
Related: #122. The parent and all 13 native Sub-issues are published in English. This documentation PR does not close the implementation tracker.
Split PRs
mastersplit/btw-result-deliverysplit/btw-entrysplit/btw-work-runtimesplit/btw-work-commandsplit/btw-work-statussplit/btw-loop-modelssplit/btw-loop-runtimesplit/btw-plugin-routessplit/btw-mcp-routessplit/btw-skill-routessplit/btw-skill-routessplit/btw-skill-routesThe three classifier PRs share the final capability branch as their base and have no dependency on one another. R1 extracts the rule implementation present in the original prototype. R2/R3 contain experiment plans because the prototype did not implement those candidates; implementation and comparative evaluation remain outstanding.
Pages
docs/zh/dev/btw-dual-loop.mddocs/en/dev/btw-dual-loop.mddocs/.vitepress/config.mjsImplementation notes
The final diff contains only the two design pages and their sidebar entries. The overview records scope, actual Issue/PR links, dependencies, and the shared classifier evaluation protocol. Runtime changes use the current message pipeline, tool catalog, Skill snapshots, authorization, and request-scoped WebChat contracts in their respective feature PRs.
Validation
pnpm run docs:buildindocs/: passed with the production/help/base.make check-md PYTHON=D:/project/Zephirum/.venv/Scripts/python.exe PARALLEL_JOBS=2: passed.git diff --check: passed.Local documentation checks use Node 24.11.1 and pnpm 11.21.0. CI validates the pinned Node 26 baseline. Feature test evidence is recorded in the individual PRs and parent Issue; this documentation change does not constitute classifier evaluation.
Compatibility and risk
The overview labels the capabilities as follow-up work until their PRs are reviewed and merged. Experimental drafts remain incomplete and do not enable routing by default. Generated documentation output is excluded, and the integrated
/help/documentation model is retained.Checklist
Agent note
Codex extracted and published the per-feature PRs and English Issue hierarchy at the user's request, retained this documentation overview, and verified the links and branch dependencies. AI assisted source analysis, extraction, current-path adaptation, tests, and bilingual documentation. Checks and remaining experimental work are stated above and in each PR. This note is not a maintainer approval or independent AI-assisted review. No PR was merged.