完善Agent持久动作与隔离任务恢复
收紧Agent DB容量、句柄锁、终态回执和动作历史身份校验 补齐pending动作、task、event、observation与receipt崩溃恢复幂等 扩展跨平台路径清洗、仓库上下文门禁和瞬时LLM重试 让隔离父run持久等待并通过parent-wake恢复同一run 修复确认动作阶段投影与最近工具状态推进 扩充Rust安全回归和真实Provider E2E验收 同步Runtime技术方案、实施计划与项目决策记录
This commit is contained in:
@@ -198,7 +198,7 @@ delegationId, instanceId, templateAgentId, runId, status,
|
||||
summary, artifacts[path, sha256], evidence[], verifiedRevision, error
|
||||
```
|
||||
|
||||
同一 group 全部终态后,Runtime 使用 `.agent/runtime/isolated-agents/join-deliveries/<groupId>.json` 持久记录交付状态,并且只允许固定 `joinRunId` 入队一次。父 run 仍持有自身 lane 时,只能通过当前已持久化 `agent.run_status` 工具动作的 `actionId` 认领 ready all-join;交付记录保存 `claimedByActionId`,同一 action 崩溃重试可幂等重读,同一 run 的其他 action 不再看到该 join。认领后取消尚未执行的固定 continuation;continuation 已开始时拒绝认领。父 run 未认领时,唯一 continuation 才在 lane 释放后执行,`dispatched -> claimed-by-parent / suppressed` 单向不可逆。恢复、并发 child 终态和重复状态查询都不能重新开放交付、生成 `-dup-*` join run 或重复调用父 LLM。
|
||||
同一 group 全部终态后,Runtime 使用 `.agent/runtime/isolated-agents/join-deliveries/<groupId>.json` 持久记录交付状态。父 run 仍在 planning / action 时不创建 continuation,只能通过当前已持久化 `agent.run_status` 工具动作的 `actionId` 认领 ready all-join;交付记录保存 `claimedByActionId`,同一 action 崩溃重试可幂等重读,同一 run 的其他 action 不再看到该 join。若父 run 在 child 尚未终态时返回空 actions,则持久进入 `waiting-for-isolated-join`、保存原 context cursor 并释放自身 lane,不继续请求 LLM,也不消耗后续上下文窗口;重复 resume 只读取等待状态。最后一个 child 就绪后写 `deliveryTarget=parent-wake` 并唤醒同一父 run / session,由模型继续调用 `agent.run_status` 认领,不能创建 `joinRunId` continuation。旧 delivery 缺少 `deliveryTarget` 时按 `continuation` 兼容;只有非活跃父任务或旧记录的兜底路径才保留固定 continuation。`dispatched -> claimed-by-parent / suppressed` 单向不可逆,恢复、并发 child 终态和重复状态查询都不能重新开放交付、生成 `-dup-*` join run 或重复调用父 LLM。
|
||||
|
||||
## 5. 真实 Provider 验收
|
||||
|
||||
@@ -356,6 +356,38 @@ npm run ai-game-creator-shell:agent-runtime:real-e2e -- --config-dir <AppData> -
|
||||
|
||||
修复后的真实 `gpt-5.5` 回归在 10 轮内完成并收束,唯一 spawn / patchset / join 均保持 1 次,两次 Git 审阅和两类 diff 同时留在最终 context bundle,重复副作用为 0。
|
||||
|
||||
## V1.6 持久动作回执与模型回查
|
||||
|
||||
- 复用 `.agent/agent.db` 作为唯一长期审计源,不新增数据库或平行事实源。每个带 `actionId` 的已落盘终态 observation 都必须在该审计源中追加或补齐 terminal receipt,身份固定包含 `agentId / taskId / sessionId / runId / actionId / actionFingerprint / tool / executionMode / status / inputSummary / summary / safeDetail / updatedAt`。
|
||||
- `safeDetail` 必须按工具类型和字段名双重白名单抽取,不能把某类工具的整段 detail 直接复制到 receipt;不得写入 `file.read` 读取的源码、命令完整输出、diff 正文、消息 / 记忆 / 委派正文、密钥或绝对路径。首版只保留 `project.patchset` 的 `checkpointId / revision / changeCount / revisionAdvanced` 等结构化字段;历史旧记录无法安全还原 detail 时允许显式标记 `detailUnavailable`,不得为补齐字段重新读取或扩散敏感正文。
|
||||
- `agent.action_history` 不能把 Agent DB 中已有 receipt 当成已经可信的展示 DTO。读取时必须再次验证终态 status、actionId、64 位 fingerprint、executionMode、tool,并把 receipt 的 task / session 与同 run 的 task ledger 绑定;`safeDetail` 还要按当前工具白名单重新解析,无法通过时只返回 `detailUnavailable`,不得回显伪造或旧版本遗留的原始 detail。
|
||||
- 新增只读模型工具 `agent.action_history`,只能查询当前 Agent 的持久终态动作。输入支持 `runId / actionId / tool / status / limit`;`limit` 默认 5、上限 10,省略 `runId` 时只查询当前 run。结果优先使用 terminal receipt,并兼容折叠历史 terminal observation;旧记录缺少安全结构化 detail 时显式返回 `detailUnavailable`。
|
||||
- `agent.action_history` 自身默认不出现在未指定 `tool` 过滤条件的结果中,避免模型查询动作递归污染历史;只有显式传入 `tool=agent.action_history` 时才允许查询它自身的 receipt。
|
||||
- 权限复用 `agent.audit`,默认 `auto`,仍可由项目或 per-Agent policy 改为 `confirm` 或 `deny`。该工具只读,不推进 project revision、不改变 verification gate,也不认领 join。
|
||||
- terminal receipt 写入失败时,对应 durable action 必须进入 `needs-reconciliation`,不得出现动作已经完成但长期历史静默缺失。恢复只能按原 `actionId` 幂等补齐 receipt,不能重放动作。
|
||||
- receipt 幂等复用 `recordType + agentId + runId + actionId` 定位,但命中旧记录后必须继续全等复核 `taskId / sessionId / actionFingerprint / tool / executionMode / status` 和结果摘要,任何冲突都失败关闭。普通 Agent DB append 明确拒绝 `agent.runtime.action_receipt`;幂等动作入口只接受字段完整、身份合法且 status 终态的 receipt,不能被任意 recordType 或非终态记录借用。Agent DB 尾部因进程强杀形成不完整 JSONL 时,只允许在追加锁内修复最后一条不完整记录;中间损坏仍失败关闭。单条 JSONL 最大 1 MiB;receipt 幂等全量扫描在文件超过 256 MiB 或完整非空记录超过 100 万条时失败关闭,不能把该阈值误解成通用 append 自动轮转上限。普通审计在约 192 MiB 或 999,936 条的任一软上限停止,同时为字节容量和记录门槛预留 64 条最大 1 MiB terminal 记录;只有 terminal receipt、带 actionId 的终态 observation / observed 和 reconciliation 能使用预留区,`command-failed / verification-failed` 与通用 `failed` 同属终态,非终态记录不得消耗预留。普通和终态追加都在同一 DB 句柄锁内真实计数,连同字节容量一起失败关闭,不做静默轮转。
|
||||
- Agent DB 的跨进程安全以已验证文件句柄为边界,不再依赖可被路径替换的普通 lock 文件。Unix 从可信目录句柄以 `openat + O_NOFOLLOW` 打开并校验普通文件、`nlink=1`,随后直接 `flock` DB 句柄;Windows 使用相对 `NtCreateFile`,拒绝 reparse point / hardlink,并以独占 share 持有句柄。每次 append、尾部补换行或截断修复都执行 `flush + sync_data`;Unix 新建 `.agent` 后同步项目根目录,新建 `agent.db` 后同步 `.agent` 目录,写入完成后再复核路径身份。同 UID 恶意进程制造的 rename / hardlink ABA 不在 v1 绝对隔离承诺内。
|
||||
- 普通历史读取使用最近 32 MiB 的有界尾窗,最多保留 16,384 个完整 JSON object,超出时返回 `truncated=true`;尾窗恰好落在记录边界、UTF-8 半字符或精确 1 MiB 最后一条记录时都不能丢弃合法完整记录。
|
||||
- pending action 的精确 project revision / verification gate 复核继续约束写入、命令、验证、预览证据和会认领 join 的协调动作;`memory.read / conversation.read / asset.list / project.index / project.search / project.diff / git.inspect / file.list / file.read / task.list / agent.action_history` 等纯读取动作在其他 Agent 推进 revision 后允许读取最新事实,并把结果作为新 observation 返回。适用仓库规范的 fingerprint gate 仍独立生效,不能借只读分类绕过规范漂移。
|
||||
- 共享项目事实读取必须使用项目一致性锁;等待锁后重新读取 durable pending sidecar,并与调用方携带的完整 pending 对象逐字段一致,再重验 policy 和 repository fingerprint。pending 被替换、迁移或损坏时失败关闭,不能继续使用锁外旧对象。`agent.run_status` 的 all-join claim 在同一项目锁内完成 revision / verification gate 复核与认领,避免检查通过后项目状态又发生变化。
|
||||
- 父 run 创建 `joinMode=all` 的隔离组后,在所有子结果终态且 ready join 已由当前父 run 的 `agent.run_status` action 认领前,Runtime 必须阻止最终回复和 `agent.action_history`,并要求继续查询状态;认领成功后才解除门禁,避免模型绕过唯一 join 提前收束或先查询不完整动作历史。
|
||||
- all-join 门禁不能只在 planning 阶段检查。最终回复取得项目锁后、创建 finalization journal 前必须再次读取持久 join 交付并确认已由当前父 run 认领;未认领时按 `Stale` 回到同 run planning,不写 journal、assistant 或 completed,关闭 planning 到最终提交之间的竞态窗口。
|
||||
- all-join 等待不能占用父 Agent lane 或伪装成新的 continuation。`waitingGroups > 0` 时父 run 保存 task / state / context / audit 后直接释放 lane;最后一个 child 在父 lane 释放前后完成的竞态由持久 `parent-wake` delivery 和释放后有界复核共同关闭。父 run 已恢复 planning / running 时,重复 dispatch 只确认现状,不创建 join task。
|
||||
- policy denied 和未知工具也要先落 durable observed pending,再追加 terminal receipt。terminal observation 一旦持久化,就不能在 receipt 前响应取消;receipt 成功后再结束取消流程。receipt 写入失败统一进入 `needs-reconciliation`,恢复仅补回执,不重放工具。
|
||||
- action 关联的 task / event 投影按 `runId + actionId + phase` 幂等,同一动作允许从 `waiting-for-confirmation` 合法推进到终态 observation,但同阶段身份冲突继续失败关闭。Agent DB 终态 observation 扫描跳过同 action 的非终态前置记录,只对既有终态做全字段复核;`recentToolCalls` 按 actionId 原位更新,不能让 waiting 投影遮住后续 `ok / failed`。
|
||||
- `agent.action_history` 结构化 detail 上限 7,200 字符;超预算时先移除可选字段,再删除完整最旧项,禁止用字符截断破坏 JSON,禁止清空 `runId / actionFingerprint`。上下文清洗覆盖 Unix、Windows 盘符、正反斜杠 UNC、`file:/...`、`file:///...` 和百分号编码的 `file:` URI,绝对路径统一替换为 `<absolute-path>`,同时保留普通相对文本与 HTTP(S) URL。
|
||||
- 后台 planning 与最终回复遇到 `Timeout / Connectivity / Transport` 或上游 `408 / 429 / 5xx` 时最多额外重试 5 次,按 `500 / 1000 / 1500 / 2000 / 2500ms` 退避;配置、请求、流协议、反序列化错误及其他 `4xx` 不重试。重试只发生在工具计划执行前或最终回复落盘前,不能重放已完成副作用。
|
||||
- 本轮只提供模型工具,不新增前端动作历史弹窗。UI 继续显示最近动作投影;后续若增加历史查看能力,必须使用点击后打开的独立弹窗,不得在当前面板下方追加内容。
|
||||
|
||||
### 2026-07-13 真实验收结果
|
||||
|
||||
- Rust 测试已覆盖 receipt 折叠、组合过滤、默认值与上限边界、跨 Agent 隔离、敏感信息清洗、历史旧记录 `detailUnavailable`、JSONL 尾部修复与中间损坏失败关闭,以及 `needs-reconciliation` 恢复按原 `actionId` 补齐。
|
||||
- 真实 Provider 已实际调用 `agent.action_history`,并证明返回的 `agentId / taskId / sessionId / runId / actionId` 与 `.agent/agent.db` identity 对齐;Runner 强制终止后恢复保持动作零重放、敏感内容零泄漏。
|
||||
|
||||
发布 AppData 中配置的真实 `gpt-5.5` 已通过最终 V1.6 `llm-runtime` 套件。模型实际调用 1 次 `agent.action_history` 并返回 1 条当前 Agent、当前 run 的历史,递归结果为 0;返回 identity 与 `.agent/agent.db` 全字段对齐。最终形成 94 条 task、158 条 event、164 条 Agent DB 记录、11 条合法工具协议、13 次结构化成功工具执行和 24 条 terminal receipt;主 run 含 18 条 receipt,要求覆盖的 3 类工具均有回执,重复 receipt、重复 action、重复 message、receipt identity 冲突、密钥泄漏和项目诱饵泄漏均为 0。
|
||||
|
||||
Runner 强制终止后恢复原 run / session 且身份稳定,project revision 为 3;项目验证、桌面 / 移动浏览器验证、3 个隔离实例和唯一 all-join 认领均通过,本次真实竞态走“活跃父 run 直接认领”路径,未创建 continuation task,真实执行顺序证明 `agent.action_history` 只在隔离结果被父 run 认领后发生。确定性 Rust 集成用例另覆盖 `parent-wake` 等待路径、多次 resume 零 LLM 请求、同父 run 唤醒和无 continuation。pending `file.read` 在其他 Agent 推进 revision 后仍读取最新事实;写入、命令、验证、预览和 join 认领保持严格 gate。
|
||||
|
||||
## 验收命令
|
||||
|
||||
- `npm run ai-game-creator-shell:typecheck`
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user