Compare commits

...
Author SHA1 Message Date
weichen a47754b494 Add EXT-1 inproc command handler proposal; align tag name to v0.2.0
Why: EXT-1 申请卡按 compat-policy §6 四要素成文,作为 surface 变更的提案记录;
compat-policy §5 的 v0.2.0-experimental 与 V1.2 定稿批复的 v0.2.0 口径不一致,统一为后者。
Evidence: 新增 docs/proposals/2026-10-05-ext-1-inproc-command-handler.md;§5 tag 命令改为 v0.2.0 并注明对齐来源。
Impact: 仅文档;下游与基座发版材料(12 号核对清单)按 v0.2.0 口径。
Refs: 2026-10-01-extension-compat-policy.md §5/§6;10-实施推进方案 V1.2 D2 批复。
2026-10-05 11:39:13 +00:00
weichen f3f823ef8d Record the SillyTavern design borrow and what shipped
Why: The comparison has to say what was borrowed, what was refused, and which product behaviors changed, without copying upstream text.
Evidence: The record lists the verified security gaps and the features implemented in this task. Proposal section 2.5 links to it.
Impact: Deferred and rejected items stay deferred and rejected. Adopted context and secret behavior is no longer described as a later batch.
Refs: feature/sillytavern-review
2026-10-04 18:20:10 +00:00
weichenandCursor 847f6815f1 Record the pi and ZCode second-pass review
Why: The October 3 durable note described Pico5 before packages 1-23 existed, and the stack proposal had no place for the v1.0.1 and ZCode v3.14.3 findings.
Evidence: The new record, the correction at the top of the durable note, and proposal section 2.4 link to each other. No code changes.
Impact: Documents only. Later batches can cite the adopt, hold, and reject rows.
Refs: Dev feature/pi-zcode-second-pass
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 15:37:49 +00:00
weichen 261c4cf21d Merge pull request 'Record the pi durable review and the four recovery fixes' (#35) from feature/pi-durable-borrow into main
Record the pi durable review and the four recovery fixes
2026-10-03 14:44:38 +00:00
weichenandCursor 63409005a7 Note that the agent-stack proposal is already on main
Why: The review record still said the proposal was unmerged, but Documents PR #34 is on main and this branch is cut from that commit.
Evidence: origin/main is 76044b5, the merge of feature/agent-stack-blueprint.
Impact: One sentence in the review record. The proposal text is unchanged.
Refs: Dev feature/pi-durable-borrow
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 14:37:48 +00:00
weichenandCursor 02a3441eb7 Record the pi durable review and the four recovery fixes
Why: Pico5's package is a contract sketch, but its commit and recovery rules match four verified gaps in how runs survive a hard kill.
Evidence: Reviewed earendil-works/pi @ c7cdb460a, packages/durable and docs/pico-v5.md, against the skeleton section 4.2 and the current backend store, run manager, and SSE handler.
Impact: Documents only. The record rejects taking the package as a dependency and lists what the matching backend task changes.
Refs: Dev feature/pi-durable-borrow
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-03 14:27:18 +00:00
weichen 76044b5895 Merge pull request 'Propose the agent stack audit and completion design' (#34) from feature/agent-stack-blueprint into main 2026-10-03 14:12:17 +00:00
weichenandCursor 233dfb54ea Propose the agent stack audit and completion design
Why: The three libraries cannot be reused or recovered independently, and the tested dependency combination is not the one the product builds.
Evidence: Read-only audit at native c6d867c, squad 590fd86, intent 1dbbe81, recorded with file references in docs/records/2026-10-02-agent-stack-audit.md.
Impact: Documents only. No library code or gitlinks change in this commit. Later stages stay separate tasks.
Refs: Dev feature/agent-stack-blueprint
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-02 16:27:37 +00:00
weichen acd6f01c40 Merge pull request 'Record the P4 workflow rulings' (#33) from feature/agent-harness-p4 into main
Merge pull request #33
2026-10-02 01:03:55 +00:00
weichenandCursor 974db6a011 Record the P4 workflow rulings
Why: The harness proposal still said the script decision was deferred, after the implementation chose both declarative workflows and two dynamic forms.
Evidence: The P4 section now states routing order, copy-on-start, step and retry caps, and the script switch.
Impact: Documentation only.
Refs: feature/agent-harness-p4
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 16:22:13 +00:00
weichen 357e3fdb24 Merge pull request 'Propose the agent harness and link it from the roadmap' (#32) from feature/agent-harness-p0 into main 2026-10-01 12:43:26 +00:00
weichenandCursor a3f0b80759 Propose the agent harness and link it from the roadmap
Why: Tool-call history is flattened into assistant text, so the model imitates calls and invents artifact ids. The fix belongs to a shared harness, not another prompt tweak.
Evidence: Proposal 2026-10-01-agent-harness.md records the 2026-10-01 session evidence, the ZCode and Claude Code patterns, and the P0-P4 acceptance.
Impact: Documents only. P0 code lands in the backend on the same branch name. P1-P4 are not implemented here.
Refs: feature/agent-harness-p0
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 12:25:31 +00:00
weichen f3a3a5deb4 Document the base extension roadmap and plugin contract v2
Why: The base must stay unmodified by downstream products, so the roadmap, compatibility window, and plugin contract need a written proposal before further forks.

Evidence: Three proposals under docs/proposals and a short pointer in the plugin development guide.

Impact: Tag commands in the compat proposal are not executed.

Refs: Agent-System-Dev feature/base-extension-roadmap
2026-10-01 10:47:11 +00:00
weichenandCursor a0da588f90 Document the base extension roadmap and plugin contract v2
Why: The base must stay unmodified by downstream products, so the roadmap, compatibility window, and plugin contract need a written proposal before further forks.
Evidence: Three proposals under docs/proposals and a short pointer in the plugin development guide.
Impact: Downstream work follows extension-point requests and a published base version. Tag commands in the compat proposal are not executed.
Refs: .task/feature-base-extension-roadmap.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 10:34:30 +00:00
weichen 8ee5d5c467 Merge pull request 'Document project inbox dual view and archive flow (CF3)' (#30) from feature/project-inbox-r1 into main 2026-09-30 19:19:28 +00:00
weichen 1b1489203d Document project inbox dual view and archive flow (CF3)
Why: D48-D51 add the project inbox (archive-to-project, aggregated
project view, project stream); guide needs the user-facing story.
Evidence: usage.md 4.2 gains project inbox bullet; badge note updated.
Impact: docs-only.
Refs: 2026-09-30-card-stream-artifact-unify-project-inbox.md (D48-D52).
2026-09-30 19:18:12 +00:00
weichen 6fdfc19c54 Merge pull request 'Document artifact facade cards and context backfill (CF-A)' (#29) from feature/artifact-unify-r1 into main 2026-09-30 18:42:14 +00:00
weichen 96c8404c5e Document artifact facade cards and context backfill (CF-A)
Why: D46/D47 add inbox facade cards for truncated tool results and
artifact text backfill; guide needs the unified presentation story.
Evidence: usage.md 4.5 gains facade/backfill bullet.
Impact: docs-only.
Refs: 2026-09-30-card-stream-artifact-unify-project-inbox.md (D46/D47).
2026-09-30 18:40:50 +00:00
weichen f5aa687cb1 Merge pull request 'Document card streaming three-tool flow (CF-S)' (#28) from feature/card-stream-r1 into main 2026-09-30 18:21:41 +00:00
weichen 3d2da4eb0b Document card streaming three-tool flow in usage guide
Why: CF-S (D44/D45) adds card_begin/card_append/card_commit; users need
the streaming flow and crash-fallback behavior documented.
Evidence: usage.md cardflow bullet updated to v2 flow.
Impact: docs-only.
Refs: 2026-09-30-card-stream-artifact-unify-project-inbox.md (D44/D45).
2026-09-30 18:19:39 +00:00
weichen 437af31118 Merge pull request 'Propose card stream, artifact unification, and project inbox (D44-D52)' (#27) from feature/cardflow-future-plan into main 2026-09-30 17:45:16 +00:00
12 changed files with 1086 additions and 2 deletions
+2
View File
@@ -5,6 +5,8 @@
参考实现:Dev 仓 `plugins/example`(覆盖全部五类能力,可直接复制改造)。 参考实现:Dev 仓 `plugins/example`(覆盖全部五类能力,可直接复制改造)。
契约 v2(声明 / 进程内编译 / sidecar / 前端 ESM,下游仓不得改基座)见提案 `docs/proposals/2026-10-01-plugin-contract-v2.md`。`make plugin-new NAME=<id>` 生成声明式骨架。
## 1. 目录结构 ## 1. 目录结构
``` ```
+4 -2
View File
@@ -83,7 +83,7 @@ AGENT_SYSTEM_CONFIG=/etc/agent-system.json agent-system serve # 环境变量
- **流式渲染**:正文逐字输出;思考过程(灰字竖线)与工具调用(芯片)实时展示;顶部状态点显示事件流连接。 - **流式渲染**:正文逐字输出;思考过程(灰字竖线)与工具调用(芯片)实时展示;顶部状态点显示事件流连接。
- **过程折叠(Cursor 式)**:流式期间思考/工具/中间轮解说照常显示;**最终回复一开始流式,过程立即折叠**为「过程输出 · N 次工具调用」摘要行,只显式显示回复(工具恢复时重新展开);任务终态后保持折叠,点击展开回看(设置 → 界面 → 可改默认行为)。 - **过程折叠(Cursor 式)**:流式期间思考/工具/中间轮解说照常显示;**最终回复一开始流式,过程立即折叠**为「过程输出 · N 次工具调用」摘要行,只显式显示回复(工具恢复时重新展开);任务终态后保持折叠,点击展开回看(设置 → 界面 → 可改默认行为)。
- **长回复折叠**:终态后超过 800 字的回复自动收成「📄 长回复 · N 字(点击展开)」摘要行——流式期间全文可见,历史消息同样适用。 - **长回复折叠**:终态后超过 800 字的回复自动收成「📄 长回复 · N 字(点击展开)」摘要行——流式期间全文可见,历史消息同样适用。
- **长结果进右侧卡片(默认关)**:设置 → 界面 →「长结果进右侧卡片」开启后,报告/纪要/方案/对比/文档草稿等**产物型**长内容经 `submit_card` 进右栏收件箱(result 卡,可置顶/收纳);中栏保留结论摘要 + 锚点部件(`📄 标题 · N 字 ▸ 展开全文 · ↗ 收件箱`)——锚点可无跳转展开全文,右栏收起时阅读不残废;解释型长回答不进卡。下一轮对话上下文自动回填卡片全文(「🧩 上下文构成」可见 card(n) 条目)。 - **长结果进右侧卡片(默认关)**:设置 → 界面 →「长结果进右侧卡片」开启后,报告/纪要/方案/对比/文档草稿等**产物型**长内容进右栏收件箱(result 卡,可置顶/收纳);长正文优先流式三段式——`card_begin` 建卡(收件箱即时出现 active 卡)→ `card_append` 逐段追加(卡片实时刷新,事件按 250ms 窗口节流)→ `card_commit` 收尾(转 done 并锚定到对话);一次性短产物走 `submit_card`。中栏保留结论摘要 + 锚点部件(`📄 标题 · N 字 ▸ 展开全文 · ↗ 收件箱`)——锚点可无跳转展开全文,右栏收起时阅读不残废;解释型长回答不进卡。下一轮对话上下文自动回填卡片全文(「🧩 上下文构成」可见 card(n) 条目);run 异常终止时未收尾的流式卡自动置 done/failed 并标注「未正常收尾」,内容不丢。
- **上下文构成观测**:每条助手消息尾部「🧩 上下文构成」折叠芯片——该轮模型实际看到什么(系统提示/历史条数/附件/知识命中)与裁剪记录(历史超窗/工具结果超限),输入规模可解释。 - **上下文构成观测**:每条助手消息尾部「🧩 上下文构成」折叠芯片——该轮模型实际看到什么(系统提示/历史条数/附件/知识命中)与裁剪记录(历史超窗/工具结果超限),输入规模可解释。
- **附件**:📎 上传 md/txt/docx/pdf/图片——文档经内置解析器(确定性代码解析:md/txt 直读、docx 解 OOXML、pdf 提取文本层)转为中间表示注入对话;解析结果与警告显式可见(不静默)。 - **附件**:📎 上传 md/txt/docx/pdf/图片——文档经内置解析器(确定性代码解析:md/txt 直读、docx 解 OOXML、pdf 提取文本层)转为中间表示注入对话;解析结果与警告显式可见(不静默)。
- **输入增强**:`/` 唤起命令菜单(/new /mock /model /profile /help);`@knowledge` 在消息中触发知识检索注入。 - **输入增强**:`/` 唤起命令菜单(/new /mock /model /profile /help);`@knowledge` 在消息中触发知识检索注入。
@@ -113,7 +113,8 @@ AGENT_SYSTEM_CONFIG=/etc/agent-system.json agent-system serve # 环境变量
- 任务卡七类(progress/confirm/compare/review-list/result/error/status):状态灯**形状+色双编码**(◆ 待处理 ● 完成 ✓ 已确认 ✕ 失败 ◌ 已关闭——不只靠颜色);置顶不随滚动;`#upload` 演示摄取→完成→冲突对比卡全周期。 - 任务卡七类(progress/confirm/compare/review-list/result/error/status):状态灯**形状+色双编码**(◆ 待处理 ● 完成 ✓ 已确认 ✕ 失败 ◌ 已关闭——不只靠颜色);置顶不随滚动;`#upload` 演示摄取→完成→冲突对比卡全周期。
- 卡上动作即命令(card 入口,见 §5);确认结论以**回执消息**进对话时间线留痕。 - 卡上动作即命令(card 入口,见 §5);确认结论以**回执消息**进对话时间线留痕。
- **收纳**:🧹 一键收纳完成卡(done→关闭;已确认卡盖章留痕不动);右栏收起时角标显示待处理数。 - **收纳**:🧹 一键收纳完成卡(done→关闭;已确认卡盖章留痕不动);右栏收起时角标显示待处理数(本会话 + 项目未决合计)。
- **项目收件箱(CF3,跨会话待办不丢)**:收件箱内「本会话 / 项目」两级切换。会话卡点 🗂 **留档到项目**——复制为项目级卡(原卡关闭留痕,不移动时间线;HITL/命令确认卡不可留档),跨会话可见;`submit_card`/`card_begin` 也可带 `scope=project` 直产项目卡。项目视图聚合 = 项目级卡片 ∪ 其他会话未决卡(done 留在原会话防噪音),经独立项目事件流实时刷新。
- 卡片↔消息**互定位**(D32):消息内 `card:` 链接点击定位收件箱卡片;HITL/确认卡锚定发起消息内联双现。 - 卡片↔消息**互定位**(D32):消息内 `card:` 链接点击定位收件箱卡片;HITL/确认卡锚定发起消息内联双现。
### 4.3 工作区弹层(深读与治理视图,⌘1–9) ### 4.3 工作区弹层(深读与治理视图,⌘1–9)
@@ -134,6 +135,7 @@ AGENT_SYSTEM_CONFIG=/etc/agent-system.json agent-system serve # 环境变量
- 上传与模型产出统一入 Artifacts;`write_artifact` 工具可被模型调用。 - 上传与模型产出统一入 Artifacts;`write_artifact` 工具可被模型调用。
- 查看:markdown 在线渲染;文本类可**编辑保存**;二进制(docx/pdf)下载;图片内联预览。 - 查看:markdown 在线渲染;文本类可**编辑保存**;二进制(docx/pdf)下载;图片内联预览。
- 文档工件与上传文本自动进**知识索引**(全文检索)。 - 文档工件与上传文本自动进**知识索引**(全文检索)。
- **工件门面卡(与产物卡统一呈现)**:工具结果被截断超限时自动在收件箱留一张 `result` 门面卡(摘要 + 来源工具 + `doc-ref` 引用,点击直开查看器)——字节权威仍在工件(可编辑/下载),卡只做门面与锚;下一轮对话上下文会自动回填工件文本(上限与产物卡同为 12000 字,超限在「🧩 上下文构成」可见 `artifact(n)` 与截断记录),模型不再对工具大结果失忆。
### 4.6 知识检索 ### 4.6 知识检索
+132
View File
@@ -0,0 +1,132 @@
# Agent harness:回合循环、结果把控与多代理编排
状态:**proposed(2026-10-01)**;owner:weichen;主任务:Dev 仓 `feature/agent-harness-p0`。
规范依据:Development-Guidelines(workflows/delivery.md、standards/collaboration.md、standards/agent-execution.md);本文是项目级消费,不复制其正文。
撰写日期:2026-10-01。对标来源:[zai-org/ZCode](https://github.com/zai-org/ZCode)(Apache-2.0,`apps/zcode-cli/packages/core`)、[Claude Code 公开文档](https://code.claude.com/docs/en/overview) 与其插件仓 [anthropics/claude-code](https://github.com/anthropics/claude-code)。
本文只定义 harness 的职责、分期与验收。P0 随本文落地;P1–P4 各自另开任务,不在本文实施。
后续:三个库如何补齐缺陷、吸收外部做法并彼此解耦,见 [2026-10-02-agent-stack-completion.md](2026-10-02-agent-stack-completion.md)。本文的 P0–P4 不因该提案作废。
---
## 1. 问题与证据
2026-10-01,会话 `cnv_96f639abf03a` 第 26 轮(开发库 `runtime/dev/data/agent-system.db`)用户要求「重新总结纪要」。助手正文里出现了 `[调用工具 run_python(…)]`、`[工具结果 ok …]`、`[产出工件 …(二进制内容,正文见工件查看器)]`,并给出工件 ID `art_1a2b3c4d5e6f`。该轮落库部件只有 `thinking` 与 `text`,没有任何 `tool_call`;`artifacts` 表里没有这个 ID,所以界面显示「未找到该文件」。同会话第 16、20 轮的 `art_7b24c0b7e2f1`、`art_4f9d2c1a8b3e` 同样不存在。第 22、24 轮才是真实的工具调用与工件。
直接原因在上下文组装:`modules/backend/internal/ctxasm/ctxasm.go` 把历史里的工具调用压成文本 `[调用工具 …]` / `[工具结果 …]` 写进助手消息。模型把这种文本当成「我以前就是这样调用工具的」格式,在正文里模仿。虚假交付守卫(`claimsFalseDeliverable`)只纠正一次,第二段「文件已正常生成并登记」既没有 `art_` 编号,也匹配不上「已生成….docx」正则,被当成最终回复放行。
同一机制下还有这些结构性缺口(2026-10-01 只读梳理):
- 三套循环各自为政。直连走 `LLMRunner`;小队成员走 `agent-native` 的 `runtime.Run`(已有 `GetSteering` / `GetFollowUp`,直连没用);`delegate_task` 背后的 `SubLLMRunner` 是单次调用、没有工具。
- 是否完成由干活的模型自报。`quality_review` 与版式检查只靠提示词约定;`agent.sub.verify` 要求输出的 `VERDICT` 没有解析方;模型评分 `Judge` 只有类型、没有接线。
- 没有逐轮提醒。计划和待办只给界面看,不回灌给模型。技能只按当前这条用户文本匹配一次。
- 子 run 的助手消息落在同一会话,列表不按 run 过滤,下一轮会被当成主对话回放。
- 插件工作流只有数据、没有执行器。`WorkflowDef.IntentID` 没有消费方。意图识别没有路由到工作流。
- 预算耗尽时直接返回错误,已生成的部件不落库。工具结果按字节截断,可能切在 UTF-8 字符中间。
## 2. 对标结论
两边的共同点:工具调用按提供方的原生消息回放,而不是压成正文;完成与否由循环或另一个评估者判定,不由干活的模型宣布;子代理上下文隔离,只把摘要交回父会话;每条续跑路径都有次数上限。
| 机制 | ZCode | Claude Code | 本基座采纳 |
|---|---|---|---|
| 历史回放 | `tool_calls` + `role=tool`;没有结果的调用补占位,并区分「未执行」与「副作用未知」 | 同形(公开文档描述为工具结果回传) | 采纳。占位文案按原因区分 |
| 完成判定 | 同模型、不带工具,按证据清单输出 `{passed, reason, nextAction}`;拿不准判不通过。验证器自身出错时放行 | `/goal`:每轮结束后由另一个模型判断,不成立就开下一轮 | 采纳证据清单。交付物场景下验证器出错改为判不通过,并设次数上限 |
| 结束门禁 | Stop hook 返回继续并注入上下文,最多 3 次 | Stop / SubagentStop 返回 `decision: block` + `reason`;`stop_hook_active` 防止死循环 | 采纳 `block + reason`,上限 3 |
| 提醒 | 按来源节流,包在 `<system-reminder>` 里,位置不破坏工具消息配对 | 规范、待办、后台完成以 system-reminder 插入,不改 system 段 | 采纳 |
| 异常 | 同名同参数连续 3 次给警告,不直接终止;输出被截断最多续写 3 次,续写提示不入库 | 公开文档未给出同等细节 | 采纳警告与续写 |
| 子代理 | Markdown frontmatter 定义;调用方不能指定模型;嵌套深度从工具白名单上限制为 1;后台完成用通知回传,多条合并 | 同形。子代理只回传最终结果,中间过程不进父上下文 | 采纳 |
| 评审 | expert 的 critic 用 JSON 判定,可按节点重开,每层有上限 | `code-review` 插件:并行找问题,每条再独立验证,未通过的丢弃 | 采纳「并行评审 → 逐条验证 → 过滤 → 汇总」 |
| 工作流 | 阶段产物落盘,下游只传路径;确定性检查优先于模型自报;人工升级以单个节点为粒度 | feature-dev:阶段之间有人工门禁 | 采纳产物落盘与确定性检查优先。P4 同时做声明式工作流、模型生成的步骤和沙箱脚本 |
## 3. 目标
harness 是基座能力,不是某个下游的领域规则。它回答四件事:这一轮看到什么、循环何时继续或停止、怎样判定结果为真、多个代理怎样拉起、分配、评审和汇总。
内核放在 `agent-native` 的 `runtime`,直连、小队和子代理共用同一套循环。`core.Part` 到模型消息的投影留在 backend:内核不认识 `core` 类型。
```text
agent-native runtime
TurnLoop 预算、输出续写、重复调用检测
Transcript 原生回放,没有结果的调用补占位
ReminderBus 按来源节流的提醒,不改 system 段
HookBus PreTool / PostTool / Stop / SubagentStop
CompletionGate 确定性检查 + 不带工具的验证器
SubagentRuntime profile、深度 1、只回传摘要、完成通知
backend 适配
LLMRunner 薄适配:事件、部件、落库
ctxasm core.Message 投影为模型消息
交付物门禁 工件存在、版式检查、quality_review
工作流执行器 引擎驱动步骤,IntentID 路由
```
## 4. 分期
### P0 回放保真与守卫加固(本文随附实施)
- 助手历史按 `assistant{ToolCalls}` 与 `tool{ToolCallID, Content, IsError}` 回放。没有结果的调用补「(无结果:运行中断)」;孤立的结果跳过;空 ID 用 `hist_<消息ID>_<序号>` 补上。
- 参数超过 4000 字符时截断其中的长字符串,结果仍是合法 JSON。
- 跳过被守卫降级的过程文本(`Process=true` 且后面紧跟的不是工具调用)。从助手历史文本里剔除存量的 `[调用工具 `、`[工具结果 `、`[产出工件 `。
- 守卫改成 `deliverableGate(body, produced, rc) (block bool, reason string)`,形状与 P1 的 Stop 门禁一致,P1 时平移进内核。
- 新增判定:正文里出现伪造调用标记;本轮没有工件时声称文件已生成、已登记或已保存。纠正上限 2 次。回灌用占位文本,不把伪造正文再塞给模型。两次之后仍谎称,终轮正文降为过程,并追加一条非过程提示,说明本轮没有实际生成文件。
- 提示词 `base.deliverables` 写明:工具只能通过函数调用触发,不要在正文里书写调用标记。版本升到 5。
验收:回放后的模型输入里不再出现 `[调用工具` 文本;用第 26 轮那种「只有伪造标记、没有真实工具调用」的输入,守卫会拦住,不会再登记一个不存在的工件 ID。
### P1 内核原语
给 `agent-native` 的 `runtime.Config` 加上 Stop 门禁、工具前后钩子、提醒通道、重复调用检测、输出被截断时的续写,以及预算耗尽时保留已有部件再收尾。`LLMRunner` 改为 `runtime.Run` 的薄适配。P0 的 `deliverableGate` 平移成一个 Stop 门禁。
验收:backend 与 agent-native 的既有测试保持通过;直连与小队成员走同一个循环。
风险:`agent-native` 在 Dev 仓 `deps/`、backend 的 `deps/`、agent-squad 的 `deps/` 各有一处 gitlink。P1 起要按依赖顺序推进指针,不能只改最外层。
### P2 结果把控
- 独立验证器:不带工具的一次模型调用,按证据清单输出 JSON `{passed, reason, nextAction}`。拿不准判不通过。验证器出错或返回无法解析的内容时也判不通过(与 ZCode 的放行不同),整个门禁最多 3 次。
- 确定性检查排在模型自报之前:本轮登记的工件确实存在、`housestyle.lint` 没有 error、`quality_review` 达到通过线。
- 计划和待办按节奏用提醒回灌。例如连续多轮没有更新待办才提醒一次,避免每轮都占上下文。
验收:评测夹具里「声称已交付但没有工件」的用例全部被拦下,并留下可核对的原因。
### P3 多代理
- 子代理用 Markdown 加 frontmatter 声明(`name`、`description`、`tools`、`model`、`maxTurns`),复用现有 `agent-sub-*.md` 并补上 frontmatter。调用方不能指定模型。
- `delegate_task` 改为带工具的子循环。工具白名单里去掉派发类工具,嵌套深度为 1。父会话只收到摘要和工件路径。
- 支持后台运行。完成后用通知回传,多条合并成一次注入。
- 子 run 的消息与父会话历史隔离,不再被下一轮当成主对话回放。
- `squad_orchestrate` 与直连子代理用同一套运行时。评审走「并行找问题 → 每条独立验证 → 丢掉未验证的 → 汇总」。
### P4 意图到工作流
执行器驱动 `prompt` / `tool` / `hitl` / `artifact` / `card` / `script`。三种来源共用同一步骤循环:插件声明的工作流、模型生成并经过同一套校验的步骤、以及在会话沙箱里跑的 Python 脚本。
- 路由先走命令总线,再看意图绑定的工作流,最后才是普通回合。小队入口不提供 `run_workflow`。
- 工作流启动时拷贝定义。插件卸载不影响已经在跑的那一份。
- 声明式工作流最多 20 步。模型生成的最多 8 步,不能含 `script`,执行前必须经用户确认。critic 每步最多重试 3 次、缺省 1 次,整条工作流最多重试 6 次。无法解析的 critic 结果按不通过处理。
- 脚本最多回调 50 次,单行请求不超过 1MB,超时缺省 600 秒、最长 1800 秒,墙钟时间包含等待人工和子代理。开关 `runtime.workflowScripts` 缺省关闭。模型写的脚本运行前必须确认;插件自带的脚本不需要确认。
- 脚本以当前用户身份运行,工作目录是会话沙箱,没有网络或文件系统隔离。回调一律经过工具执行入口,不能调用 `run_python`、`run_shell`、`run_workflow` 和派发类工具。
## 5. 不在本文范围
- 单份纪要的内容质量(例如「3 项决议」与表格行数对不上)。那是生成质量,不是回放与门禁。
- 把直连路径并进小队入口。小队仍是一种入口,循环归内核。
- 下游领域的阶段门禁、文档树和追溯关系。那些留在下游仓。
## 6. 已知待查项
以下来自同一次只读梳理,不在 P0 修改。各自另开任务前先复核当前代码。
- `quality_review` 的工作区路径疑似用了全局根,而文件工具用的是 `<根>/<owner>/<会话>`。单测把根直接设成文件所在目录,可能测不出来。
- 工具确认卡算出了参数预览,但没有放进卡片,用户看不到参数就要批准。
- 小队入口读的是默认 owner 的设置,不是当前租户的。
- 插件宿主 `SettingsSchema` 持读锁时再调用会再次取读锁,中间若有写锁等待,有死锁风险。
## 7. 验收(P0)
- `modules/documents`:本文与路线图第 3 阶段的链接。
- `modules/backend`:`go test ./internal/ctxasm/ ./internal/runtime/ ./internal/prompts/` 通过;随后在 Dev 仓 `make test` 做组合验证。
- 回放用例覆盖:真实工具调用变成原生消息、缺结果时补齐、超长参数仍是合法 JSON、降级文本被跳过、存量伪造标记被剔除、工件正文回填保持不变。
- 守卫用例覆盖:只有伪造标记也会拦截、纠正两次后附系统提示、回灌的是占位文本、引用已存在的工件不算谎称。
@@ -0,0 +1,80 @@
# 基座路线图修正:基座不可改,下游独立仓二次开发
状态:**实施中(2026-10-01)**;owner:weichen;主任务:Dev 仓 `feature/base-extension-roadmap`。
规范依据:Development-Guidelines(workflows/delivery.md、standards/collaboration.md);本文是项目级消费,不复制其正文。
撰写日期:2026-10-01。
---
## 1. 基准
Agent-System 是通用智能体基座:提供入口、能力、接口和基础功能,并能理解需求、对话、完成任务。页面、功能、工具、技能、MCP、知识库等模组通过插件接入。
`~/downloads/agent-system-design`(智能研发管理系统,9 篇文档)是**下游特别系统**。它将在一个或多个独立仓库里二次开发,**不得修改基座**。修改基座的唯一合法理由是新增基础能力或扩展接口,路径为:下游提出扩展点申请 → 基座实现并发版 → 下游升级。
基座只提供引擎、注册点和公开接口。领域数据与领域规则留在下游。延续骨架设计 4.12:知识库是数据,检索引擎是基础设施。
已拍板且本次不推翻的约束:非容器化、单二进制优先;不迁移老系统资产与数据格式;在途 run 不做透明恢复(标记 `interrupted`,用户重发)。
## 2. 原差距清单的处置
对照基准从「两树一网蓝图」改为「通用基座 + 下游不改基座」。原清单编号沿用讨论稿。
### 2.1 留在基座
| 编号 | 项 | 本次落点 |
|---|---|---|
| 3 | 持久化后台任务,与会话锁解耦 | `background_tasks` + `/api/tasks` |
| 5 | 仅 cmdbus 待确认态持久化 | `pending_confirms`;run 仍不透明恢复 |
| 6 | HITL 超时计时 | `AskHITL` 尊重 `TimeoutSec` |
| 8 | 记忆实现 | `memory_entries` + `/api/memory` |
| 9 | 默认可选嵌入 | `models.embedding.provider=hash` 本地档;未设置仍按 D23 退化为全文 |
| 13 | 通用审计(不含领域依据包) | `audit_events` + `/api/audit` |
| 17 | LLM 成本与预算告警 | 日 token 累计,超预算写审计 |
| 20 | 命令双入口对拍 | 每个内置命令必须出现在 `command-pairs.json`,否则测试失败 |
| 22 | logging / error-code 接线 | 启动日志走 `logging-system`;插件失败带 `sys.unavailable` |
| 24 | 依赖边界 lint | `make test-boundaries` 并入 `make test` |
上下文滚动压缩:`summarizeBeyond=true` 时,窗口外历史改为抽取式摘要注入,不再只丢弃。
代码执行:沿用已有解释器沙箱(工作区、超时、`runtime.exec` 开关),不另开无沙箱执行器。
### 2.2 交给下游仓
版本链与三态关系边、两速管道、文档/切片并发编辑、切片标签 schema、加密与 OCR 档位、ABAC 四元组、审计依据包、飞书业务、通知聚合、领域模板与流程骨架、领域评测指标、三套环境与故障注入、领域导出导入,以及设计文档自己的数据模型欠账。
基座只提供这些下游会用到的通用口:渠道声明、知识数据集声明、后台任务、审计写入、插件隔离存储、混合检索。
### 2.3 不采纳
- 容器编排交付(与骨架「非容器、单二进制」冲突)。
- 旧系统资产导入(与「不迁移、不兼容老数据格式」冲突)。
- 为单一领域预造版本链、三态边、时间点重建。出现第二个使用方再上提基座。
## 3. 阶段
1. **公开扩展面与兼容规则**(本提案第 4 节 + 同日兼容提案)。下游今天必须能依赖基座,而不是 fork。
2. **插件契约 v2**(同日插件提案):声明、进程内编译、sidecar、前端 ESM。
3. **智能体质量回路**:后台任务、记忆、上下文摘要、本地嵌入档、HITL 超时、确认态持久化、LLM 预算、通用评测夹具。回合循环、完成判定与多代理编排见 [2026-10-01-agent-harness.md](2026-10-01-agent-harness.md)(P0 起实施,内核下沉到 agent-native)。三个库的缺陷、借鉴与解耦见 [2026-10-02-agent-stack-completion.md](2026-10-02-agent-stack-completion.md)。
4. **工程基座**:日志与错误码、通用审计、边界 lint、命令对拍、会话导出。
目标系统薄切片放在 `examples/downstream-rdm`。它只 import 公开包。独立远程仓库由 owner 另行创建;本次不 `git init` 新仓。
## 4. 公开扩展面(不改模块路径)
后端业务代码继续留在 `internal/`。新增可导入包:
- `agent-system-backend/pkg/extension`:进程内注册、宿主 API 客户端、`Main`。
- `agent-system-backend/pkg/extension/link`:把 `Main` 接到 `app.Run`(仅基座与下游 main 空白导入)。
模块路径保持 `agent-system-backend` / `agent-system-core` / `agent-system-ui`。下游 `go.mod` 用 `replace` 指向检出或 Gitea SSH。改成可直接 `go get` 的路径会打断现有全部 import,留到单独发版任务。
`agent-system-ui` 仍是源码组合(提案 D29:出现第二宿主再发 npm)。本次补上 `exports` 指向的插件 SDK 源文件,不执行 npm 发布。
公开面版本:`extension.SurfaceVersion = 2.0.0-experimental`。契约快照测试锁定导出符号;增删导出必须改快照。
版本 tag 与包发布是高后果操作。准备命令见兼容提案,由 owner 确认后执行。本次不打 tag、不推送。
## 5. 扩展点申请
下游缺能力时开任务卡,写清:调用场景、希望的公开符号或 HTTP 路径、不改基座时的阻碍。基座只在批准后加公开面并发版。下游禁止提交修改 `internal/` 的补丁作为交付。
@@ -0,0 +1,60 @@
# 扩展面兼容与演进
状态:**实施中(2026-10-01)**;owner:weichen;主任务:Dev 仓 `feature/base-extension-roadmap`。
撰写日期:2026-10-01。配套:[基座路线图修正](2026-10-01-base-extension-roadmap.md)、[插件契约 v2](2026-10-01-plugin-contract-v2.md)。
## 1. 版本
| 面 | 标识 | 规则 |
|---|---|---|
| 插件清单 | `apiVersion` | 接受 `1.0` 与 `2.0-experimental`。其他值整包拒绝(fail-closed) |
| 公开 Go 包 | `extension.SurfaceVersion` | `2.0.0-experimental`。实验窗口内允许破坏性调整,但必须改契约快照并在提案里写一句 |
| HTTP API | 路径前缀 `/api/` 与 `/host/v1/` | 已发布路径只加字段,不改语义、不删字段。新能力用新路径 |
弃用:公开符号先标记注释 `Deprecated:`,至少一个下游可升级的版本之后才删除。实验窗口内的删除仍要改快照测试,不能静默。
## 2. 兼容窗口对插件清单的具体含义
- `1.0`:原有 tools/panels/cards/intents/prompts/settings/commands/workspaces 行为不变。`requires.network` 为空时出站仍放行(历史清单没有白名单)。
- `2.0-experimental`:同上,并接受 skills、mcpServers、knowledge、parsers、agents、channels、pages、scope、backend。`requires.network` 为空时 **拒绝** 声明式 HTTP 出站。
新增 JSON 字段一律 `omitempty`,避免 1.0 黄金清单的编码发生变化。
## 3. 契约快照
`modules/backend/pkg/extension/surface_test.go` 用 AST 列出 `pkg/extension` 的导出类型与函数,与 `surface.txt` 比较。CI 通过 `make test`(含 `go test ./...`)执行。未更新快照的导出变更会失败。
依赖边界:`make test-boundaries` 拒绝
- `pkg/extension` 导入 `agent-system-backend/internal`
- 本仓 `examples/` 导入 `agent-system-backend/internal`
## 4. 下游依赖(replace,本次不改模块路径)
```
require agent-system-backend v0.0.0
require agent-system-core v0.0.0
replace agent-system-backend => ../Agent-System-Dev/modules/backend
replace agent-system-core => ../Agent-System-Dev/modules/core
```
后端模块自身的 `replace`(agent-native、logging-system 等)不会被下游继承。下游若编译进 `pkg/extension/link`(会拉起整个后端),需要复制后端 `go.mod` 里的 replace,并把路径改到基座检出。只做声明式插件则不需要 Go 依赖。
## 5. 发版命令(owner 执行,本次未执行)
tag 名以 V1.2 实施推进方案批复的 `v0.2.0` 为准(2026-10-05 对齐;五仓 backend/core/frontend/ui/Dev 同打):
```
git tag -a v0.2.0 -m "extension surface 2.0.0-experimental"
git push origin v0.2.0
```
npm 发布 `agent-system-ui` 仍按 D29 推迟到第二宿主出现。不要在实验窗口把 `private` 改成 false 后直接发布。
## 6. 扩展点申请模板
- 场景:哪一个下游仓、哪条用户路径走不通。
- 希望新增的公开符号或 `/api`、`/host/v1` 路径。
- 为何不能用现有注册点、后台任务、审计、KV、命令总线组合出来。
- 兼容:加字段还是新路径。
@@ -0,0 +1,79 @@
# 插件契约 v2
状态:**实施中(2026-10-01)**;owner:weichen;主任务:Dev 仓 `feature/base-extension-roadmap`。
撰写日期:2026-10-01。配套:[路线图](2026-10-01-base-extension-roadmap.md)、[兼容规则](2026-10-01-extension-compat-policy.md)。
## 1. 四种形态
下游按需要选一种或组合。基座对四种形态使用同一份 manifest 与同一套 fail-closed:任一资产解析失败则整包拒绝,保留上一状态。
| 形态 | 下游交付 | 基座职责 |
|---|---|---|
| 声明 | `plugin.json` + 资产目录 | 注册 tools/skills/mcp/knowledge/parsers/agents/channels/pages/commands/… |
| 进程内编译 | 下游 `main` import `pkg/extension` 并 `RegisterCompiled` | `link` + builder 模式产出**下游自己的**单二进制 |
| sidecar | 原生进程,manifest `backend.mode=sidecar` | 拉起、健康(进程存活)、停止;环境变量注入 Host API |
| 前端 ESM | `pages/*.json` 指向模块 | UI SDK 只暴露命令、SSE 与 `@ui` 组件;页面不得另起设计令牌 |
进程内编译适合要事务一致性的领域逻辑。sidecar 适合异构语言或强隔离。声明式 HTTP 工具仍然只描述调用,不携带任意代码。
## 2. manifest 增补
`apiVersion` 用 `2.0-experimental` 才强制网络白名单。字段见 `modules/core/plugin_v2.go`。
- `provides.skills`:`skills/<name>.md`,宿主写入 `<data>/skills/plugin-<id>/`。
- `provides.mcpServers`:并入小队 MCP 配置(与 `tools.mcp.servers` 合并)。
- `provides.knowledge|parsers|agents|channels|pages`:声明登记,供清单 API 与前端加载。
- `scope.tenants/projects`:空 = 全部可见。
- `backend.command`:sidecar 可执行文件(相对插件目录或 PATH)。`schemaVersion` 记入 `plugin_schema`。
密钥查找顺序:`secrets.<pluginId>.<authRef>`(按工具执行时的 `OwnerID`),再回落同名全局设置。不再在绑定期固定读 `OwnerDefault`。
## 3. Host API(sidecar 与外部进程)
监听由基座在本机拉起,令牌随进程环境变量 `AGENT_HOST_API`、`AGENT_HOST_TOKEN` 下发。
| 方法 | 路径 | 作用 |
|---|---|---|
| PUT/GET | `/host/v1/kv/{key}` | 插件隔离 KV(带 owner 与 plugin id) |
| POST | `/host/v1/audit` | 写通用审计 |
| POST | `/host/v1/commands/{id}` | 走命令总线 |
| POST | `/host/v1/llm` | 代理聊天补全;未配置模型返回 503,不静默伪造 |
| GET | `/host/v1/knowledge/search` | 混合检索(引擎在基座) |
插件故障不拖垮基座:sidecar 退出只记日志与审计,不退出主进程。
## 4. 进程内注册
```go
func init() {
extension.RegisterCompiled(extension.InProc{
ID: "rdm",
Register: func(reg extension.Registry) error {
return reg.AddSkill("rdm-note", "# …")
},
})
}
```
下游 main:
```go
import _ "agent-system-backend/pkg/extension/link"
func main() { extension.Main(extension.Options{Addr: "127.0.0.1:8787"}) }
```
## 5. 前端
`@ui` 导出 `createPluginBridge`:`execCommand` 与 `subscribeEvents`。插件页面模块默认导出 `mount(el, bridge)` / `unmount()`。宿主按 `pages[].module` 动态 `import()`。插件 TSX 与基座一样受 design-guard 约束(令牌、无私人 keyframes、无行内字号)。
## 6. 生命周期
- 启用:`POST /api/plugins/{id}/enabled`,按租户记 `plugin_grants`。
- 升级:`backend.schemaVersion` 大于已记录版本时调用宿主迁移钩子(KV 命名空间不清空;钩子由进程内扩展提供)。版本只升不降,降级拒绝装载。
- 管理面:`GET /api/plugins` 返回 manifest(含 v2 provides)以及技能/页面/渠道声明。既有设置页「插件」分组继续承担热重载。
## 7. 开发体验
- `make plugin-new NAME=<id>` 在 `examples/plugins/<id>` 生成声明式骨架。
- `examples/plugins/decl-demo`、`examples/downstream-rdm`、`examples/sidecar-demo`、`examples/page-demo` 各覆盖一种形态。
- 验收:`examples/downstream-rdm` 的测试不导入 `internal`,并由 `make test-boundaries` 卡住。
@@ -0,0 +1,269 @@
# 通用智能体、小队与意图:完善、借鉴与解耦
状态:**proposed(2026-10-02)**;owner:weichen;主任务:Dev 仓 `feature/agent-stack-blueprint`。
规范依据:Development-Guidelines(workflows/delivery.md、standards/collaboration.md、standards/agent-execution.md);本文是项目级消费,不复制其正文。
撰写日期:2026-10-02。证据见 [2026-10-02-agent-stack-audit.md](../records/2026-10-02-agent-stack-audit.md)。
前序:[2026-10-01-agent-harness.md](2026-10-01-agent-harness.md) 定义了回合循环、完成判定和 P0–P4。那些阶段已经落在产品 Backend。本文把同一类能力收成三个可独立使用的库,并补上审计里的缺陷与对外借鉴。
本文只定义目标、裁定、分期和验收。不在本文实施。
---
## 1. 问题
三个库今天能跑,但换一个宿主就要重写一遍,而且库与库之间还互相依赖。
- **通用能力长在产品里。** 终轮验证、提醒、子代理画像、后台通知、评审、意图到工作流、critic 在 `modules/backend/internal/runtime` 与 `internal/runmgr`。直连、小队、子代理没有共用同一套门禁:`adapters/native` 不透传 Stop、Reminders、RepeatGuard、BeforeTool(审计 N31)。
- **测过的组合不是发布的组合。** Backend 编译用自己的 `deps/`(intent `7d9d505`、squad `5cbac00`)。Dev 指针是 intent `1dbbe81`、squad `590fd86`。Squad 自测用的 native 是 `a34689c`,不是产品里的 `c6d867c`(审计 X1)。
- **恢复不可靠。** 内核在工具预算用尽时留下没有结果的工具调用(N1)。Squad 的状态文件不是原子写,授权和修复预算不落盘,恢复会重跑正在执行的步骤,带 `RequiresApproval` 的步骤批准后再次挂起(Q1–Q5)。
- **实现级耦合。** 内核的 go.mod 为了没有调用方的 `adapters/intentgate` 依赖 Intent(X4)。Squad 的公开类型直接露出 `exec.Backend`、`*llm.Client`、`tools.*`(X5)。意图闸门有四套,动作键有四种(X8)。
编排裁定(本次已确认):多智能体编排统一到 Agent-Squad。`core.WorkflowDef` 仍是插件作者面对的契约,Backend 把它编译成 Squad 的图,现有 `internal/workflow` 执行器退役。
## 2. 借鉴什么
只列会改这三个库的行为或接口的条目。链接是公开文档,版本以 2026-10-02 查阅为准。
### 2.1 回合循环(Agent-Native)
| 借鉴 | 来源 | 落到本库 |
|---|---|---|
| 钩子先于规则;deny 在放行模式下仍然生效;allow 不能跳过 deny / ask;规则可按参数模式匹配;子代理继承父会话权限,插件里的智能体定义不能声明钩子、MCP 和权限模式 | [Claude Agent SDK hooks](https://code.claude.com/docs/en/agent-sdk/hooks)、[permissions](https://code.claude.com/docs/en/agent-sdk/permissions.md)、[subagents](https://code.claude.com/docs/en/subagents) | HookBus。事件对齐 PreToolUse、PostToolUse、PostToolUseFailure、PostToolBatch、Stop、SubagentStart/Stop、PreCompact、PermissionRequest、SessionStart/End。决策为 allow / deny / ask / modify / block,带原因,并带「已在续跑中」标志防止递归 |
| 输入检查只作用于入口智能体,输出检查只作用于给出最终结果的那个,工具检查每次调用都跑 | [OpenAI guardrails](https://openai.github.io/openai-agents-python/guardrails/) | 三类 guardrail。Backend 的终轮验证下沉为输出 guardrail |
| span 名为 `invoke_agent`、`chat`、`execute_tool`、`plan`、`invoke_workflow`;提示词和工具参数默认不采集 | [OTel GenAI agent spans](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-agent-spans.md)、[可观测性说明](https://opentelemetry.io/blog/2026/genai-observability/) | 三库事件用同一套名字。层级是工作流包含智能体调用,智能体调用包含模型调用和工具执行 |
| 压缩、清理旧工具结果、记忆是三种不同手段;压缩后保留摘要和最近用过的文件;前缀稳定、只追加、序列化结果确定,才能吃到缓存;工具定义不在运行中增删;完整结果落文件,上下文里留可恢复的路径 | [上下文工程](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents)、[cookbook](https://platform.claude.com/cookbook/tool-use-context-engineering-context-engineering-tools)、[Manus](https://manus.im/en/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus) | ContextManager。压缩按工具配对边界裁剪(同时修 N7)。system 段不放秒级时间戳。工具清单顺序固定。旧工具结果可替换成路径。计划提醒按成本节流:反复改写待办会占掉大量动作([整理](https://rlancemartin.github.io/2025/10/15/manus/)) |
| 长任务先落功能清单和进度文件,之后每个会话只做一项并留下可交接的状态;有的模型需要清空上下文再交接,有的可以靠自动压缩连续跑 | [长任务 harness](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents)、[harness 设计](https://www.anthropic.com/engineering/harness-design-long-running-apps) | 内核负责可恢复的 RunState。会话清空再交接是 Squad 的一种拓扑,不是内核默认行为 |
| 工具按需加载;模型写代码批量调用工具,中间结果不进上下文 | [advanced tool use](https://www.anthropic.com/engineering/advanced-tool-use)、[programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) | 注册表支持延迟加载和 `tool_search`。Backend P4 的脚本宿主下沉为「代码编排工具」端口,产品只留开关和确认 |
| 只对写文件类工具做检查点,shell 改动不追踪 | [file checkpointing](https://code.claude.com/docs/en/agent-sdk/file-checkpointing) | write / edit / multi_edit 记录改前内容,可回滚到某条用户消息;同时改成临时文件再 rename(修 N15) |
| 子代理 frontmatter:说明、提示、工具、禁用工具、模型、权限、技能、轮次上限、记忆范围、力度、是否后台、是否隔离工作树 | [subagents](https://code.claude.com/docs/en/subagents) | AgentSpec 采用这些字段,再加 `outputSchema` |
| `SKILL.md` 的 name 与目录同名、小写连字符、description 说明何时使用;客户端扫描 `.agents/skills/` | [Agent Skills](https://agentskills.io/specification) | 加载器按该规范校验,并扫描 `.agents/skills/` |
| MCP 2026-07-28:无状态核心;长任务用 tasks/get、update、cancel;需要用户输入时返回 `input_required`,客户端重试原请求;工具 schema 放到 JSON Schema 2020-12 | [changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog.md)、[RC 说明](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) | 客户端认 `resultType`。`input_required` 接到审批端口。Tasks 接到后台任务。同时修 N3、N18 |
### 2.2 编排(Agent-Squad)
| 借鉴 | 来源 | 落到本库 |
|---|---|---|
| 工作流分五类:链式、路由、并行、编排者-工人、评估-优化 | [Building effective agents](https://www.anthropic.com/engineering/building-effective-agents) | 现有 standard / router / leader 分别覆盖前三类里的大部和编排者-工人。缺评估-优化循环和投票。critic 从 Backend 工作流收成一种拓扑 |
| 执行器加有类型的边;按 superstep 推进,步结束做检查点;恢复要求图结构不变。检查点以 thread 为键,并行分支里已成功的写入单独保存,崩溃不丢。挂起与恢复是同一对原语。持久化分退出时、异步、同步三档 | [MAF workflows](https://learn.microsoft.com/en-us/agent-framework/workflows/workflows)、[edges](https://learn.microsoft.com/en-us/agent-framework/workflows/edges)、[checkpoints](https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints)、[LangGraph checkpointers](https://docs.langchain.com/oss/python/langgraph/checkpointers)、[interrupts](https://docs.langchain.com/oss/python/langgraph/interrupts) | 图执行取代「相邻 Parallel 标记」。步骤带 attempt id。这一层同时修 Q1–Q5 和 Q7 |
| 内置顺序、并行、群聊、交接、Magentic。并行分支默认不共享对话,状态通过事件里的增量写入 | [MAF magentic](https://learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/magentic)、[ADK parallel](https://adk.dev/agents/workflow-agents/parallel-agents/)、[ADK state](https://adk.dev/sessions/state/) | Topology 插件加这组内置实现。黑板改成可回放的增量 |
| 编排者维护任务账本(事实、猜测、计划)和进度账本(是否完成、是否卡住);卡住就重规划,并限制轮数、连续卡顿和重置次数 | [Magentic-One](https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/magentic-one.html) | Squad 黑板已接近任务账本。leader 补进度账本、卡顿检测和重规划上限 |
| 终止条件可用 AND / OR 组合 | [AutoGen termination](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/termination.html) | 轮次、修复预算、唤醒次数、超时收成 Termination |
| 交接把最终回答的所有权交出去;当作工具调用则管理者保留所有权。能用一个智能体就先用一个 | [orchestration](https://developers.openai.com/api/docs/guides/agents/orchestration)、[handoffs](https://openai.github.io/openai-agents-python/handoffs/)、[Swarm](https://microsoft.github.io/autogen/0.7.3/user-guide/agentchat-user-guide/swarm.html) | handoff 原语带历史过滤器。TeamSpec 声明谁拥有最终回答 |
| 团队:每人独立上下文,共享可认领的任务表(依赖未完成不能领),点对点邮箱。别人发来的消息不能代替用户表示同意 | [agent teams](https://code.claude.com/docs/en/agent-teams) | platform 的队列改成 task-board 拓扑,加 mailbox。智能体消息不能携带用户授权,从根上去掉 wakeup 自动批准(Q8) |
| 委派要写清目标、输出格式、工具和边界;投入随问题伸缩。写操作保持单线程,因为动作带着别人看不见的决定;只读探索可以并行 | [多智能体研究系统](https://www.anthropic.com/engineering/multi-agent-research-system)、[Don't Build Multi-Agents](https://cognition.com/blog/dont-build-multi-agents)、[后续](https://cognition.com/blog/multi-agents-working) | 步骤声明自己写哪些工件。调度默认:只读可并行,写同一工件必须串行。委派模板结构化。上游产物落文件,下游拿路径和最新黑板(修 Q11) |
| A2A 1.0:Agent Card、每接口一个协议版本、任务有固定状态机 | [A2A 1.0](https://a2a-protocol.org/v1.0.0/specification/)、[v1 变化](https://a2a-protocol.org/latest/whats-new-v1/) | 适配器:远端智能体作为一种 Executor;本团队也可发布 Agent Card。跨库结果状态对齐 A2A 任务状态,不自造第四套词 |
### 2.3 理解(Agent-Intent)
| 借鉴 | 来源 | 落到本库 |
|---|---|---|
| 一次用户话可以产出多条命令:开始流程、填槽、改槽、取消、澄清、闲聊、转人工。规则触发排在模型之前 | [Rasa 对话理解](https://rasa.com/docs/learn/concepts/dialogue-understanding/)、[命令生成](https://rasa.com/docs/reference/config/components/llm-command-generators/)、[流程启动](https://rasa.com/docs/reference/primitives/starting-flows/) | 输出从「一个意图」改为命令列表。Start 带 ActionRef。现有规则漏斗仍是确定性第一层。模型层是可选端口,执行仍归宿主 |
| 用示例句的向量相似度做路由,低于阈值则不命中 | [semantic-router](https://github.com/aurelio-labs/semantic-router) | 规则层和模型层之间的语义端口。向量模型由宿主注入,核心不做 I/O。IntentSpec 增加 examples。用来减轻中文子串误命中(I3) |
| 只把相关流程交给生成器;评测逐轮写出期望命令 | [命令生成器](https://rasa.com/docs/pro/customize/command-generator/)、[评测](https://rasa.com/docs/reference/testing/e2e/dialogue-understanding-tests/) | Catalog 按相关度取 top-k,Metadata 白名单。`intentctl eval` 改为多轮并输出 precision / recall |
### 2.4 二次对照增补(2026-10-03)
2026-10-03 重新读了 [pi v1.0.1](https://github.com/earendil-works/pi)(`4c6fb7cf`,MIT)和 [ZCode v3.14.3](https://github.com/zai-org/ZCode)(`29628c9a`,Apache-2.0)。评级、本仓落点和拒绝理由见 [2026-10-03-pi-zcode-second-pass.md](../records/2026-10-03-pi-zcode-second-pass.md)。pi-durable 在 v1.0.1 的事实更正见 [2026-10-03-pi-durable-review.md](../records/2026-10-03-pi-durable-review.md) 文首。
这一节只把会改三个库或产品行为的新增条目收进来。不改变第 5 节的阶段顺序,也不把上游包引进来。
| 借鉴 | 来源 | 落到本库 | 阶段 |
|---|---|---|---|
| 溢出识别用模式表;Retry-After 解析失败时指数退避;只在还没向调用方发出增量时重试;`length` 或未写完的工具调用不执行 | pi `packages/ai`,v0.99–v1.0.1 | Native 的 provider 客户端与 SSE 解析。Backend 直连默认有限次重试。用量拆成上一请求和累计 | N3 的模型回退与流重试;本项先做 |
| 运行内把旧工具结果换成路径;压缩按工具配对边界;写文件前必须读过且文件未被改过 | ZCode `packages/core` 的 compact 与 read-file-state;pi durable 的 compaction-as-entry | Native 的 ContextManager 与会话压缩。提醒不再追加成 user 消息 | N2 |
| 权限分计划、默认、自动改文件三档;deny 先于 ask 先于 allow;钩子事件与决策走同一条总线 | ZCode `packages/core` 的 permission 与 hooks;本提案 §2.1 已列事件名 | Native 的 PermissionPolicy 与 HookBus。不提供绕过确认的档位 | N1 |
| 工具调用先记意图再记结果;只有两次声明都是 safe 才重放;写文件可回滚,shell 不追踪 | pi durable README 的 Tools 与 Compaction;ZCode `contracts/rewind` | Native 的 RunState 与文件检查点。不回滚对话历史,不做透明续跑 | N3 |
| 模型写脚本批量调用工具,中间结果不进上下文;MCP 工具可延迟暴露,首条提示不等服务器连上 | pi `packages/codemode` 与 `docs/mcp.md`;本提案 §2.1 的代码编排端口 | Native 的代码编排端口与 MCP 客户端。产品开关默认关 | N2 |
### 2.5 SillyTavern 对照里会改产品行为的条目(2026-10-04)
对照 [SillyTavern 1.19.0](https://github.com/SillyTavern/SillyTavern) `06bde939`。只借设计。许可证、评级、拒绝项和暂缓项见 [2026-10-04-sillytavern-review.md](../records/2026-10-04-sillytavern-review.md)。下列条目在同一任务里落地。
| 借鉴 | 来源设计 | 落到本仓 |
|---|---|---|
| 带标签的多密钥,同时只生效一把;读出只给掩码;迁入前先留设置快照 | 密钥与设置快照 | Backend `secret_keys` / `settings_snapshots`。运行时优先读生效密钥,否则仍读设置里的旧字段 |
| 个人指令和会话要求附在上下文末尾 | persona 与 Author's Note | `context.userInstructions` 与会话 `sessionNote`,经 `reminders.Attach` 注入 |
| 关键词或正则、次关键词、扫描最近 N 条、常驻、预算 | 世界书的精简子集 | `context_entries`。不做概率、递归、分组和定时 |
| 组装时给出分段估算和系统前缀哈希;估算为 UTF-8 字节 / 3.35,并用上一请求用量校准 | 提示词分段视图 | `ContextObservation.sections` / `prefixHash`。`context.contextWindow` 只丢掉最早的历史 |
| 合并相邻同角色;严格模式再收起多余的 system | 按 endpoint 做消息整形 | agent-native `PrepareMessages`,设置 `context.messageShape` |
| 复制、成组排除出上下文、追加式候选回复并联动文件回滚 | 对话操作 | 不删除消息。重新生成排除旧候选,新回复写入同一 `variantGroup` |
| 用户 `/` 命令展开成提示词 | 快捷指令 | `user_commands`,登记为 agent-command 的 `user` 来源。不自动触发 |
## 3. 解耦
三个库各自能单独编译、单独测试、单独被第三方使用。配合发生在组合根,不发生在核心模块里。
依赖规则:
- 核心模块互不依赖。只依赖 error-code-system 和 logging-system。
- 端口由使用方定义。Squad 定义 Executor、ModelClient、IntentGate、Store。Native 定义 Provider、Tool、Memory、Checkpoint、Telemetry。Intent 定义 Classifier、Embedder、CommandGenerator。
- 跨库适配器放在使用方仓库的独立子模块,各自有 go.mod。只有适配器可以同时依赖两个库。
- 互通格式由定义它的库发布 JSON Schema 和样例,不共享 Go 类型。AgentSpec 归 Native,IntentSpec / 命令 / ActionRef 归 Intent,TeamSpec / 图归 Squad。
- 共同词表直接用外部标准:事件名用 OTel GenAI,结果状态用 A2A 任务状态,trace 沿用 logging-system 的 trace_id。不新建共享协议模块。
模块布局:
```text
agent-native/ 回合循环、HookBus、ContextManager、完成判定、
provider、tools、mcp、skills、session、
memory、checkpoint、telemetry。
exec 是唯一对外入口。schema/
agent-intent/ 规则漏斗、命令列表、ActionRef、Catalog。schema/
agent-squad/ 图执行、Topology、Termination、端口定义。schema/
adapters/native/ 独立 go.mod。实现 Executor 与 ModelClient
adapters/intent/ 独立 go.mod。实现 IntentGate 与目录映射(现 squadintent)
adapters/a2a/ 独立 go.mod
adapters/sqlite/ 独立 go.mod。现 platform 的持久层
cmd/agent-squad/ 独立 go.mod。CLI 与 HTTP 组合根
```
每个概念只留一处:
| 概念 | 唯一处 | 不再出现在 |
|---|---|---|
| 智能体定义 | AgentSpec | Backend 的 `agent-sub-*.md` 迁成 AgentSpec 文件。RoleSpec 只加团队属性 |
| 意图识别 | agent-intent | 删除 native 的 `adapters/intentgate`。Backend 闸门改为消费同一次识别结果 |
| 路由 | 宿主里的一个 Router,按 ActionRef 分发 | 命令、工作流、小队、普通回合四个分支不再各写一套判断 |
| 能力目录 | intent 的 Catalog | Squad 规划器和直连路径都注入这一份 |
| 工作流执行 | Squad 图执行 | Backend `internal/workflow` 退役。`core.WorkflowDef` 只作为编译输入 |
| 人工确认 | 宿主渲染并回填 | 库只发信号:权限请求、MCP `input_required`、图的 interrupt、Clarify 命令 |
| 预算 | Native 管一个智能体;Squad 管团队并分给步骤;Backend 管配额 | 三处不再各算各的 token 账 |
版本:
- 模块路径改为可 `go get` 的路径并打 semver。改路径会打断现有 import,与路线图第 4 节一样,单独发版,不塞进功能阶段。
- 库仓不再嵌套兄弟库的 gitlink。适配器子模块用 go.mod 版本钉依赖。
- Dev 根增加 `go.work`,让 `make test` 测的就是将要发布的组合。增加检查:Backend 编译用到的每个库 SHA 等于 Dev 指针。
- 这项会改变 `make build` 实际编译的代码来源。实施 R0 时要同时核对 Releases 仓从本仓固定提交导出的流程,避免导出物突然改用另一套 SHA。
- `lint-boundaries` 增加:核心模块的 `go list -deps` 不得出现兄弟库;agent-intent 只允许被 `adapters/intent` 和 Backend 的识别适配导入。
```mermaid
flowchart TB
subgraph host [宿主]
Router["按 ActionRef 分发"]
Root["组合根装配适配器"]
end
subgraph intentCore [agent-intent]
Commands["命令列表与 Catalog"]
end
subgraph squadCore [agent-squad]
GraphExec["图执行"]
SquadPorts["Executor / ModelClient / IntentGate / Store"]
end
subgraph nativeCore [agent-native]
Loop["回合循环"]
end
subgraph adapters [agent-squad/adapters]
AdNative["native"]
AdIntent["intent"]
end
Router --> Commands
Router --> GraphExec
Router --> Loop
Root --> AdNative
Root --> AdIntent
AdNative --> SquadPorts
AdNative --> Loop
AdIntent --> SquadPorts
AdIntent --> Commands
```
## 4. 目标行为
一个智能体是一份 AgentSpec:工具、模型、轮次、技能、权限、钩子、输出 schema。直连会话、子代理、小队角色用同一份。小队角色只多团队属性(并发上限、是否拥有最终回答、可交接给谁)。
一次小队运行是一张图。调度按依赖和读写集决定谁可以并行。每步结束写检查点。需要人时挂起,同一入口恢复。进程死在任意步边界,恢复不重复已成功步骤的副作用,也不丢掉已批准的授权。
一次用户话先变成命令列表。规则层能确定的不过模型。宿主的 Router 只看 ActionRef:命令、工作流、小队或普通回合。Intent 不执行。
插件作者仍然写 `WorkflowDef`、意图 JSON 和工具声明。新增智能体写 AgentSpec 文件。新增小队写 TeamSpec。新增拓扑实现 Topology。这些入口在各阶段的验收里逐个对照,避免再长出第五套键。
沙箱仍由宿主负责。库声明自己不是操作系统沙箱。
## 5. 分期
每阶段单独任务。依赖:R0 先做。R1 在三个库内并行。R1 之后做 D1。D1 之后 N1、Q1、I1 可以同时做。它们完成后再做 N2、Q2、I2。N3 在 N2 之后。Q3 在 Q2、N1、I1 之后。
### R0 组合一致性
Dev 根 `go.work`。检查脚本对比 Backend `deps/` 与 Dev 指针。`lint-boundaries` 加上文第 3 节的依赖断言。核对 Releases 导出是否跟随 `go.work`。
验收:故意把 Backend 的 squad 指针留在旧 SHA 时检查失败;指针对齐时 `make test` 通过。文档写明 `make build` 编译的是哪一套 SHA。
### R1 缺陷
三个库各自独立 PR,只修审计中的 P0,外加安全向 P1:N13、N14、Q8、Q14、Q15、Q16。每条带回归测试。
验收:审计表里对应编号有测试名。Squad 的崩溃恢复至少覆盖「批准后能执行」「重启后授权还在」「running 步骤不执行第二次」。Native 的预算用尽用例里,历史中每个 tool_call 都有结果。
### D1 结构解耦
Native 去掉对 agent-intent 的依赖,合并 contract 与 exec,拆 Options。Squad 核心改为自有端口,跨库代码进 adapters 子模块,CLI 进 cmd。Intent 的校验器改为 Registry 作用域,并校验分类器输出。三库发布 JSON Schema 样例。删除嵌套兄弟库 gitlink 与改模块路径分开发版任务,本阶段只把依赖方向改对,路径可以仍用 replace。
验收:`go list -deps` 符合第 3 节。Squad 核心测试不链接 agent-native。用适配器子模块跑通现有 conformance。Backend 行为不变,用现有 `make test` 证明。
### N1 内核 harness
AgentSpec、HookBus、PermissionPolicy、三类 guardrail。CompletionGate 与提醒从 Backend 下沉。`adapters/native` 透传钩子。Backend 改为消费,A/B 对照直连行为。
验收:同一组脚本化模型输出,下沉前后的停止原因、提醒次数和工具拒绝一致。插件里的 AgentSpec 不能带钩子和权限模式。
### N2 上下文、协议与工具
ContextManager 与缓存友好规则。Anthropic 原生消息含 tool_use 与 thinking 签名。延迟加载工具、tool_search、代码编排工具端口。MCP 认 `resultType`。skills 按开放规范校验。子代理可后台、有独立转录、可恢复。
验收:选 anthropic 预设能完成一轮工具调用(可用录制的 HTTP 夹具,不要求真实密钥)。压缩后的历史不出现孤立 tool_call。MCP 取消不再留下第二个读者(修 N3 若 R1 未覆盖到协议层)。
### N3 持久与可观测
RunState 可恢复。文件检查点可回滚。Memory 端口。事件可导出为 OTel GenAI span。成本分「上一请求」与「累计」。模型回退链。流式错误默认重试。
验收:回合中途杀进程,恢复后工具配对仍成立。回滚只撤销 write / edit / multi_edit。span 名与约定一致,默认不含提示词正文。
### Q1 图执行
superstep、三档检查点、attempt id、interrupt / resume。`Needs` 驱动依赖。读写集。可组合 Termination。Store 接口。platform 的 SQLite 成为 Store 的一个实现,不再单独驱动一套运行。
验收:在每个 superstep 边界杀进程再恢复,已成功步骤的副作用次数为 1。两个写同一工件的步骤不会并行。文件 run 与 SQLite 不再各记一份步骤状态。
### Q2 拓扑
内置:sequential、parallel、loop、router、leader(含进度账本和重规划上限)、handoff、group-chat、review、evaluator-optimizer、long-running、task-board 与 mailbox。
验收:每种拓扑有一个脚本化后端的 conformance。review 等价于今天的「并行找问题、逐条验证、丢掉未验证」。mailbox 消息不能把挂起的权限批准掉。
### Q3 收敛与互通
Backend 把 `WorkflowDef` 编译成 Squad 的图。`internal/workflow` 与把小队步骤压成 `SpawnSubagent` 的路径退役。委派模板含目标、输出格式、工具和边界。`adapters/a2a` 双向。产品默认执行后端改为 agent-native,用同一批任务的 A/B 对照作门禁。
验收:现有示例插件 `demo.brief` 与 `demo.script` 经编译后的图跑通,行为与 P4 裁定一致(步数上限、确认、脚本开关)。A/B 对照记录差异,不要求字节级相同。
### I1 命令化识别
命令列表与 ActionRef。中文匹配的词界或最短词长。同分时可按 priority 决胜。Catalog 白名单与 top-k。Backend 唯一 Router 消费 ActionRef。
验收:审计 I1、I2、I3、I5 有回归。一条用户话可以产出 SetSlot 加 Start。命令 id、工作流 IntentID、小队 blueprint 都通过 ActionRef 到达 Router,插件清单不改字段名。
### I2 分层与评测
语义端口与 examples。模型命令生成端口,输出做与 I1 相同的校验。对话状态仍由宿主持有,库只提供「给定状态,产出命令」的函数。多轮评测输出 precision / recall。文档写明无繁简转换。
验收:分类器返回未知 ID 时结果保持 Ambiguous。eval 对一份不少于现有语料规模的多轮夹具给出指标,而不是只打印分支名。
### E 评测(每阶段都带,不单列阻塞)
Native:配对、取消、预算、压缩。Squad:步边界杀进程。Intent:多轮语料。Dev 组合:少量真实任务加人工复核,不把模型自报当通过。
## 6. 不在本文范围
- 合入上述任何阶段。
- 操作系统级沙箱。宿主隔离责任保持现状,文档继续声明。
- 改模块路径和打 tag。那是发版动作,由所有者确认后执行。
- 为单一产品预造领域角色库。TeamSpec 只定义格式;角色内容仍由产品和插件提供。
- 把 PromptTrigger 关键词并进 Intent。技能触发仍是提示词装配。若两套同时命中,Router 的优先级在 I1 任务里写明,本文不预设谁赢。
## 7. 验收(本文)
- 本文件与审计记录互相链接。
- [2026-10-01-agent-harness.md](2026-10-01-agent-harness.md) 与 [2026-10-01-base-extension-roadmap.md](2026-10-01-base-extension-roadmap.md) 指向本文。
- 不改 Go 代码,不改三个库的指针。
@@ -0,0 +1,47 @@
# EXT-1:进程内命令处理器与 owner 作用域宿主句柄(最小集)
状态:**实施中(2026-10-05)**;owner:weichen;主任务:Dev 仓 `feature/ext-1-inproc-handler`(任务卡即本提案的申请卡)。
撰写日期:2026-10-05。配套:[扩展面兼容与演进](2026-10-01-extension-compat-policy.md)、[基座路线图修正](2026-10-01-base-extension-roadmap.md)、V1.2 实施推进方案(批注 1 最小/完整集划分)。
## 1. 申请卡(compat-policy §6 四要素)
- **场景**:Agent-System-RDM 的 `rdm.ingest` 等命令经四入口(button/card/slash/chat)分发到 `cmdbus.Dispatch` 时被以 `no host handler bound` 拒绝——`Registry.AddCommand` 与 manifest `provides.commands` 只注册规格不绑处理器;进程内插件同时没有任何 owner 凭证与基座服务端回调身份(H4)。
- **希望符号**:`pkg/extension` 新增 `Request` / `Handler` / `Host`(owner 作用域宿主句柄最小集)与 `Registry.AddCommandHandler(spec, h)`;基座侧配套 `cmdbus.Bus.RegisterPluginCommand`。
- **为何现有注册点组合不出来**:`RegisterPluginCommands` 仅登记 spec;`HostClient` 走 sidecar HTTP 且需要进程内不可得的 host token;owner 作用域必须由基座在分发时构造,插件无法自造。
- **兼容**:只加符号与方法,不改既有路径语义;`SurfaceVersion` 保持 `2.0.0-experimental`,实验窗口内的接口调整以本提案为记录。
## 2. 公开面变更(surface.txt 12 → 15 项)
`pkg/extension` 新增导出符号 `Handler`、`Host`、`Request`(见 `host.go`);`Registry` 接口追加 `AddCommandHandler` 方法(方法不计入快照)。
**依赖变化**:根包自此 import `agent-system-core` 类型(`KnowledgeDoc`/`RetrievalHit`/`Artifact`/`ID`),此前为零外部依赖。声明式 Go 插件的 `go.mod` 需按 compat-policy §4 模板同时 require+replace `agent-system-core`。
## 3. Host 接口(最小集)
```
PluginID() / Owner()
KVPut / KVGet // 按 pluginID+owner 隔离
Audit(action, target, evidence) // owner 名下审计事件,Actor=plugin:<id>
Complete(ctx, prompt) // 经基座 LLM 路由(owner 模型配置生效)
KnowledgeSearch(ctx, projectID, text, topK)
KnowledgeWrite(ctx, docs, embed) // Owner 强制改写为本次分发 owner;Source 空则补 plugin:<id>;
// embed=false 只写全文,embed=true 建向量(失败降级纯全文)
TaskCreate / TaskUpdate // 后台任务台账(执行体由下游自跑)
ArtifactGet / ArtifactContent // 工件只读(R1-3 ingest 读原件)
```
artifacts **写入**属完整集,R1-7 前再评估(V1.2 批注 1)。
## 4. 基座实现要点
- `cmdbus.Bus.RegisterPluginCommand(spec, h)`:登记规格(SourcePlugin)+ 绑定处理器;高危命令的内联确认卡拦截位于 handler 查找之前,插件命令天然覆盖。
- `internal/app.hostcaps`:聚合 store/检索管线/LLM 路由,同一实现同时服务 hostapi.Server 闭包与进程内 `inprocHost`(批注 1:复用 owner 作用域函数,不新造句柄)。
- `inprocHost` 每次分发按 `req.Owner` 新建,所有 store 调用以该 owner 为第一参数。
- `rag.Pipeline.IndexChunks` 自 `IndexDocument` 抽出:写全文(回填 ID)后建向量,embed 失败降级语义不变。
## 5. 验证
- cmdbus:处理器被 Dispatch 调用返回 committed;仅登记规格仍 rejected(no host handler bound);高危插件命令先确认卡后执行。
- internal/app(组合根级):两 owner 各分发一次,审计/知识/KV/任务台账各落自己 owner 名下(D8 烟测的单元级前置);`provider=hash` 档 `KnowledgeWrite(embed=true)` 写出向量行且 Owner/Source 被强制改写。
- `examples/downstream-rdm` 增加 `rdm.ping` 处理器示范(读工件元数据 + 写审计)。
- 权威验证源:CI 在 PR HEAD 上 `make test` + `make build`(.gitea/workflows/ci.yaml)。
@@ -0,0 +1,169 @@
# 记录:Agent-Native / Agent-Squad / Agent-Intent 只读审计
日期:2026-10-02。任务主记录:Dev 仓 `feature/agent-stack-blueprint`。
设计结论见 [2026-10-02-agent-stack-completion.md](../proposals/2026-10-02-agent-stack-completion.md)。本文只记证据,不实施修复。
基线(Dev 顶层 `deps/`,`git rev-parse`):
- agent-native `c6d867cde7d87696feaf3073f097ca15b0145273`
- agent-squad `590fd86f4c8bfada0ec76efbfb57133c61607bd6`
- agent-intent `1dbbe8149c561b36282bf781e61a06d1284be3a1`
方法:只读读码。P0 条目已对照源码复核。标注「待确认」的条目没有在本次复核中跑测试。路径相对各库根目录。
## 1. 跨库与集成
| 编号 | 级 | 现象 | 证据 | 影响 | 方向 |
|---|---|---|---|---|---|
| X1 | P0 | 测过的组合不等于发布的组合 | Backend `go.mod` replace 指向自己的 `deps/`:intent `7d9d505`、native `c6d867c`、squad `5cbac00`。Squad 自测钉 native `a34689c`、intent `3674dc4`。Dev 顶层是 intent `1dbbe81`、squad `590fd86`。Go 只认主模块的 replace | 各库测试全绿也不能证明产品编译的组合正确 | Dev 根 `go.work`;`make test` 断言 Backend 用到的 SHA 等于 Dev 指针 |
| X2 | P1 | gitlink 递归嵌套 | `deps/agent-squad/deps/agent-native/deps/agent-intent` 等。agent-intent 在 Dev 检出里有多份副本,SHA 不完全一致 | 读码容易看错副本;磁盘上的嵌套仓不参与 Backend 编译 | 库仓不再嵌套兄弟库;适配器子模块用 go.mod 版本号钉依赖 |
| X3 | P1 | 边界检查不管库与库 | `scripts/lint-boundaries.sh` 只禁止扩展 SDK 与示例 import `agent-system-backend/internal` | 实现级耦合没有门禁 | 用 `go list -deps` 断言核心模块不得依赖兄弟库 |
| X4 | P1 | 内核为了一个没有调用方的包依赖 Intent | `deps/agent-native/go.mod` require `agent-intent`;唯一引用是 `adapters/intentgate`。Backend 零引用该包 | 内核不能单独使用 | 删除该适配器,或迁到独立子模块 |
| X5 | P1 | Squad 公开 API 暴露内核类型 | `squad/backend.go`:`BackendFactory` 返回 `exec.Backend`;`AgentNativeConfig` 含 `*llm.Client`、`tools.SearchProvider`、`tools.ExecConfig`、`mcp.Manager`、`subagent.Factory`。`planner_llm.go` 的 `LLMPlanner.Client` 是 `*llm.Client`。`intent.go` 的 `IntentRunner` 返回 `*exec.Result` | 换执行器必须改 Squad 核心;编译期去不掉 agent-native | 核心只定义 Executor、ModelClient 端口;跨库代码进 `adapters/native` |
| X6 | P1 | platform 绕过执行端口 | `platform/runtime.go` 的 `RunInProcess` 直接 `runtime.NewAgentSession` | 内核内部类型进入 platform 公开签名 | 经 Executor 端口 |
| X7 | P1 | CLI 直接 import Intent | `deps/agent-squad/cli/cli.go` import `agent-intent/intent` | 与「只经 squadintent 接入」的约定不一致 | CLI 迁到独立 `cmd/`,经适配器装配 |
| X8 | P1 | 同一概念多处定义 | 意图闸门:`squad.IntentGate`、`squadintent.Gate`、`adapters/intentgate`、`backend/internal/intentgate`。目录三套。HITL:Squad pending、Backend 卡片、命令确认、Intent 的 Question。预算:`exec.Budget`、Backend `RunBudget`、设置项。事件三套。工作流:`squad.WorkflowPlan` 与 `core.WorkflowDef`。结果状态:command 四态、intent 五分支、native 的 Success 布尔 | 扩展作者要对齐四套键:`handler`、`metadata.blueprint`、`CommandSpec.id`、`WorkflowDef.IntentID` | 每个概念一处定义,见提案第 4 节 |
| X9 | P1 | 产品没有接上 Squad 的意图能力 | `modules/backend/internal/squadadapter` 未装配 IntentGate、Catalog、Runner | Squad 侧已做的目录注入与计划校验在产品路径不生效 | 组合根装配适配器 |
| X10 | P2 | 模块路径是裸名 | `go.mod` 的 `module agent-native` / `agent-squad` / `agent-intent` | 第三方不能 `go get`,只能 replace | 改为可获取路径并打 semver tag。路线图已把改模块路径列为单独发版任务,不在本次 |
## 2. Agent-Native(`c6d867c`)
### P0
| 编号 | 现象 | 证据 | 影响 | 方向 |
|---|---|---|---|---|
| N1 | 工具预算超限留下没有结果的 tool_calls | `runtime/loop.go` 先 `persist` 带 tool_calls 的 assistant,再在 `toolCalls+len > Max` 时 `budgetStop`。`budgetStop`(同文件约 542 行)不补工具结果 | 续跑时 OpenAI / Anthropic 会拒绝这段历史 | 超预算则不落盘该 assistant,或先写入合成的 error 结果再停 |
| N2 | 并行工具的事件回调有数据竞争 | `executeParallel`(`loop.go` 约 366 行)每个工具一个 goroutine 调 `runOne`→`emit`。`adapters/native/backend.go` 约 332 行的 OnEvent 无锁写同一个 `strings.Builder` | `-race` 会报;日志交错 | 串行投递事件,或每工具私有缓冲再合并 |
| N3 | MCP stdio 取消后读协程不退出 | `runtime/mcp/stdio.go` 的 `readResponseLine`:`ctx.Done` 返回后,后台 goroutine 仍在 `ReadBytes`。下次调用再起一个读者 | stdout 帧错乱、卡死 | 取消时关闭进程,或单读循环按 id 分发 |
| N4 | 预设与真实客户端协议不一致 | `runtime/llm/client.go` 固定 POST `…/chat/completions`。`BuildBody` 的 Anthropic / Google 分支不被该 Client 使用。Anthropic body(`providers.go` 约 303 行)把 tool 结果收成普通 user 文本,不带 tool_use / thinking 签名,也不挂 tools | 选 anthropic / google 预设不能多轮工具 | 按 Protocol 分流真实客户端;未实现的协议不要出现在可用预设里 |
### P1
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| N5 | token 预算只在回合开头检查,超限后本轮工具仍执行 | `loop.go` 用量在 consume 之后累加,执行工具前只查 `MaxToolCalls` | 累加后、执行工具前再查 token 预算 |
| N6 | `Stop.Replacement` 只改内存 | `loop.go` 改最后一条消息内容,不 `persist` | 追加更正记录或提供改写 API |
| N7 | compaction 按条数裁尾,可切断工具配对 | `runtime/session/compact.go` 的 `keepLastN` 不按 tool 组对齐。现有测试没有工具消息 | 裁切对齐 tool_calls 与 tool 结果 |
| N8 | compact 与 append 有并发窗口 | compact 读完条目后解锁再摘要,再加锁追加 | 摘要期间禁止追加,或按版本号 CAS |
| N9 | 会话追加无 fsync、无跨进程锁 | `runtime/session/tree.go` 用 `O_APPEND` 写,锁只在单进程内 | 可选 Sync;文档写明单写者,或加文件锁 |
| N10 | 流式错误不落盘、默认不重试 | `loop.go` 遇到错误帧直接返回。`WithRetry` 只在 CLI 包装 | Backend 适配默认重试;写明错误帧是否落盘 |
| N11 | SSE 不拼接多行 `data:` | `runtime/llm/sse.go` 逐行处理 | 按空行分事件再拼接 |
| N12 | 取消一批工具时要等全部结束 | `executeParallel` 用 `Wait`;顺序路径工具之间不看 `ctx.Err()` | 批内协作取消;超时后仍写 error 结果以保持配对 |
| N13 | fetch_url 有 DNS 重绑定窗口 | `runtime/tools/fetch.go` 先 `LookupIP` 校验,`client.Do` 再解析 | Dial 时连已校验的 IP |
| N14 | 宿主自带 `CheckRedirect` 时跳过重定向校验 | `fetch.go` 仅在 `CheckRedirect==nil` 时包装 | 始终链式包装 |
| N15 | write / edit 不是崩溃原子 | `runtime/tools/builtin.go` 直接 `os.WriteFile` | 临时文件再 rename |
| N16 | 按字节截断,破坏 UTF-8 | `builtin.go`、`exec.go`、`mcp/manager.go` | 按 rune 回退 |
| N17 | 工具 schema 校验过松 | `runtime/tools/registry.go` 不强制 `additionalProperties:false`,array / object 一律放行 | 至少拒绝未知键 |
| N18 | MCP「失败重连一次」只有注释 | `runtime/mcp/manager.go` 注释与实现不符,失败只返回错误 | 实现或删注释 |
| N19 | MCP HTTP `Close` 是空操作 | `runtime/mcp/http.go` | 发送 session terminate |
| N20 | 用量把每轮完整历史的 prompt token 累加 | `loop.go` | 分开「上一请求」与「累计计费」 |
| N21 | 毒化轮转状态只在内存 | `adapters/native/backend.go` 的 `lastFailure` | 旁路文件落盘 |
| N22 | 提醒消息是追加的 user 角色 | `loop.go` 的 `withEphemeral`。OpenAI 在 tool 结果之后通常可接受。Anthropic Messages 是否拒绝,待确认 | 用提供方的 ephemeral / system 通道;补一条 Anthropic 往返测试 |
### P2
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| N23 | `run_python` 的 `ScriptDir` 不经路径解析 | `runtime/tools/exec.go` 约 112 行:`filepath.Join(t.root, ScriptDir)`。该字段只能由宿主配置,模型参数改不了,因此从 P0 降为加固 | `Resolve` 后再用;单测覆盖 `..` |
| N24 | grep / WalkDir 无文件数与总字节上限 | `builtin.go` 只有命中条数和单文件上限 | 加 maxFiles / maxBytes |
| N25 | HTTP 客户端默认超时很长、无空闲读超时 | `runtime/llm/client.go` | ResponseHeaderTimeout 与读空闲超时 |
| N26 | Registry 无锁 | `registry.go` 的 Register / Restrict 与 Execute 并发不安全 | 运行前冻结快照 |
| N27 | SSE 工具名用 `+=` 拼接 | `sse.go` 若 delta 重复全名会拼重 | 仅空时写入 |
| N28 | 绝对路径 jail 用未 Eval 的根做 Rel | `runtime/tools/jail.go`。symlink 根加绝对真实路径是否可绕,待确认 | 校验统一对已解析的根 |
| N29 | `contract/` 与 `exec/` 两套契约 | `contract` 只有 conformance 的 fake 在用;native 实现的是 `exec.Backend` | 保留 `exec`,删除或并入 `contract` |
| N30 | `native.Options` 汇集全部配置 | MCP、技能、压缩、宿主工具、执行、抓取、搜索在同一结构 | 按端口拆分 |
| N31 | `adapters/native` 不透传 Stop / Reminders / RepeatGuard / BeforeTool | `adapters/native/backend.go` 的 run 不设置这些字段 | 小队路径拿不到直连的门禁。透传或由宿主显式配置 |
缺测(本次未找到对应用例):N1 配对、N2 race、N3 取消双读、N4 Anthropic 线上格式、N7 切断工具组、N13 DNS 重绑定、N6 持久化。
## 3. Agent-Squad(`590fd86`)
### P0
| 编号 | 现象 | 证据 | 影响 | 方向 |
|---|---|---|---|---|
| Q1 | `RequiresApproval` 批准后再次挂起 | `squad/workflow.go` 约 60 行检查 `Approved["step:"+id]`。全仓只写 `Approved["role:…"]`(`engine.go` 约 578 行)。`Respond` 的确认只恢复挂起,不写 step 键 | 该步骤永不执行 | 批准时写入 `step:` 键并落盘;补回归 |
| Q2 | 授权、修复预算、澄清、steering 不落盘 | `runState` 有这些字段(`squad/runstate.go`)。`save()` 只写 workspace、workflow、squad、blackboard、usage | 重启后授权丢失、修复预算归零 | 原子写入 `runstate.json`,Resume 必读 |
| Q3 | 状态文件不是原子写 | `squad/workspace.go` 的 `writeJSON` 直接 `os.WriteFile`。只有黑板用临时文件再 rename(`blackboard.go`) | 崩溃留下半截 JSON,Resume 失败 | 统一 tmp + Sync + rename |
| Q4 | Resume 会重跑 status=running 的步骤 | `advance` 只跳过 completed / skipped / failed(`workflow.go` 约 33 行)。崩溃可留下 running。`Resume` 无挂起则直接 `advance` | 写文件、工具、MCP 副作用重复 | 步骤 attempt id;恢复前把 running 标成待重试并校验产物 |
| Q5 | 没有跨进程 run 锁 | `engine.go` 约 144 行只有进程内 `map` | 两进程可交错写同一 run 目录 | run 目录文件锁,或单写者 |
### P1
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| Q6 | HITL 超时字段未实现 | `hitl.go` 有 `TimeoutAfter` / `ExpireAction`,无扫描 | sweeper 按到期动作合成决策 |
| Q7 | `Respond` 先删 pending 再应用决策 | `engine.go` 约 477 行先 `saveGate(nil)`。二次回应走「无挂起」 | 先记 answered 再删 |
| Q8 | wakeup 自动批准 confirmation / permission / quality | `engine.go` 约 901 行 | 高风险 kind 禁止自动批准 |
| Q9 | `SweepWakeups` 在锁外改 `WakeupFires` | 先自增写盘再 `Respond`(锁在 Respond 内) | 整段放进 run 锁 |
| Q10 | 并行组无 fail-fast,且丢掉 gate | `runParallel` 等全部结束;忽略 `runOneStep` 的 gate | 失败即取消兄弟 |
| Q11 | 步骤间上下文是过期快照 | `BuildSubWorkspaces` 用当时黑板写 `TASK.md`,顺序执行不刷新 | 每步前用最新黑板和上游产物索引重写 |
| Q12 | LLM 规划失败无回退、无规模上限、目标原文注入 | `planner_llm.go` fail-closed;prompt 直接拼 `Goal.Text` | 可配置回退;目标分段;硬上限 |
| Q13 | intent 步要求注册表里存在名为 intent 的角色 | `materialize` 对每步 `Registry.Get(role)` | `IntentID` 非空时跳过普通角色检查 |
| Q14 | Server 与 platform HTTP 无鉴权 | `server/server.go`、`platform/http.go` | 至少 API token |
| Q15 | run id 未校验 | `filepath.Join(DataDir, "runs", runID)`,id 来自路径 | 拒绝非 `run_` 前缀的安全字符集 |
| Q16 | 请求体无大小上限(webhook 除外) | `handleCreateRun` 无 `MaxBytesReader`;webhook 才有 256KiB | 全局 body 上限 |
| Q17 | SSE 扇出关闭竞态、慢消费者丢帧 | `progress.go` default 丢帧;回放 goroutine 往已关闭 channel 写 | done 信号停回放 |
| Q18 | 事件序号重启后从 1 重计 | `store/eventsink.go` 的 seq 只在内存 | 启动时读最后一行 |
| Q19 | Claim 并发上限先查后改;未开 WAL | `platform/queue.go` 先 COUNT 再 UPDATE;`db.go` 只有 busy_timeout | 单条条件更新;`journal_mode=WAL` |
| Q20 | 文件 runs 与 SQLite 两个真相源 | `platform/db.go` 注释;server 可同时挂两者 | platform 降为 Store 实现,状态以图执行为准 |
| Q21 | `StepSpec.Needs` 未参与调度 | 只定义在 `models.go`。调度看相邻 `Parallel`(`workflow.go`) | 实现 DAG 或删除字段 |
### P2
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| Q22 | leader 先 `Turns++` 再执行 | `leader.go` 约 242 行 | 成功后再递增 |
| Q23 | `MaxLeaderTurns` 用尽直接失败,无降级收口 | `leader.go` | 可配置收口 |
| Q24 | 步骤重试共享同一个 deadline | `workflow.go` 约 209 行。短超时下重试几乎无预算 | 文档写明,或每次重试单独预算 |
| Q25 | 角色与蓝图写盘忽略错误 | `store.go` 的 `_ = os.WriteFile` | 原子写并返回错误 |
| Q26 | 人工 retry 直接把修复预算加一 | `engine.go` 约 608 行。是否有意,待确认 | 设上限 |
| Q27 | 拓扑写死 | standard / leader / router 在引擎内分支,无 Topology 接口 | 插件接口 |
缺测:Q1 全路径、Q4 从 running 恢复、Q5 多进程、Q6 超时、Q8 自动批准、Q15 路径穿越。
## 4. Agent-Intent(`1dbbe81`)
### P0
| 编号 | 现象 | 证据 | 影响 | 方向 |
|---|---|---|---|---|
| I1 | `Classifier.Refine` 返回值不校验 | `intent/recognizer.go` 约 97–123 行:不检查 ID 是否属于原候选,不把置信度限制在 0 到 1,不禁止改 Metadata / Handler。失败才保持 Ambiguous | LLM 分类器可以把任意 ID 抬成 Top | ID 集合必须相等;置信度钳制;禁止新增特权字段 |
| I2 | 槽位校验器是进程级全局表 | `intent/slots.go` 的 `slotValidators sync.Map`,无注销、无命名空间 | 多 Registry 同名互相覆盖 | 挂到 Registry / Options |
### P1
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| I3 | 中文子串匹配、无词界 | `intent/registry.go` 约 120 行 `strings.Contains`。语料只有正向命中 | 词界模式或最短词长告警;补假阳性负例 |
| I4 | 只做 `ToLower`,无 NFKC / 全半角 / 繁简 | `recognizer.go` 折叠;slot normalize 仅 trim / lower / upper | 可选 NFKC;文档写明无繁简 |
| I5 | 同分恒为 Ambiguous,priority 不参与裁决 | 分差小于 margin 即 Ambiguous(`recognizer.go`)。priority 只用于排序 | 允许 margin 为 0 时按 priority 决胜。当前 `<=0` 会被改回 0.1(`registry.go`) |
| I6 | 分类器解歧后的 Top 可能没有槽位 | `candidatesOf(..., false)` 先不抽槽 | 裁决成功后再对 Top 抽槽 |
| I7 | Snapshot 的 Metadata 浅拷贝 | `SpecByID` / `Catalog` 拷贝 map 但共享底层 | 深拷贝 |
| I8 | Catalog 整表输出 Metadata 与 Handler | `intent/catalog.go` | 白名单键;默认不进 Markdown |
| I9 | 闸门钉死旧 `*Snapshot` | `squadintent/gate.go`、`adapters/intentgate` 不持有 Registry | Reload 后须重建,或持有 Registry |
| I10 | 识别是全量线性扫描 | `evalRules`,上限 2048 | 规模上来再做倒排;先写明上限 |
| I11 | 多槽位各自第一次匹配,无重叠消解 | `intent/slots.go` | 文档写明;冲突时标歧义 |
| I12 | 否定只靠 `excludes_any` 子串 | 语料 `negated_write_readonly` 依赖硬编码「配置」 | 不在库内做语义否定;交给命令层的 Cancel |
### P2
| 编号 | 现象 | 证据 | 方向 |
|---|---|---|---|
| I13 | pattern 无长度上限 | 条数上限 `MaxPatternsPerSpec=32`,单条无长度限制 | 构建期限制长度 |
| I14 | 评测语料约 10 条,无 precision / recall | `testdata/corpus.json` | 多轮、逐轮期望命令,输出指标 |
已核实相对健全:Snapshot 用原子指针交换且失败保留旧快照;正则在构建期编译(RE2);同分排序稳定(score、priority、注册序)。
## 5. 与产品侧的关系(不在三库内,供路线引用)
这些能力今天在 Backend,提案主张下沉到库,本次不改:
- 终轮验证与计划提醒:`modules/backend/internal/runtime/verify.go`
- 评审流水线:`internal/runtime/review.go`
- 工作流执行与 critic:`internal/runtime/workflow.go`、`internal/workflow/`
- 子代理循环与 profile:`internal/runtime/sub_loop.go`、`internal/prompts/library/agent-sub-*.md`
- 后台子代理通知:`internal/runmgr/runmgr.go`
- 直连回合:`internal/runtime/llm_direct.go`(约 692 行)
`core.WorkflowDef` 仍是插件作者面对的契约。执行器与 `squad.WorkflowPlan` 是两套编排。
@@ -0,0 +1,94 @@
# 调研:pi `packages/durable`(Pico5)有什么可以借鉴
## 更正(v1.0.1,2026-10-03)
对照对象已从本地克隆 `c7cdb460`(2026-09-21)前进到 [earendil-works/pi](https://github.com/earendil-works/pi) `4c6fb7cfe8c538a668726f6f8b3554098c39faee`(2026-10-03,v1.0.1)。下面四条替换正文里对应的判断;正文保留,作为当时的证据。完整二次对照见 [2026-10-03-pi-zcode-second-pass.md](2026-10-03-pi-zcode-second-pass.md)。
- **Packages 1–23 已经实现。** `packages/durable/docs/pico-v5-handoff.md` 的状态栏不再是 “No Pico5 implementation exists”。可运行的内容包括会话、工具回合、收件箱、中止、子代理句柄、结构化并发、压缩与溢出、扩展,以及生命周期和任务图。存储有内存、SQLite、JSONL 三套,并带一致性测试。正文 §1 的“只有记录契约和一份内存存储”只对 `c7cdb460` 成立。
- **不把 `@earendil-works/pi-durable` 或 Chord 当依赖引进来,这个结论不变。** 包仍标 Experimental;Chord 仍声明不是稳定公开契约;实现是 TypeScript,本仓执行与存储在 Go。
- **正文 §2.2 的试点规则升级为采纳设计。** 重放取交集、意图与结果分两次提交、先写者胜出的 memo、内存实现作语义参照、侧车提交标记、压缩只追加并带 head 指针,都已有可对照的实现。落地仍在后续批次,不在本更正里改代码。结果状态继续对齐 A2A,不新增 `faulted` 枚举。
- **`packages/agent` 里的实验内核 pico3 已在 v1.0.0 删除。** 正文引用的 `effect_pending` 与 `replay: "never" | "safe"` 现位于 durable 的工具任务。不要再把 pico3 当参考入口。
透明续跑仍然暂缓:骨架设计 §4.2 不做。服务端 steer、会话分叉和所有权级联改为条件试点,触发条件仍是正文 §2.3 写的那些产品需求。
- 状态:记录(2026-10-03);owner:weichen;任务:Dev 仓 `feature/pi-durable-borrow`
- 对象:本机参考克隆 `/home/weichen/projects/Agent-System/.refs/pi`,`origin` 为 `https://github.com/earendil-works/pi.git`,HEAD `c7cdb460aa8a0cebef3446c4166729b8a0d97ead`(2026-09-21,`fix(coding-agent): reject bug reports offline`)。包版本 `@earendil-works/pi-durable` 0.86.1,MIT(仓库根 LICENSE 版权 2025 Mario Zechner;`packages/durable/package.json` 的 `license` 为 MIT)。
- 对照的本仓事实以 Dev `a5c9fd3385` 为准。库层对照的是提案 [2026-10-02-agent-stack-completion.md](../proposals/2026-10-02-agent-stack-completion.md)(Documents PR #34,本记录合入时已经在 documents main)及其审计记录。
结论先说:**不把这个包或 Chord 当成依赖引进来。** 它现在只有记录契约和一份内存存储。值得拿走的是几条恢复规则,而且用我们已有的表和事件词汇就能落地。本任务在 Backend 修四个已经核实的缺陷;库层规则写在这里。提案已经合入,在提案里加链接留到后续小任务,本记录不改提案正文。
---
## 1. 这个包实际有什么
`packages/durable` 的 README 写明公开 API 是持久记录契约和一份脱离调用方的内存存储。交接文档 `docs/pico-v5-handoff.md` 的状态栏写着:旧的 pico、pico4 原型已删,pico3 还在,**No Pico5 implementation exists**。24 个实现包里,代码只覆盖第 1 包:
| 文件 | 内容 |
|---|---|
| `src/types.ts` | `ConversationRecord`、`EntryRecord`(`model` / `data` / `head` / `edits`)、`Input` 四态、`TaskRecord` / `TaskState` / `TaskOutcome`、`DocumentRecord`、`Storage` |
| `src/memory-storage.ts` | `MemoryStorage`:原子提交、只读副本、分叉历史扫描、任务整行替换 |
| `test/memory-storage.test.ts` | 12 个存储行为测试 |
| `docs/pico-v5.md` | 规范。Session、文档、任务、收件箱、存储后端都在这里,没有对应实现 |
同仓里真正跑起来的同类代码在别的包,不在 `durable`:
- `packages/agent/src/harness/pico3/`:实验内核,约 8000 行,导出路径是 `@earendil-works/pi-agent-core/experimental/pico3`。交接文档要求只把它当参考,不要搬它的外观接口。
- `packages/agent/src/harness/session/types.ts`:工具调用状态含 `effect_pending`,并带 `replay: "never" | "safe"`。这是 Pico5「先记意图再执行」在现行 harness 里的落地,比规范更值得对照。
- `packages/coding-agent` 的会话仍是 JSONL 树,**不依赖** `@earendil-works/pi-durable`。
- 依赖的 `@earendil-works/chord` 0.86.1 也是 MIT,但 `packages/chord/PLANNING.md` 写明 **not a stable public API contract yet**。
因此评级是 **Reject:不引入 `@earendil-works/pi-durable`,也不引入 Chord。** 语言栈是 TypeScript,我们的执行与存储在 Go;包本身没有 Session、调度器或 SQLite 后端可以链接。
## 2. 规范里值得单独拿走的规则
Pico5 的核心句(`docs/pico-v5.md` 开头):一次 Session 提交原子地写下不可变条目、完整任务记录和文档;只有提交成功的状态可被观察到。下面每条都标了我们这边的落点。
### 2.1 采纳,本任务在 Backend 实施
这四条不改变骨架设计 §4.2「不做透明恢复」。它们补的是那条决策已经承诺、但硬杀路径没有做到的事。
| 规则 | Pico5 出处 | 我们的缺口(修复前) | 落点 |
|---|---|---|---|
| 重开时把活着的任务收成终态 | 规范 §5.1:打开时把残留的 `running` 改回可调度;未知任务种类收成 `orphaned` | `InterruptRunning` 只在收到 SIGINT/SIGTERM 时遍历**内存里**的 run(`app.Run` 停机路径)。进程被硬杀后,库里的 `pending` / `running` / `waiting_hitl` 没有启动扫描。前端 `activeRun` 把一切非终态都当成进行中,outbox 因此不再发送 | 启动对账。SQLite 扫描全部;PostgreSQL 只扫描本实例(外加升级前 `instance_id` 为空的行)。收成 `interrupted`,未决 HITL 以 `cancelled` 关闭,发出已有的 `run.failed`(码 `run.interrupted`)。不新增 Core 状态 |
| 输入先准入,再落库;用请求键去重 | 规范 §6:`requestId` 在写入前按会话去重;忙时 `reject` 则什么都不写 | `postMessage` 先 `AppendMessage` 再 `StartConversationRun`。槽位被占时返回 409,用户消息已经在库里。前端把同一条放回 outbox 队首,回合结束后再发一次,于是出现两条用户消息,第一条没有 run | 回合和工作流先抢槽位,忙则 409 且不写。`messages.request_id` 部分唯一索引。重复请求返回已有消息和 run。命令路由仍不占槽位 |
| 终态一次提交 | 规范 §1 不变量 1、§5.3:终态任务、结果条目、输入结算在同一次提交 | `finishRun` 先 `UpdateRun`,再 `AppendMessage`;消息失败被跳过。可能留下 `completed` 且没有助手消息 | `SettleRun` 在一个事务里更新 run 并追加助手消息。失败则把 run 收成 `failed`(码 `run.settle_failed`)并发 `run.failed`,不再静默 |
| 只发布已提交的状态;慢消费者靠持久游标收敛 | 规范 §1 不变量 2–3;§9.2 积压过多时用一份完整快照替换未送达的增量 | 事件是先落盘再 `Publish`,这一侧已经符合。缺口在总线:订阅缓冲满时直接丢掉,连接不断。浏览器不会带 `Last-Event-ID` 重连,注释里的「重放补齐」不会发生。另外 `EventsAfter` 只取一页 1000 条,更早的积压被跳过 | 缓冲满则关闭该订阅。SSE 看到通道关闭就结束响应,由 EventSource 重连。重放按页读到不足一页为止 |
多实例约束来自 [双档存储提案](../proposals/2026-09-30-dual-tier-storage-and-multitenancy.md) 的 D24:run 在所属实例的内存里执行,跨实例不迁移。所以 PostgreSQL 上的对账不能扫描别的实例正在跑的 run。实例标识来自启动配置 `instance`(环境变量 `AGENT_SYSTEM_INSTANCE`),缺省是「主机名:监听地址」。
### 2.2 试点,写给库层,本任务不改代码
这些规则对得上 agent-stack 提案的 N3(可恢复 RunState)、Q1(图执行检查点)和审计 Q4(恢复会重跑 `running` 步骤)。提案合入后再开任务,在提案 §2.1 / §2.2 加链接。不要把 Pico5 的状态词原样加进我们的结果枚举:提案写了跨库结果状态对齐 A2A,不自造第四套词。下面的 `faulted` 用原因码表达。
| 规则 | Pico5 出处 | 对应我们的缺口 | 试点时怎么用 |
|---|---|---|---|
| 重放取交集:存储时的策略和当前声明都是 safe 才重跑;否则写一条 interrupted 结果。当前的 safe 不能把当初的 unsafe 升级成可重跑 | 规范 §7.2;现行代码在 `harness/session/types.ts` 的 `effect_pending` | 审计 Q4:`Resume` 会把 `running` 步骤再执行一次。`contract` 有 `SideEffects`,但没有工具级的 replay 位,而且 contract 还没接到 runtime | Native 的工具声明加 replay 位,缺省 unsafe。Squad 的 attempt id 恢复时读这个位:unsafe 写中断结果,不调用执行器 |
| 意图和结果分两次提交。一轮没有持久进展就失败 | 规范 §5.1 第 6 步、§5.2 effect sandwich | 审计 N1:预算用尽时 assistant 已经落盘,工具结果没有。重试会再打出副作用 | 工具调用先落「即将执行」再执行,执行后落结果。恢复时按上一行的交集决定重跑还是写中断结果。无进展用现有失败状态加原因码,不新增 `faulted` 枚举 |
| 先写者胜出的 memo,和检查点分开,终态时丢掉 | 规范 §5.2 | 审计 Q1/Q2:批准和预算不是原子落盘,恢复后丢失或再次挂起 | 放在步骤记录上,候选插入和读出赢家同一次写入。批准、修复预算用它,不要再靠散落字段 |
| 内存实现是语义参照,SQLite / JSONL 跑同一套一致性测试 | 规范 §10 末、§11.1;`MemoryStorage` 的测试就是这套测试的雏形 | Squad 的文件 run 和 platform SQLite 各记一份步骤状态(提案 Q1 验收已经要求合成一份) | 先有内存参照和共用测试,再写文件与 SQLite 后端。不要每个后端自己发明恢复语义 |
| 多文件写入用「先写侧车、再写主标记、标记成功才对外可见」 | 规范 §11.3 | 审计 Q3:`writeJSON` 直接 `os.WriteFile`,崩溃留下半截 JSON | 文件档的检查点用这个协议。没有 fsync 时只承诺扛住进程崩溃,不承诺扛住断电;这一点写进文档,不要暗示成完整耐久 |
| 压缩不改历史:追加一条带 `head` 的摘要,旧条目还在 | 规范 §2.1、§8 | 骨架设计:窗口外原文不删,滚动摘要是远期。Native 的 compaction 会按条数裁尾,审计 N7 指出可能切断工具配对 | 压缩产物是新条目加下界指针,回放从指针开始。不要重写 transcript 文件 |
### 2.3 暂缓,以及什么情况下再看
| 规则 | 为什么现在不拿 | 重新评估的条件 |
|---|---|---|
| 透明续跑(从检查点接着做,而不是标记中断后让用户重发) | 骨架 §4.2 明确不做,避免分布式续跑。N3 还没落地 | N3 的 RunState 可恢复已经合入,并且有「杀进程后工具配对仍成立」的测试 |
| 服务端 steer / follow-up 队列 | 直连路径是 409 + 浏览器 outbox。`agent-native` 的 `AgentSession` 已有队列,但没有接到 HTTP | harness 提案 P1:直连和小队共用同一个回合循环 |
| 崩溃后把未写完的助手输出收成一条 interrupted 消息 | 增量在 150ms 缓冲里,终态消息只在 run 结束时写。恢复它要定义「事件残片算不算消息」 | 终态原子提交稳定之后,如果用户确实需要看到半截回复 |
| 把系统提示增量写进对话记录(`SystemMessage.sections` / `toolsAdded`) | 我们的系统段由 ctxasm 每次组装,不进消息表。pi-ai 已有这个类型,但是另一套回放 | 只有当我们要把「当时实际给模型的系统和工具」做成可回放事实时 |
| 带作用域、历史和分叉策略的类型化文档,以及 Chord 增量订阅 | 我们已有持久 seq + Query 快照 + SSE 重放,订阅模型够用。Chord API 不稳定 | 出现真正的多文档、可分叉、要按历史时点读取的产品需求 |
| 会话分叉(fork)和所有权树(owner 管中止,parent 管历史) | 领域模型没有会话分叉。子 run 用 `parent_run_id`,取消靠 context 级联 | 产品要做「从某一条消息开分支」或跨进程的子树取消 |
## 3. 本任务实施了什么
对应 §2.1,不改 Core 契约:
- Backend 迁移 v14:`runs.instance_id`,`messages.request_id` 及 `(conversation_id, request_id)` 部分唯一索引。
- 启动对账 `ReconcileInterrupted`,与停机时的 `InterruptRunning` 共用收尾。
- 发消息先查 requestId、回合与工作流先抢槽位再落库。
- `SettleRun` 单事务写 run 终态和助手消息。
- 事件总线在订阅缓冲满时关闭连接;SSE 按页重放直到不足一页。
- Frontend 数据层在发送和 outbox 重发时携带同一个 requestId。界面不变。
不在本任务:透明续跑、服务端 steer、把半截输出收成消息、修改 Documents PR #34。
@@ -0,0 +1,79 @@
# 调研:pi v1.0.1 与 ZCode v3.14.3 二次对照
- 状态:记录(2026-10-03);owner:weichen;任务:Dev 仓 `feature/pi-zcode-second-pass`
- 对象:
- [earendil-works/pi](https://github.com/earendil-works/pi) `4c6fb7cfe8c538a668726f6f8b3554098c39faee`(2026-10-03,v1.0.1)。相对本机参考克隆 `c7cdb460`(2026-09-21)前进 239 个提交。MIT,仓库根 LICENSE 版权 2025 Mario Zechner。
- [zai-org/ZCode](https://github.com/zai-org/ZCode) `29628c9acdb81b703bbd4080c207a0e7ce5e276e`(2026-09-24,v3.14.3)。本机对照树 `/tmp/ZCode-study`。Apache-2.0,根目录 LICENSE、NOTICE.md、THIRD-PARTY-NOTICES.md。
- 本仓事实以 Dev `7139e58df0` 为准。前序:[2026-10-01-agent-harness.md](../proposals/2026-10-01-agent-harness.md) 的 P0–P4、[2026-10-02-agent-stack-completion.md](../proposals/2026-10-02-agent-stack-completion.md)、[2026-10-02-agent-stack-audit.md](2026-10-02-agent-stack-audit.md)、[2026-10-03-pi-durable-review.md](2026-10-03-pi-durable-review.md)。
- 提示词不逐字照搬。移植设计时在 NOTICE 里注明来源与修改;不把上游包、插件市场或技能正文整仓拷入。
结论先说:P0–P4 已经借走回放、结束门禁、续写、验证器、子代理和工作流骨架。这一轮要补的是模型层健壮性、上下文工程、权限与钩子、文件回滚、代码编排端口。不引入 `@earendil-works/pi-durable`、Chord,也不移植 ZCode 的 TypeScript 动态工作流。
---
## 1. 来源现在是什么
pi 仍是最小可扩展的 agent harness:交互、print、JSON、RPC 和 TypeScript SDK。v1.0.1 的实质增量是 codemode、MCP 产品化、虚拟模型、分类器基础设施,以及整棵 Pico5 运行时。`packages/durable` 仍标 Experimental;`packages/agent` 在 v1.0.0 删掉了实验内核 pico3。coding-agent 的实验 TUI 已迁到 durable。
ZCode 是桌面、Web 和 CLI 共用的编程工作台。本记录只看 `apps/zcode-cli/packages/core` 与 `contracts` 里的 harness,以及产品壳里和运行态有关的协议。P0–P4 已经对齐的部分(原生工具回放、Stop 上限 3、输出续写 3 次、同名同参提醒、证据清单验证、子代理深度 1、声明式工作流)不再重复评级。
## 2. 评级
每条都标了本仓落点。阶段名沿用 agent-stack 提案的 N1、N2、N3;「批次」指本任务之后的实现批次。
| 主题 | 上游 | 本仓 | 评级 | 批次 |
|---|---|---|---|---|
| 上下文溢出识别 | pi `packages/ai`:按供应商文案识别,含 Z.AI 中文站 | `deps/agent-native/runtime/llm/providers.go` 的 `ContextOverflow` 要求错误信息同时含 “context” 与 overflow/too long/length | adopt | 2,N3 |
| Retry-After 与容量重试 | pi:解析失败则指数退避;“Selected model is at capacity” 可重试 | `runtime/llm/client.go` 对 429/5xx 标可重试,不读 Retry-After | adopt | 2,N3 |
| 未完成的工具调用 | pi `1b2aa0ca0`:Responses 未完成的 tool call 拒绝执行 | `runtime/llm/sse.go` 的 `mapFinish`:finish 为 `length` 或空、且已有工具增量时返回 `StopToolCalls` | adopt | 2 |
| 流式重试不吞增量 | pi 的重试发生在收到正文之前 | `WithRetry` 先缓冲整次尝试再转发。默认打开会让流式失效 | adopt | 2,N10 |
| 用量拆分 | pi 会话成本区分请求与累计 | 循环把每轮 prompt token 累加(审计 N20) | adopt | 2,N20 |
| 运行内清理旧工具结果 | ZCode compact:保留最近若干条,更早的 Read/Bash/Grep 结果可替换 | 产品回放保留全文;内核 `session/compact.go` 按条数裁尾,可切断工具配对(审计 N7) | adopt | 4,N2 |
| 压缩与追加的竞态 | pi durable:压缩是新条目加 head,不改历史 | `compact.go` 读完解锁再摘要(审计 N8) | adopt | 4,N2 |
| 先读后改、文件过期 | ZCode `tool/read-file-state.ts`、edit handler:未读或 mtime 变化则拒绝 | 内置 write/edit 与后端 `write_file` 无此检查 | adopt | 4,N2 |
| 提醒不占 user 角色 | ZCode `system-reminder/source.ts`:按来源节流,不破坏工具配对 | `runtime/loop.go` 的 `withEphemeral` 追加 user 消息(审计 N22) | adopt | 4,N1/N2 |
| 子代理结果上限 | ZCode agent handler 约 120KB | `modules/backend/internal/runtime/sub_loop.go` 把结果原文交回 | adopt | 4,N2 |
| 默认工具 `+name/-name` | pi settings,`30a1d1849` | `runtime/tools/registry.go` 只有精确名和通配 | adopt | 4,N2 |
| RPC 回执 | pi `e473b5cd8`:`started` / `queued` / `handled` | `rpc/server.go` 只返回 OK 与正文 | adopt | 4 |
| 按字符截断 | ZCode 与审计 N16 | `runmgr.go` 的 `ExecuteTool` 用字节切片截断工具结果 | adopt | 4 |
| 权限三档 | ZCode:plan / build / edit;另有未实现的 auto 与旁路 yolo | 画像策略 deny / confirm / allow;无计划档 | adopt 三档;reject 旁路档 | 6,N1 |
| 钩子总线 | ZCode `contracts/hooks`:SessionStart、UserPromptSubmit、PreToolUse、PermissionRequest、PostToolUse、PostToolUseFailure、Stop、SubagentStart/Stop;决策 allow/deny/ask/modify/block;超时 60s | 内核只有 BeforeTool、AfterTool、Stop 回调;无配置 schema。`adapters/native/backend.go` 不透传(审计 N31) | adopt 进程内总线;hold 进程级钩子 | 6,N1 |
| 向用户提问 | ZCode AskUserQuestion | HITL 有 choice/input/confirm/form,没有一等工具 | adopt | 6,N1 |
| 计划模式工具闸 | ZCode EnterPlanMode / ExitPlanMode | `update_plan` 只写计划,不限制写工具 | adopt | 6,N1 |
| 确认卡参数 | 本仓缺口,不是上游新能力 | `ExecuteTool` 算出 `argsPreview` 后丢弃 | adopt | 6 |
| 意图与结果分两次提交 | pi durable Tools:`replay` 取存储时与当前声明的交集 | 工具结果在执行后一次写入;崩溃留下无结果的调用(审计 N1 的同类) | adopt | 7,N3 |
| 只回滚写文件 | ZCode `contracts/rewind`;pi 文件检查点只追踪写 | 无文件回滚 | adopt | 7,N3 |
| 请求只读已落盘投影 | pi `466db0fec`:prepareRequest 以持久投影为权威 | AgentSession 的队列在内存里进入下一次请求 | adopt | 7,N2 |
| 模型回退链 | pi 虚拟模型是每请求路由器,不是静态链 | 无回退;溢出不重试 | adopt 静态链,默认空;hold 虚拟模型路由 | 7,N3 |
| GenAI span | 提案 §2.1 已列名字;ZCode telemetry 默认少采正文 | 有 logging-system 的 trace_id,无这组 span | adopt | 7,N3 |
| 代码编排工具 | pi `packages/codemode`:模型写 JS,跑在 QuickJS,中间结果不进上下文;bash 对脚本最多 1MiB | 后端 `workflow_script.go` 是工作流步骤,不是模型侧工具 | adopt 端口语义,不搬 QuickJS | 8,N2 |
| MCP 延迟暴露 | pi `docs/mcp.md`:exposure、tool_search、首条提示不等服务器 | `runtime/mcp/manager.go` 启动即连接;stdio 取消后读协程不退出(审计 N3);失败重连只有注释(N18);HTTP Close 为空(N19) | adopt | 8,N2 |
| 运行态单一所有者 | ZCode 协议把 in-flight 与 settled 分开;发送即 ack | 前端 `store.ts` 的 live 与 runs 分别更新。Dev #161 已修可见性回归 | adopt 剩余的单一状态源与事件穷举 | 5 |
| 架构基线棘轮 | ZCode `architecture-policy.yaml` 与 knip | Dev 已有库间导入棘轮,不管 backend internal 分层,也不查死导出 | adopt | 3 |
## 3. 拒绝,以及为什么
| 项 | 理由 |
|---|---|
| 依赖 pi-durable 或 Chord | TypeScript、Experimental、Chord 不是稳定契约。只借鉴规则和测试意图 |
| 透明续跑 | 骨架设计 §4.2 明确不做 |
| 回滚对话历史 | 文件可以回到某条用户消息之前;消息树保持追加 |
| 绕过确认的权限档 | ZCode 的 yolo 与本仓「deny 不能被 allow 跳过」冲突 |
| 进程级钩子、MCP OAuth | 宿主是多租户后端,不是单用户 CLI。钩子先做进程内回调 |
| ZCode 动态工作流 DSL | 编排已裁定归 Agent-Squad。不移植 TypeScript 沙箱脚本图 |
| ZCode TUI、桌面、远程 SSH、CUA、cron、插件市场 | 产品形态是单二进制加 Web。CUA 在上游也是占位 |
| pi 虚拟模型与分类器路由 | 当前没有「每个请求选模型」的产品需求。回退链用静态列表 |
| Anthropic 原生协议与 thinking 签名回放 | 仍按提案 N2 单独做。本轮只堵住会执行未完成工具调用的解析 |
## 4. 不回退的既有决定
- 交付守卫失败即不通过。ZCode 在验证器自身出错时放行。见 harness 提案 §2。
- 正文里的伪造工具调用要拦截。见 harness 提案 P0。
- 动态工作流步数、脚本确认和回调白名单保持 P4 的数字。代码编排端口默认关闭。
- 回合默认仍有轮次和预算上限。ZCode 的长任务哲学是不按工具次数硬停;本仓保留上限作安全阀。
- 库声明自己不是操作系统沙箱。
## 5. 本记录的验收
- 本文件、pi-durable 记录文首更正、提案 §2.4 互相链接。
- 不改 Go 或 TypeScript 代码,不改三个库的指针。
@@ -0,0 +1,71 @@
# 调研:SillyTavern 1.19.0
- 状态:记录(2026-10-04);owner:weichen;任务:Dev 仓 `feature/sillytavern-review`
- 对象:[SillyTavern/SillyTavern](https://github.com/SillyTavern/SillyTavern) `06bde939`(release 1.19.0,2026-09-14)。AGPL-3.0,另有标注 GPLv3 的 RisuAI 片段。按固定提交只读对照,不建长期克隆,不拷源码。
- 本仓事实以任务起点 Dev `4fd03eee` 为准。前序:[2026-10-03-pi-zcode-second-pass.md](2026-10-03-pi-zcode-second-pass.md)、[2026-10-02-agent-stack-completion.md](../proposals/2026-10-02-agent-stack-completion.md)。
- 规则与 pi / ZCode 两份记录相同:只借设计。不拷代码、提示词或界面文案。本仓各子仓没有 LICENSE、以单二进制分发,拷入 AGPL 代码会让整个产品落入 AGPL,网络使用条款同样生效。
结论先说:上下文工程值得借的是「统一注入登记 + 按预算组装」,平台侧值得借的是带标签的密钥、设置快照、以及听口令时拒绝无认证的对外监听。本仓的 SSRF 与账户已经更严,不借。安全缺口(输出未清洗、缺响应头、不校验来源、对外监听无认证)和下面的采纳项在同一任务里落地。扩展系统、脚本宏、群聊、角色卡、自定义 CSS、JSONL 存储、删除消息和自动触发的快捷回复不借。
---
## 1. 来源现在是什么
SillyTavern 把要注入模型的文本放进统一登记表:每条有位置、深度、角色、顺序和扫描范围,再由一次按 token 预算的组装把它们收成请求。预算不够时只裁最老的历史。世界书、作者注、记忆和向量库都走这条登记,而不是各写一条拼接。
平台侧用带标签的多密钥(同一服务一把生效)、设置快照、以及「监听且未开认证就拒绝启动」。请求侧还有 Host / Origin 检查。聊天用 JSONL,带滑动候选和父指针检查点。扩展是未沙箱的同源脚本。
本仓已经有更严的 SSRF(`fetch_url` 和链接预览在拨号时校验解析出的 IP,黑名单含 `0.0.0.0/8` 和 `100.64.0.0/10`)和账户(argon2id、重置密码即吊销服务端会话、登录限流)。这些不改。
## 2. 对照时核实的安全缺口
行号是任务起点那一版本仓源码,实施后位置会移动。
| 缺口 | 起点位置 | 处置 |
|---|---|---|
| 模型输出、卡片和文档预览把 HTML 直接写入页面。`marked` 15.0.12 不清洗 | Frontend `Conversation.tsx`、`previewsetup.ts`;UI `UIRenderer.tsx`、`FilePreview.tsx` | `@ui` 的 `safeHtml` 成为唯一写入点。外部图片改成「外部图片 · 域名」链接。同源地址不再用 `allow-same-origin` 内嵌 |
| 没有 CSP / nosniff。HTML 工件按页面直出 | Backend `server.go` 的静态页与 `artifactContent` | 页面 CSP、nosniff、`Referrer-Policy`。html/svg/xml 工件加 `sandbox` |
| 不校验 Origin / Host。SQLite 档默认不开认证 | `app.go` 的认证开关 | `RequestGuard` 包在组合根。写请求核对 Origin 或 `Sec-Fetch-Site`。未开认证时 Host 只允许 IP、`localhost` 和 `allowedHosts` |
| 对外监听且未开认证时照常启动 | `.env.dev` 曾把 dev 听在 `0.0.0.0:8788` | 非回环、未开认证、又没有 `--insecure-listen` 时,打开数据库之前拒绝启动 |
## 3. 采纳并已在本任务实施
原先记为后续批次。任务范围改为同一任务做完,落点如下。
| 主题 | 上游设计 | 本仓落点 |
|---|---|---|
| 密钥独立存储 | `src/endpoints/secrets.js`:多把带标签的 key,一把生效,读出只给掩码,迁移前备份 | `secret_keys`。GET 只返回掩码。启动时先写设置快照,再把 `models.*.apiKey` 和 `tools.tavily.apiKeys` 迁入并从表单设置清掉。运行时优先用生效密钥 |
| 设置快照 | 启动和保存后留档。上游恢复前不留档,这一点不照搬 | `settings_snapshots`。正文与上一份相同则跳过。保存间隔短于 10 分钟则跳过 |
| 个人指令与会话要求 | persona 与 Author's Note | `context.userInstructions` 与会话 `sessionNote`。经 agent-native `reminders.Attach` 附在最后一条用户或工具消息上,系统前缀不动 |
| 条件注入 | 世界书的关键词、次关键词逻辑、扫描深度、常驻、顺序、预算 | `context_entries`。`matchModule` 仍只看本轮子串;条目在组装末尾另判 |
| 上下文快照 | 提示词分段视图 | `ContextObservation` 增加 `sections` 与 `prefixHash`。界面折叠条显示估算 token 和前缀哈希 |
| 估算口径 | 上游按 token 预算裁历史 | 统一为 UTF-8 字节 / 3.35,再用上一请求的 prompt token 校准。`context.contextWindow` 进入 schema,默认 32000,只丢掉最早的历史 |
| 消息整形 | `src/prompt-converters.js` | `PrepareMessages`。`merge` 合并相邻同角色。`strict` 再把第一条之后的 system 收成 user。工具调用和结果不合并 |
| 复制、排除、候选回复 | 滑动候选与检查点 | 复制在界面完成。排除按消息及其同一 run 成组,记录仍在。重新生成不删旧回复,写入同一 `variantGroup`,并按用户消息做文件回滚 |
| 用户 `/` 命令 | 快捷指令,但不做自动触发 | `user_commands`。名称符合 `user.<slug>` 时登记为 agent-command 的 `SourceUser`。发送时把模板展开成普通用户消息 |
## 4. 暂缓
- Claude 缓存断点:等 Anthropic 原生协议。
- 编辑重发与会话分支:等候选回复用过再看父指针方案。
- 窗口外同会话召回、用户级知识库:等会话经常超出窗口再看。
- 世界书的概率、递归、包含组和定时效果。
- 密钥静态加密。
- i18n:真要做时不能拿原文当键。
- 出站代理:只能用于模型端点。
- 画像导入导出。
## 5. 拒绝
- 拷代码、提示词和界面文案。
- 扩展系统(同源 JS、没有沙箱)和带完整 Node 权限的服务器插件。
- STscript 和宏语言。
- 群聊、角色卡、视觉小说。
- 自定义 CSS 和平滑打字流式:与 DESIGN.md 冲突。
- JSONL 整文件覆盖和逐行扫描搜索。
- 删除消息。用「排除出上下文」代替。
- 自动执行的快捷回复触发器。
## 6. 不回退的既有决定
认证默认仍是 `auto`:显式 `on`,或不是 `off` 且配置了 DSN,才开认证。不加 CSRF token,不加 CSP 上报端点。不改 SSRF 黑名单,不改账户哈希和会话吊销。