@fitlab-ai/agent-infra 0.11.13 → 0.11.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/internal-cli.js +2 -7
- package/dist/lib/agent-clients/adapter.js +0 -3
- package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +5 -9
- package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +1 -5
- package/dist/lib/agent-clients/adapters/codex-orchestration.js +1 -3
- package/dist/lib/agent-clients/adapters/codex.js +0 -2
- package/dist/lib/internal/cli-route-inventory.js +0 -1
- package/dist/lib/internal/task-finalization.js +7 -5
- package/dist/lib/internal/task-lifecycle.js +3 -1
- package/dist/lib/internal/task-operation-registry.js +1 -3
- package/dist/lib/internal/task-validate.js +14 -124
- package/dist/lib/platform/pr-change-report.js +10 -3
- package/dist/lib/platform/release-notes.js +2 -2
- package/dist/lib/process-data/index.js +15 -4
- package/dist/lib/sandbox/commands/control-status.js +0 -1
- package/dist/lib/sandbox/commands/create.js +5 -4
- package/dist/lib/sandbox/config.js +12 -0
- package/dist/lib/sandbox/control/audit.js +1 -2
- package/dist/lib/sandbox/control/client.js +12 -47
- package/dist/lib/sandbox/control/executor.js +6 -10
- package/dist/lib/sandbox/control/host-recovery.js +89 -27
- package/dist/lib/sandbox/control/lifecycle.js +22 -37
- package/dist/lib/sandbox/control/protocol.js +8 -36
- package/dist/lib/sandbox/control/server.js +2 -21
- package/dist/lib/sandbox/environment.js +7 -8
- package/dist/lib/sandbox/mounts.js +71 -59
- package/dist/lib/sandbox/recovery.js +2 -3
- package/dist/lib/sandbox/removal.js +19 -4
- package/dist/lib/sandbox/workspace-identity.js +30 -15
- package/dist/lib/sandbox/workspace-view.js +41 -4
- package/dist/lib/task/control-authority.js +39 -17
- package/dist/lib/task/control-recovery.js +1 -2
- package/dist/lib/task/finalization.js +13 -2
- package/dist/lib/task/lifecycle.js +122 -35
- package/dist/lib/task/short-id.js +2 -2
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapter.ts +0 -27
- package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +6 -12
- package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -7
- package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +1 -5
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +1 -4
- package/lib/agent-clients/adapters/codex-orchestration.ts +1 -3
- package/lib/agent-clients/adapters/codex.ts +0 -2
- package/lib/internal/cli-route-inventory.ts +0 -1
- package/lib/internal/task-finalization.ts +8 -6
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +1 -3
- package/lib/internal/task-validate.ts +13 -127
- package/lib/platform/pr-change-report.ts +9 -3
- package/lib/platform/release-notes.ts +2 -2
- package/lib/process-data/index.ts +15 -4
- package/lib/sandbox/commands/control-status.ts +0 -1
- package/lib/sandbox/commands/create.ts +5 -4
- package/lib/sandbox/config.ts +13 -0
- package/lib/sandbox/control/audit.ts +1 -2
- package/lib/sandbox/control/client.ts +13 -61
- package/lib/sandbox/control/executor.ts +7 -13
- package/lib/sandbox/control/host-recovery.ts +94 -21
- package/lib/sandbox/control/lifecycle.ts +22 -37
- package/lib/sandbox/control/protocol.ts +9 -45
- package/lib/sandbox/control/server.ts +2 -17
- package/lib/sandbox/environment.ts +7 -8
- package/lib/sandbox/mounts.ts +92 -75
- package/lib/sandbox/recovery.ts +2 -3
- package/lib/sandbox/removal.ts +16 -4
- package/lib/sandbox/workspace-identity.ts +36 -13
- package/lib/sandbox/workspace-view.ts +40 -5
- package/lib/task/control-authority.ts +40 -26
- package/lib/task/control-recovery.ts +2 -3
- package/lib/task/finalization.ts +15 -2
- package/lib/task/lifecycle.ts +135 -33
- package/lib/task/short-id.ts +5 -2
- package/package.json +1 -1
- package/templates/.agents/rules/lifecycle-orchestration.en.md +3 -4
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +3 -4
- package/templates/.agents/rules/task-short-id.en.md +18 -12
- package/templates/.agents/rules/task-short-id.zh-CN.md +12 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -2
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +12 -2
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +12 -2
- package/templates/.agents/skills/create-release-note/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -5
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +13 -2
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/watch-pr/SKILL.en.md +19 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +20 -3
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +14 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +15 -1
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-client.js +0 -130
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +0 -127
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-operation.js +0 -131
- package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-registration.js +0 -275
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.js +0 -106
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +0 -345
- package/dist/lib/runtime/agent-runtime.js +0 -33
- package/dist/lib/sandbox/control/agent-operation.js +0 -34
- package/dist/lib/task/runtime-paths.js +0 -10
- package/lib/agent-clients/adapters/codex-lifecycle/controller-client.ts +0 -158
- package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +0 -163
- package/lib/agent-clients/adapters/codex-lifecycle/controller-operation.ts +0 -151
- package/lib/agent-clients/adapters/codex-lifecycle/controller-registration.ts +0 -352
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller-cli.ts +0 -109
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +0 -437
- package/lib/runtime/agent-runtime.ts +0 -41
- package/lib/sandbox/control/agent-operation.ts +0 -49
- package/lib/task/runtime-paths.ts +0 -12
|
@@ -27,7 +27,7 @@ Before generating a validation report, read `.agents/rules/evidence-reporting.md
|
|
|
27
27
|
- Never manipulate temporary worktrees, leases, or containers directly; only call `agent-infra-internal task-validate`.
|
|
28
28
|
- Never record tokens, environment variables, full argv, absolute user paths, or raw transcripts in the artifact; the core must write one current-only evidence envelope for each validation target.
|
|
29
29
|
- Before generating validation artifact Markdown referenced by the task comment, read `.agents/rules/sync-content-generation.md` and follow its generator-side constraints. Keep the validation artifact local instead of publishing it as an Issue artifact comment.
|
|
30
|
-
- The branch-only fallback
|
|
30
|
+
- The branch-only fallback creates one unique `tmp-` directory under the system temporary directory and is marked `recoverable: false`: no task.md write-back, lifecycle events, Issue sync, or `task-verify`; keep the report and evidence together, provide the retrieval path in the result, and carry the artifact back to the host that owns the task workspace for registration.
|
|
31
31
|
|
|
32
32
|
## Step 0: State Check (pre-execution hard gate)
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ Branch-only has no `{task-id}`: skip this step and record `not-applicable (branc
|
|
|
44
44
|
1. Read `reference/discovery-and-execution.md` and parse the input mode. Stop before started without an artifact for invalid or partial input.
|
|
45
45
|
2. Run `agent-infra-internal task-artifact {task-id} inspect --family validation-run` and read the latest review-code manual-validation items. Then run `agent-infra-internal platform-pr inspect {task-id}` and use the reference status matrix to discover, merge, and number items. Only automatic mode stops before started when reliable sources are empty or the sole possible source is unreadable; valid explicit mode always continues with the user command as effective work.
|
|
46
46
|
3. Take the round and artifact name from the core result. After confirming valid explicit work or a non-empty discovered list, run `agent-infra-internal task-event {task-id} validation-run.started --agent {standard-agent-token} --initiator {trigger-initiator} --request-id {request-id} --reason-code {reason-code}` and classify each item as `executable|unavailable|unknown|unsafe|unresolved`.
|
|
47
|
-
4.
|
|
47
|
+
4. Choose the output directory once for this run using `reference/discovery-and-execution.md`, then allocate a unique `{evidence-file}` for each target and execution scope. Pass each path as one quoted argument to the existing `--evidence-file`; invoke every executable item separately with `agent-infra-internal task-validate {task-ref} --scope snapshot --format json --evidence-file {evidence-file} -- {command...}`. Only make a second explicit inplace invocation for that item when evidence proves it is required. If no item is executable, run no fabricated command but still produce coverage-gap evidence.
|
|
48
48
|
5. Read `reference/report-template.md`, create `validation-run.md|validation-run-r{N}.md`, and record the input mode, discovered list, per-item results, CLI JSON allowlist, and sanitized summaries.
|
|
49
49
|
6. Run `agent-infra-internal task-event {task-id} validation-run.completed --agent {standard-agent-token} --initiator {trigger-initiator} --request-id {request-id} --reason-code {reason-code} --artifact {artifact}`. When an Issue exists, run only `agent-infra-internal platform-comment sync {task-id} --kind task --agent {standard-agent-token}`; do not publish the validation artifact as an Issue comment.
|
|
50
50
|
7. Run `agent-infra-internal task-verify {task-id} validation-run.completed --artifact {artifact} --format text`; fix failures and rerun it, then present the summary per `.agents/rules/validation-output.md` with necessary diagnostics.
|
|
@@ -57,10 +57,10 @@ With `--branch <ref>`, only the following differ from the steps above; every oth
|
|
|
57
57
|
- **Explicit mode only**: `task-artifact` and `platform-pr inspect` both require a task ref, so branch-only cannot discover items. Stop before writing any artifact when the user command after `--` is missing.
|
|
58
58
|
- **Skip Step 0 and the Step 2 discovery**: record both sources as `unavailable` and list this round's items under the `explicit` source in the artifact's `## Discovered Items` section.
|
|
59
59
|
- **Step 3 skips only the event call**: emit no `validation-run.started`, but still classify every item as `executable|unavailable|unknown|unsafe|unresolved`.
|
|
60
|
-
- **Step 4 runs unchanged**: write evidence files in
|
|
61
|
-
- **Step 5 relocates the artifact**: write
|
|
60
|
+
- **Step 4 runs unchanged**: write evidence files in this run's system temporary directory and invoke `agent-infra-internal task-validate {branch-ref} --scope snapshot --format json --evidence-file {evidence-file} -- {command...}`.
|
|
61
|
+
- **Step 5 relocates the artifact**: write `validation-run.md` in the same temporary directory using `reference/report-template.md`. See `reference/discovery-and-execution.md` for directory creation and evidence filenames.
|
|
62
62
|
- **Skip Steps 6 and 7**: emit no `validation-run.completed`, run no `platform-comment sync`, and run no `task-verify`.
|
|
63
|
-
- **Step 8 adds a notice**: state that the artifact is unrecoverable and unregistered, and must be carried back to the host owning the task workspace before a maintainer runs `complete-manual-validation`.
|
|
63
|
+
- **Step 8 adds a notice**: state that the artifact is unrecoverable and unregistered, provide the temporary directory retrieval path, and explain that it must be carried back to the host owning the task workspace before a maintainer runs `complete-manual-validation`.
|
|
64
64
|
|
|
65
65
|
## Completion Checklist
|
|
66
66
|
|
|
@@ -27,7 +27,7 @@ description: >
|
|
|
27
27
|
- 禁止直接操作临时 worktree、lease 或 container;只调用 `agent-infra-internal task-validate`。
|
|
28
28
|
- 产物不得记录 token、环境变量、完整 argv、绝对用户路径或原始 transcript;每个验证目标必须由核心写入同一 current-only evidence envelope。
|
|
29
29
|
- 生成会由 task 评论引用的验证 artifact Markdown 前,先读取 `.agents/rules/sync-content-generation.md` 并遵循其中的生成端约束;验证 artifact 保留在本地,不发布为 Issue artifact 评论。
|
|
30
|
-
- branch-only
|
|
30
|
+
- branch-only 降级路径在系统临时目录下创建一次 `tmp-` 开头的独立目录,标记 `recoverable: false`:不写 task.md、不发 lifecycle 事件、不同步 Issue、不跑 `task-verify`;报告与 evidence 同目录,结果提供取回路径,产物须带回宿主机由维护者登记。
|
|
31
31
|
|
|
32
32
|
## 第 0 步:状态核对(执行前硬约束)
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ branch-only 无 `{task-id}`,跳过本步,并在产物 `## 状态核对` 记
|
|
|
44
44
|
1. 读取 `reference/discovery-and-execution.md`,解析输入模式;非法或半截输入在 started 前停止,不写产物。
|
|
45
45
|
2. 运行 `agent-infra-internal task-artifact {task-id} inspect --family validation-run`,读取最新 review-code 人工校验项;再运行 `agent-infra-internal platform-pr inspect {task-id}`,按 reference 的状态矩阵发现、归并并编号。仅在自动模式下,可靠来源无项或唯一可能来源不可读时才在 started 前停止;合法显式模式始终以用户命令作为有效工作继续。
|
|
46
46
|
3. 从核心结果取得轮次和产物名;确认存在有效显式工作或非空发现清单后,运行 `agent-infra-internal task-event {task-id} validation-run.started --agent {standard-agent-token} --initiator {trigger-initiator} --request-id {request-id} --reason-code {reason-code}`,并逐项分类为 `executable|unavailable|unknown|unsafe|unresolved`。
|
|
47
|
-
4.
|
|
47
|
+
4. 按 `reference/discovery-and-execution.md` 为本轮选择一次产物目录,并为每个验证目标和执行 scope 分配唯一 `{evidence-file}`;将路径作为一个引用参数传给现有 `--evidence-file`。分别调用 `agent-infra-internal task-validate {task-ref} --scope snapshot --format json --evidence-file {evidence-file} -- {command...}`;只有证据表明必须原位时才对该项进行第二次显式 inplace 调用。零项可执行时不运行伪造命令,但仍继续产出覆盖缺口证据。
|
|
48
48
|
5. 读取 `reference/report-template.md`,创建 `validation-run.md|validation-run-r{N}.md`;记录输入模式、发现清单、逐项结果、CLI JSON allowlist 与去敏摘要。
|
|
49
49
|
6. 运行 `agent-infra-internal task-event {task-id} validation-run.completed --agent {standard-agent-token} --initiator {trigger-initiator} --request-id {request-id} --reason-code {reason-code} --artifact {artifact}`。存在 Issue 时仅运行 `agent-infra-internal platform-comment sync {task-id} --kind task --agent {standard-agent-token}`;验证 artifact 不发布为 Issue 评论。
|
|
50
50
|
7. 运行 `agent-infra-internal task-verify {task-id} validation-run.completed --artifact {artifact} --format text`;未通过则修复后重跑,并按 `.agents/rules/validation-output.md` 展示摘要和必要诊断。
|
|
@@ -57,10 +57,10 @@ branch-only 无 `{task-id}`,跳过本步,并在产物 `## 状态核对` 记
|
|
|
57
57
|
- **仅接受显式模式**:`task-artifact` 和 `platform-pr inspect` 都要求 task ref,branch-only 无法自动发现。缺少 `--` 后的用户命令时,在写入任何产物前停止。
|
|
58
58
|
- **跳过第 0 步与第 2 步的发现**:把两个来源记为 `unavailable`,并在产物 `## 发现清单` 用 `explicit` 来源登记本轮项。
|
|
59
59
|
- **第 3 步只跳过事件调用**:不发 `validation-run.started`,但逐项分类为 `executable|unavailable|unknown|unsafe|unresolved` 照常执行。
|
|
60
|
-
- **第 4 步照常执行**:将 evidence
|
|
61
|
-
- **第 5
|
|
60
|
+
- **第 4 步照常执行**:将 evidence 文件写入本轮系统临时目录,调用 `agent-infra-internal task-validate {branch-ref} --scope snapshot --format json --evidence-file {evidence-file} -- {command...}`。
|
|
61
|
+
- **第 5 步改写产物位置**:报告写入同一临时目录,命名为 `validation-run.md`,沿用 `reference/report-template.md`。目录创建和 evidence 命名规则见 `reference/discovery-and-execution.md`。
|
|
62
62
|
- **跳过第 6、7 步**:不发 `validation-run.completed`,不做 `platform-comment sync`,不跑 `task-verify`。
|
|
63
|
-
- **第 8
|
|
63
|
+
- **第 8 步追加提示**:明确告知产物不可恢复、未登记账本,输出临时目录的取回路径,并说明需带回持有任务工作区的宿主机后再由维护者执行 `complete-manual-validation`。
|
|
64
64
|
|
|
65
65
|
## 完成检查清单
|
|
66
66
|
|
package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md
CHANGED
|
@@ -56,15 +56,26 @@ Assign exactly one classification per item:
|
|
|
56
56
|
|
|
57
57
|
Only `executable` items may run. Commands must not expose credentials, environment variables, full argv, absolute user paths, or raw transcripts. Leave a coverage gap when safety cannot be established.
|
|
58
58
|
|
|
59
|
+
## Artifact Directory and Evidence Paths
|
|
60
|
+
|
|
61
|
+
Choose `{evidence-dir}` once per run. Write the report and every evidence JSON generated in that run to this directory:
|
|
62
|
+
|
|
63
|
+
- `task-bound`: use the `TaskDir` returned in the structured result of `task-context resolve`; never construct it from the task ID.
|
|
64
|
+
- `branch-only`: only after the user explicitly passes a valid `--branch` with valid explicit-mode input, create the directory once with Node built-ins: `fs.mkdtempSync(path.join(os.tmpdir(), "tmp-"))`. Use the existing Node runtime's `node:fs`, `node:os`, and `node:path`; do not add a script, helper, or storage layer. A task-resolution failure must not enter this branch.
|
|
65
|
+
|
|
66
|
+
Keep the report name returned by the core: `validation-run.md|validation-run-r{N}.md`. For task-bound evidence, remove `.md` from that report name to get `{artifact-stem}`, then name each file `{artifact-stem}-{MV-N}-{snapshot|inplace}.json`; different report rounds, targets, and scopes therefore do not overwrite each other. Branch-only writes `validation-run.md` in its new directory and uses `validation-run-{MV-N}-{snapshot|inplace}.json`. Generate paths with Node `path.join(evidence-dir, filename)` and pass the full path as one argument, correctly quoted for the current shell, to `--evidence-file`. Do not split paths containing spaces. Do not create a file for a scope that was not run.
|
|
67
|
+
|
|
68
|
+
Put evidence relative filenames in the report, never absolute user paths. The branch-only completion result separately gives `{evidence-dir}` as the retrieval path; the report and all JSON files are in that directory.
|
|
69
|
+
|
|
59
70
|
## Per-item Execution
|
|
60
71
|
|
|
61
72
|
1. Invoke each executable item separately:
|
|
62
73
|
|
|
63
74
|
```bash
|
|
64
|
-
agent-infra-internal task-validate {task-ref|branch-ref} --scope snapshot --format json -- {command...}
|
|
75
|
+
agent-infra-internal task-validate {task-ref|branch-ref} --scope snapshot --format json --evidence-file "{evidence-file}" -- {command...}
|
|
65
76
|
```
|
|
66
77
|
|
|
67
|
-
2. Only when the item or first-run evidence proves a dependency on uncommitted content, original mounts, or in-place permissions, explicitly invoke that item a second time with `--scope inplace
|
|
78
|
+
2. Only when the item or first-run evidence proves a dependency on uncommitted content, original mounts, or in-place permissions, explicitly invoke that item a second time with `--scope inplace`, use its separate scope evidence path, and record the reason.
|
|
68
79
|
3. One item failure does not stop independent remaining items. Record exit status, cleanup, and the sanitized JSON allowlist separately.
|
|
69
80
|
4. When the list is non-empty but no item is `executable`, still create and complete a validation-run artifact after started; do not run placeholder or fabricated commands.
|
|
70
81
|
5. Always stop before started for invalid input. In automatic mode, also stop before started without an artifact when both sources are reliably empty or the sole possible source is unreadable with no local items; those source-based stop conditions do not apply to valid explicit mode.
|
package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md
CHANGED
|
@@ -56,15 +56,26 @@ branch-only 没有 task ref,两个发现来源都不可读,一律记为 `una
|
|
|
56
56
|
|
|
57
57
|
只有 `executable` 可以运行。命令不得输出凭据、环境变量、完整 argv、绝对用户路径或原始 transcript;不能证明安全就保留覆盖缺口。
|
|
58
58
|
|
|
59
|
+
## 产物目录与 evidence 路径
|
|
60
|
+
|
|
61
|
+
每轮只选择一次 `{evidence-dir}`,报告和该轮生成的所有 evidence JSON 都写入该目录:
|
|
62
|
+
|
|
63
|
+
- `task-bound`:直接使用 `task-context resolve` 结构化结果返回的 `TaskDir`。不得按 task ID 拼路径。
|
|
64
|
+
- `branch-only`:仅在用户显式传入合法 `--branch` 且输入是合法显式模式后,使用 Node 内置 API 创建一次目录:`fs.mkdtempSync(path.join(os.tmpdir(), "tmp-"))`。调用现有 Node 运行环境的 `node:fs`、`node:os`、`node:path`;不创建脚本、helper 或额外存储层。不得在 task resolution 失败时走此分支。
|
|
65
|
+
|
|
66
|
+
报告沿用核心返回的 `validation-run.md|validation-run-r{N}.md` 名称;task-bound evidence 取该报告名去掉 `.md` 后作为 `{artifact-stem}`,文件名为 `{artifact-stem}-{MV-N}-{snapshot|inplace}.json`,因此不同报告轮次、目标及同一目标的 snapshot/inplace 调用不会覆盖。branch-only 在新目录中写 `validation-run.md`,并使用 `validation-run-{MV-N}-{snapshot|inplace}.json`。通过 Node `path.join(evidence-dir, filename)` 生成路径;将整条路径作为一个被当前 shell 正确引用的 `--evidence-file` 参数传入,不拆分含空格的路径。未执行的 scope 不生成文件。
|
|
67
|
+
|
|
68
|
+
报告只写 evidence 相对文件名,不写绝对用户路径。branch-only 的完成结果另行提供 `{evidence-dir}` 取回路径;报告自身与全部 JSON 均在该目录中。
|
|
69
|
+
|
|
59
70
|
## 逐项执行
|
|
60
71
|
|
|
61
72
|
1. 每个可执行项分别调用:
|
|
62
73
|
|
|
63
74
|
```bash
|
|
64
|
-
agent-infra-internal task-validate {task-ref|branch-ref} --scope snapshot --format json -- {command...}
|
|
75
|
+
agent-infra-internal task-validate {task-ref|branch-ref} --scope snapshot --format json --evidence-file "{evidence-file}" -- {command...}
|
|
65
76
|
```
|
|
66
77
|
|
|
67
|
-
2. 仅当项目要求或首次证据证明依赖未提交内容、原挂载或原位权限时,再为该项显式调用一次 `--scope inplace
|
|
78
|
+
2. 仅当项目要求或首次证据证明依赖未提交内容、原挂载或原位权限时,再为该项显式调用一次 `--scope inplace`,用该 scope 独立的 evidence 路径并记录升级理由。
|
|
68
79
|
3. 单项失败不阻止其余独立项;分别记录退出状态、cleanup 和去敏 JSON allowlist。
|
|
69
80
|
4. 清单非空但没有 `executable` 项时,仍在 started 后生成 validation-run 产物并完成 lifecycle,不运行占位或伪造命令。
|
|
70
81
|
5. 输入非法时始终在 started 前停止。自动模式下,两来源可靠地确认无项,或唯一可能来源不可读且本地无项时,也在 started 前停止且不写产物;合法显式模式不适用这两个来源停止条件。
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- Mode: `{explicit|automatic}`
|
|
6
6
|
- Context: `{task-bound|branch-only}` (branch-only also records `recoverable: false`)
|
|
7
|
+
- Report and evidence: same artifact directory; record evidence filenames only, never absolute paths
|
|
7
8
|
- Input decision: {sanitized-input-decision}
|
|
8
9
|
- PR source status: `{success|no-op|failed|blocked}` / `{stable-code-or-none}`
|
|
9
10
|
|
|
@@ -50,6 +51,7 @@ agent-infra-internal task-snapshot {task-id} --format text
|
|
|
50
51
|
- Source: `{source}`
|
|
51
52
|
- Classification: `{classification}`
|
|
52
53
|
- Scope: `{snapshot|inplace|not-run}`
|
|
54
|
+
- Evidence file: `{evidence-filename|not-created}` (filename only)
|
|
53
55
|
- Command name: `{basename-only|not-run}`
|
|
54
56
|
- Exit status: `{exit-status|not-run}`
|
|
55
57
|
- Runtime upgrade: {none-or-reason}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
- 模式:`{explicit|automatic}`
|
|
6
6
|
- 上下文:`{task-bound|branch-only}`(branch-only 另记 `recoverable: false`)
|
|
7
|
+
- 报告与 evidence:同一产物目录;仅记录 evidence 文件名,不记录绝对路径
|
|
7
8
|
- 输入判定:{sanitized-input-decision}
|
|
8
9
|
- PR 来源状态:`{success|no-op|failed|blocked}` / `{stable-code-or-none}`
|
|
9
10
|
|
|
@@ -50,6 +51,7 @@ agent-infra-internal task-snapshot {task-id} --format text
|
|
|
50
51
|
- 来源:`{source}`
|
|
51
52
|
- 分类:`{classification}`
|
|
52
53
|
- Scope:`{snapshot|inplace|not-run}`
|
|
54
|
+
- Evidence 文件:`{evidence-filename|not-created}`(仅相对文件名)
|
|
53
55
|
- 命令名称:`{basename-only|not-run}`
|
|
54
56
|
- 退出状态:`{exit-status|not-run}`
|
|
55
57
|
- 运行时升级:{none-or-reason}
|
|
@@ -14,7 +14,7 @@ The entry point may omit the task ref; explicit task scope accepts only `--task
|
|
|
14
14
|
The orchestrator delegates only and never executes a stage skill itself. First read `.agents/rules/no-mid-flow-questions.md`, `.agents/rules/lifecycle-orchestration.md`, and `reference/host-validation.md`.
|
|
15
15
|
|
|
16
16
|
1. Resolve the canonical task ID, current Agent Client, and optional atomic policy flags, then run `task-snapshot`. If one explicit role field is present, all four are required.
|
|
17
|
-
2. Use the current host's native child spawn, wait, and result interfaces. Task-bound sandboxes execute ordinary task orchestration locally in the current worktree
|
|
17
|
+
2. Use the current host's native child spawn, wait, and result interfaces. Task-bound sandboxes execute ordinary task orchestration locally in the current worktree. Retain task binding and checks for real external process operations.
|
|
18
18
|
3. Before each loop, run `agent-infra-internal task-lifecycle {task-id} recover-started --agent {client} --auto`. The client adapter owns recovery capability and stop-evidence validation; an unsupported adapter or a run that needs no recovery returns `no-op`. Continue only after `no-op` or `applied`. Stop on every other result; a later operator invocation retries from persisted state without routing or dispatching a child.
|
|
19
19
|
4. Run `task-orchestration {task-id} begin-or-resume --client {client}` with the complete explicit policy. With no explicit policy, core reads that client's configured policy; existing runs use persisted policy. Disk state outside the complete current structure fails closed without rewriting; finish or clear active runs before upgrading. Stop on paused/completed results.
|
|
20
20
|
5. Run `route`. On `completed`, run `agent-infra-internal task-verify {task-id} run-task.completed --format text`, present its summary per `.agents/rules/validation-output.md` while retaining necessary diagnostics, and stop. Only a `running` result with non-null `next` supplies action, role, round, artifact, requested model, and requested effort.
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
总控只编排,不直接执行任何阶段技能。执行前先读取 `.agents/rules/no-mid-flow-questions.md`、`.agents/rules/lifecycle-orchestration.md` 与 `reference/host-validation.md`。
|
|
15
15
|
|
|
16
16
|
1. 解析规范任务 ID、当前 Agent Client,以及可选的原子策略 `--executor-model`、`--executor-reasoning-effort`、`--reviewer-model`、`--reviewer-reasoning-effort`,并执行 `agent-infra-internal task-snapshot {task-id} --format text`。任一显式策略字段出现时四个 role 字段必须完整,不得与配置拼接;两个角色可以使用同一模型。
|
|
17
|
-
2. 使用当前宿主的原生子 Agent 启动、等待与结果接口。task-bound sandbox 在当前 worktree
|
|
17
|
+
2. 使用当前宿主的原生子 Agent 启动、等待与结果接口。task-bound sandbox 在当前 worktree 本地执行普通任务编排。保留任务绑定与真实外部进程操作检查。
|
|
18
18
|
3. 每次进入循环时先调用 `agent-infra-internal task-lifecycle {task-id} recover-started --agent {client} --auto`。客户端适配器负责恢复能力和停止证据校验;没有该能力或无需恢复时返回 `no-op`。仅在结构化结果为 `no-op` 或 `applied` 时继续,其他结果安全停止;下一次人为调用按持久化状态幂等重试,不 route、不派发 child。
|
|
19
19
|
4. 调用 `agent-infra-internal task-orchestration {task-id} begin-or-resume --client {client}` 并转发完整显式策略。完全没有显式策略时由核心读取当前 client 的 `agentClients[].orchestration`;existing run 使用持久化策略。磁盘状态不符合当前完整结构时核心失败关闭且不改写;升级前必须完成或清空 active run。仅当核心返回 `ORCHESTRATION_MODEL_POLICY_REQUIRED` 时,先用 `agent-client model-selection` 展示 complete/partial/interactive-only 来源,再一次收集完整策略;未回答则不创建 run。若为 paused/completed,按结构化结果停止。
|
|
20
20
|
5. 调用 `route` 并读取结构化结果。若返回 `completed`,立即运行 `agent-infra-internal task-verify {task-id} run-task.completed --format text` 并停止;按 `.agents/rules/validation-output.md` 展示摘要并保留必要诊断。仅当返回 `running` 且 `next` 非空时读取唯一 action、role、round、artifact、`requestedModel` 和 `requestedReasoningEffort`,不得自行推断。
|
|
@@ -48,7 +48,22 @@ Resolve the target PR number `{pr#}` and an optional `{task-id}` via these deter
|
|
|
48
48
|
|
|
49
49
|
Before running this step, read `reference/monitor-and-heal.md` and `.agents/rules/pr-checks-commands.md`.
|
|
50
50
|
|
|
51
|
-
Initialize `repairCommits=[]` and `rebaseAttempts=0`. Before
|
|
51
|
+
Initialize `repairCommits=[]` and `rebaseAttempts=0`. Before overwriting the current report in each round, validate and retain its prior identity and head using the core reader; a missing or invalid report means there is no prior head:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node --experimental-strip-types --input-type=module -e '
|
|
55
|
+
import { readPrChangeReport } from "./lib/platform/pr-change-report.ts";
|
|
56
|
+
const report = readPrChangeReport(`.agents/workspace/active/${process.argv[1]}/pr-change-report.json`);
|
|
57
|
+
const previous = report.ok
|
|
58
|
+
? { repository: report.value.identity.repository, resource: report.value.identity.resource, head: report.value.identity.head.sha }
|
|
59
|
+
: { status: "unavailable" };
|
|
60
|
+
process.stdout.write(`${JSON.stringify(previous)}\n`);
|
|
61
|
+
' {task-id}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then run `platform-pr summary-context` and compare the retained repository/resource identity with the authoritative snapshot; discard a mismatch and mark the increment unconfirmed. Rebuild the task-bound `pr-change-report.json` for the authoritative PR head using the shared PR change-report contract, and refresh the single summary with a body containing one `<!-- canonical-pr-change-report -->` placeholder via `summary-sync --change-report-file ... --result no_op --strict`. Complete-PR statistics keep the current base/head/merge-base/patch and existing totals. Only when the prior report is valid, the PR identity matches, and the heads differ, describe the separate head-to-head tree diff using `git diff --find-renames --numstat {previous-head} {current-head}`, `--name-status`, and `--summary`. Do not substitute a merge-base diff for this range or add increment totals to the full PR. If no valid prior head exists, state that it is unconfirmed; do not infer from complete-PR statistics or fill in zeroes. Matching heads mean there was no new head transition.
|
|
65
|
+
|
|
66
|
+
`--strict` preserves report or summary refresh failures as `failed/blocked` instead of treating warnings as success. A report or summary refresh failure must not enter the ready/complete exit; use step 4. After a successful refresh, consume `nextAction` from the structured `summary-sync` result: `review-code` exits readiness/self-heal and follows the code-review branch in step 7; only `watch-pr` continues to `agent-infra-internal platform-checks watch {task-id} --interval-seconds 30 --deadline-seconds 1800`. Missing or invalid `nextAction` follows the refresh-failure exit; do not infer `watch-pr`. Route readiness only by `readiness.state`: `ready` to step 7, `conflicting|checks-failed` to step 3, and `pending|timed-out|cancelled` to step 4.
|
|
52
67
|
|
|
53
68
|
### 3. Self-Heal Loop
|
|
54
69
|
|
|
@@ -77,7 +92,7 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
77
92
|
- **Do not change** `pr_delivery_fact` or `current_step`
|
|
78
93
|
- **Append** to `## Activity Log` (do not overwrite prior entries; `{N}` = number of existing Watch PR entries for this task + 1):
|
|
79
94
|
```
|
|
80
|
-
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Watch PR (Round {N})** by {agent} — {success: PR ready, repair commits: {k} [{SHA summary}] /
|
|
95
|
+
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Watch PR (Round {N})** by {agent} — {success: PR ready, repair commits: {k} [{SHA summary}] / review required: nextAction=review-code / blocked: {summary}}
|
|
81
96
|
```
|
|
82
97
|
|
|
83
98
|
### 6. Verification Gate
|
|
@@ -126,6 +141,8 @@ Populate `{next-step-commands}` for this scenario by running `agent-infra-intern
|
|
|
126
141
|
{next-step-commands}
|
|
127
142
|
```
|
|
128
143
|
|
|
144
|
+
- `summary-sync.nextAction == review-code`: state that the report requires code review; this does not mean PR readiness passed. Generate the sole next step with `agent-infra-internal agent-client next-steps --skill review-code --task-ref {task-ref}` and end the round. Do not continue readiness/self-heal or edit code automatically.
|
|
145
|
+
|
|
129
146
|
- "Blocked": output only the step 4 blocker explanation; do not recommend a next-step command.
|
|
130
147
|
|
|
131
148
|
## Completion Checklist
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
当需要持续监控 PR 直到门禁通过且明确可合入时使用。
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
#
|
|
8
|
+
# 监控拉取请求
|
|
9
9
|
|
|
10
10
|
在 `create-pr` 之后持续监控同一 PR head 的全部 checks 与 mergeability。只有全部 checks 通过且平台明确可合入才进入成功出口;check 失败走既有修复,合并冲突走受限 rebase 自愈,未知或无法安全闭环时 fail-closed。
|
|
11
11
|
|
|
@@ -48,7 +48,22 @@ description: >
|
|
|
48
48
|
|
|
49
49
|
执行此步骤前,先读取 `reference/monitor-and-heal.md` 与 `.agents/rules/pr-checks-commands.md`。
|
|
50
50
|
|
|
51
|
-
进入本轮时初始化 `repairCommits=[]` 与 `rebaseAttempts=0
|
|
51
|
+
进入本轮时初始化 `repairCommits=[]` 与 `rebaseAttempts=0`。每次覆盖当前报告前,先用 core 导出的 reader 验证并暂存现有报告的身份与 head;文件不存在或验证失败即记为无上一 head:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
node --experimental-strip-types --input-type=module -e '
|
|
55
|
+
import { readPrChangeReport } from "./lib/platform/pr-change-report.ts";
|
|
56
|
+
const report = readPrChangeReport(`.agents/workspace/active/${process.argv[1]}/pr-change-report.json`);
|
|
57
|
+
const previous = report.ok
|
|
58
|
+
? { repository: report.value.identity.repository, resource: report.value.identity.resource, head: report.value.identity.head.sha }
|
|
59
|
+
: { status: "unavailable" };
|
|
60
|
+
process.stdout.write(`${JSON.stringify(previous)}\n`);
|
|
61
|
+
' {task-id}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
随后调用 `platform-pr summary-context`,比较暂存的 repository/resource identity 与当前权威 snapshot;不匹配时丢弃旧 head 并标记增量未确认。按共享 PR change-report contract 用当前权威 PR head 重建任务绑定的 `pr-change-report.json`,再用含唯一 `<!-- canonical-pr-change-report -->` 占位符的正文调用 `summary-sync --change-report-file ... --result no_op --strict` 刷新同一条摘要。完整 PR 仍使用当前 base/head/merge-base/patch 与现有 totals。只有在上一报告有效、PR identity 相同且两个 head 不同的情况下,才用 `git diff --find-renames --numstat {previous-head} {current-head}`、`--name-status`、`--summary` 描述独立的 head→head 树差异;不使用 merge-base 替代该范围,不把增量 totals 加入完整 PR。无有效上一 head 时写明未确认,不从完整 PR 推算或填零;head 相同只说明没有新的 head 转换。
|
|
65
|
+
|
|
66
|
+
`--strict` 将报告或摘要刷新失败保留为 `failed/blocked`,不得把 warning 当作成功;刷新失败时不得进入 ready/complete 出口,按步骤 4 记录阻塞。刷新成功后,消费 `summary-sync` 结构化结果的 `nextAction`:`review-code` 立即结束 readiness/self-heal 路径,按步骤 7 的代码审查分支退出;`watch-pr` 才继续调用 `agent-infra-internal platform-checks watch {task-id} --interval-seconds 30 --deadline-seconds 1800`。缺失或 invalid `nextAction` 按刷新失败处理,不推断为 `watch-pr`。readiness 只按结构化 `readiness.state` 分流:`ready` 进入步骤 7,`conflicting` 或 `checks-failed` 进入步骤 3,`pending|timed-out|cancelled` 进入步骤 4。
|
|
52
67
|
|
|
53
68
|
### 3. 自愈循环
|
|
54
69
|
|
|
@@ -77,7 +92,7 @@ date "+%Y-%m-%d %H:%M:%S%z" | sed 's/\([+-][0-9][0-9]\)\([0-9][0-9]\)$/\1:\2/'
|
|
|
77
92
|
- **不改** `pr_delivery_fact` 与 `current_step`
|
|
78
93
|
- **追加**到 `## 活动日志`(不要覆盖之前的记录;`{N}` = 本任务已有 Watch PR 条目数 + 1):
|
|
79
94
|
```
|
|
80
|
-
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Watch PR (Round {N})** by {agent} — {成功:PR ready, repair commits: {k} [{sha 摘要}] / 阻塞:blocked: {简述}}
|
|
95
|
+
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Watch PR (Round {N})** by {agent} — {成功:PR ready, repair commits: {k} [{sha 摘要}] / 需复核:nextAction=review-code / 阻塞:blocked: {简述}}
|
|
81
96
|
```
|
|
82
97
|
|
|
83
98
|
### 6. 完成校验
|
|
@@ -124,6 +139,8 @@ agent-infra-internal task-verify {task-id} watch-pr.completed --format text
|
|
|
124
139
|
{next-step-commands}
|
|
125
140
|
```
|
|
126
141
|
|
|
142
|
+
- `summary-sync.nextAction == review-code`:说明报告要求代码审查;这不表示 PR readiness 已通过。使用 `agent-infra-internal agent-client next-steps --skill review-code --task-ref {task-ref}` 生成唯一下一步,结束本轮。不得继续 readiness/self-heal 或自行修改代码。
|
|
143
|
+
|
|
127
144
|
- 「阻塞」:仅输出步骤 4 的阻塞说明,不推荐下一步命令。
|
|
128
145
|
|
|
129
146
|
## 完成检查清单
|
|
@@ -17,6 +17,20 @@ Before readiness in every round, and after an external push, self-heal commit, o
|
|
|
17
17
|
|
|
18
18
|
When rebuilding the precheck, each of the six `rationale` fields must state the corresponding high-level finding and include at least one path, line range, and concise `detail` as evidence. `change-composition` must describe where responsibilities are concentrated; compatibility, legacy-path, redundancy, and scope checks must state their actual results separately, without reused generic boilerplate. Use the deployed skill's language for rationales and evidence details; Chinese skill deployments must use Chinese.
|
|
19
19
|
|
|
20
|
+
Use the six existing `rationale` fields and `Evidence[]` to explain each logical change group: its goal and acceptance-criteria alignment, behavior added or removed, necessity, redundancy or duplication, scope, related tests, and test adequacy. Cite locatable code, test, or documentation evidence; mark unsupported points as unconfirmed. The renderer displays every evidence item. Source line counts, execution/call counts, and workflow unification are distinct claims: diff line counts cannot prove execution-count or workflow-behavior changes, and green CI cannot prove necessity. Analysis must not expand self-heal or refactoring authority.
|
|
21
|
+
|
|
22
|
+
Keep complete-PR scope bound to the current authoritative base/head/merge-base/patch and its full statistics. Report the increment separately as previous valid report head → current head. Before overwriting the prior sidecar, validate it with `readPrChangeReport`; use its `identity.head.sha` only when its repository/resource identity matches the current PR. Compare the two head trees directly and state both SHAs. Do not substitute a merge-base diff for these endpoints or add increment totals to complete-PR totals. If no prior report exists, it is invalid, or its identity differs, mark the previous head unconfirmed; do not infer values from the full PR or fill in zeroes. Identical SHAs mean the head did not change. After a rebase, compare the old and new head trees. An invalid sidecar makes only the increment unconfirmed; the existing full-PR report flow continues.
|
|
23
|
+
|
|
24
|
+
## Precheck Route
|
|
25
|
+
|
|
26
|
+
After `summary-sync --strict` succeeds, consume its structured `nextAction` result before entering readiness:
|
|
27
|
+
|
|
28
|
+
- `nextAction=review-code`: stop this Watch PR readiness/self-heal loop, recommend `review-code` through the unified helper, and end the round. Green CI does not clear necessity concerns, and Watch PR must not perform automatic redundancy cleanup or refactoring.
|
|
29
|
+
- `nextAction=watch-pr`: continue through the existing readiness classification and self-heal decision tree.
|
|
30
|
+
- Missing or invalid `nextAction`: use the strict-refresh help exit; do not infer `watch-pr`.
|
|
31
|
+
|
|
32
|
+
This route reuses the result already derived by the core from the six checks. It does not grant Watch PR authority to write a `review-code` report or perform automatic fixes.
|
|
33
|
+
|
|
20
34
|
## Self-Heal Decision Tree
|
|
21
35
|
|
|
22
36
|
```text
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`watch-pr` 步骤 2/3/4 的平台无关判定逻辑。具体平台命令(监控、解析失败 run、拉日志、读取 PR 号)见 `.agents/rules/pr-checks-commands.md`;本文件只描述与平台无关的分类与决策。
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 就绪状态分类
|
|
6
6
|
|
|
7
7
|
按 `.agents/rules/pr-checks-commands.md` 的监控命令执行后,依其退出码归为三类:
|
|
8
8
|
|
|
@@ -17,6 +17,20 @@
|
|
|
17
17
|
|
|
18
18
|
每轮重建 precheck 时,六项 `rationale` 都必须是对应检查的实际高层结论,并各带至少一条路径、行号和简短 `detail` 证据;`change-composition` 要说明职责集中位置,兼容、旧路径、冗余和范围检查要分别说明实际结果,禁止复用泛化套话。理由和证据详情遵循当前已部署技能的语言,中文技能必须使用中文。
|
|
19
19
|
|
|
20
|
+
按逻辑变化组使用现有六项 `rationale` 和 `Evidence[]` 说明目标/验收对应、行为增减、必要性、冗余或重复、范围、相关测试及测试充分性。列出可定位的代码/测试/文档证据;缺证据时明确写未确认。renderer 会展示每项的全部 evidence。源码行数、执行/调用次数和流程统一是不同结论:diff 行数不能证明执行次数或流程行为变化;CI 全绿也不能证明必要性。不得由分析建议自行扩大自愈或重构权限。
|
|
21
|
+
|
|
22
|
+
完整 PR 的范围继续绑定当前权威 base/head/merge-base/patch 与完整统计。增量单独绑定上一有效报告 head → 当前 head:在覆盖旧 sidecar 前,用 `readPrChangeReport` 校验旧报告,只在 repository/resource identity 匹配当前 PR 时采用旧 `identity.head.sha`。对两个 head 做 tree-to-tree diff 并明确列出 SHA,不用 merge-base 替代端点差异,不把增量 totals 加到完整 PR totals。没有旧报告、报告无效或身份不符时标注上一 head 未确认,不从全量推算或填零。两个 SHA 相同只报告 head 未变化;rebase 后仍比较原 head 和新 head 的树差异。sidecar 无效只使增量不可确认,不改变现有 full-PR 报告流程。
|
|
23
|
+
|
|
24
|
+
## 预检分流
|
|
25
|
+
|
|
26
|
+
`summary-sync --strict` 成功后,消费其结构化结果中的 `nextAction`,再决定是否进入 readiness:
|
|
27
|
+
|
|
28
|
+
- `nextAction=review-code`:停止当前 Watch PR readiness 与自愈循环,按统一 helper 推荐 `review-code` 并结束本轮。不得把 CI 绿色当作清除必要性疑点,也不得自动实施冗余清理或重构。
|
|
29
|
+
- `nextAction=watch-pr`:继续现有 readiness 分类与自愈决策树。
|
|
30
|
+
- `nextAction` 缺失或 invalid:遵循严格刷新失败出口,进入求助流程,不推断为 `watch-pr`。
|
|
31
|
+
|
|
32
|
+
此 route 只复用 core 已由六项 checks 推导的结果;不会把 `review-code` 报告或自动修复权限授予 Watch PR。
|
|
33
|
+
|
|
20
34
|
## 自愈决策树
|
|
21
35
|
|
|
22
36
|
```text
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { SandboxControlClientError, requestSandboxAgentOperation } from '../../../sandbox/control/client.js';
|
|
2
|
-
function fail(code, message, accepted = true) {
|
|
3
|
-
throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable: false }, accepted);
|
|
4
|
-
}
|
|
5
|
-
function exactKeys(value, keys) {
|
|
6
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
7
|
-
}
|
|
8
|
-
function validProcess(value) {
|
|
9
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
10
|
-
return false;
|
|
11
|
-
const process = value;
|
|
12
|
-
return exactKeys(process, ['pid', 'startTime'])
|
|
13
|
-
&& Number.isSafeInteger(process.pid) && process.pid > 0
|
|
14
|
-
&& Number.isSafeInteger(process.startTime) && process.startTime >= 0;
|
|
15
|
-
}
|
|
16
|
-
function validBuild(value) {
|
|
17
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
18
|
-
return false;
|
|
19
|
-
const build = value;
|
|
20
|
-
return exactKeys(build, ['internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'])
|
|
21
|
-
&& build.protocolVersion === 3
|
|
22
|
-
&& typeof build.packageVersion === 'string' && build.packageVersion.length > 0
|
|
23
|
-
&& typeof build.internalExecutableBuildHash === 'string' && /^[a-f0-9]{64}$/u.test(build.internalExecutableBuildHash)
|
|
24
|
-
&& typeof build.lifecycleContractHash === 'string' && /^[a-f0-9]{64}$/u.test(build.lifecycleContractHash);
|
|
25
|
-
}
|
|
26
|
-
function parseCodexControllerResult(response) {
|
|
27
|
-
if (response.phase !== 'completed' || response.error !== null || response.stderr !== '') {
|
|
28
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result outer response is invalid');
|
|
29
|
-
}
|
|
30
|
-
let value;
|
|
31
|
-
try {
|
|
32
|
-
if (!response.stdout.endsWith('\n') || response.stdout.slice(0, -1).includes('\n'))
|
|
33
|
-
throw new Error('not canonical');
|
|
34
|
-
value = JSON.parse(response.stdout);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid');
|
|
38
|
-
}
|
|
39
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
40
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result payload is invalid');
|
|
41
|
-
}
|
|
42
|
-
const result = value;
|
|
43
|
-
const keys = result.status === 'verified'
|
|
44
|
-
? ['binding', 'changed', 'error', 'lease', 'status', 'version']
|
|
45
|
-
: ['changed', 'error', 'lease', 'status', 'version'];
|
|
46
|
-
if (!exactKeys(result, keys) || result.version !== 1
|
|
47
|
-
|| !['opened', 'closed', 'failed', 'verified'].includes(result.status)) {
|
|
48
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result schema is invalid');
|
|
49
|
-
}
|
|
50
|
-
if (result.status === 'failed') {
|
|
51
|
-
const error = result.error;
|
|
52
|
-
if (response.exitCode !== 1 || result.changed !== false || result.lease !== null || !error
|
|
53
|
-
|| !exactKeys(error, ['code', 'message', 'retryable'])
|
|
54
|
-
|| typeof error.code !== 'string' || !/^[A-Z][A-Z0-9_]+$/u.test(error.code)
|
|
55
|
-
|| typeof error.message !== 'string' || error.retryable !== false) {
|
|
56
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller failure result is invalid');
|
|
57
|
-
}
|
|
58
|
-
fail(error.code, error.message);
|
|
59
|
-
}
|
|
60
|
-
if (response.exitCode !== 0 || result.error !== null) {
|
|
61
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller success result is invalid');
|
|
62
|
-
}
|
|
63
|
-
if (result.status === 'closed') {
|
|
64
|
-
if (typeof result.changed !== 'boolean' || result.lease !== null)
|
|
65
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller close result is invalid');
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
if (result.status === 'verified') {
|
|
69
|
-
const binding = result.binding;
|
|
70
|
-
if (result.changed !== false || result.lease !== null || !binding
|
|
71
|
-
|| !exactKeys(binding, ['controllerInstanceDigest', 'controlGeneration', 'taskId'])
|
|
72
|
-
|| typeof binding.taskId !== 'string' || binding.taskId.length === 0
|
|
73
|
-
|| typeof binding.controlGeneration !== 'string' || binding.controlGeneration.length === 0
|
|
74
|
-
|| typeof binding.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(binding.controllerInstanceDigest)) {
|
|
75
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify result is invalid');
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
const lease = result.lease;
|
|
80
|
-
if (result.changed !== true || !lease
|
|
81
|
-
|| !exactKeys(lease, ['buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerProcess', 'expiresAt', 'issuedAt', 'leaseId', 'leaseSecret', 'taskId', 'version'])
|
|
82
|
-
|| lease.version !== 1 || typeof lease.leaseId !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseId)
|
|
83
|
-
|| typeof lease.leaseSecret !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.leaseSecret)
|
|
84
|
-
|| typeof lease.taskId !== 'string' || lease.taskId.length === 0
|
|
85
|
-
|| typeof lease.controlGeneration !== 'string' || lease.controlGeneration.length === 0
|
|
86
|
-
|| typeof lease.controllerInstanceDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(lease.controllerInstanceDigest)
|
|
87
|
-
|| !validProcess(lease.controllerProcess) || !validBuild(lease.buildIdentity)
|
|
88
|
-
|| !Number.isSafeInteger(lease.issuedAt) || !Number.isSafeInteger(lease.expiresAt)
|
|
89
|
-
|| lease.expiresAt <= lease.issuedAt) {
|
|
90
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller open result is invalid');
|
|
91
|
-
}
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
function requestController(params) {
|
|
95
|
-
const result = parseCodexControllerResult(requestSandboxAgentOperation({
|
|
96
|
-
agentClient: 'codex',
|
|
97
|
-
operation: `controller.${params.command}`,
|
|
98
|
-
payload: params.command === 'open'
|
|
99
|
-
? { controllerProcess: params.controllerProcess }
|
|
100
|
-
: { proof: params.controllerProof },
|
|
101
|
-
...params
|
|
102
|
-
}));
|
|
103
|
-
if (result.status === 'opened'
|
|
104
|
-
&& (result.lease.controlGeneration !== (params.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION)
|
|
105
|
-
|| result.lease.controllerProcess.pid !== params.controllerProcess.pid
|
|
106
|
-
|| result.lease.controllerProcess.startTime !== params.controllerProcess.startTime)) {
|
|
107
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller result does not match the request');
|
|
108
|
-
}
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
function requestCodexControllerOpen(params) {
|
|
112
|
-
const result = requestController({ ...params, command: 'open', controllerProof: null });
|
|
113
|
-
if (result.status !== 'opened')
|
|
114
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller open returned the wrong result');
|
|
115
|
-
return result;
|
|
116
|
-
}
|
|
117
|
-
function requestCodexControllerClose(params) {
|
|
118
|
-
const result = requestController({ ...params, command: 'close' });
|
|
119
|
-
if (result.status !== 'closed')
|
|
120
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller close returned the wrong result');
|
|
121
|
-
return result;
|
|
122
|
-
}
|
|
123
|
-
function requestCodexControllerVerify(params) {
|
|
124
|
-
const result = requestController({ ...params, controllerProcess: params.controllerProof.controllerProcess, command: 'verify' });
|
|
125
|
-
if (result.status !== 'verified')
|
|
126
|
-
fail('SANDBOX_CONTROL_RESULT_INVALID', 'controller verify returned the wrong result');
|
|
127
|
-
return result;
|
|
128
|
-
}
|
|
129
|
-
export { parseCodexControllerResult, requestCodexControllerClose, requestCodexControllerOpen, requestCodexControllerVerify };
|
|
130
|
-
//# sourceMappingURL=controller-client.js.map
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getProcessIdentityState } from '../../../server/process-state.js';
|
|
5
|
-
import { computeLifecycleBuildIdentity, verifyLifecycleBuildIdentity } from './build-identity.js';
|
|
6
|
-
const HEX_256 = /^[a-f0-9]{64}$/u;
|
|
7
|
-
function exactKeys(value, keys) {
|
|
8
|
-
return Object.keys(value).sort().join(',') === [...keys].sort().join(',');
|
|
9
|
-
}
|
|
10
|
-
function validProcess(value) {
|
|
11
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
12
|
-
return false;
|
|
13
|
-
const identity = value;
|
|
14
|
-
return exactKeys(identity, ['pid', 'startTime'])
|
|
15
|
-
&& Number.isSafeInteger(identity.pid) && identity.pid > 0
|
|
16
|
-
&& Number.isSafeInteger(identity.startTime) && identity.startTime >= 0;
|
|
17
|
-
}
|
|
18
|
-
function validBuildIdentity(value) {
|
|
19
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
20
|
-
return false;
|
|
21
|
-
const identity = value;
|
|
22
|
-
return exactKeys(identity, [
|
|
23
|
-
'internalExecutableBuildHash', 'lifecycleContractHash', 'packageVersion', 'protocolVersion'
|
|
24
|
-
])
|
|
25
|
-
&& identity.protocolVersion === 3
|
|
26
|
-
&& typeof identity.packageVersion === 'string' && identity.packageVersion.length > 0
|
|
27
|
-
&& typeof identity.internalExecutableBuildHash === 'string' && HEX_256.test(identity.internalExecutableBuildHash)
|
|
28
|
-
&& typeof identity.lifecycleContractHash === 'string' && HEX_256.test(identity.lifecycleContractHash);
|
|
29
|
-
}
|
|
30
|
-
function validateContext(value) {
|
|
31
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
32
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
33
|
-
}
|
|
34
|
-
const context = value;
|
|
35
|
-
const lease = context.controllerLease;
|
|
36
|
-
if (!exactKeys(context, [
|
|
37
|
-
'buildIdentity', 'controlGeneration', 'controllerInstanceDigest', 'controllerLease',
|
|
38
|
-
'controllerProcess', 'expiresAt', 'hookDefinitionHash', 'issuedAt',
|
|
39
|
-
'taskId', 'version'
|
|
40
|
-
])
|
|
41
|
-
|| context.version !== 2
|
|
42
|
-
|| typeof context.taskId !== 'string' || context.taskId.length === 0
|
|
43
|
-
|| typeof context.controlGeneration !== 'string' || context.controlGeneration.length === 0
|
|
44
|
-
|| typeof context.controllerInstanceDigest !== 'string' || !HEX_256.test(context.controllerInstanceDigest)
|
|
45
|
-
|| !validProcess(context.controllerProcess)
|
|
46
|
-
|| !lease || !exactKeys(lease, ['leaseId', 'leaseSecret', 'version'])
|
|
47
|
-
|| lease.version !== 1
|
|
48
|
-
|| typeof lease.leaseId !== 'string' || !HEX_256.test(lease.leaseId)
|
|
49
|
-
|| typeof lease.leaseSecret !== 'string' || !HEX_256.test(lease.leaseSecret)
|
|
50
|
-
|| !Number.isSafeInteger(context.issuedAt)
|
|
51
|
-
|| !Number.isSafeInteger(context.expiresAt)
|
|
52
|
-
|| context.expiresAt <= context.issuedAt
|
|
53
|
-
|| !validBuildIdentity(context.buildIdentity)
|
|
54
|
-
|| typeof context.hookDefinitionHash !== 'string' || !HEX_256.test(context.hookDefinitionHash)) {
|
|
55
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
56
|
-
}
|
|
57
|
-
return context;
|
|
58
|
-
}
|
|
59
|
-
export function contextFromControllerLease(lease, input) {
|
|
60
|
-
return Object.freeze({
|
|
61
|
-
version: 2,
|
|
62
|
-
taskId: lease.taskId,
|
|
63
|
-
controlGeneration: lease.controlGeneration,
|
|
64
|
-
controllerInstanceDigest: lease.controllerInstanceDigest,
|
|
65
|
-
controllerProcess: lease.controllerProcess,
|
|
66
|
-
controllerLease: Object.freeze({ version: 1, leaseId: lease.leaseId, leaseSecret: lease.leaseSecret }),
|
|
67
|
-
issuedAt: lease.issuedAt,
|
|
68
|
-
expiresAt: lease.expiresAt,
|
|
69
|
-
buildIdentity: lease.buildIdentity,
|
|
70
|
-
hookDefinitionHash: input.hookDefinitionHash
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
export function writeCodexSandboxControllerContext(contextPath, context) {
|
|
74
|
-
validateContext(context);
|
|
75
|
-
fs.mkdirSync(path.dirname(contextPath), { recursive: true, mode: 0o700 });
|
|
76
|
-
const temporary = path.join(path.dirname(contextPath), `.${path.basename(contextPath)}.${process.pid}.${crypto.randomUUID()}.tmp`);
|
|
77
|
-
try {
|
|
78
|
-
fs.writeFileSync(temporary, `${JSON.stringify(context, null, 2)}\n`, { mode: 0o600, flag: 'wx' });
|
|
79
|
-
fs.chmodSync(temporary, 0o600);
|
|
80
|
-
fs.renameSync(temporary, contextPath);
|
|
81
|
-
}
|
|
82
|
-
finally {
|
|
83
|
-
fs.rmSync(temporary, { force: true });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
export function verifyCodexSandboxControllerContextWithWarnings(contextPath, options = {}) {
|
|
87
|
-
let stat;
|
|
88
|
-
try {
|
|
89
|
-
stat = fs.lstatSync(contextPath);
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
93
|
-
}
|
|
94
|
-
if (!stat.isFile() || stat.isSymbolicLink()
|
|
95
|
-
|| (process.platform !== 'win32' && (stat.mode & 0o077) !== 0)) {
|
|
96
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
97
|
-
}
|
|
98
|
-
let context;
|
|
99
|
-
try {
|
|
100
|
-
context = validateContext(JSON.parse(fs.readFileSync(contextPath, 'utf8')));
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
104
|
-
}
|
|
105
|
-
const repoRoot = options.repoRoot ?? process.cwd();
|
|
106
|
-
const generation = options.generation ?? process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
107
|
-
const state = (options.probeProcess ?? getProcessIdentityState)(context.controllerProcess);
|
|
108
|
-
if (context.controlGeneration !== generation
|
|
109
|
-
|| context.expiresAt <= (options.now ?? Date.now())
|
|
110
|
-
|| state !== 'alive') {
|
|
111
|
-
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTEXT_INVALID');
|
|
112
|
-
}
|
|
113
|
-
const currentBuildIdentity = computeLifecycleBuildIdentity(repoRoot);
|
|
114
|
-
const identity = verifyLifecycleBuildIdentity(context.buildIdentity, currentBuildIdentity);
|
|
115
|
-
if (!identity.ok)
|
|
116
|
-
throw new Error(`${identity.code}: ${identity.message}`);
|
|
117
|
-
return Object.freeze({ context: Object.freeze(context), warnings: identity.warnings });
|
|
118
|
-
}
|
|
119
|
-
export function controllerProofFromContext(context) {
|
|
120
|
-
return Object.freeze({
|
|
121
|
-
version: 1,
|
|
122
|
-
leaseId: context.controllerLease.leaseId,
|
|
123
|
-
leaseSecret: context.controllerLease.leaseSecret,
|
|
124
|
-
controllerProcess: context.controllerProcess
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
//# sourceMappingURL=controller-context.js.map
|