加强Goal原生协议真实验收

强制 Goal 成功计划与格式修复全程使用原生工具目录。
为 Goal 阶段等待增加 Runtime 终态快速失败。
补充正式 Provider 复验证据并关闭过期计划状态。
This commit is contained in:
AIGameCreator App
2026-07-16 09:06:13 +08:00
parent 69159b2b39
commit 0b608edd01
5 changed files with 127 additions and 5 deletions
@@ -7235,6 +7235,25 @@ async function readGoalStatus() {
return goal;
}
async function assertGoalRuntimeCanProgress(codePrefix) {
const [taskSnapshot, runtime] = await Promise.all([
readTaskSnapshot(),
readJson(mainRuntimeStatePath()).catch(() => null),
]);
const current = taskSnapshot.latest.find(
(task) => task.agentId === mainAgentId && task.runId === state.initialRunId,
);
if (current && isFailedTask(current)) {
throw codedError(`${codePrefix}-runtime-failed`);
}
if (
runtime?.status === 'needs-reconciliation' ||
runtime?.phase === 'needs-reconciliation'
) {
throw codedError(`${codePrefix}-runtime-needs-reconciliation`);
}
}
function assertGoalContextSnapshot(runtime, contextBundle, goal, codePrefix) {
const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(goal);
assert(
@@ -7497,6 +7516,7 @@ async function waitForGoalOldActionBlocked(initialPending) {
const deadline = Date.now() + 120_000;
let lastError = null;
while (Date.now() < deadline) {
await assertGoalRuntimeCanProgress('goal-old-action-block');
await assertGoalInitialMarkerAbsent(
'goal-old-action-block-poll',
initialPending.actionId,
@@ -7574,6 +7594,7 @@ async function waitForGoalRevisionTwoAgentVerificationFailure() {
const deadline = Date.now() + runTimeoutMs;
let lastError = null;
while (Date.now() < deadline) {
await assertGoalRuntimeCanProgress('goal-revision-two-agent-failure');
await assertGoalInitialMarkerAbsent('goal-revision-two-verify-poll');
const records = await readOptionalJsonl(
path.join(state.projectRoot, '.agent/agent.db'),
@@ -10410,6 +10431,7 @@ async function monitorGoalWriteActionUntilSettled(pending) {
state.goal.monitoredWriteActionIds.add(pending.actionId);
const deadline = Date.now() + 120_000;
while (Date.now() < deadline) {
await assertGoalRuntimeCanProgress('goal-write-action-settle');
await assertGoalInitialMarkerAbsent(
'goal-write-after-confirm',
pending.actionId,
@@ -14046,7 +14068,8 @@ async function validateGoalRuntimeEvidence() {
state.lureLeakCount = await countLureLeaks();
assert(state.lureLeakCount === 0, 'sensitive-lure-leak-detected');
const protocolCount = validateMainRunToolPlanProtocols(agentDb);
const nativeToolPlanProtocolEvidence =
validateNativeRuntimeToolPlanProtocolEvidence(agentDb);
const successfulToolExecutionCount = receipts.filter(
(record) =>
record.agentId === mainAgentId &&
@@ -14060,7 +14083,7 @@ async function validateGoalRuntimeEvidence() {
agentDbRecordCount: agentDb.length,
conversationMessageCount: conversations.length,
successfulToolExecutionCount,
toolPlanProtocolCount: protocolCount,
...nativeToolPlanProtocolEvidence,
structuredPlanRevision: plan.revision,
structuredPlanCompletedStepCount: plan.completedStepHashes.length,
goalInitialCompletedStepCount: state.goal.initialCompletedStepHashes.length,
@@ -15219,6 +15242,12 @@ function emptyGoalEvidence() {
conversationMessageCount: 0,
successfulToolExecutionCount: 0,
toolPlanProtocolCount: 0,
nativeRuntimeToolPlanCount: 0,
toolPlanRepairCount: 0,
nativeRuntimeToolPlanRepairCount: 0,
wrapperToolPlanFallbackCount: 0,
textJsonToolPlanFallbackCount: 0,
toolPlanAuditPayloadLeakCount: 0,
structuredPlanRevision: 0,
structuredPlanCompletedStepCount: 0,
goalInitialCompletedStepCount: 0,
@@ -15728,6 +15757,8 @@ async function collectPartialGoalEvidence() {
const completedStepCount = Array.isArray(runtime?.planSteps)
? runtime.planSteps.filter((step) => step.status === 'completed').length
: 0;
const nativeToolPlanProtocolEvidence =
collectNativeRuntimeToolPlanProtocolEvidence(agentDb);
return {
taskCount: taskSnapshot.all.length,
eventCount: events.length,
@@ -15736,9 +15767,7 @@ async function collectPartialGoalEvidence() {
successfulToolExecutionCount: receipts.filter(
(record) => record.status === 'ok',
).length,
toolPlanProtocolCount: agentDb.filter(
(record) => record.recordType === 'agent.runtime.tool_plan.protocol',
).length,
...nativeToolPlanProtocolEvidence,
structuredPlanRevision: runtime?.planRevision ?? 0,
structuredPlanCompletedStepCount: completedStepCount,
goalEditedEvidenceAbsentBeforeEdit:
@@ -16210,6 +16239,80 @@ function validateMainRunToolPlanProtocols(records) {
return protocols.length;
}
function collectNativeRuntimeToolPlanProtocolEvidence(records) {
const protocols = records.filter(
(record) =>
record.recordType === 'agent.runtime.tool_plan.protocol' &&
record.agentId === mainAgentId &&
record.runId === state.initialRunId,
);
const repairs = records.filter(
(record) =>
record.recordType === 'agent.runtime.tool_plan.repair' &&
record.agentId === mainAgentId &&
record.runId === state.initialRunId,
);
const audits = [...protocols, ...repairs];
return {
toolPlanProtocolCount: protocols.length,
nativeRuntimeToolPlanCount: protocols.filter(
(record) => record.protocol === 'native_runtime_tools',
).length,
toolPlanRepairCount: repairs.length,
nativeRuntimeToolPlanRepairCount: repairs.filter(
(record) => record.protocol === 'native_runtime_tools',
).length,
wrapperToolPlanFallbackCount: audits.filter(
(record) => record.protocol === 'native_function',
).length,
textJsonToolPlanFallbackCount: audits.filter(
(record) => record.protocol === 'text_json',
).length,
toolPlanAuditPayloadLeakCount: audits.filter(
(record) =>
Object.hasOwn(record, 'arguments') ||
Object.hasOwn(record, 'response') ||
Object.hasOwn(record, 'toolArguments'),
).length,
};
}
function validateNativeRuntimeToolPlanProtocolEvidence(records) {
const protocolCount = validateMainRunToolPlanProtocols(records);
const evidence = collectNativeRuntimeToolPlanProtocolEvidence(records);
const protocols = records.filter(
(record) =>
record.recordType === 'agent.runtime.tool_plan.protocol' &&
record.agentId === mainAgentId &&
record.runId === state.initialRunId,
);
assert(
evidence.toolPlanProtocolCount === protocolCount &&
evidence.nativeRuntimeToolPlanCount === protocolCount &&
evidence.nativeRuntimeToolPlanRepairCount ===
evidence.toolPlanRepairCount &&
evidence.wrapperToolPlanFallbackCount === 0 &&
evidence.textJsonToolPlanFallbackCount === 0 &&
evidence.toolPlanAuditPayloadLeakCount === 0 &&
protocols.every(
(record) =>
Number.isSafeInteger(record.functionCallCount) &&
record.functionCallCount > 0 &&
Array.isArray(record.callIds) &&
record.callIds.length === record.functionCallCount &&
new Set(record.callIds).size === record.callIds.length &&
Array.isArray(record.functionNames) &&
record.functionNames.length === record.functionCallCount &&
record.functionNames.every(
(name) =>
isNonEmptyString(name) && name !== 'submit_agent_tool_plan',
),
),
'goal-native-tool-plan-protocol-required',
);
return evidence;
}
async function validateSameRunSteerEvidence({
agentDb,
activity,
@@ -16,6 +16,14 @@
---
## 2026-07-16 AI 游戏创作 Goal 真实验收强制原生工具协议
- 背景:V1.18 的 `goal-runtime` 已证明 edit/pause/Runner 强杀/resume/finalization,但该 PASS 早于 V1.26 原生工具目录;旧验收只接受协议兼容集合,无法证明长任务没有静默退回 wrapper/text JSON。阶段等待在 Runtime 已因外部 transport 失败时还可能继续等 30 分钟。
- 决策:Goal PASS 必须要求同一主 run 的全部成功 tool-plan 和 repair audit 都是 `native_runtime_tools`wrapper/text fallback 为 0function call 数量、call id、函数名数组完整且协议审计不含 arguments/response/toolArguments。PASS、部分失败与空报告统一输出协议计数。旧动作 blocked、revision 2 失败验证和写动作 settle 等长等待必须同步读取 task/runtimefailed、cancelled、budget-exhausted 或 needs-reconciliation 立即结构化失败。
- 影响范围:`apps/ai-game-creator-shell/scripts/agent-runtime-real-e2e.mjs`、AI 游戏创作 Runtime 与 App 实施计划;生产 Goal/Runner 状态机不改变。
- 验证方式:正式 `openai_chat / gpt-5.5` 加强版 `goal-runtime` 最终成功计划 21/21、repair 17/17 全原生,fallback/协议 payload 为 0Goal revision 1 -> 2、真实失败后 patchset 修复、pause、pidfd Runner 强杀、paused 零推进、显式同 run resume、verification、四阶段 finalization、唯一 assistant、零重复/重放/泄漏全部 PASS。独立 transport failure 报告的成功 6/6、repair 5/5 仍全原生,并促成 terminal fail-fast。
- 关联文档:`docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md``docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`
## 2026-07-16 AI 游戏创作 Agent Runtime 使用 Provider 原生工具目录
- 背景:OpenAI-compatible planning 虽已使用 function calling,但只向 Provider 提供 `submit_agent_tool_plan` 包装函数,真实工具藏在 `actions[].tool + input` 中,具体工具名和参数主要依赖长提示词,Provider 不能按工具 schema 约束选择与输入。
@@ -14,6 +14,14 @@
- 关联:相关文件、文档、提交或 Issue
```
## Agent 真实验收的阶段等待必须同步观察 Runtime 终态
- 现象:真实 Provider 已因 transport、格式修复或其它不可恢复错误把 task/Runtime 写成 failed,专项验收仍在等待某个 pending action、observation 或 receipt,直到 30 分钟总超时才返回。
- 原因:阶段等待只轮询“想看到的成功证据”,没有同时读取 owning Agent/run 的最新 task 与 Runtime phase;外部错误发生在该证据之前时,目标条件永远不会出现。
- 处理:所有分钟级阶段等待都要在每轮先检查 owning task 的 failed/cancelled/budget-exhausted,以及 Runtime 的 needs-reconciliation;命中后立即抛出带阶段前缀的结构化错误。正常 pause 必须保留为可恢复状态,不能被 fail-fast 当失败;Runner 强杀后的 paused 稳定窗口继续按签名零推进单独验证。
- 验证:用正式 `goal-runtime` 观察 Provider repair transport failure,确认部分报告立即保留成功/repair 协议计数、生命周期闭合和零泄漏证据;随后完整复跑仍能通过 Goal edit/pause/Runner kill/resume/finalization,证明 fail-fast 未破坏正常恢复路径。
- 关联:`apps/ai-game-creator-shell/scripts/agent-runtime-real-e2e.mjs``docs/technical/【技术方案】AI游戏创作Agent Runtime V1.1-2026-07-12.md`
## 禁止 Data URL 持久化时不要漏掉异步任务 JSON
- 现象:工程、素材、图层和元数据都已禁止 Data URL 后,服务器仍在生成高峰出现 SpacetimeDB / api-server 内存急剧膨胀甚至 OOM;读取少量正式生成任务也会造成远大于响应体的瞬时内存增长。
@@ -802,6 +802,8 @@ V1.18 对标 Codex CLI `/goal` 的长任务语义:目标文本既是首轮任
- 最终 Goal、Runtime 和最新 task 必须在原 Agent/Session/run 上 completed,结构化计划全部完成,Goal completion evidence 必须精确匹配当前 Goal/plan/verification/run/session;同一 finalizationId 必须按严格七槽物理顺序形成完整记录,finalization sidecar 最终不残留,目标 Session 只允许一个 assistant。Goal sidecar、task JSONL、Runtime state、v5 context 和 conversation 属于本地私有执行事实,可包含完成目标所需正文;event、Agent DB、receipt、activity、output 与最终报告不得保存 task、Goal/steer、委派任务、verify 命令或 error 正文,只允许身份/状态、SHA-256、字符/字节/条目计数和经 URL、项目根、其它绝对路径及凭据清洗的有界摘要。公共 task 统一不保留正文;委派只保留 `taskSha256 / taskChars`verify 只保留脚本安全标识、`expectedCommandSha256 / expectedCommandChars`、timeout 和结果计数,error 只保留 kind/fingerprint/chars 或脱敏摘要,禁止任何正文、preview、head 或 tail。验收必须扫描完整 Goal/编辑/委派/verify/error canary、已加载密钥和一次性项目绝对路径在全部公共持久面泄漏为 0,并确认动作、消息、receipt 和 Provider lifecycle 均无重复。
- 2026-07-16 使用正式 AppData 的 `openai_chat / gpt-5.5` 路由执行隔离 `goal-runtime` suiteV1.18 真实 Provider 门禁 **PASS**。同一 Agent/Session/run 从 Goal revision 1 编辑到 revision 2,保留 1 个已完成计划步骤;旧写动作形成 1 条 `runtime.goal / blocked` receipt,执行与重放均为 0。Agent 先取得真实退出码 1,再用 1 个 patchset 修复并通过 revision 2 verification;暂停前后、Runner pidfd 强杀换 boot 后的稳定窗口中 task/plan/conversation/Provider/action 均零推进,显式 resume 后恢复原 execution owner 和原 run。最终代码快照复跑的计划 revision 11 的 8 步全部完成,11 组 Provider lifecycle 均唯一闭合,finalization v3 四阶段和两层 completed projection 完整,Session 只有 1 条 assistant3 个副作用无重放,重复 action/message/receipt、Goal 正文、失败证据 canary、API Key、诱饵和项目绝对路径公共泄漏均为 0。验收过程同时修正 `file.write` 静默裁剪末尾换行、旧 Goal action receipt 的合法 tool/摘要转换、file/project diff 正文进入公共 event、file/memory 审计保存绝对路径,以及旧 event detail 在新脱敏投影下被误判幂等冲突五类真实缺陷;隔离 Runner、AppData 和 disposable 项目均已按 sentinel 清理。
2026-07-16 在 V1.26 Provider 原生工具目录落地后再次执行加强版 `goal-runtime`,正式 `openai_chat / gpt-5.5` 路由 **PASS**。验收器现在强制成功计划与格式 repair 全部使用 `native_runtime_tools`,拒绝 wrapper/text fallback,逐条校验 function call 数量、call id 唯一性、函数名数组,并拒绝协议审计携带 arguments、response 或 toolArguments 正文;失败报告也输出同一组协议计数。最终复跑的成功计划 21/21、repair 17/17 均为原生目录,fallback 与协议审计 payload 为 0;同一 Goal revision 1 -> 2、旧动作 blocked、真实退出码 1、单 patchset 修复、pause、Runner pidfd 强杀、paused 零推进、显式同 run resume、计划 revision 11 的 6 步完成、verification、finalization v3 四阶段、两层 completed projection 和唯一 assistant 全部成立,3 个副作用无重放,重复与正文/Key/诱饵/路径泄漏为 0,隔离资源完整清理。另一次独立复跑在 revision 2 repair lifecycle 遇到上游 transport failureRuntime 正确失败且 6/6 成功计划、5/5 repair 仍全为原生目录;Goal 专用长等待现会同步检查 task/runtime 终态,在 failed 或 needs-reconciliation 时立即返回结构化失败,不再空等 30 分钟总超时。
## V1.19 后台 Agent 真流式最终回复
V1.19 对标 Codex 富客户端的增量 turn 事件:工具开始、完成和等待继续沿用现有 Runtime state/event;只有已经进入 `phase=response` 的用户可见最终回复允许输出 Provider 文本 delta。后台工具 planning、function arguments、`thinkingSummary`、原始 observation 和修复上下文不得进入流。禁止前端拆字、定时补字或先生成完整正文再伪装流式。
@@ -574,4 +574,5 @@ game-project/
- 2026-07-16 V1.25 已完成真实验收:正式 `openai_chat / gpt-5.5``project-skill` suite 先让 hash-only 原始验收真实失败;Agent 在首个变更前精确读取匹配 Skill 1 次、无关 Skill 0 次,以 1 个变更动作只修改目标文件,Agent `project.verify` 与宿主复验均通过。最终脚本复跑记录 25 条 task、41 条 event、57 条 Agent DB 和 5 个成功工具动作;4 组 tool-plan Provider lifecycle 唯一闭合,最终 assistant/completed 各 1Skill 正文、API Key、诱饵、项目/配置路径泄漏和重复持久化均为 0,隔离 Runner/AppData/项目完整清理。
- 2026-07-16 起,同一 Runtime 文档的“V1.26 Provider 原生工具目录”作为 OpenAI-compatible planning 协议事实源。Chat / Responses 不再只广告 `submit_agent_tool_plan` 包装函数,而是直接提供 `update_agent_plan``respond_to_user`、全部内置 Runtime action 和动态 MCP function;每个函数使用独立 schema,Runtime 继续负责身份、权限、确认、沙箱、revision、验证、恢复与副作用防重放。Anthropic 与历史 fixture 保留 text JSON / wrapper 解析兼容,但新请求和 repair 不能静默降级。plan-only 是合法持久 checkpoint,未完成计划仍阻止最终化。
- 2026-07-16 V1.26 已完成真实验收:正式 `openai_chat / gpt-5.5``project-skill` suite 中 9/9 个成功工具计划与 6/6 个格式修复全部使用 `native_runtime_tools`wrapper/text fallback 均为 0。Agent 自主读取匹配 Skill、只改唯一目标文件并完成 Agent/宿主双重验证;15 个 tool-plan 和 1 个 final-reply lifecycle 唯一闭合,最终 assistant/completed 各 1,重复、Skill 正文、API Key、诱饵、项目/配置路径和报告泄漏均为 0,隔离 Runner/AppData/项目完整清理。
- 2026-07-16 V1.26 后重新加强并复验 `goal-runtime`:Goal suite 现在把成功计划、repair、call metadata、wrapper/text fallback 和协议审计零 payload 纳入硬门禁。正式 `openai_chat / gpt-5.5` 最终复跑的成功计划 21/21、repair 17/17 全为 `native_runtime_tools`;Goal edit、旧动作失效、真实失败修复、pause、Runner 强杀、显式同 run resume、verification、finalization 和唯一回复全部 PASS,重复、重放、正文、密钥、诱饵与路径泄漏均为 0。Goal 阶段等待同时增加 terminal fail-fastProvider transport failure 不再占满 30 分钟验收超时。
- 开发模式可通过本地项目文件面板执行 `file.list/read/write/delete`,普通用户界面不暴露文件面板。