补齐Agent Runtime工具计划强杀恢复门禁

新增真实 Provider 工具计划交接 checkpoint、Runner 强杀恢复与零重放验收。

收紧 Supervisor 首波协作、返工 runId 和持久 delivery 合同。

扩展敏感正文扫描、跨平台安全回归和 E2E 自测。

注册两级 npm 命令并同步 Runtime 验收文档与当前证据。
This commit is contained in:
AIGameCreator App
2026-07-20 15:48:07 +08:00
parent 791a50ffe8
commit 0fbb76fb94
11 changed files with 3012 additions and 51 deletions
+1
View File
@@ -20,6 +20,7 @@
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
"agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-final-reply-transient-retry",
"agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-tool-plan-handoff-runner-kill",
"agent-runtime:steer-real-e2e": "node scripts/agent-runtime-steer-real-e2e.mjs",
"agent-runtime:steer-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite steer-runner-kill",
"typecheck": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json --noEmit && node scripts/check-config.mjs"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -492,7 +492,18 @@ fn validate_native_agent_delegate_input(
160,
true,
)?;
validate_native_delegate_string(object.get("runId"), "runId", 160, true)
validate_native_delegate_string(object.get("runId"), "runId", 160, true)?;
if object
.get("repairOfDelegationId")
.is_some_and(Value::is_string)
&& !object.get("runId").is_some_and(Value::is_null)
{
return Err(protocol_error(
AgentRuntimeToolPlanProtocolErrorKind::ArgumentsSchema,
"Agent 原生工具协议错误:agent.delegate 返工委派时 runId 必须为 JSON null",
));
}
Ok(())
}
fn validate_native_delegate_string(
@@ -685,7 +696,9 @@ fn runtime_tool_description(tool: &str) -> &'static str {
"canvas.asset_generate" => "通过已配置平台生成并登记首版美术素材。",
"blackboard.write" => "向项目级共享黑板追加稳定结论。",
"agent.message" => "向一个目标 Agent 写入定向上下文消息。",
"agent.delegate" => "用持久验收合同把边界清晰的后台任务委派给另一个 Agent。",
"agent.delegate" => {
"用持久验收合同把边界清晰的后台任务委派给另一个 Agent;返工时 repairOfDelegationId 指向原 delivery,且 runId 必须为 null。"
}
"agent.spawn_isolated" => "创建最多三个写范围互不重叠的隔离子 Agent。",
"agent.schedule_ready" => "调度依赖已完成的 ready manifest 任务。",
"agent.action_history" => "查询当前 Agent 的持久终态动作历史。",
@@ -1035,3 +1048,66 @@ fn project_patchset_input_schema() -> Value {
}
})
}
#[cfg(test)]
mod tests {
use super::*;
fn valid_delegate_input(repair_of_delegation_id: Value, run_id: Value) -> Value {
json!({
"agentId": "specialist",
"task": "完成委派任务",
"acceptanceCriteria": ["定向测试通过"],
"expectedArtifacts": [],
"repairOfDelegationId": repair_of_delegation_id,
"runId": run_id,
})
}
#[test]
fn native_agent_delegate_repair_rejects_string_run_id() {
let repair_id = "delegation-value-must-not-leak";
let run_id = "run-value-must-not-leak";
let error = validate_native_agent_delegate_input(&valid_delegate_input(
json!(repair_id),
json!(run_id),
))
.expect_err("repair delegate must not accept a string runId");
assert_eq!(
error.kind(),
AgentRuntimeToolPlanProtocolErrorKind::ArgumentsSchema
);
let detail = error.to_string();
assert_eq!(
detail,
"Agent 原生工具协议错误:agent.delegate 返工委派时 runId 必须为 JSON null"
);
assert!(!detail.contains(repair_id));
assert!(!detail.contains(run_id));
}
#[test]
fn native_agent_delegate_repair_accepts_null_run_id() {
let input = valid_delegate_input(json!("delegation-id"), Value::Null);
validate_native_agent_delegate_input(&input)
.expect("repair delegate should accept a null runId");
}
#[test]
fn native_agent_delegate_initial_accepts_string_run_id() {
let input = valid_delegate_input(Value::Null, json!("initial-run-id"));
validate_native_agent_delegate_input(&input)
.expect("initial delegate should accept a valid string runId");
}
#[test]
fn native_agent_delegate_description_explains_repair_run_identity() {
let description = runtime_tool_description("agent.delegate");
assert!(description.contains("repairOfDelegationId 指向原 delivery"));
assert!(description.contains("runId 必须为 null"));
}
}
@@ -778,12 +778,26 @@ pub(crate) fn preflight_supervisor_collaboration_plan(
policy: &SupervisorCollaborationPolicy,
state: &SupervisorCollaborationState,
) -> Result<SupervisorCollaborationPreflight, String> {
if agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID || actions.is_empty() {
if agent_id != GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID {
return Ok(SupervisorCollaborationPreflight::default());
}
let policy = normalize_supervisor_collaboration_policy(policy.clone())?;
let summary = summarize_supervisor_collaboration_actions(actions)?;
let initial_wave = !state.has_collaboration();
if actions.is_empty() {
if initial_wave && supervisor_collaboration_policy_has_initial_requirements(&policy) {
return Ok(SupervisorCollaborationPreflight {
violation: Some(SupervisorCollaborationViolation {
summary: "Project Supervisor 首批协作不能停留在计划更新".to_string(),
detail: "当前父 run 尚无协作事实,且项目 policy 明确要求首批专业协作;首批不能停留在计划更新,必须在同一 Provider 批次完整提交协作。".to_string(),
}),
..SupervisorCollaborationPreflight::default()
});
}
return Ok(SupervisorCollaborationPreflight::default());
}
let summary = summarize_supervisor_collaboration_actions(actions)?;
let has_collaboration_action = summary.has_collaboration_action();
if !initial_wave
@@ -1363,6 +1377,58 @@ mod tests {
assert!(error.contains("minIsolatedGroupsBeforeClaim 不能超过 16"));
}
#[test]
fn supervisor_collaboration_policy_blocks_empty_required_initial_wave() {
let result = preflight_supervisor_collaboration_plan(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
&[],
&mixed_policy(),
&SupervisorCollaborationState::default(),
)
.expect("preflight empty required initial wave");
let violation = result
.violation
.expect("required initial wave must reject empty actions");
assert_eq!(
violation.summary,
"Project Supervisor 首批协作不能停留在计划更新"
);
assert!(violation.detail.contains("首批不能停留在计划更新"));
assert!(violation
.detail
.contains("必须在同一 Provider 批次完整提交协作"));
assert!(result.contract.is_none());
assert!(!result.force_durable_batch);
}
#[test]
fn supervisor_collaboration_policy_allows_empty_actions_after_collaboration() {
let state = SupervisorCollaborationState {
initial_static_agent_ids: vec!["design-director".to_string()],
..SupervisorCollaborationState::default()
};
let result = preflight_supervisor_collaboration_plan(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
&[],
&mixed_policy(),
&state,
)
.expect("preflight empty actions after collaboration");
assert_eq!(result, SupervisorCollaborationPreflight::default());
}
#[test]
fn supervisor_collaboration_policy_allows_empty_actions_without_initial_requirements() {
let result = preflight_supervisor_collaboration_plan(
GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
&[],
&SupervisorCollaborationPolicy::default(),
&SupervisorCollaborationState::default(),
)
.expect("preflight empty actions without initial requirements");
assert_eq!(result, SupervisorCollaborationPreflight::default());
}
#[test]
fn supervisor_collaboration_policy_blocks_partial_mixed_wave() {
let result = preflight_supervisor_collaboration_plan(
File diff suppressed because it is too large Load Diff
@@ -4940,5 +4940,5 @@
- 提交顺序:Provider 成功后必须先追加并回读 tool-plan handoff,之后才可为同一实际 requestId 写 lifecycle `completed`,再进入 parser、repair 或动作预检。`repair-0` 与所有 `repair-N` 统一使用持久 transient retry;恢复从当前 loop base 开始按序回放已有 entry,已成功请求零网络,前序回放不得删除后继 repair retry。
- 隐私与失败关闭:function arguments 只存在于私有 handoff 和后续 pending/action batch,公共 task/event/Agent DB/CLI/report 只写安全身份、哈希与计数。tool-plan protocol/repair 公共审计共同保存 Agent/task/Session/run/source、loop/repair/slot、响应指纹、Provider request ID SHA-256 和 protocolprotocol 只额外保存 function call 数量、call ID SHA-256 数组、catalog-bound function names、response ID SHA-256/字符数和归一化元数据,repair 只额外保存 attempt/maxAttempts、协议错误/响应 preview 哈希与字符数、call ID/function name SHA-256,不保存原始 callId/callIds/responseId/providerRequestId。审计写入在 Agent DB append 锁内按完整身份做全历史 compare-and-append,不使用 32 MiB 尾部近似去重。参数为保持语义不得静默脱敏;命中密钥、配置痕迹、结构化可执行路径中的项目/其它绝对路径、超限、乱序、slot/identity/requestId/response 冲突时进入 reconciliation。源码正文与计划叙述只做密钥检查,不能把 HTML 闭合标签或叙述路径误判为执行参数。格式错误但安全有界的 opaque arguments 只用于重建 repair,严格 parser/schema/catalog 通过前不能执行;未闭合或孤立 thinking wrapper 只持久化无正文的无效元数据,重放时仍必须进入 repair。
- 所有权与清理:账本保留同一 run 的已成功 planning entry,直到 run 完成、取消、失败、作废或明确 reconciliation 清理;这样单动作、多动作、confirmation、协作 batch 和直接回复都不会在下一 durable owner 建立前丢失。steer/cancel/终态/漂移清理前必须按整本账本补齐所有实际 requestId lifecycle,任一条失败时保留账本并进入 reconciliation。Runner 恢复会严格扫描 hash 路径、primary/`.previous` 和安全原子临时文件,清理合法终态遗留;Unix 全程使用固定目录句柄和根目录/Agent 目录 `flock`,安装用 `RENAME_EXCHANGE` 复核回滚,删除用 `RENAME_NOREPLACE` quarantine、inode 复核和原 fd 清空同步;Windows 使用相对父句柄及 `GetFileInformationByHandleEx` 句柄枚举,拒绝 reparse point/junction/硬链接并以禁止共享的独占句柄表示活跃 temp。两端都不依赖 PID 存活判断。未知、链接、目录身份替换或内容冲突项失败关闭。primary、`.previous` 或损坏账本阻止 `runner.shutdown_if_idle`。非协作同 UID 进程可主动忽略 Unix advisory lock,属于宿主 OS 信任边界,不纳入完整沙箱承诺。
- 验收边界:确定性测试必须分别覆盖 base handoff 与 repair handoff 在 lifecycle completed 前停止,关闭 mock Provider 后恢复零网络、原 requestId 唯一闭合、repair/protocol audit 幂等、唯一 assistant/completed/committed stream和终局零 sidecar。规划中的真实 `supervisor-swarm-tool-plan-handoff-runner-kill` 应作为独立非默认 suite,使用 sentinel-owned AppData、随机 capability、精确 Agent/run/slot 和 pidfd 强杀;但该 suite 当前尚未实现、尚未注册,因此未执行且不得记 PASS,更不能记为真实外部验收。Provider 成功到 handoff 原子回读前的 unknown-result 及手动 context-compaction 仍不在本决策承诺内。
- 当前证据:`tool_plan_` 61/61`tool_plan_handoff_` 36/36`provider_handoff_` 11/11`provider_retry_` 21/21`response_stream_` 31/31`finalization_` 48/48、`finalization_resume_` 12/12Tauri/Rust 串行全量 1043 tests 为 `1039 passed / 4 ignored / 0 failed`Linux `cargo check``x86_64-pc-windows-gnu cargo check --tests` 均通过。默认并发全量曾分别在两个共享执行器异步投影断言上波动,两个失败用例精确复跑均通过,因此现行稳定门禁使用 `--test-threads=1`,默认并发结果只作竞态诊断。客户端 `308/308`(其中 `appSurface 280/280`)、E2E self-test、typecheck、变更脚本 ESLint、encoding、`platform-llm 41/41``platform-agent game_creation 17/17``shared-contracts game_creation_app 7/7` 与 agent-run smoke 全部通过。实现过程中发现并修复 thinking 归一化、源码路径误判、repair 漂移删账本、durable control 清理遗漏后继 repair lifecycle、复数敏感 key/Provider ID 泄漏、malformed JSON trivia 路径绕过、Agent DB 审计字段扩张、PID 复用 temp 误判、中间目录/文件名称换绑 TOCTOU、Windows 路径枚举 ABA 和审计尾部近似去重问题。`supervisor-swarm-tool-plan-handoff-runner-kill` 当前尚未实现、尚未注册,所以本轮没有执行,仍不得记 PASS。
- 验收边界:确定性测试必须分别覆盖 base handoff 与 repair handoff 在 lifecycle completed 前停止,关闭 mock Provider 后恢复零网络、原 requestId 唯一闭合、repair/protocol audit 幂等、唯一 assistant/completed/committed stream和终局零 sidecar。独立非默认真实 suite `supervisor-swarm-tool-plan-handoff-runner-kill` 已实现并完成 Shell/Root 两级注册;它只使用 sentinel-owned sibling AppData 与 metadata-only zero-fault proxy,每轮随机 capability 严格绑定 project/Agent/run/实际 request slot。断点只能在 handoff 原子落盘并回读一致后、同一实际 requestId lifecycle `completed` 前 ACKACK 后才通过 pidfd `SIGKILL` 强杀 suite 自有 Runner。恢复必须在同一轮证明同一 requestId 唯一闭合、`networkReplayCount=0`、protocol/repair audit 幂等、handoff 与 durable batch plan fingerprint 对应、恢复消费前 action/pending/delivery/claim 等副作用为 `0`,并在终局得到零 sidecar、零重复、零临时资源残留和零正文/凭据/URL/绝对路径泄漏。2026-07-20 的真实单轮已经到达并通过上述 checkpoint,但随后因专业 Agent 连续连接失败而整轮 FAIL;另一独立轮因首批工具数不满足 fixture 也未通过,不能拼接为 PASS。Provider 成功到 handoff 原子落盘回读前的 unknown-result 及手动 context-compaction 仍不在本决策承诺内。
- 当前证据:`tool_plan_``tool_plan_handoff_``provider_handoff_``provider_retry_``response_stream_``finalization_` `finalization_resume_` 定向门禁均保持通过;本轮 `tool_plan_handoff_``44/44`Supervisor collaboration 相关过滤为 `55/55`,权威返工合同用例为 `1/1`Tauri/Rust 串行全量 1058 tests 为 `1054 passed / 4 ignored / 0 failed`Linux `cargo check --tests``x86_64-pc-windows-gnu cargo check --tests` 均通过E2E self-test、typecheck、变更脚本 ESLint、encoding 和 `git diff --check` 通过。默认并发全量只作竞态诊断,不替代 `--test-threads=1`。实现过程中发现并修复 thinking 归一化、源码路径误判、repair 漂移删账本、durable control 清理遗漏后继 repair lifecycle、复数敏感 key/Provider ID 泄漏、malformed JSON trivia 路径绕过、Agent DB 审计字段扩张、PID 复用 temp 误判、中间目录/文件名称换绑 TOCTOU、Windows 路径枚举 ABA 和审计尾部近似去重问题。真实 suite 的 checkpoint 已有单轮外部证据,但整轮仍无 PASS。
@@ -530,4 +530,13 @@ npm run check:server-rs-ddd
- function arguments 只能进入私有 `0600` handoff 和后续 pending/action batch。参数命中密钥、配置痕迹或结构化可执行路径中的绝对路径时失败关闭,不得先脱敏再执行;源码正文与计划叙述不能用日志路径 token 扫描,以免把 HTML `</tag>` 当路径。未闭合/错配 thinking wrapper 要保留无正文的无效事实并走 repair,不能清洗成可执行计划。公共事件、Agent DB、CLI 和报告只保留哈希、计数与安全身份字段;tool-plan protocol 不保存原始 callId/callIds/responseId/providerRequestId,只保存 call ID SHA-256 数组、catalog-bound function names、response ID SHA-256/字符数和 Provider request ID SHA-256repair 只保存 call ID/function name SHA-256 及协议错误/preview 哈希。protocol/repair 审计必须在 Agent DB append 锁内按完整身份全历史 compare-and-append。
- steer/cancel/终态/身份漂移删除 tool-plan handoff 前,必须先按账本顺序幂等闭合全部实际 requestId lifecycle;不能只闭合当前 base entry 后删除后继 repair。Runner 恢复必须扫描 hash 路径归属、primary/`.previous` 和安全临时文件,回收合法终态残留;Unix 读写、扫描和删除固定在逐层打开的目录句柄,handoff 根目录和 Agent 目录用跨进程 `flock` 序列化,临时文件再用非阻塞 `flock` 判断写入方是否仍持有。已有 primary 的安装通过 `RENAME_EXCHANGE` 双端复核并在冲突时回滚;删除先以 `RENAME_NOREPLACE` 隔离到可恢复 temp 名、复核 inode,再按原 fd 清空并同步私有内容。Windows 逐层使用相对父句柄打开,并用 `GetFileInformationByHandleEx` 直接枚举已验证目录句柄,拒绝 reparse point/junction 与硬链接,临时文件以禁止共享的独占句柄表示活跃写入。不得再用文件名中的 PID 或进程存活推断临时文件所有权。未知、链接、身份替换或内容冲突项保持 busy 并失败关闭;主动忽略 advisory lock 的同 UID 进程仍属于宿主 OS 信任边界,不能宣称为完整沙箱隔离。
- 修改 Provider handoff/retry/Runner idle 判断后,至少运行 `tool_plan_``tool_plan_handoff_``provider_handoff_``provider_retry_`、相关强杀恢复用例、Tauri 串行全量 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --test-threads=1`、编码检查和 `git diff --check`;涉及跨平台扫描、PID 或临时文件回收时追加 `cargo check --tests --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --target x86_64-pc-windows-gnu`。当前默认并发全量会受 Tauri 共享执行器饱和影响,曾在不同异步投影断言上偶发失败;它只作竞态诊断,失败时必须精确复跑,不能替代串行门禁,也不能把精确复跑结果伪装成默认并发 PASS。真实 Provider suite 单轮 PASS 前,确定性 mock 结果不得写成外部验收完成。
- `supervisor-swarm-tool-plan-handoff-runner-kill` 目前只是规划中的真实 Provider 门禁,尚未实现且未注册;因此当前不能执行,也不得把“本轮没跑”或其它确定性结果记成该 suite PASS。
- `supervisor-swarm-tool-plan-handoff-runner-kill` 已实现并在 Shell/Root 两级注册。真实外部 Provider 复验从仓库根目录运行:
```bash
npm run ai-game-creator-shell:agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e -- --config-dir <发布AppData绝对路径>
```
- suite 必须使用 sentinel-owned sibling AppDatametadata-only zero-fault proxy 不注入 Provider 故障,为转发请求只做协议校验,请求日志仅记录验收所需的序号/时间等元数据,不得持久化或暴露 URL、method、headers、正文或凭据。每轮随机 capability 必须严格绑定 disposable project、目标 Agent、run 和实际 request slot,任一身份漂移、复用或越权命中都失败关闭。
- checkpoint 只允许在目标 tool-plan handoff 原子落盘并逐字段回读一致后、同一实际 requestId lifecycle `completed` 前 ACK;收到 ACK 后才可用 pidfd `SIGKILL` 强杀 suite 自有 Runner。新 boot 恢复前不得出现由该计划产生的 action、pending、delivery、claim 或其它副作用。
- 单轮验收必须证明同一 requestId 唯一闭合且没有替代 identityproxy `networkReplayCount=0`protocol/repair audit 幂等,handoff plan fingerprint 与恢复后的 durable pending/action batch 对应;终局 retry/tool-plan handoff/provider handoff/finalization/confirmation sidecar、重复 lifecycle/audit/action/message、capability/Runner/AppData 临时资源和正文/API Key/Provider URL/项目及正式配置绝对路径泄漏全部为 `0`。失败轮不得与后续轮拼接。
- 当前该 suite 的实现、E2E self-test、Tauri/Rust 串行全量 `1054 passed / 4 ignored / 0 failed`、Linux `cargo check --tests``x86_64-pc-windows-gnu cargo check --tests` 已通过。2026-07-20 的真实外部 Provider 单轮已到达 checkpoint,并证明旧/新 Runner boot 切换、同一请求恢复、`networkReplayCount=0`、恢复前零 action/pending/delivery 与生命周期唯一闭合;但该轮随后因专业 Agent 连续连接失败而以 FAIL 结束,另一独立轮首批工具数不满足 fixture 也以 FAIL 结束,因此仍没有该 suite 的外部 PASS,且不得拼接两轮证据。Provider 成功到 handoff 原子落盘回读前的 unknown-result 仍未关闭,手动 context-compaction 也不在覆盖内;确定性 mock、命令注册成功或其它 suite PASS 都不能替代单轮完整真实验收。
@@ -1487,9 +1487,10 @@ V1.43 不放宽 V1.41 的文本型 `game-creator-provider-handoff.v1`,而是
- 单元测试覆盖严格 schema、原生 tool call 精确 round-trip、thinking 去除、base+repair 单调追加、幂等重写、乱序/冲突/超限、敏感内容拒绝、`.previous` 恢复、活跃/死亡 temp 判定、目录替换和双副本安全清理;Agent DB 审计另以真实双进程竞争固定 compare-and-append。primary、`.previous` 或损坏账本都必须使 Runner 保持 busy。
- Runtime 集成测试至少在 `repair-0` handoff 落盘且 lifecycle 仅 `started`、以及 malformed base 已完成而 `repair-1` handoff 落盘且 lifecycle 仅 `started` 两个断点停止 Runner。关闭 mock Provider 后恢复必须零网络,原 physical request lifecycle 唯一闭合,protocol/repair audit 不重复,最终 assistant/completed/stream 唯一,终局 retry/tool-plan handoff/finalization 均为零。
- 规划中的真实 Provider 门禁名为非默认 `supervisor-swarm-tool-plan-handoff-runner-kill`:实现后只允许 sentinel-owned 隔离 AppData、随机 capability 和目标 Agent/run/request slot 启用断点,并复用 pidfd Runner 强杀、metadata-only proxy 与零泄漏扫描;它必须证明 checkpoint 到 durable batch 之间 `networkReplayCount=0`、handoff 与 batch plan fingerprint 一致、动作/pending/delivery/claim 无提前副作用和终局零残留。该 suite 当前尚未实现、尚未注册,因此未执行且不得记 PASS;确定性 mock PASS 不得替代它
- 非默认真实 Provider 门禁 `supervisor-swarm-tool-plan-handoff-runner-kill` 已实现并完成 Shell/Root 两级命令注册。suite 只使用 sentinel-owned sibling AppData 和不注入故障的 metadata-only zero-fault proxy;代理为转发请求只做协议校验,但请求日志仅保存序号与时间元数据,不持久化或暴露 URL、method、headers、正文或凭据。每轮生成随机 capability,并严格绑定 disposable project、目标 Agentrun 与实际 request slot,任一身份不匹配都不得 ACK 或强杀。断点只能在目标 tool-plan handoff 已原子落盘并逐字段回读一致、同一实际 requestId 的 lifecycle 尚未写入 `completed` 时 ACK,随后才允许通过 pidfd 向 suite 自有 Runner 发送 `SIGKILL`
- 恢复验收必须在同一轮证明:同一 requestId 只闭合一次且不产生替代 requestIdproxy 的 `networkReplayCount=0`protocol/repair audit compare-and-append 幂等,handoff 与恢复后 durable pending/action batch 的 plan fingerprint 对应;ACK、强杀和恢复消费前不得出现由目标计划产生的 action、pending、delivery、claim 或其它副作用。终局 retry/tool-plan handoff/provider handoff/finalization/confirmation 等 sidecar、重复 lifecycle/audit/action/message、临时 capability/Runner 资源与 AppData 残留均为 `0`,公共报告中的 Provider URL、headers、正文、凭据及项目/正式配置绝对路径泄漏命中也必须为 `0`。2026-07-20 的真实外部 Provider 单轮已证明 checkpoint、Runner boot 切换、同一请求零网络重放、恢复前零副作用与唯一生命周期闭合,但随后专业 Agent 连续连接失败使整轮 FAIL;另一独立轮首批工具数不满足 fixture,同样未通过。两轮不得拼接,当前仍无该 suite 的完整外部 PASS。
- V1.43 仍不关闭“外部 Provider 已成功返回、但本地 handoff 尚未完成原子写入并回读”的 unknown-result 窗口;没有 Provider 级幂等键或结果查询能力时,该窗口继续进入人工 reconciliation,不能宣称端到端物理调用 exactly-once。手动 context-compaction 也不在本切片。
- 2026-07-20 当前确定性证据:`tool_plan_` 61/61、`tool_plan_handoff_` 36/36、`provider_handoff_` 11/11、`provider_retry_` 21/21、`response_stream_` 31/31、`finalization_` 48/48、`finalization_resume_` 12/12Tauri/Rust 串行全量 1043 tests 为 `1039 passed / 4 ignored / 0 failed`Linux `cargo check``x86_64-pc-windows-gnu cargo check --tests` 均通过。默认并发全量曾分别在两个 Tauri 共享执行器异步投影断言上波动,两个失败用例精确复跑均通过,因此稳定门禁使用 `--test-threads=1`,默认并发只作竞态诊断。客户端测试总计 `308/308`,其中 `appSurface``280/280`E2E self-test、typecheck、变更脚本 ESLint、encoding、`platform-llm 41/41``platform-agent game_creation 17/17` 和本地 agent-run smoke 均通过。实现过程中发现并固定 thinking 归一化与无效 wrapper、源码路径误判、repair 漂移删账本、durable control 清理遗漏后继 repair lifecycle、复数敏感 key/Provider ID 泄漏、malformed JSON trivia 路径绕过、Agent DB 审计字段扩张、PID 复用 temp 误判、中间目录/文件名称换绑 TOCTOU、Windows 路径枚举 ABA、终态/temp 遗留及 Agent DB 尾部近似去重问题。Unix handoff 存储使用固定目录句柄、根/Agent 双层 `flock``RENAME_EXCHANGE` 安装回滚和 `RENAME_NOREPLACE` quarantineWindows 使用相对父句柄、`GetFileInformationByHandleEx` 句柄枚举与独占 temp 句柄,并拒绝 junction/reparse point 与硬链接。非协作同 UID 进程仍属于宿主 OS 信任边界,不能据此宣称完整沙箱。真实 `supervisor-swarm-tool-plan-handoff-runner-kill` 尚未实现、尚未注册,所以本轮没有执行,仍不得记 PASS。
- 2026-07-20 当前确定性证据:本轮 `tool_plan_handoff_` `44/44`Supervisor collaboration 相关过滤为 `55/55`,权威返工合同用例为 `1/1`Tauri/Rust 串行全量 1058 tests 为 `1054 passed / 4 ignored / 0 failed`Linux `cargo check --tests``x86_64-pc-windows-gnu cargo check --tests` 均通过。E2E self-test、typecheck、变更脚本 ESLint、encoding 与 `git diff --check` 通过。默认并发全量只作竞态诊断,不替代 `--test-threads=1`。Unix handoff 存储使用固定目录句柄、根/Agent 双层 `flock``RENAME_EXCHANGE` 安装回滚和 `RENAME_NOREPLACE` quarantineWindows 使用相对父句柄、`GetFileInformationByHandleEx` 句柄枚举与独占 temp 句柄,并拒绝 junction/reparse point 与硬链接。非协作同 UID 进程仍属于宿主 OS 信任边界,不能据此宣称完整沙箱。真实 suite 的 checkpoint 已有单轮外部证据,但整轮仍无 PASS。
## 验收命令
@@ -620,5 +620,5 @@ game-project/
- 第六轮 PASS 不改变 V1.41 handoff 原子落盘并回读前的 unknown-result 边界,tool-plan 成功响应/function arguments 的 durable handoff 仍未覆盖。
- 2026-07-20 起,同一 Runtime 文档的“V1.43 tool-plan 成功响应持久交接与 repair 链恢复”作为规划成功响应的现行恢复契约。V1.41 文本 handoff 保持不变;新增独立 `game-creator-tool-plan-handoff.v1` 私有账本,按同一 Agent/run 的 loop/repair 顺序保存实际 Provider requestId、retry identity、去 thinking 的响应、完整 function call envelope/arguments、usage 与响应指纹。`repair-0` 和全部 `repair-N` 统一进入持久 retry/handoff-first 路径,Runner 可从 base 开始零网络重放既有 repair 链。
- tool-plan arguments 只允许出现在 `0600` 原子 sidecar 及后续 pending/action batch,不得进入 task/event/Agent DB/CLI/report。公共 protocol/repair 审计共同保存 Agent/task/Session/run/source、loop/repair/slot、响应指纹、Provider request ID SHA-256 和 protocolprotocol 只保存 function call 数量、call ID SHA-256 数组、catalog-bound function names、response ID SHA-256/字符数及 normalization 元数据,repair 只保存 attempt/maxAttempts、协议错误/preview 哈希和 call ID/function name SHA-256,不保存原始 callId/callIds/responseId/providerRequestId,并在 Agent DB append 锁内按完整身份全历史幂等追加。为了保持执行语义,参数禁止静默脱敏;命中密钥、配置痕迹、敏感 JSON key、Provider ID 中的秘密/绝对路径、结构化可执行路径中的项目或其它绝对路径、大小/顺序/身份冲突时直接 reconciliation。源码正文和计划叙述只做密钥检查,不能把 HTML 闭合标签当绝对路径;未闭合 thinking 只留无正文无效元数据并继续 repair。账本保留到 run 终态或明确作废;steer/cancel/漂移/终态清理前先闭合整本账本的实际 requestIdRunner 恢复严格扫描 hash/primary/`.previous`/安全临时文件并回收合法终态残留,确保单动作、多动作、confirmation、协作 batch 与直接回复在下一 durable owner 建立前都有恢复来源;未知、冲突、primary、`.previous` 或损坏账本都阻止 Runner idle shutdown。
- V1.43 的确定性门禁必须覆盖 base handoff 与 repair handoff 两个 lifecycle-completed 前断点,关闭 mock Provider 后恢复零网络、原 requestId 唯一闭合、repair/protocol audit 幂等、唯一 assistant/completed/committed stream及终局零 sidecar。规划中的真实 Provider 门禁名为 `supervisor-swarm-tool-plan-handoff-runner-kill`,但该独立非默认 suite 当前尚未实现、尚未注册,因此未执行且不得记 PASS;不能把 mock 结果记为它的外部验收。Provider 成功到 handoff 原子回读前的 unknown-result 和手动 context-compaction 仍不在本切片承诺内。
- V1.43 当前确定性实现已通过 `tool_plan_` 61/61、`tool_plan_handoff_` 36/36、`provider_handoff_` 11/11、`provider_retry_` 21/21、`response_stream_` 31/31、`finalization_` 48/48、`finalization_resume_` 12/12,以及 Tauri/Rust 串行全量 `1039 passed / 4 ignored`Linux `cargo check``x86_64-pc-windows-gnu cargo check --tests` 均通过。默认并发全量仍有共享执行器异步投影时序波动,精确失败用例均通过,因此不记默认并发 PASS。客户端 `308/308`(其中 `appSurface 280/280`)、E2E self-test、typecheck、变更脚本 ESLint、encoding、`platform-llm 41/41``platform-agent game_creation 17/17``shared-contracts game_creation_app 7/7` 与 agent-run smoke 已通过;Supervisor 真实 E2E 报告已把 `toolPlanHandoffSidecarCount` 纳入终局残留。handoff 跨平台存储使用 Unix 固定目录句柄、目录 `flock`、exchange/quarantine 与 Windows 相对父句柄、句柄枚举、独占 temp,不再根据 PID 推断写入方是否存活;主动忽略锁的同 UID 进程仍属于宿主 OS 信任边界。`supervisor-swarm-tool-plan-handoff-runner-kill` 尚未实现、尚未注册,所以本轮没有执行,不能记为外部 PASS。
- V1.43 的确定性门禁必须覆盖 base handoff 与 repair handoff 两个 lifecycle-completed 前断点,关闭 mock Provider 后恢复零网络、原 requestId 唯一闭合、repair/protocol audit 幂等、唯一 assistant/completed/committed stream及终局零 sidecar。独立非默认真实门禁 `supervisor-swarm-tool-plan-handoff-runner-kill` 已实现并完成 Shell/Root 两级注册:它使用 sentinel-owned sibling AppData 与 metadata-only zero-fault proxy,以每轮随机 capability 严格绑定 project/Agent/run/实际 request slot;只有 tool-plan handoff 原子落盘并回读一致、同一实际 requestId lifecycle 尚未 `completed` 时才 ACK,随后通过 pidfd `SIGKILL` 强杀 suite 自有 Runner。恢复必须证明同一 requestId 唯一闭合且 `networkReplayCount=0`、protocol/repair audit 幂等、handoff 与 durable batch plan fingerprint 对应、恢复消费前 action/pending/delivery/claim 等副作用为 `0`,并在终局把 sidecar、重复记录、临时 capability/Runner/AppData 资源及公共正文、凭据、URL、项目/正式配置路径泄漏全部清零。2026-07-20 的真实外部 Provider 单轮已到达并通过 checkpoint,但随后专业 Agent 连续连接失败使整轮 FAIL;另一独立轮首批工具数不满足 fixture,也未通过。两轮不得拼接,当前仍无该 suite 的完整外部 PASS。Provider 成功到 handoff 原子落盘回读前的 unknown-result 和手动 context-compaction 仍不在本切片承诺内。
- V1.43 当前确定性实现已通过本轮 `tool_plan_handoff_ 44/44`、Supervisor collaboration 相关过滤 `55/55`、权威返工合同 `1/1`,以及 Tauri/Rust 串行全量 `1054 passed / 4 ignored / 0 failed`Linux `cargo check --tests``x86_64-pc-windows-gnu cargo check --tests` 均通过。E2E self-test、typecheck、变更脚本 ESLint、encoding 与 `git diff --check` 通过;默认并发全量只作竞态诊断,不替代串行门禁。Supervisor 真实 E2E 报告已把 `toolPlanHandoffSidecarCount` 纳入终局残留。handoff 跨平台存储使用 Unix 固定目录句柄、目录 `flock`、exchange/quarantine 与 Windows 相对父句柄、句柄枚举、独占 temp,不再根据 PID 推断写入方是否存活;主动忽略锁的同 UID 进程仍属于宿主 OS 信任边界。
+1
View File
@@ -151,6 +151,7 @@
"ai-game-creator-shell:agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-autonomous-chat-real-e2e --",
"ai-game-creator-shell:agent-runtime:supervisor-swarm-transient-retry-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-transient-retry-real-e2e --",
"ai-game-creator-shell:agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-final-reply-transient-retry-real-e2e --",
"ai-game-creator-shell:agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-swarm-tool-plan-handoff-runner-kill-real-e2e --",
"ai-game-creator-shell:agent-runtime:steer-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:steer-real-e2e --",
"ai-game-creator-shell:agent-runtime:steer-runner-kill-real-e2e": "npm --prefix apps/ai-game-creator-shell run agent-runtime:steer-runner-kill-real-e2e --",
"ai-game-creator-shell:typecheck": "npm --prefix apps/ai-game-creator-shell run typecheck",