实现项目总控Agent与并行协作闭环

将普通主聊天接入独立project-supervisor Session和持久Runtime
新增静态委派delivery、claim、等待唤醒与唯一最终回复协议
支持同轮多专业Agent并行、回执汇总和同Session多轮对话
修复Session锁序、Agent DB旁路、恢复拒绝和迟到回执竞态
收紧Runner定向唤醒、重启恢复和父任务终态收束语义
更新客户端状态展示、共享契约、定向测试与项目文档
完成真实Provider双Agent并行和多轮聊天验收
This commit is contained in:
AIGameCreator App
2026-07-15 00:21:14 +08:00
parent 21e0877dfa
commit 5ab664b22e
17 changed files with 6771 additions and 476 deletions
@@ -57,6 +57,7 @@ const rustSharedContractSource = fs.readFileSync(
'utf8',
);
const allowedUncalledTauriCommands = [
'chat_with_game_creator_agent',
'open_game_creator_launcher_window',
'open_game_creator_workspace_window',
];
File diff suppressed because it is too large Load Diff
@@ -1022,6 +1022,14 @@ pub(crate) fn resolve_game_creator_llm_config_for_agent(
agent_id: &str,
) -> GameCreatorLlmConfig {
let mut llm = config.llm.clone();
if agent_id == GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID {
if let Some(patch) = config
.agent_llm
.get(GAME_CREATOR_LEGACY_CHAT_AGENT_CONFIG_ID)
{
merge_game_creator_llm_config(&mut llm, patch.clone());
}
}
if let Some(patch) = config.agent_llm.get(agent_id) {
merge_game_creator_llm_config(&mut llm, patch.clone());
}
File diff suppressed because it is too large Load Diff
@@ -52,6 +52,7 @@ mod commands;
mod config;
#[cfg(all(debug_assertions, not(test)))]
mod debug;
mod delegation;
mod git_inspect;
mod image_inspect;
mod isolated_agent;
@@ -74,6 +75,7 @@ use command_output::*;
use command_sandbox::*;
use commands::*;
use config::*;
use delegation::*;
use git_inspect::*;
use image_inspect::*;
use isolated_agent::*;
@@ -996,6 +998,26 @@ struct AgentGroupDefinition {
roles: &'static [AgentRoleDefinition],
}
pub(crate) const GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID: &str = "project-supervisor";
const GAME_CREATOR_LEGACY_CHAT_AGENT_CONFIG_ID: &str = "chat";
const GAME_CREATOR_PROJECT_SUPERVISOR_MEMORY_PATH: &str = "memory/agents/project-supervisor.md";
static PROJECT_SUPERVISOR_AGENT_ROLES: [AgentRoleDefinition; 1] = [AgentRoleDefinition {
id: "project-supervisor",
role: "Project Supervisor",
task_id: GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID,
tool_id: "agent.runtime.project-supervisor",
brief_path_name: "project-supervisor.md",
}];
static PROJECT_SUPERVISOR_AGENT_DEFINITION: AgentGroupDefinition = AgentGroupDefinition {
id: "supervisor",
label: "项目总控",
role: "Project Supervisor",
brief_path_name: "project-supervisor.md",
roles: &PROJECT_SUPERVISOR_AGENT_ROLES,
};
static DESIGN_AGENT_ROLES: [AgentRoleDefinition; 2] = [
AgentRoleDefinition {
id: "director",
@@ -1179,8 +1201,8 @@ struct GameCreatorLlmAgentStatusDefinition {
fn game_creator_llm_agent_status_definitions() -> Vec<GameCreatorLlmAgentStatusDefinition> {
let mut agents = vec![
GameCreatorLlmAgentStatusDefinition {
agent_id: "chat".to_string(),
label: "主聊天 Agent".to_string(),
agent_id: GAME_CREATOR_PROJECT_SUPERVISOR_AGENT_ID.to_string(),
label: "项目总控 Agent".to_string(),
},
GameCreatorLlmAgentStatusDefinition {
agent_id: "planner".to_string(),
@@ -855,7 +855,33 @@ pub(crate) fn append_agent_db_record(root: &Path, record: serde_json::Value) ->
append_agent_db_record_internal(root, record)
}
#[cfg(test)]
fn take_agent_db_record_failure_injection(
root: &Path,
record_type: Option<&str>,
) -> Result<(), String> {
let failure_path = root.join(".agent/runtime/test-fail-next-agent-db-record");
match fs::read_to_string(&failure_path) {
Ok(expected_record_type) if record_type == Some(expected_record_type.trim()) => {
fs::remove_file(&failure_path)
.map_err(|error| format!("清理 Agent DB 测试失败注入标记失败:{error}"))?;
Err(format!(
"测试注入 Agent DB 记录失败:{}",
expected_record_type.trim()
))
}
Ok(_) => Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
Err(error) => Err(format!("读取 Agent DB 测试失败注入标记失败:{error}")),
}
}
fn append_agent_db_record_internal(root: &Path, record: serde_json::Value) -> Result<(), String> {
#[cfg(test)]
take_agent_db_record_failure_injection(
root,
record.get("recordType").and_then(serde_json::Value::as_str),
)?;
let uses_terminal_reserve = agent_db_record_uses_terminal_reserve(&record);
let path = root.join(".agent/agent.db");
let directory = open_agent_db_directory(root, true)?
@@ -1066,6 +1092,8 @@ where
if !matches_identity {
return Err("Agent 本地索引幂等记录身份不匹配".to_string());
}
#[cfg(test)]
take_agent_db_record_failure_injection(root, Some(record_type))?;
let path = root.join(".agent/agent.db");
before_lock();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4551,6 +4551,18 @@
- 接口:Tauri 新增 `fork_game_creator_agent_session(projectPath, agentId, sourceSessionId, title)`;开发 Agent 窗口提供分叉按钮、来源与复制消息数显示,成功后按返回的 activeSessionId 加载历史。运行中或 reconciliation 禁用;归档 Session 保持只读,但 lane 空闲时仍可作为分叉源。
- 验收:Tauri 全量 639 项中 635 通过、4 项真实浏览器 opt-in 用例按设计忽略;分叉定向覆盖空会话、消息与 messageId 精确复制、active / archived / legacy、源与分支隔离、重复分叉、非 active 源任务、委派 child、损坏 journal、catalog 失败清理、Runtime 入队竞态和未提交文件不可见。客户端测试目录 268/268 通过,覆盖精确源 Session、复制历史、新 Session 后续写入、切回源会话隔离、归档源分叉和忙碌禁用;shell typecheck 通过。
## 2026-07-14 AI 游戏创作 Project Supervisor 总控 Agent
- 决策:正式用户主聊天的规范 Runtime Agent ID 固定为 `project-supervisor`。它使用现有 External Runner、工具策略、active Agent Session、steer、黑板和 finalization,不新增平行 Runtime、队列或数据库;不进入 manifest、专业组和 isolated template 白名单。LLM 路由优先 `agentLlm.project-supervisor`,旧 `agentLlm.chat` 只作兼容回退。
- 决策:新的普通用户消息和 assistant 只写 Supervisor SessionReact 不再把同一轮双写到 `.agent/conversations/project.jsonl`。legacy project conversation 只作为有界历史背景,项目初始化和旧 slash 命令仍可保留原路径。普通用户界面固定使用 active Supervisor Session,不暴露开发用 Session 管理。后台任务在 Session lane 内完成 durable 入队,通知 External Runner 必须在释放 lane 后发送,避免 Runner 反向启动同一 Agent 时形成跨进程自锁。
- 决策:静态 `agent.delegate` 增加 durable delivery 与同一父 run 完成屏障。同一 Supervisor 父 run 最多同时等待 3 个 `dispatched / ready` 专业 Agent;第 4 个新委派在 child 创建前拒绝,已预留的同 action delivery 恢复必须复用原 target Session/run。同一工具计划的委派动作提交完毕后,只要存在 running child 或 ready 未认领回执,Runtime 就必须在下一次 Provider planning 前进入 `waiting-for-delegate-receipts` 并释放 lane;不能让模型反复轮询全量状态。子终态唤醒同一 run。正常 Supervisor 路径不创建第二个 `delegate-receipt-*` run。
- 决策:delivery journal 状态为 `dispatched -> ready -> claimed-by-parent / suppressed`claim journal 状态为 `Prepared -> Committed -> Observed``agent.run_status` 以当前 actionId 认领时,先持有 claim 锁,再对 delegationId 排序去重并按序取齐 delivery 锁;任一锁不可得时不创建 claim、不改写任一 delivery。全部锁就绪后才按 Prepared、delivery 绑定、Committed 推进,pending observation 持久化后再写 Observed;恢复可补交 Prepared,未 Observed 继续阻断完成。delivery / claim / pending observation 是事实源,Agent DB 只作 best-effort 诊断投影,审计追加失败不回滚已持久化协议。
- 决策:executing 恢复只对 `project-supervisor``agent.delegate / agent.run_status` 开放专用门禁;项目锁内必须重验 durable pending、Session/run/action fingerprint、Runtime 与 delivery/claim/child 完整身份,尚无副作用时还要重新执行 policy/确认判定。只有 delivery 预留且无 child 时可安全退回确认,用户拒绝必须 CAS suppress 该预留并清除完成屏障。`agent.run_status` 的 claim 身份由 delivery/claim journal 约束,对专业 Agent 写黑板或项目文件造成的全局 revision / repository fingerprint 漂移保持中立。其他 executing 动作或身份冲突直接进入 `needs-reconciliation`,不通用重放。
- 决策:parent-wake 以 project/Agent/run 做 coalescing singleflight;已有 worker 期间到达的新信号设置 rerun,worker 退出与信号消费在同一 registry 锁内完成。只对 lane 忙、暂时连接、连接中止、broken pipe、unexpected EOF、资源暂不可用和超时类错误做有界重试;损坏 journal、身份冲突和重启扫描中的损坏 barrier 投影 `needs-reconciliation`。External Runner `runtime.wake_pending` 的 requestId 由项目根、method、Agent、runId 和 loop iteration 稳定派生,只有精确目标已推进或无需推进时才缓存成功。子终态在 ready 或 suppression 前必须核对 parent Agent/Session/run/action、delegationId、target Agent/Session/run、child source 和反向链接;错配 child 不得 suppress 或改写原 delivery。父任务先进入 completed / failed / cancelled / budget-exhausted 时,终态写入路径枚举并 suppress 尚未认领的匹配 delivery,合法迟到 child 不能重新写 ready。
- 决策:finalization 在项目锁内复核 process session、isolated join、static delivery 的 waiting / ready-unclaimed / unobserved-claim 与 verification gate。只有全部清零时,原 Supervisor Session/run 的 finalization journal 才能幂等写入唯一 assistant 并投影 completed。
- UI:普通用户只看到总控 Agent 的紧凑状态、等待对象、协作数量、安全确认和唯一最终回复;不展示内部工具计划、原始 observation、动态 child 或开发控制台。Runner 未提供 token delta 时只显示真实状态,不做伪流式。
- 验收:Rust `project_supervisor_` 定向回归覆盖 ID/prompt/config、delivery/claim 幂等、排序锁零部分认领、Agent DB 旁路、未 Observed 门禁、Provider planning 前 durable 等待、parent-wake coalescing/结构性错误投影、重启损坏 barrier、完整身份与迟到 child suppression、delivery `.previous` 恢复、旧 receipt runId 冲突、executing `run_status` 续接与委派 policy 重验;Runner 内部回归覆盖定向 wake 只有目标推进后成功、可重试结果不缓存;Session lane 回归覆盖入队后才通知 Runner。客户端定向回归覆盖 active Supervisor Session、same-run steer、legacy 历史合并、确认/拒绝和唯一终态 assistant。修复后真实 Provider 已证明 design/art 两个专业 Agent 同秒进入 running 并重叠 20 秒,父 run 只写 1 条 waiting、同一 `Observed` claim 认领 2 份回执、首轮恰好 1 条 user / 1 条 assistant、无 reconciliation;同一 Session 第二轮引用上文完成且未新增委派。项目范围精确密钥扫描为 0。V1.15 首轮跑偏和本轮修复前 revision 误伤仍只保留为负向历史,不作为通过证据。
## 2026-07-13 普通微信支付 V3 退款使用统一观察事务闭环
- 背景:普通微信支付 V3 的退款申请响应、退款结果回调、主动查单和商户平台手工退款发现可能重复、乱序或只出现其中一种;原充值订单只有单一终态,无法表达多次部分退款、权益回收欠款和会员人工处理。
@@ -3161,6 +3161,16 @@
- 验证:定向用例让最终回复包含 challenge、完整 ready/echo/stopped 行和 `PIN=1234` 的短值局部回显,要求统一变为固定摘要;没有私有 poll 正文的普通回复保持原样。真实 Provider 继续扫描 task/event/Agent DB/receipt/conversation/activity/output/runtime state/report,所有正文泄漏必须为 0。
- 关联:`apps/ai-game-creator-shell/src-tauri/src/agent.rs``tests.rs``agent-runtime-real-e2e.mjs`
## Project Supervisor 专业回执不能靠 Agent DB 或新 continuation 收束
- 现象:同一条用户目标在专业 Agent 完成后出现第二个 `delegate-receipt-*` run 和第二条面向用户的 assistant;或 Runner 重启后回执丢失、被不同 action 重复认领、父 run 永久等待,甚至错配 child 把真实 delivery 误标为 suppressed。另一类实测症状是首条 Supervisor 消息报“Agent Session Runtime 启动跨进程锁超时”,或父 Agent 在两个 child 运行时持续调用 `agent.run_status`,随后被 child 的黑板写入推进 project revision 而误判为旧动作。
- 原因:把 `.agent/agent.db` 诊断投影当成回执协议事实,或直接从 ready 跳到已消费,没有可恢复的 claim 阶段和 observation 门禁。多回执认领若不先按稳定顺序取齐所有 delivery 锁,会死锁或留下部分认领;parent-wake 若每次生成随机 Runner requestId、把“全局扫描成功”当成“目标已推进”或对结构性错误无界重试,会丢 wake、重复唤醒或掩盖损坏状态。持有 Session lane 同步通知 Runner 会让 Runner 反向启动同一 Agent 时自锁;把 claim 型 `agent.run_status` 当成项目写动作做 revision/fingerprint 门禁,会被合法的专业 Agent 写入误伤。
- 处理:正式主聊天只路由到 `project-supervisor` active Session,活跃期输入继续 same-run steer;同一父 run 最多同时保留 3 个 `dispatched / ready` 静态专业委派,已预留的同 action delivery 恢复复用原 target Session/run,不另占名额。同一工具计划完成委派后,Runtime 在下一次 Provider planning 前直接持久化 `waiting-for-delegate-receipts` 并释放 lane,不让模型轮询等待。delivery 单向推进 `dispatched -> ready -> claimed-by-parent / suppressed`claim 单向推进 `Prepared -> Committed -> Observed`;先持有 claim 锁,再对 delegationId 排序去重并按序取齐 delivery 锁,任一锁不可得时零状态推进。delivery / claim journal 与 pending observation 是事实源;Agent DB append 只能 best-effort,失败不得推翻已持久化结果。入队在 Session lane 内完成,Runner 通知在 lane 外发送;`agent.run_status` 保留 claim 身份校验但不绑定全局 project revision/fingerprint。
- 恢复门禁:只有 `project-supervisor` 的 executing `agent.delegate / agent.run_status` 可在项目锁内重验 durable pending、Session/run/action fingerprint、delivery/claim/child 身份和当前 policy 后补交;只有 delivery 预留且无 child 时,拒绝动作必须把该预留 CAS 为 suppressed。其他 executing 动作或副作用身份不明必须进入 `needs-reconciliation`。parent-wake 以 project/Agent/run 做 coalescing singleflight,新信号不能在已有 worker 退出窗口丢失;有界重试接受 lane 竞争、暂时连接、连接中止、broken pipe、unexpected EOF、资源暂不可用和超时类错误。损坏 journal、身份冲突及重启扫描中的损坏 barrier 直接投影 reconciliation。External Runner wake 用项目根、method、Agent、runId 和 loop iteration 派生稳定 requestId,目标未观察到、仍 waiting 或 lane 忙时返回不缓存的可重试错误。
- 身份与收束:子终态发布前同时核对 parent Agent/Session/run/action、delegationId 派生、target Agent/Session/run、child source 和 child 反向 parent/delegation 链接。错配 child 保持原 delivery 不变并记录冲突;父任务先进入 completed / failed / cancelled / budget-exhausted 时,终态写入路径 suppress 尚未认领的匹配 delivery,合法迟到 child 不能重新写 ready。父 run 在 waiting、ready-unclaimed 或 unobserved claim 任一非零时都不得 final;全部清零后仍由原 Supervisor Session/run 的 finalization journal 幂等写入唯一 assistant,不创建新 receipt run。
- 验证:Rust 定向回归使用 `project_supervisor_` 前缀,覆盖 delivery/claim 状态机、同 action 幂等、第 4 个新委派拒绝与已预留委派复用/拒绝 suppression、后续 delivery 锁忙时零部分认领、Agent DB 故障后回执仍可重放、未 Observed 阻断 final、Provider planning 前 durable 等待、parent-wake coalescing/结构性错误、重启损坏 barrier、错配和迟到 child、executing `run_status` 续接与 delegate policy 重验;`agent_background_enqueue_notifies_only_after_session_lane_release` 覆盖入队锁序,Runner 内部测试覆盖定向 wake 与不缓存重试。真实 Provider 必须同时证明专业 Agent 时间区间重叠、父 run 仅一次 waiting、同一 Observed claim 认领全部回执、唯一 assistant、第二轮历史引用不新增委派和项目范围密钥扫描为 0。
- 关联:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md``apps/ai-game-creator-shell/src-tauri/src/delegation.rs``agent.rs``runner.rs``tests.rs`
## iOS 退款问询的 result_code 不是 debug 状态
- 现象:为了先观察真实 iOS 退款通知,回调返回 `ErrCode=0 + IosRefundQueryResponse.result_code=1`,并把 evidence 写成“调试阶段不执行自动退款决策”,看起来像安全 ACK,实际已经向微信建议拒绝退款。
@@ -668,6 +668,24 @@ V1.15 新增不依赖 Tauri WebView 或正常客户端 GUI 的终端聊天入口
2026-07-14 首轮真实 `gpt-5.5` 验证已证明终端入口能够启动真实 External Runner、持久化父 Session、实时展示状态 / event / parent / delegation、并行运行 `design-foundation``balance-seed`,并在终端完成两次 `agent.delegate` approve、一次重复委派 reject、重启恢复、receipt 续跑、`file.write` reject 和活跃 run 中 `/quit`。独立收束复验在同一 `code-prototype` Session 连续完成 4 轮固定回复,重启后 `/history` 读取 8 条 user / assistant 消息,最后一轮按 `idle -> 安静窗口 -> receipt/join 恢复扫描 -> 安静窗口 -> 最终回复` 返回 `FOURTH_OK`。但端到端 swarm 汇总未通过:第一轮父 Agent 反复调用全量 `agent.run_status`,因输出截断无法看到目标 Agent,18 轮后 `budget-exhausted` 并压掉两条排队 receipt;第二轮按 receipt 模式续跑时,父 Agent 没有恢复原始“只读汇总”目标,转而读取项目并请求写 `game/balance.json`,已由终端拒绝。当前结论只能是“V1.15 验证入口可用、现有静态委派的父回执汇总策略未验收”,不得标记完整 Agent Swarm 通过;后续需修复定向状态查询或 receipt continuation 原目标恢复后再跑唯一最终父回复验收。动态 isolated child / all-join 也仍待通过该入口真实复验。
## V1.16 Project Supervisor 总控 Agent
V1.16 把正式用户主聊天从一次性自然语言问答升级为现有 External Runner 中的根协调 Agent。规范 Runtime ID 固定为 `project-supervisor`,显示名为“项目总控 Agent”;它不是 manifest 任务、专业组角色或 isolated spawn 模板,不加入 `GAME_CREATOR_AGENT_GROUP_DEFINITIONS`。恢复扫描必须固定包含该 ID。LLM 首选 `agentLlm.project-supervisor`,发布 AppData 仍只有旧 `agentLlm.chat` 时把它作为兼容回退并继续继承全局配置,不复制或暴露 API Key。
- Supervisor 使用自己的 active Agent Session 和 `.agent/conversations/agents/project-supervisor/` 会话事实源,普通用户界面不提供 Session 新建、切换、归档或分叉控件。历史 `.agent/conversations/project.jsonl` 保持只读兼容背景,不复制成新消息、不伪造 messageId,也不作为新 Runtime 消息的第二写入目标。项目初始化、旧 slash 命令和历史生成链路仍可读取 legacy project conversationSupervisor prompt 同时读取有界 legacy 项目历史、当前 Supervisor Session、项目记忆、黑板、资产与仓库启动上下文。
- 普通文本在 Supervisor 空闲时创建新 run;存在匹配 Session 的非终态 run 时默认走 V1.13 same-run steer。用户消息由 Runtime 入队路径在返回前写入 Supervisor Sessionassistant 只由 finalization journal 恰好一次写入;React 不再把同一轮 user / assistant 追加到 project JSONL。后台任务的 durable 入队必须在持有 Agent Session lane 时完成,但 External Runner 通知必须在释放该 lane 后发送,禁止入队方持锁等待 Runner 反向取得同一 lane。断线、刷新和 App 重启后通过 Session conversation、完整 Runtime snapshot 和 resume 恢复,Tauri event 只作刷新提示。
- Supervisor planning prompt 必须持续携带当前用户原始目标、已委派目标、待回执集合、已认领结果和当前完成条件;它负责澄清、直接回复、读取、行动或委派,并优先把边界清晰的专业工作交给静态专业 Agent,把互不重叠的临时并行检查交给 `agent.spawn_isolated`。同一 Supervisor 父 run 同时处于 `dispatched / ready` 的静态专业 Agent 最多 3 个;第 4 个新委派在创建 child 前拒绝,但崩溃后重放同一 action 的已预留 delivery 必须复用原 target Session/run,不能被容量门禁误伤或重复计数。稳定跨 Agent 结论写项目黑板,Supervisor 私有长期协调经验写自己的 Agent memory;专业 Agent 默认不直接抢占用户主会话。
- `agent.delegate` 新增 durable static delivery。delivery journal 固定绑定 `parentAgentId / parentSessionId / parentRunId / parentActionId / delegationId / targetAgentId / targetSessionId / targetRunId`,状态单向推进 `dispatched -> ready -> claimed-by-parent / suppressed`。同一工具计划中的委派动作全部提交后,只要还有 running child 或 ready 未认领回执,Runtime 必须在下一次 Provider planning 前进入 `waiting-for-delegate-receipts`、持久化 context cursor 并释放 lane,不能依赖模型反复调用 `agent.run_status` 轮询。子终态写 ready 后唤醒同一父 run;`agent.run_status` 只把当前父 run 的 ready receipts 放在 detail 首部,并用当前 actionId 幂等认领。该工具的 claim 身份由 delivery/claim journal 约束,不受专业 Agent 写黑板或项目文件导致的全局 project revision / repository fingerprint 漂移误伤。旧 `delegate-receipt-*` continuation 仅处理历史任务,V1.16 Supervisor 正常路径不得创建第二个 receipt run。
- ready receipts 的认领使用独立 claim journal,状态为 `Prepared -> Committed -> Observed`。Runtime 先取得当前 `parentAgentId + parentRunId + actionId` 的 claim 锁,再对全部 delegationId 排序、去重并按固定顺序取得 delivery 锁;任一后续锁不可得时必须保持全部 delivery 为 ready 且不创建 claim。全部锁就绪后先写 `Prepared`,再把对应 delivery 绑定到当前 actionId 并写 `Committed`;只有 terminal observation 已持久化到 pending action 后才写 `Observed`。恢复扫描会补交未完成的 `Prepared`,未 `Observed` 的 claim 继续阻断 finalization。delivery / claim journal 和 pending observation 是协议事实源;`.agent/agent.db` 只是 best-effort 诊断投影,其追加失败不得撤销已持久化的认领或让父 run 重复消费回执。
- executing 恢复不做通用工具重放;只有 `project-supervisor``agent.delegate / agent.run_status` 可进入专用恢复门禁。Runtime 必须先取得项目锁,重新核对 durable pending 全对象、Session/run/action fingerprint、Runtime 状态与已有 delivery/claim/child 的全部身份;尚无 durable 副作用时还要重跑当前 policy/确认门禁,已有副作用时只按原身份补全 observation。其他 executing 动作、身份冲突或 child 存在但 delivery 缺失时进入 `needs-reconciliation`,不自动重放。
- static parent-wake 以 `project root + parentAgentId + parentRunId` 做进程内 coalescing singleflight,最多进行 40 次有界等待;已有 worker 执行期间到达的新 wake 必须设置 rerun 标记,worker 退出与标记消费在同一 registry 锁内完成,不能丢掉最后一个 child 的 ready 信号。只有 lane 忙、暂时连接、连接中止、broken pipe、unexpected EOF、资源暂不可用或超时类错误可重试,损坏 journal、身份冲突等结构性错误立即投影为 `needs-reconciliation`;Runner 重启扫描遇到损坏 static barrier 也必须投影 reconciliation,不能静默保持 waiting。通知 External Runner 时,`runtime.wake_pending` 的 requestId 由规范项目根、method、Agent、runId 和 loop iteration 派生,同一唤醒重试必须使用稳定 requestId;只有精确目标 run 已推进或已不再需要 wake 才返回并缓存成功,目标 lane 忙、未观察到目标或仍处于 waiting 时返回不缓存的可重试错误。
- 子终态发布前必须完整核对 parent Agent/Session/run/action、delegationId 派生、target Agent/Session/run、child source 和 child 反向 parent/delegation 链接。父任务写入 completed / failed / cancelled / budget-exhausted 终态后必须枚举并 suppress 尚未认领的匹配 delivery;这样无论 parent 与 child 谁先取得 delivery 锁,合法迟到回执都只能停在 suppressed。错配 child 不得改写或 suppress 原 delivery。executing 恢复若只有精确 delivery 预留而尚无 child,当前 policy 仍可退回确认;用户拒绝时必须在 delivery 锁内复核无 child 并把该预留 CAS 为 suppressed,不能留下永久 `dispatched` 屏障。finalization 在项目锁内同时复核 process session、isolated join、`waiting / ready-unclaimed / unobserved-claim` 三类 static delivery 障碍和 verification gate;全部清零后才由原 Supervisor Session/run 的 finalization journal 幂等写入唯一 assistant。`agent.run_status(scope=all)` 的有界文本列表不能作为静态回执完成协议。
- 正式用户界面只展示 Supervisor 的紧凑状态、当前阶段、等待对象、专业 Agent 协作数量和安全待确认动作;确认/拒绝继续调用现有 Runtime action 路径。专业 Agent 的工具计划、原始 observation、内部 task/event/Agent DB、动态 child ID 和开发调试面板不进入普通用户面。External Runner 暂未向 App 传递 Provider token delta 时,只展示真实 Runtime 状态和持久化后的最终回复,不做拆字延时等伪流式输出。
确定性验收必须覆盖:Supervisor ID/config fallback/恢复扫描、独立 Session 与 legacy project history 背景、普通消息不双写、same-run steer、入队通知锁序、静态 delegate 单个与最多 3 个专业 Agent 并行/第 4 个拒绝、Provider planning 前 durable 等待、已预留委派重放与拒绝 suppression、delivery/claim 状态机、排序锁失败时零部分认领、Agent DB 失败后 journal 仍可重放、`Prepared / Committed / Observed` 恢复与完成门禁、executing 专用恢复与 policy 重验、parent-wake coalescing/结构性错误投影/稳定且按目标确认的 Runner requestId、父终态与迟到 child suppression、与 isolated all-join 混合、确认动作、黑板共享、同一父 run 与最终唯一 assistant,以及 Supervisor 不能作为 isolated template。Rust 定向回归使用 `project_supervisor_` 前缀,并额外覆盖 `agent_background_enqueue_notifies_only_after_session_lane_release`;回执事实源、原子认领、未观察门禁、parent-wake、重启损坏屏障、迟到 child、Agent DB 旁路、executing 恢复与拒绝预留分别由 `project_supervisor_run_status_uses_durable_claim_when_agent_db_audit_fails``project_supervisor_ready_claim_is_atomic_when_later_delivery_lock_is_busy``project_supervisor_unobserved_claim_blocks_finalization_until_observed``project_supervisor_parent_wake_is_singleflight_and_projects_structural_errors``project_supervisor_parent_wake_singleflight_coalesces_late_signal``project_supervisor_restart_recovery_projects_delegate_barrier_errors``project_supervisor_terminal_parent_suppresses_late_matching_child_delivery``project_supervisor_waiting_state_survives_agent_db_audit_failure``project_supervisor_resume_replays_executing_run_status_observation``project_supervisor_resume_rechecks_delegate_policy_after_delivery_reservation` 取证;Runner 定向 wake 的目标推进与不缓存重试由 `runner::tests::project_supervisor_*` 取证。前端定向回归覆盖主 Session 路由、same-run steer、唯一终态 assistant 与确认/拒绝。真实 Provider 验收必须从 task/event/delivery/claim/conversation 等 journal 事实与 Agent DB 诊断投影交叉证明至少两个专业 Agent 并行、原始用户目标在回执后仍存在、父 run/session 不变、只有一个面向用户最终回复、重复 action/message/receipt 为 0 且密钥零泄漏。
2026-07-14 修复后真实 Provider 验收已通过。一次性项目 `/tmp/gameagent-supervisor-parallel-e2e-pass-*` 中,父 run `swarm-project-supervisor-1784044475952` 同时创建 `design-director``art-director` 两个静态委派;两者均在时间戳 `1784044486` 进入 running,分别于 `1784044506``1784044536` completed,存在 20 秒真实重叠。父 run 只写入 1 条 `waiting-for-delegate-receipts` task 记录,期间没有继续 Provider 轮询;随后同一 actionId 认领两份 delivery,两个 delivery 均为 `claimed-by-parent`,唯一 claim 为 `Observed` 且 receiptCount=2,父 run 无 reconciliation 并 completed。首轮 Supervisor Session 恰好写入 1 条 user 与 1 条 assistant;同一 Session 的第二轮“基于上文、不要重新委派”请求直接使用历史完成三句回复,delivery 总数仍为 2。项目范围精确 secret 扫描无 API Key、Bearer token 或 `sk-*` 命中。
## 验收命令
- `npm run ai-game-creator-shell:typecheck`
@@ -48,6 +48,12 @@ V1.11 的受保护仓库控制目录同时包含 `.git / .agent / .agents / .cod
2026-07-14 起,同一文档的“V1.15 Agent Swarm 纯聊天验证入口”补充无 GUI 开发验收面。`npm run agc:swarm -- --config-dir <项目外AppData> [--init] <project> <parentAgentId>` 直接把每轮输入投递给真实父 Agent background Runtime,复用 External Runner、active Session、conversation、静态委派、动态隔离 child、私有记忆、项目黑板、确认策略和 all-join,不调用一次性 `--agent-chat`,不新建本地 HTTP 服务或平行数据库。终端持续显示全 Agent 状态、事件和父子 / 委派关系,提供 `/agents``/status``/history``/help``/quit` 以及 approve / reject;父 run 活跃时普通输入走 same-run steerstdin channel 保证运行中仍可退出。入口启动和收束前执行恢复扫描,只有全 Runtime 非活跃、队列为空、恢复扫描无新增工作并通过稳定观察窗口后才输出绑定父 Session 的最后回复。首版没有 Runner Provider token delta,只承诺状态 / 事件实时输出和最终回复。真实 Provider 已证明入口、两个静态 Agent 并行、确认 / 拒绝、重启恢复和活跃退出,但父 Agent 的全量状态轮询与 receipt 原目标恢复仍导致汇总失败;当前不得宣称完整 swarm 已通过,动态 isolated child / all-join 也待复验。
2026-07-14 起,Runtime V1.1 文档的“V1.16 Project Supervisor 总控 Agent”作为正式用户主聊天的新事实源。规范 ID 为 `project-supervisor`,使用独立 active Agent Session 和现有 External Runner;空闲输入创建新 run,匹配 Session 的活跃输入继续走 same-run steer。它可以澄清、使用白名单工具、维护黑板、在同一父 run 内最多并行等待 3 个静态专业 Agent 并汇总结果。旧 `agentLlm.chat` 只作 `agentLlm.project-supervisor` 的配置兼容回退。静态 `agent.delegate` 必须通过 durable delivery 和同一父 run 的等待 / 唤醒 / 认领屏障收束,不能再靠新 `delegate-receipt-*` run 产生第二次用户回复;普通用户面只展示紧凑状态、安全确认和唯一最终回复。
2026-07-14 修复后,V1.16 的纯聊天真实 Provider 验收已通过:两个专业 Agent 同秒进入 running 并真实重叠 20 秒;父 run 在下一次 planning 前 durable 挂起,只写 1 条等待记录;两份回执 ready 后自动唤醒同一 run,以一个 `Observed` claim 原子认领并只写 1 条用户可见 assistant。随后同一 Supervisor Session 的第二轮请求直接引用上轮结论完成回复且没有新增委派。正式实现因此不再沿用 V1.15 的“模型反复全量 `agent.run_status`”等待策略:`agent.run_status` 对专业 Agent 的项目 revision 漂移保持中立,Session 入队释放 lane 后才通知 Runner,定向 wake 只有在精确目标已推进或无需推进时才缓存成功。
当前委派协议已把 delivery 与 `Prepared -> Committed -> Observed` claim journal 作为事实源,认领前按 delegationId 排序并取得全部 delivery 锁,`.agent/agent.db` 只作 best-effort 诊断投影。恢复中的 executing 动作只允许 Supervisor `agent.delegate / agent.run_status` 经项目锁、pending 全身份和 policy 重验后补交;parent-wake 使用 singleflight、有界错误分类和稳定 Runner requestId。子终态只有在 parent/child/delivery 完整身份一致后才能 ready 或 suppression;错配不得改写 delivery。最终回复继续由原父 run 的 finalization journal 幂等写入。
2026-07-12 真实验收:发布 AppData 中的真实 `gpt-5.5` 已通过最终安全收紧后的 `llm-runtime` 套件,覆盖 Runner 强杀恢复且 run/session 身份稳定、仓库上下文、checkpoint/精确修改、失败命令诊断与修复复验、6 套确认生命周期、项目验证、桌面与移动非空画布证据、3 个隔离实例并行和唯一 all-join95 条 task、161 条 event、137 条 Agent DB、13 条合法工具协议、副作用判重、终态投影、assistant audit、消息、回执和密钥泄露均以结构化落盘事实验收。`full` 套件仍要求 External Editor API 配置,缺失时必须返回 `BLOCKED(editorApi)`,不得记为通过。
2026-07-13 V1.3 真实验收:同一真实 Provider 套件已改为先读取 SHA-256,再用唯一一次 `project.patchset` 同时更新和创建文件,并使用自动 checkpointId 读取 2 项内容 hunksprepared / completed 审计各 1 条、patchset revision 增量为 1Runner 强杀恢复、命令和项目验证、双视口浏览器验证、隔离 Agent join、重复副作用与密钥扫描继续全部通过。
@@ -430,7 +436,7 @@ game-project/
- `game.generate_draft` 写入最终产物后会复用白名单受限命令 `game.static_smoke` 做一次生成后自检,至少检查 `game/index.html` 包含 canvas、canvas 渲染上下文、绘制调用、主循环、非空输入监听、明确目标、失败或胜利状态和重开路径,且不使用远程资源、`eval``new Function``localStorage``fetch``WebSocket``ServiceWorker`,也不得包含固定星核传送门模板词、纯按钮计分模板或 `TODO` / `待实现` / `这里省略` 等未完成实现;画板资源占位引用允许出现在 asset id 或说明中,并把该工具调用写入 `.agent/run.latest.json``.agent/logs/command.log`;自检失败则本次命令失败,不继续启动预览。
- `ArtifactWriter` step 使用 `file.write.local_artifacts` 工具调用记录最终写入的 `memory/``memory/agents/``game/``assets/``exports/``.agent/manifest.json` 路径;写入完成后 `nextStep` 指向 `game.static_smoke`
- `preview.start` / `preview.stop` 会追加 `.agent/logs/preview.log`,并在 `.agent/run.latest.json` 已存在时追加 `Preview` step 和 `preview.*` toolCall,记录本地 HTTP 预览 URL 与停止事件;单全局本地预览被新项目替换时,会 best-effort 把旧项目 manifest、preview log 和 trace 记录为 stopped,避免旧项目残留 running;本地 HTTP server 的 `/` 映射到 `game/index.html`,只允许读取 canonical 后仍位于项目真实 `game/` 或真实 `assets/` 下的文件,拒绝 `memory/``.agent/``exports/``..`、一级 `game` / `assets` 符号链接目录和内部符号链接越界,并为常见图片、音频、视频和 Web 资源返回对应 MIME;静态 `HEAD` 返回真实 `Content-Length` 但不返回 body,确保浏览器和媒体资源探测可用;上传和画板回流资产可被生成游戏引用但不会暴露记忆或 trace;没有 run trace 的手动预览启动不阻断。
- 普通聊天文本进入主聊天 Agent;主聊天 Agent 读取短期记忆、长期记忆、项目黑板、最近项目对话和本地资产摘要作为背景,只做自然语言交互、澄清、建议和 slash 命令引导,不写项目、不运行工具、不伪装已经生成产物,也不直接触发 `game.generate_draft`用户显式输入 `/generate <创作想法>``/draft <创作想法>` 时才生成待确认的 `game.generate_draft` 内置命令;用户确认后,正式用户聊天会实时展示 Planner LLM、Orchestrator、6 组角色 brief、Generator LLM、Evaluator 质量评审、ArtifactWriter 和 `game.static_smoke` 的进度,再把 LLM 返回的结构化草案写入短期记忆 `memory/session.md`、长期记忆 `memory/project.md`、项目黑板 `memory/blackboard.md`、角色私有记忆 `memory/agents/<group>/<role>.md`、设计草案 `game/game_design.md`、数值配置 `game/balance.json`、美术清单 `assets/manifest.art.json`、音乐音效清单 `assets/manifest.audio.json`、发布包装草案 `exports/README.md` 和可运行 `game/index.html`。生成完成后,普通聊天消息会自动展示最近一次 Agent loop 的 Run、LLM 对话、轮次、工具调用、active / carry-over 任务、返工焦点、编排轮次、最近步骤、画板同步建议命令和本地产物快照;单 agent 最近证据中的输入 / 输出路径可一键填入 `/read <path>` 草稿,画板同步建议可一键填入 `/sync-canvas-project ` 草稿,再由用户补齐参数并走原确认流;完整证据仍由 `/trace` 读取同一份 `.agent/run.latest.json`
- 普通聊天文本进入 `project-supervisor` 的 active Session;匹配 Session 已有非终态 run 时转为 same-run steer,不切换父 Session/run。它读取当前 Supervisor Session、legacy 项目对话、短期 / 长期记忆、项目黑板、本地资产和有界仓库上下文,可以正常澄清、使用 Runtime 白名单工具、按项目权限请求确认,并把专业工作委派给现有 Agent;同一父 run 同时活跃的静态专业委派不超过 3 个。静态 delivery/claim 与 isolated all-join 都必须由同一父 run 收齐、认领并完成 observation 持久化后才允许形成唯一最终回复;专业 Agent 的内部对话、工具计划和动态 child 不直接展示给普通用户显式 `/generate <创作想法>``/draft <创作想法>` 继续保留为确定性旧生成 loop 的待确认快捷命令;它不替代 Supervisor 的自主 Runtime。用户确认旧生成命令后,正式用户聊天展示 Planner、Orchestrator、6 组角色 brief、Generator、Evaluator、ArtifactWriter 和 `game.static_smoke`真实进度,并按原契约写入本地产物与记忆
- `game.generate_draft``game/index.html` 必须是可试玩原型,至少包含输入、主循环、目标、失败或胜利状态和重开路径;不能只输出按钮计分或纯展示页。
- `game.generate_draft` 会校验 LLM 输出:`balance`、美术清单和音乐清单必须是 JSON object`gameHtml` 必须是自包含 HTML、包含 `canvas``requestAnimationFrame`,不得加载远程脚本或资源,不得使用 `eval` / `new Function` / `localStorage` / `fetch` / `WebSocket` / `ServiceWorker`,不得把包含 `<` / `>` 的用户输入原样写入 HTML。
- 同一项目内多次 `game.generate_draft` 不覆盖记忆文件,而是继续追加短期对话记录、长期创作目标记录、项目黑板摘要和角色私有摘要,保留用户迭代历史。
@@ -245,10 +245,11 @@ describe('AI 游戏创作 App 共享契约', () => {
(capability) => capability.id,
);
expect(GAME_CREATION_AGENT_CAPABILITIES).toHaveLength(36);
expect(GAME_CREATION_AGENT_CAPABILITIES).toHaveLength(37);
expect(capabilityIds).toEqual(
expect.arrayContaining([
'chat',
'project-supervisor',
'file-upload',
'llm-draft-generation',
'task-decomposition',
@@ -85,6 +85,7 @@ export interface GameCreationAgentCapabilityDescriptor {
export const GAME_CREATION_AGENT_CAPABILITIES = [
{ id: 'chat', area: 'user', title: '聊天入口' },
{ id: 'project-supervisor', area: 'agent-runtime', title: '项目总控 Agent' },
{ id: 'file-upload', area: 'user', title: '上传文件' },
{ id: 'built-in-commands', area: 'agent-runtime', title: '内置命令调用' },
{ id: 'llm-draft-generation', area: 'agent-runtime', title: 'LLM 草案生成' },
@@ -102,8 +102,13 @@ pub struct GameCreationAgentCapabilityDescriptor {
pub platforms: Option<&'static [&'static str]>,
}
pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 36] = [
pub const GAME_CREATION_AGENT_CAPABILITIES: [GameCreationAgentCapabilityDescriptor; 37] = [
capability("chat", "user", "聊天入口"),
capability(
"project-supervisor",
"agent-runtime",
"项目总控 Agent",
),
capability("file-upload", "user", "上传文件"),
capability("built-in-commands", "agent-runtime", "内置命令调用"),
capability("llm-draft-generation", "agent-runtime", "LLM 草案生成"),
@@ -1023,7 +1028,7 @@ mod tests {
#[test]
fn capabilities_cover_standard_agent_runtime_needs() {
assert_eq!(GAME_CREATION_AGENT_CAPABILITIES.len(), 36);
assert_eq!(GAME_CREATION_AGENT_CAPABILITIES.len(), 37);
let ids = GAME_CREATION_AGENT_CAPABILITIES
.iter()
@@ -1032,6 +1037,7 @@ mod tests {
for expected in [
"chat",
"project-supervisor",
"file-upload",
"task-decomposition",
"orchestration",