@fitlab-ai/agent-infra 0.9.15 → 0.11.0
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/README.md +2 -6
- package/README.zh-CN.md +2 -6
- package/bin/internal-cli.sh +0 -13
- package/dist/bin/internal-cli.js +11 -49
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +6 -2
- package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +43 -3
- package/dist/lib/fs/durable-write.js +2 -0
- package/dist/lib/internal/cli-route-inventory.js +3 -4
- package/dist/lib/internal/manual-validation.js +17 -8
- package/dist/lib/internal/platform-comment.js +60 -2
- package/dist/lib/internal/task-artifact.js +1 -1
- package/dist/lib/internal/task-finalization.js +2 -2
- package/dist/lib/internal/task-lifecycle.js +4 -4
- package/dist/lib/internal/task-operation-registry.js +22 -13
- package/dist/lib/internal/task-orchestration.js +2 -2
- package/dist/lib/internal/task-review.js +12 -7
- package/dist/lib/platform/activity-recovery.js +159 -0
- package/dist/lib/platform/issue-comments.js +89 -31
- package/dist/lib/platform/issues.js +1 -1
- package/dist/lib/platform/pr-change-report.js +5 -4
- package/dist/lib/platform/pr-summary.js +64 -32
- package/dist/lib/platform/pull-requests.js +3 -3
- package/dist/lib/platform/task-comment-projection.js +70 -0
- package/dist/lib/platform/task-identities.js +2 -9
- package/dist/lib/platform/task-recovery.js +55 -0
- package/dist/lib/platform/verification-sync.js +45 -7
- package/dist/lib/pr-review/evidence-grading.js +4 -8
- package/dist/lib/sandbox/commands/rm.js +21 -2
- package/dist/lib/sandbox/control/executor.js +4 -2
- package/dist/lib/sandbox/control/server.js +29 -9
- package/dist/lib/sandbox/control/state.js +7 -1
- package/dist/lib/sandbox/control/task-workflow.js +1 -1
- package/dist/lib/sandbox/control/workflow-executor.js +87 -35
- package/dist/lib/sandbox/index.js +1 -2
- package/dist/lib/sandbox/removal.js +116 -1011
- package/dist/lib/sandbox/workspace-identity.js +17 -1
- package/dist/lib/server/process-state.js +17 -5
- package/dist/lib/task/artifact-command.js +11 -30
- package/dist/lib/task/artifact-operations.js +17 -231
- package/dist/lib/task/artifact-recovery.js +476 -0
- package/dist/lib/task/artifact-repair-intent.js +90 -39
- package/dist/lib/task/commit-operation.js +31 -4
- package/dist/lib/task/control-authority.js +39 -8
- package/dist/lib/task/control-recovery.js +14 -2
- package/dist/lib/task/create-service.js +1 -8
- package/dist/lib/task/delegation-receipts.js +64 -3
- package/dist/lib/task/events.js +165 -23
- package/dist/lib/task/human-override.js +5 -8
- package/dist/lib/task/intermediate-cleanup.js +91 -8
- package/dist/lib/task/lifecycle-recovery.js +549 -0
- package/dist/lib/task/lifecycle.js +12 -3
- package/dist/lib/task/local-artifact-finalization.js +212 -123
- package/dist/lib/task/manual-validation-receipt.js +5 -1
- package/dist/lib/task/orchestration.js +52 -2
- package/dist/lib/task/pr-delivery-fact.js +12 -56
- package/dist/lib/task/recovery-warning.js +19 -0
- package/dist/lib/task/review-command.js +3 -2
- package/dist/lib/task/review-finalization.js +294 -74
- package/dist/lib/{host-control → task}/secure-fs.js +8 -19
- package/dist/lib/task/short-id.js +15 -7
- package/dist/lib/task/task-execution-lock.js +1 -1
- package/dist/lib/task/verification-engine.js +3 -14
- package/dist/lib/task/workflow-command.js +2 -1
- package/dist/lib/task/workflow-dispatch.js +161 -0
- package/dist/lib/version.js +1 -11
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +7 -2
- package/lib/agent-clients/adapters/codex-lifecycle/store.ts +55 -3
- package/lib/fs/durable-write.ts +1 -0
- package/lib/internal/cli-route-inventory.ts +3 -4
- package/lib/internal/manual-validation.ts +17 -8
- package/lib/internal/platform-comment.ts +53 -2
- package/lib/internal/task-artifact.ts +1 -1
- package/lib/internal/task-finalization.ts +1 -1
- package/lib/internal/task-lifecycle.ts +5 -4
- package/lib/internal/task-operation-registry.ts +22 -13
- package/lib/internal/task-orchestration.ts +1 -1
- package/lib/internal/task-review.ts +11 -7
- package/lib/platform/activity-recovery.ts +179 -0
- package/lib/platform/issue-comments.ts +105 -30
- package/lib/platform/issues.ts +1 -1
- package/lib/platform/pr-change-report.ts +7 -5
- package/lib/platform/pr-summary.ts +64 -32
- package/lib/platform/pull-requests.ts +3 -3
- package/lib/platform/task-comment-projection.ts +79 -0
- package/lib/platform/task-identities.ts +3 -14
- package/lib/platform/task-recovery.ts +54 -0
- package/lib/platform/verification-sync.ts +48 -11
- package/lib/pr-review/evidence-grading.ts +4 -9
- package/lib/sandbox/commands/rm.ts +34 -2
- package/lib/sandbox/control/executor.ts +4 -2
- package/lib/sandbox/control/protocol.ts +6 -1
- package/lib/sandbox/control/server.ts +30 -10
- package/lib/sandbox/control/state.ts +9 -1
- package/lib/sandbox/control/task-workflow.ts +1 -1
- package/lib/sandbox/control/workflow-executor.ts +106 -35
- package/lib/sandbox/index.ts +1 -2
- package/lib/sandbox/removal.ts +126 -1128
- package/lib/sandbox/workspace-identity.ts +19 -3
- package/lib/server/process-state.ts +26 -5
- package/lib/task/artifact-command.ts +12 -29
- package/lib/task/artifact-operations.ts +22 -252
- package/lib/task/artifact-recovery.ts +572 -0
- package/lib/task/artifact-repair-intent.ts +126 -51
- package/lib/task/commit-operation.ts +35 -4
- package/lib/task/control-authority.ts +48 -15
- package/lib/task/control-recovery.ts +13 -1
- package/lib/task/create-service.ts +1 -10
- package/lib/task/delegation-receipts.ts +73 -2
- package/lib/task/events.ts +188 -24
- package/lib/task/human-override.ts +5 -8
- package/lib/task/intermediate-cleanup.ts +66 -12
- package/lib/task/lifecycle-recovery.ts +691 -0
- package/lib/task/lifecycle.ts +13 -4
- package/lib/task/local-artifact-finalization.ts +249 -131
- package/lib/task/manual-validation-receipt.ts +5 -1
- package/lib/task/orchestration.ts +65 -0
- package/lib/task/pr-delivery-fact.ts +15 -62
- package/lib/task/recovery-warning.ts +21 -0
- package/lib/task/review-command.ts +2 -1
- package/lib/task/review-finalization.ts +298 -84
- package/lib/{host-control → task}/secure-fs.ts +10 -32
- package/lib/task/short-id.ts +27 -5
- package/lib/task/task-execution-lock.ts +1 -1
- package/lib/task/verification-engine.ts +3 -17
- package/lib/task/verification-types.ts +0 -2
- package/lib/task/workflow-command.ts +2 -1
- package/lib/task/workflow-dispatch.ts +171 -0
- package/lib/version.ts +1 -15
- package/package.json +1 -1
- package/templates/.agents/rules/create-issue.en.md +1 -1
- package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +12 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +13 -5
- package/templates/.agents/rules/local-artifact-repair.en.md +47 -39
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +45 -36
- package/templates/.agents/skills/analyze-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -3
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +7 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/code-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +2 -2
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +2 -2
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +1 -1
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/create-pr/config/verify.json +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +2 -2
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +2 -2
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -5
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -5
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-issue/config/verify.json +2 -2
- package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +11 -6
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +11 -6
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +6 -6
- package/templates/.agents/skills/review-code/SKILL.en.md +5 -4
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -4
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-code/reference/report-template.en.md +12 -12
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +12 -12
- package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -7
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -7
- package/templates/.agents/skills/review-pr/config/verify.en.json +1 -1
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +3 -3
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +3 -3
- package/templates/.agents/skills/run-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/run-manual-validation/config/verify.zh-CN.json +1 -1
- 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/templates/task.en.md +1 -1
- package/templates/.agents/templates/task.zh-CN.md +1 -1
- package/templates/.claude/commands/import-issue.en.md +1 -1
- package/templates/.claude/commands/import-issue.zh-CN.md +1 -1
- package/templates/.claude/commands/restore-task.en.md +1 -1
- package/templates/.claude/commands/restore-task.zh-CN.md +1 -1
- package/dist/lib/host-control/audit.js +0 -19
- package/dist/lib/host-control/client.js +0 -142
- package/dist/lib/host-control/command.js +0 -49
- package/dist/lib/host-control/path.js +0 -160
- package/dist/lib/host-control/server.js +0 -159
- package/dist/lib/host-control/service.js +0 -112
- package/dist/lib/internal/host-control.js +0 -28
- package/lib/host-control/audit.ts +0 -22
- package/lib/host-control/client.ts +0 -172
- package/lib/host-control/command.ts +0 -60
- package/lib/host-control/path.ts +0 -167
- package/lib/host-control/server.ts +0 -180
- package/lib/host-control/service.ts +0 -133
- package/lib/internal/host-control.ts +0 -28
package/README.md
CHANGED
|
@@ -95,13 +95,9 @@ Every command above works the same way in Claude Code, Codex, Antigravity CLI, a
|
|
|
95
95
|
|
|
96
96
|
### Sandbox control boundary
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
Direct-host lifecycle and workflow commands execute in the current CLI process. Task-bound sandboxes route requests to their broker; the broker verifies the request, manifest, generation, owner, lease, and controller before starting a one-request executor. There is no host socket, worker token, or user-level task-control service.
|
|
99
99
|
|
|
100
|
-
The
|
|
101
|
-
|
|
102
|
-
On macOS, the installer retries service registration during reload. If registration or startup still fails, it exits with an error and a recovery command.
|
|
103
|
-
|
|
104
|
-
The task directory exposed inside a task-bound sandbox is a writable projection. Artifact commands accept only typed workflow operations and canonical artifact basenames. The host opens candidates with `O_NOFOLLOW`, reads and hashes them through one file descriptor, checks observable metadata changes, and atomically lands validated bytes into the authoritative task directory. Changes to the projection's protected topology fail closed and never turn the projection into task state.
|
|
100
|
+
The task directory exposed inside a task-bound sandbox is writable only for its bound task. Artifact commands accept typed workflow operations and canonical artifact basenames. The executor opens candidates with `O_NOFOLLOW`, reads and hashes them through one file descriptor, checks observable metadata changes, and atomically lands validated bytes into the authoritative task directory.
|
|
105
101
|
|
|
106
102
|
## Quick Start
|
|
107
103
|
|
package/README.zh-CN.md
CHANGED
|
@@ -94,13 +94,9 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
|
|
|
94
94
|
|
|
95
95
|
### 沙箱控制边界
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
direct-host 生命周期和工作流命令在当前 CLI 进程中执行。任务沙箱把请求发送给所属 broker;broker 会先校验请求、manifest、generation、owner、lease 和 controller,再启动一次性 executor。系统不再使用宿主 socket、worker token 或用户级 task-control 服务。
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
macOS 重载期间,安装脚本会重试服务注册;若最终仍无法注册或启动,则以错误退出并提供恢复命令。
|
|
102
|
-
|
|
103
|
-
任务沙箱内的 task 目录是可写投影。产物命令只接受类型化 workflow 操作和规范的产物 basename。宿主使用 `O_NOFOLLOW` 打开候选文件,通过同一个文件描述符读取并计算摘要,检查可观察的元数据变化,再把已校验字节原子落入权威 task 目录。投影的受保护拓扑发生变化时会失败关闭,投影修改不会变成任务状态。
|
|
99
|
+
任务沙箱内的 task 目录只允许其绑定任务读写。产物命令只接受类型化 workflow 操作和规范的产物 basename。executor 使用 `O_NOFOLLOW` 打开候选文件,通过同一个文件描述符读取并计算摘要,检查可观察的元数据变化,再把已校验字节原子落入权威 task 目录。
|
|
104
100
|
|
|
105
101
|
## 快速开始
|
|
106
102
|
|
package/bin/internal-cli.sh
CHANGED
|
@@ -26,19 +26,6 @@ else
|
|
|
26
26
|
internal_cli="$script_dir/../dist/bin/internal-cli.js"
|
|
27
27
|
fi
|
|
28
28
|
|
|
29
|
-
command=${1-}
|
|
30
|
-
task_control_command=false
|
|
31
|
-
case "$command" in
|
|
32
|
-
task-lifecycle|task-orchestration|task-finalization) task_control_command=true ;;
|
|
33
|
-
esac
|
|
34
|
-
|
|
35
|
-
status_mount_present=false
|
|
36
|
-
if [ -d /run/agent-infra/control-status ]; then status_mount_present=true; fi
|
|
37
|
-
launch_config=${AGENT_INFRA_TASK_ID-}${AGENT_INFRA_CONTROL_TOKEN-}${AGENT_INFRA_CONTROL_GENERATION-}${AGENT_INFRA_CONTROL_DIR-}${AGENT_INFRA_CONTROL_STATUS_DIR-}${AGENT_INFRA_RUNTIME_DIR-}${AGENT_INFRA_EXECUTOR_MANIFEST-}
|
|
38
|
-
if [ "$task_control_command" = true ] && [ "$status_mount_present" = true ] && [ -z "$launch_config" ] && [ -z "${AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT-}" ]; then
|
|
39
|
-
printf '%s\n' '{"status":"failed","changed":false,"error":{"code":"SANDBOX_CONTROL_IDENTITY_MISSING","message":"sandbox control identity is present but its launch configuration is missing"}}'
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
29
|
# The outer launcher owns the Node startup boundary. A task-bound process must
|
|
43
30
|
# not pass preload, import, or loader injection into the control router.
|
|
44
31
|
unset NODE_OPTIONS NODE_PATH
|
package/dist/bin/internal-cli.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { formatTaskViewDiagnostic, guardTaskOperation, resolveSandboxControlTransport, TaskViewOperationError } from '../lib/internal/task-operation-registry.js';
|
|
3
3
|
import { INTERNAL_HANDLER_ROUTE_SELECTORS } from '../lib/internal/cli-route-inventory.js';
|
|
4
|
-
import { hostControlRequestForCommand, requestHostControl, HostControlClientError, HOST_CONTROL_TEST_ENVIRONMENT_KEYS } from '../lib/host-control/client.js';
|
|
5
|
-
import { resolveHostControlEndpoint, readHostControlWorkerToken } from '../lib/host-control/path.js';
|
|
6
4
|
const [major = 0, minor = 0] = process.versions.node.split('.').map((part) => parseInt(part, 10));
|
|
7
5
|
if (major < 22 || (major === 22 && minor < 9)) {
|
|
8
6
|
process.stderr.write(`agent-infra-internal requires Node.js >= 22.9.0 (current: ${process.version})\n`);
|
|
@@ -18,9 +16,13 @@ const taskWorkflowCommand = command === 'task-artifact'
|
|
|
18
16
|
|| command === 'task-invalidation'
|
|
19
17
|
|| command === 'task-warning';
|
|
20
18
|
const manualValidationWorkflowCommand = command === 'manual-validation';
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
const taskControlMarkers = [
|
|
20
|
+
'AGENT_INFRA_TASK_ID', 'AGENT_INFRA_CONTROL_TOKEN', 'AGENT_INFRA_CONTROL_GENERATION',
|
|
21
|
+
'AGENT_INFRA_CONTROL_DIR', 'AGENT_INFRA_CONTROL_STATUS_DIR', 'AGENT_INFRA_RUNTIME_DIR',
|
|
22
|
+
'AGENT_INFRA_CONTROL_CONTROLLER_BINDING', 'AGENT_INFRA_EXECUTOR_MANIFEST'
|
|
23
|
+
];
|
|
24
|
+
const markerlessHelp = ['--help', '-h'].includes(process.argv[3] ?? '')
|
|
25
|
+
&& taskControlMarkers.every((key) => !process.env[key]);
|
|
24
26
|
let taskViewGuardFailed = false;
|
|
25
27
|
try {
|
|
26
28
|
const guard = guardTaskOperation('internal', command, process.argv.slice(3));
|
|
@@ -45,44 +47,8 @@ function taskControlTransportFailure(message, code = 'TASK_CONTROL_TRANSPORT_INV
|
|
|
45
47
|
})}\n`);
|
|
46
48
|
process.exit(1);
|
|
47
49
|
}
|
|
48
|
-
async function runHostControlCommand(commandName, args) {
|
|
49
|
-
let response;
|
|
50
|
-
try {
|
|
51
|
-
const testEndpoint = process.env.AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT;
|
|
52
|
-
const forwardedEnvironment = testEndpoint
|
|
53
|
-
? Object.fromEntries(HOST_CONTROL_TEST_ENVIRONMENT_KEYS.flatMap((key) => process.env[key] === undefined ? [] : [[key, process.env[key]]]))
|
|
54
|
-
: {};
|
|
55
|
-
response = await requestHostControl({
|
|
56
|
-
request: hostControlRequestForCommand(commandName, args, process.cwd(), forwardedEnvironment),
|
|
57
|
-
endpoint: process.env.AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT ?? resolveHostControlEndpoint()
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
if (error instanceof HostControlClientError) {
|
|
62
|
-
taskControlTransportFailure(error.message, error.changed === false
|
|
63
|
-
? 'SANDBOX_CONTROL_HOST_AUTHORITY_UNAVAILABLE' : error.code, error.changed);
|
|
64
|
-
}
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
process.stdout.write(response.stdout);
|
|
68
|
-
process.stderr.write(response.stderr);
|
|
69
|
-
process.exitCode = response.exitCode;
|
|
70
|
-
}
|
|
71
|
-
const hostWorkerRequested = process.env.AGENT_INFRA_HOST_CONTROL_WORKER === '1';
|
|
72
|
-
const hostWorker = hostWorkerRequested && (() => {
|
|
73
|
-
try {
|
|
74
|
-
const endpoint = process.env.AGENT_INFRA_TEST_HOST_CONTROL_ENDPOINT ?? resolveHostControlEndpoint();
|
|
75
|
-
return process.env.AGENT_INFRA_HOST_CONTROL_WORKER_TOKEN === readHostControlWorkerToken(endpoint);
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
return false;
|
|
79
|
-
}
|
|
80
|
-
})();
|
|
81
|
-
if (hostWorkerRequested && !hostWorker && (taskControlCommand || taskWorkflowCommand || manualValidationWorkflowCommand)) {
|
|
82
|
-
taskControlTransportFailure('host-control worker authorization is invalid', 'SANDBOX_CONTROL_HOST_AUTHORITY_UNAVAILABLE');
|
|
83
|
-
}
|
|
84
50
|
let controlRouted = false;
|
|
85
|
-
if (!taskViewGuardFailed && (taskControlCommand || taskWorkflowCommand || manualValidationWorkflowCommand)
|
|
51
|
+
if (!taskViewGuardFailed && !markerlessHelp && (taskControlCommand || taskWorkflowCommand || manualValidationWorkflowCommand)) {
|
|
86
52
|
const transport = resolveSandboxControlTransport(process.env);
|
|
87
53
|
switch (transport.kind) {
|
|
88
54
|
case 'fail-closed': {
|
|
@@ -91,7 +57,8 @@ if (!taskViewGuardFailed && (taskControlCommand || taskWorkflowCommand || manual
|
|
|
91
57
|
break;
|
|
92
58
|
}
|
|
93
59
|
case 'direct-host':
|
|
94
|
-
|
|
60
|
+
// The host CLI is the direct-host authority. Domain handlers retain their
|
|
61
|
+
// task locks, atomic writes, and operation-specific recovery facts.
|
|
95
62
|
break;
|
|
96
63
|
case 'broker-client': {
|
|
97
64
|
const { sandboxControl } = await import('../lib/internal/sandbox-control.js');
|
|
@@ -99,7 +66,7 @@ if (!taskViewGuardFailed && (taskControlCommand || taskWorkflowCommand || manual
|
|
|
99
66
|
break;
|
|
100
67
|
}
|
|
101
68
|
}
|
|
102
|
-
controlRouted =
|
|
69
|
+
controlRouted = transport.kind === 'broker-client';
|
|
103
70
|
}
|
|
104
71
|
if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
|
|
105
72
|
switch (command) {
|
|
@@ -128,11 +95,6 @@ if (!controlRouted && !taskViewGuardFailed && internalRouteRegistered)
|
|
|
128
95
|
await sandboxControl(process.argv.slice(3));
|
|
129
96
|
break;
|
|
130
97
|
}
|
|
131
|
-
case 'host-control': {
|
|
132
|
-
const { hostControl } = await import('../lib/internal/host-control.js');
|
|
133
|
-
await hostControl(process.argv.slice(3));
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
98
|
case 'agent-client': {
|
|
137
99
|
const { agentClient } = await import('../lib/internal/agent-client.js');
|
|
138
100
|
agentClient(process.argv.slice(3));
|
|
@@ -22,13 +22,14 @@ function copyRegular(source, destination, mode) {
|
|
|
22
22
|
function controlFromEnvironment() {
|
|
23
23
|
const token = process.env.AGENT_INFRA_CONTROL_TOKEN;
|
|
24
24
|
const generation = process.env.AGENT_INFRA_CONTROL_GENERATION;
|
|
25
|
+
const rootId = process.env.AGENT_INFRA_CONTROL_ROOT_ID;
|
|
25
26
|
const channelDir = process.env.AGENT_INFRA_CONTROL_DIR;
|
|
26
27
|
const statusDir = process.env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
27
28
|
const runtimeDir = process.env.AGENT_INFRA_RUNTIME_DIR;
|
|
28
|
-
if (!token || !generation || !channelDir || !statusDir || !runtimeDir) {
|
|
29
|
+
if (!token || !generation || !rootId || !channelDir || !statusDir || !runtimeDir) {
|
|
29
30
|
throw new Error('CODEX_SANDBOX_CONTROLLER_CONTROL_MISSING');
|
|
30
31
|
}
|
|
31
|
-
return { token, generation, channelDir, statusDir, runtimeDir };
|
|
32
|
+
return { token, generation, rootId, channelDir, statusDir, runtimeDir };
|
|
32
33
|
}
|
|
33
34
|
function verifyRuntime(runtimeDir) {
|
|
34
35
|
let stat;
|
|
@@ -188,6 +189,9 @@ function isolatedEnvironment(home, shimDir, contextPath, taskId, control, provid
|
|
|
188
189
|
AGENT_INFRA_TASK_ID: taskId,
|
|
189
190
|
AGENT_INFRA_CONTROL_TOKEN: control.token,
|
|
190
191
|
AGENT_INFRA_CONTROL_GENERATION: control.generation,
|
|
192
|
+
...(control.rootId || sourceEnvironment.AGENT_INFRA_CONTROL_ROOT_ID
|
|
193
|
+
? { AGENT_INFRA_CONTROL_ROOT_ID: control.rootId ?? sourceEnvironment.AGENT_INFRA_CONTROL_ROOT_ID }
|
|
194
|
+
: {}),
|
|
191
195
|
AGENT_INFRA_CONTROL_DIR: control.channelDir,
|
|
192
196
|
AGENT_INFRA_CONTROL_STATUS_DIR: control.statusDir,
|
|
193
197
|
AGENT_INFRA_RUNTIME_DIR: control.runtimeDir,
|
|
@@ -6,9 +6,19 @@ import { resolveAgentRuntimeStoreRoot } from '../../../runtime/agent-runtime.js'
|
|
|
6
6
|
const LOCK_RETRY_MS = 10;
|
|
7
7
|
const LOCK_TIMEOUT_MS = 1_000;
|
|
8
8
|
const LOCK_STALE_MS = 30_000;
|
|
9
|
+
const RECOVERY_CONSUMER_RE = /^lifecycle-recovery:([^:\r\n]+):([^:\r\n]+)$/u;
|
|
9
10
|
function digest(value) {
|
|
10
11
|
return crypto.createHash('sha256').update(value).digest('hex');
|
|
11
12
|
}
|
|
13
|
+
function isRecoveryConsumer(value) {
|
|
14
|
+
return RECOVERY_CONSUMER_RE.test(value);
|
|
15
|
+
}
|
|
16
|
+
function recoveryConsumer(taskId, receiptId) {
|
|
17
|
+
if (!/^[^:\r\n]+$/u.test(taskId) || !/^[^:\r\n]+$/u.test(receiptId)) {
|
|
18
|
+
throw new Error('Codex lifecycle recovery claim identity is invalid');
|
|
19
|
+
}
|
|
20
|
+
return `lifecycle-recovery:${taskId}:${receiptId}`;
|
|
21
|
+
}
|
|
12
22
|
function readRecord(file) {
|
|
13
23
|
const value = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
14
24
|
if (value.schemaVersion !== 1
|
|
@@ -202,11 +212,13 @@ function createCodexLifecycleStore(options) {
|
|
|
202
212
|
}
|
|
203
213
|
function read(childThreadId) {
|
|
204
214
|
const matches = findByChild(childThreadId);
|
|
205
|
-
if (matches.length
|
|
215
|
+
if (matches.length === 0)
|
|
206
216
|
throw new Error(`Codex lifecycle child '${childThreadId}' was not found uniquely`);
|
|
217
|
+
if (matches.length > 1)
|
|
218
|
+
throw new Error(`Codex lifecycle child '${childThreadId}' is ambiguous`);
|
|
207
219
|
return readRecord(matches[0]);
|
|
208
220
|
}
|
|
209
|
-
function
|
|
221
|
+
function consumeInternal(childThreadId, consumer, expectedHookDefinitionHash) {
|
|
210
222
|
if (!consumer.trim())
|
|
211
223
|
throw new Error('Codex lifecycle consumer is required');
|
|
212
224
|
return withWriteLock(() => {
|
|
@@ -240,6 +252,32 @@ function createCodexLifecycleStore(options) {
|
|
|
240
252
|
return next;
|
|
241
253
|
});
|
|
242
254
|
}
|
|
255
|
+
function consume(childThreadId, consumer, expectedHookDefinitionHash) {
|
|
256
|
+
if (!consumer.trim())
|
|
257
|
+
throw new Error('Codex lifecycle consumer is required');
|
|
258
|
+
if (consumer.startsWith('lifecycle-recovery:')) {
|
|
259
|
+
throw new Error('Codex lifecycle recovery claims require claimRecovery');
|
|
260
|
+
}
|
|
261
|
+
return consumeInternal(childThreadId, consumer, expectedHookDefinitionHash);
|
|
262
|
+
}
|
|
263
|
+
function claimRecovery(childThreadId, taskId, receiptId, expectedHookDefinitionHash) {
|
|
264
|
+
return consumeInternal(childThreadId, recoveryConsumer(taskId, receiptId), expectedHookDefinitionHash);
|
|
265
|
+
}
|
|
266
|
+
function releaseRecovery(childThreadId, consumer) {
|
|
267
|
+
if (!/^lifecycle-recovery:[^:\r\n]+:[^:\r\n]+$/.test(consumer))
|
|
268
|
+
return false;
|
|
269
|
+
return withWriteLock(() => {
|
|
270
|
+
const matches = findByChild(childThreadId);
|
|
271
|
+
if (matches.length !== 1)
|
|
272
|
+
return false;
|
|
273
|
+
const file = matches[0];
|
|
274
|
+
const current = readRecord(file);
|
|
275
|
+
if (current.state.status !== 'stop-ready' || current.consumer !== consumer || !current.consumedAt)
|
|
276
|
+
return false;
|
|
277
|
+
fs.unlinkSync(file);
|
|
278
|
+
return true;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
243
281
|
function expireBefore(cutoff) {
|
|
244
282
|
return withWriteLock(() => {
|
|
245
283
|
let changed = 0;
|
|
@@ -247,6 +285,8 @@ function createCodexLifecycleStore(options) {
|
|
|
247
285
|
const current = readRecord(file);
|
|
248
286
|
if (current.updatedAt >= cutoff)
|
|
249
287
|
continue;
|
|
288
|
+
if (current.consumer && isRecoveryConsumer(current.consumer))
|
|
289
|
+
continue;
|
|
250
290
|
if (current.consumer || ['invalid', 'expired', 'stop-ready'].includes(current.state.status)) {
|
|
251
291
|
fs.unlinkSync(file);
|
|
252
292
|
changed += 1;
|
|
@@ -266,7 +306,7 @@ function createCodexLifecycleStore(options) {
|
|
|
266
306
|
return changed;
|
|
267
307
|
});
|
|
268
308
|
}
|
|
269
|
-
return Object.freeze({ root, apply, applyToSpawn, consume, expireBefore, findByParent, read });
|
|
309
|
+
return Object.freeze({ root, apply, applyToSpawn, consume, claimRecovery, releaseRecovery, expireBefore, findByParent, read });
|
|
270
310
|
}
|
|
271
311
|
export { createCodexLifecycleStore, hasActiveCodexLifecycleEvidence };
|
|
272
312
|
//# sourceMappingURL=store.js.map
|
|
@@ -2,6 +2,8 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
export function fsyncDirectory(directory) {
|
|
5
|
+
if (process.platform === 'win32')
|
|
6
|
+
return;
|
|
5
7
|
const descriptor = fs.openSync(directory, 'r');
|
|
6
8
|
try {
|
|
7
9
|
fs.fsyncSync(descriptor);
|
|
@@ -33,7 +33,6 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
33
33
|
'task-create': ['input'],
|
|
34
34
|
'task-qualification': ['proposal', 'confirm', 'supersede', 'revoke'],
|
|
35
35
|
'sandbox-control': ['serve', 'execute', 'recover', 'client'],
|
|
36
|
-
'host-control': ['serve', 'status', 'install', 'uninstall'],
|
|
37
36
|
'agent-client': ['next-steps', 'model-selection'],
|
|
38
37
|
'codex-lifecycle': ['preflight', 'capability-arm', 'hook-event', 'resolve-start', 'resolve-stop', 'consume'],
|
|
39
38
|
'codex-sandbox-controller': ['verify-context', 'run'],
|
|
@@ -44,7 +43,7 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
44
43
|
'platform-security': ['read', 'dismiss'],
|
|
45
44
|
'platform-metadata': ['init-labels', 'init-milestones'],
|
|
46
45
|
'platform-context': ['resolve'],
|
|
47
|
-
'platform-comment': ['list', 'owner', 'backfill', 'sync'],
|
|
46
|
+
'platform-comment': ['list', 'recover', 'owner', 'backfill', 'sync'],
|
|
48
47
|
'platform-issue': ['inspect', 'create', 'bind', 'sync'],
|
|
49
48
|
'platform-pr': ['inspect', 'summary-context', 'resolve-external', 'create', 'bind', 'skip', 'sync', 'change-report', 'summary-sync', 'sync-in-labels'],
|
|
50
49
|
'platform-pr-review': ['inspect', 'list', 'publish-pr', 'publish-task'],
|
|
@@ -54,9 +53,9 @@ export const INTERNAL_HANDLER_ROUTE_SELECTORS = Object.freeze({
|
|
|
54
53
|
'task-ledger': ['decision-next-id', 'stage-status', 'finding-upsert', 'finding-respond', 'finding-review', 'decision-upsert', 'rework-intent-upsert'],
|
|
55
54
|
'task-warning': ['list', 'add', 'set-status'],
|
|
56
55
|
'task-activity': ['pr-review-inspect', 'pr-review-start', 'pr-review-complete', 'pr-review-terminate'],
|
|
57
|
-
'task-artifact': ['inspect', 'init', '
|
|
56
|
+
'task-artifact': ['inspect', 'init', 'preflight', 'finalize-local'],
|
|
58
57
|
'task-orchestration': ['status', 'progress'],
|
|
59
|
-
'task-review': ['finalize-summary'],
|
|
58
|
+
'task-review': ['preflight', 'finalize-summary'],
|
|
60
59
|
'task-event': ['event'],
|
|
61
60
|
'task-invalidation': ['reconcile'],
|
|
62
61
|
'task-override': ['diagnose', 'issue', 'consume'],
|
|
@@ -22,15 +22,24 @@ function printFailure(format, error) {
|
|
|
22
22
|
process.stderr.write(`${error.code}: ${error.message}\n`);
|
|
23
23
|
process.exitCode = 1;
|
|
24
24
|
}
|
|
25
|
+
const MANUAL_VALIDATION_STATUS_SECTION = /^###\s+(?:⚠️\s+(?:需人工校验|Manual Validation Required)|✅\s+(?:人工验证已通过|无需人工校验|Manual Validation Passed|No Manual Validation Required)|⏳\s+(?:人工验证待收尾|Manual Validation Pending))\s*$[\s\S]*?(?=^#{1,3}\s|(?![\s\S]))/gmu;
|
|
25
26
|
function manualSummaryBody(body, phase, transactionId, receiptDigest = '', evidenceDigest = '', prHeadSha = '') {
|
|
26
|
-
const
|
|
27
|
-
.replace(/^###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)\s*$/gmu, '')
|
|
28
|
-
.replace(/^###\s+⏳\s+(?:Manual Validation Pending|人工验证待收尾)\s*$/gmu, '')
|
|
29
|
-
.replace(/Manual validation passed(?:\s+→)?/giu, 'Manual validation pending');
|
|
27
|
+
const chinese = /###\s+(?:⚠️\s+需人工校验|✅\s+(?:人工验证已通过|无需人工校验)|⏳\s+人工验证待收尾)\s*$/mu.test(body);
|
|
30
28
|
const section = phase === 'pending'
|
|
31
|
-
?
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
? chinese
|
|
30
|
+
? `### ⏳ 人工验证待收尾\n\n人工验证证据已暂存;事务 ${transactionId} 正在等待最终完成。`
|
|
31
|
+
: `### ⏳ Manual Validation Pending\n\nManual validation evidence is staged; transaction ${transactionId} is awaiting final promotion.`
|
|
32
|
+
: chinese
|
|
33
|
+
? `### ✅ 人工验证已通过\n\n人工验证已通过;transaction=${transactionId}; receipt=${receiptDigest}; evidence=${evidenceDigest}; head=${prHeadSha}.`
|
|
34
|
+
: `### ✅ Manual Validation Passed\n\nManual validation passed; transaction=${transactionId}; receipt=${receiptDigest}; evidence=${evidenceDigest}; head=${prHeadSha}.`;
|
|
35
|
+
let inserted = false;
|
|
36
|
+
const updated = body.replace(MANUAL_VALIDATION_STATUS_SECTION, () => {
|
|
37
|
+
if (inserted)
|
|
38
|
+
return '';
|
|
39
|
+
inserted = true;
|
|
40
|
+
return `${section}\n\n`;
|
|
41
|
+
});
|
|
42
|
+
return inserted ? `${updated.replace(/\s+$/u, '')}\n` : `${body.replace(/\s+$/u, '')}\n\n${section}\n`;
|
|
34
43
|
}
|
|
35
44
|
function openManualValidationStarted(taskMdPath) {
|
|
36
45
|
let content;
|
|
@@ -242,7 +251,7 @@ async function executeManualValidationTransactionLocked(taskRef, values, cwd, re
|
|
|
242
251
|
if (!['applied', 'no-op'].includes(promoted.status))
|
|
243
252
|
transactionFailure(promoted.error ?? { code: 'MANUAL_VALIDATION_TRANSACTION_RECOVERY_REQUIRED', message: 'final summary promotion failed' });
|
|
244
253
|
const postWrite = await summaryCommentState(taskRef, { cwd, client: options.client });
|
|
245
|
-
if (
|
|
254
|
+
if (!/^###\s+✅\s+(?:Manual Validation Passed|人工验证已通过)\s*$/mu.test(postWrite.comment?.body ?? '') || postWrite.pullRequest?.head.sha !== receipt.prHeadSha || !manualValidationFinalSummaryProjectionMatches(postWrite.comment?.body ?? '', receipt))
|
|
246
255
|
transactionFailure({ code: 'MANUAL_VALIDATION_TRANSACTION_RECOVERY_REQUIRED', message: 'final summary post-write verification failed' });
|
|
247
256
|
transitionAndPersist('committed', { postWriteVerified: true });
|
|
248
257
|
return result('applied', null, { transaction, receipt, idempotent: false });
|
|
@@ -2,9 +2,11 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { normalizeAgentToken, AGENT_USAGE_HINT } from '../agent-clients/tokens.js';
|
|
4
4
|
import { checkPlatformCommentOwner, listPlatformComments, syncPlatformComment } from '../platform/issue-comments.js';
|
|
5
|
+
import { recoverTaskFromComments } from '../platform/task-recovery.js';
|
|
5
6
|
import { backfillCompletionComments } from '../platform/completion-backfill.js';
|
|
6
7
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
7
8
|
const USAGE = `Usage: agent-infra-internal platform-comment list --issue <token> [--cwd <path>]
|
|
9
|
+
agent-infra-internal platform-comment recover --issue <token> --task-id <TASK-id> --output <staging-task.md> [--cwd <path>]
|
|
8
10
|
agent-infra-internal platform-comment owner <task-ref> [--cwd <path>]
|
|
9
11
|
agent-infra-internal platform-comment backfill <task-ref> --agent <agent> [--cwd <path>]
|
|
10
12
|
agent-infra-internal platform-comment sync <task-ref> --kind <kind> --agent <agent> [--artifact <file>] [--body-file <path|->] [--status-label <label>] [--backfill] [--cwd <path>]
|
|
@@ -26,7 +28,7 @@ function parseFlags(args, start) {
|
|
|
26
28
|
const seen = new Set();
|
|
27
29
|
for (let index = start; index < args.length; index += 1) {
|
|
28
30
|
const flag = args[index];
|
|
29
|
-
if (!['--issue', '--cwd', '--kind', '--agent', '--artifact', '--body-file', '--status-label', '--backfill'].includes(flag)) {
|
|
31
|
+
if (!['--issue', '--task-id', '--output', '--cwd', '--kind', '--agent', '--artifact', '--body-file', '--status-label', '--backfill'].includes(flag)) {
|
|
30
32
|
return { values, error: `unknown option '${flag}'` };
|
|
31
33
|
}
|
|
32
34
|
if (seen.has(flag))
|
|
@@ -58,6 +60,7 @@ async function platformComment(args = []) {
|
|
|
58
60
|
const operation = args[0];
|
|
59
61
|
if (!operation || ![
|
|
60
62
|
internalHandlerRoute('platform-comment', 'list', operation),
|
|
63
|
+
internalHandlerRoute('platform-comment', 'recover', operation),
|
|
61
64
|
internalHandlerRoute('platform-comment', 'owner', operation),
|
|
62
65
|
internalHandlerRoute('platform-comment', 'backfill', operation),
|
|
63
66
|
internalHandlerRoute('platform-comment', 'sync', operation)
|
|
@@ -65,7 +68,7 @@ async function platformComment(args = []) {
|
|
|
65
68
|
fail('a valid operation is required');
|
|
66
69
|
return;
|
|
67
70
|
}
|
|
68
|
-
const hasTaskRef = operation !== 'list';
|
|
71
|
+
const hasTaskRef = operation !== 'list' && operation !== 'recover';
|
|
69
72
|
const taskRef = hasTaskRef ? args[1] : undefined;
|
|
70
73
|
if (hasTaskRef && (!taskRef || taskRef.startsWith('--'))) {
|
|
71
74
|
fail(`${operation} requires a task ref`);
|
|
@@ -91,6 +94,61 @@ async function platformComment(args = []) {
|
|
|
91
94
|
finish(await listPlatformComments(issue, cwd));
|
|
92
95
|
return;
|
|
93
96
|
}
|
|
97
|
+
if (internalHandlerRoute('platform-comment', 'recover', operation)) {
|
|
98
|
+
const issue = parsed.values.issue;
|
|
99
|
+
const taskId = parsed.values.taskId;
|
|
100
|
+
const output = parsed.values.output;
|
|
101
|
+
if (typeof issue !== 'string' || !issue || typeof taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/.test(taskId)
|
|
102
|
+
|| typeof output !== 'string' || !output) {
|
|
103
|
+
fail('recover requires --issue <token> --task-id <TASK-id> --output <staging-task.md>');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const unexpected = Object.keys(parsed.values).find((key) => !['issue', 'taskId', 'output', 'cwd'].includes(key));
|
|
107
|
+
if (unexpected) {
|
|
108
|
+
fail(`recover does not accept '--${unexpected}'`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const outputPath = path.resolve(cwd, output);
|
|
112
|
+
const workspaceDir = path.resolve(cwd, '.agents', 'workspace');
|
|
113
|
+
const stagingDir = path.dirname(outputPath);
|
|
114
|
+
const stagingParent = fs.existsSync(stagingDir) ? path.dirname(fs.realpathSync(stagingDir)) : '';
|
|
115
|
+
const realWorkspace = fs.existsSync(workspaceDir) ? fs.realpathSync(workspaceDir) : '';
|
|
116
|
+
if (path.basename(outputPath) !== 'task.md' || stagingParent !== realWorkspace
|
|
117
|
+
|| !path.basename(stagingDir).startsWith('.restore-staging-') || !fs.existsSync(stagingDir)
|
|
118
|
+
|| !fs.lstatSync(stagingDir).isDirectory() || fs.existsSync(outputPath)) {
|
|
119
|
+
fail('recover output must be a new task.md inside an existing .agents/workspace/.restore-staging-* directory');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const listed = await listPlatformComments(issue, cwd);
|
|
123
|
+
if (listed.status === 'failed' || listed.status === 'blocked' || !listed.comments) {
|
|
124
|
+
finish(listed);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const taskContent = recoverTaskFromComments({ taskId, comments: listed.comments });
|
|
129
|
+
fs.writeFileSync(outputPath, taskContent, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
|
|
130
|
+
finish({
|
|
131
|
+
...listed,
|
|
132
|
+
status: 'applied',
|
|
133
|
+
changed: true,
|
|
134
|
+
operations: [{ name: `recovery:task:${taskId}`, status: 'applied', reasonCode: null }],
|
|
135
|
+
error: null
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
finish({
|
|
140
|
+
...listed,
|
|
141
|
+
status: 'failed',
|
|
142
|
+
changed: false,
|
|
143
|
+
error: {
|
|
144
|
+
code: 'RECOVERY_EVIDENCE_MISSING',
|
|
145
|
+
message: error instanceof Error ? error.message : String(error),
|
|
146
|
+
retryable: false
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
94
152
|
if (internalHandlerRoute('platform-comment', 'owner', operation)) {
|
|
95
153
|
const unexpected = Object.keys(parsed.values).find((key) => key !== 'cwd');
|
|
96
154
|
if (unexpected) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseArtifactCommand, executeArtifactCommand } from '../task/artifact-command.js';
|
|
2
2
|
import { ensureInternalHandlerRoute } from './cli-route-inventory.js';
|
|
3
|
-
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id>
|
|
3
|
+
const USAGE = `Usage: agent-infra-internal task-artifact <N | TASK-id> inspect --family <family>\n agent-infra-internal task-artifact <N | TASK-id> init --family <family> --artifact <artifact> [--locale <zh-CN|en>]\n agent-infra-internal task-artifact <N | TASK-id> preflight|finalize-local --family <analysis|plan|code> --artifact <artifact> [--recovery-id <id>]\n\nInspect, initialize, preflight, or finalize a workflow artifact without changing task state.\n`;
|
|
4
4
|
function taskArtifact(args = []) {
|
|
5
5
|
if (!ensureInternalHandlerRoute('task-artifact', args))
|
|
6
6
|
return;
|
|
@@ -20,12 +20,12 @@ function parseFailure(error) {
|
|
|
20
20
|
return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
|
|
21
21
|
}
|
|
22
22
|
async function taskFinalization(args = []) {
|
|
23
|
-
if (!ensureInternalHandlerRoute('task-finalization', args))
|
|
24
|
-
return;
|
|
25
23
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
26
24
|
process.stdout.write(USAGE);
|
|
27
25
|
return;
|
|
28
26
|
}
|
|
27
|
+
if (!ensureInternalHandlerRoute('task-finalization', args))
|
|
28
|
+
return;
|
|
29
29
|
let operation;
|
|
30
30
|
try {
|
|
31
31
|
operation = parseTaskControlOperation('task-finalization', args);
|
|
@@ -2,7 +2,7 @@ import { createDirectHostExecutionContext, dispatchTaskControlOperation, parseTa
|
|
|
2
2
|
import { lifecycleIntentCatalog } from '../task/lifecycle.js';
|
|
3
3
|
import { detectRepoRoot, resolveTaskRef } from '../task/resolve-ref.js';
|
|
4
4
|
import { ensureInternalHandlerRoute, internalHandlerRoute } from './cli-route-inventory.js';
|
|
5
|
-
const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\nOverride: --override-ticket <ticket> --override-target <target> --override-scope <scope>\n`;
|
|
5
|
+
const USAGE = `Usage: agent-infra-internal task-lifecycle <N | TASK-id> <intent> --agent <agent> [intent flags] [--dry-run]\n\nIntents: ${lifecycleIntentCatalog.join(', ')}\nrecover-started: --stage <stage> --round <round> --artifact <artifact> --reason <reason>\nOverride: --override-ticket <ticket> --override-target <target> --override-scope <scope>\n`;
|
|
6
6
|
function usageFailure(message) {
|
|
7
7
|
process.stdout.write(`${JSON.stringify({ status: 'failed', changed: false, error: { code: 'LIFECYCLE_PAYLOAD_INVALID', message } })}\n`);
|
|
8
8
|
process.stderr.write(USAGE);
|
|
@@ -13,12 +13,12 @@ function parseFailure(error) {
|
|
|
13
13
|
return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
|
|
14
14
|
}
|
|
15
15
|
async function taskLifecycle(args = []) {
|
|
16
|
-
if (!ensureInternalHandlerRoute('task-lifecycle', args))
|
|
17
|
-
return;
|
|
18
16
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
19
17
|
process.stdout.write(USAGE);
|
|
20
18
|
return;
|
|
21
19
|
}
|
|
20
|
+
if (!ensureInternalHandlerRoute('task-lifecycle', args))
|
|
21
|
+
return;
|
|
22
22
|
let operation;
|
|
23
23
|
try {
|
|
24
24
|
operation = parseTaskControlOperation('task-lifecycle', args);
|
|
@@ -54,7 +54,7 @@ async function taskLifecycle(args = []) {
|
|
|
54
54
|
}
|
|
55
55
|
const result = await dispatchTaskControlOperation(createDirectHostExecutionContext({ repoRoot }), operation);
|
|
56
56
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
57
|
-
if (result.status === 'failed')
|
|
57
|
+
if (result.status === 'failed' || result.status === 'owner-unknown' || result.status === 'conflict')
|
|
58
58
|
process.exitCode = 1;
|
|
59
59
|
}
|
|
60
60
|
export { taskLifecycle };
|
|
@@ -16,10 +16,6 @@ function internalTaskRoutes() {
|
|
|
16
16
|
descriptor('internal', 'sandbox-control', 'execute', 'non-task', 'progress', 'none'),
|
|
17
17
|
descriptor('internal', 'sandbox-control', 'recover', 'conditional', 'recovery', 'delegated'),
|
|
18
18
|
descriptor('internal', 'sandbox-control', 'client', 'conditional', 'progress', 'delegated'),
|
|
19
|
-
descriptor('internal', 'host-control', 'serve', 'non-task', 'progress', 'none'),
|
|
20
|
-
descriptor('internal', 'host-control', 'status', 'non-task', 'diagnostic', 'none'),
|
|
21
|
-
descriptor('internal', 'host-control', 'install', 'non-task', 'progress', 'none'),
|
|
22
|
-
descriptor('internal', 'host-control', 'uninstall', 'non-task', 'progress', 'none'),
|
|
23
19
|
descriptor('internal', 'agent-client', 'next-steps', 'non-task', 'diagnostic', 'none'),
|
|
24
20
|
descriptor('internal', 'agent-client', 'model-selection', 'non-task', 'diagnostic', 'none'),
|
|
25
21
|
descriptor('internal', 'codex-lifecycle', 'preflight', 'non-task', 'diagnostic', 'none'),
|
|
@@ -51,6 +47,7 @@ function internalTaskRoutes() {
|
|
|
51
47
|
descriptor('internal', 'platform-metadata', 'init-milestones', 'non-task', 'remote-write', 'none'),
|
|
52
48
|
descriptor('internal', 'platform-context', 'resolve', 'non-task', 'diagnostic', 'none'),
|
|
53
49
|
descriptor('internal', 'platform-comment', 'list', 'non-task', 'diagnostic', 'none'),
|
|
50
|
+
descriptor('internal', 'platform-comment', 'recover', 'non-task', 'recovery', 'none'),
|
|
54
51
|
descriptor('internal', 'platform-comment', 'owner', 'task-bound', 'diagnostic'),
|
|
55
52
|
descriptor('internal', 'platform-comment', 'backfill', 'task-bound', 'remote-write'),
|
|
56
53
|
descriptor('internal', 'platform-comment', 'sync', 'task-bound', 'remote-write'),
|
|
@@ -96,10 +93,11 @@ function internalTaskRoutes() {
|
|
|
96
93
|
descriptor('internal', 'task-activity', 'pr-review-terminate', 'task-bound', 'progress'),
|
|
97
94
|
descriptor('internal', 'task-artifact', 'inspect', 'task-bound', 'diagnostic'),
|
|
98
95
|
descriptor('internal', 'task-artifact', 'init', 'task-bound', 'artifact-write'),
|
|
99
|
-
descriptor('internal', 'task-artifact', '
|
|
96
|
+
descriptor('internal', 'task-artifact', 'preflight', 'task-bound', 'artifact-write'),
|
|
100
97
|
descriptor('internal', 'task-artifact', 'finalize-local', 'task-bound', 'artifact-write'),
|
|
101
98
|
descriptor('internal', 'task-orchestration', 'status', 'task-bound', 'diagnostic'),
|
|
102
99
|
descriptor('internal', 'task-orchestration', 'progress', 'task-bound', 'progress'),
|
|
100
|
+
descriptor('internal', 'task-review', 'preflight', 'task-bound', 'artifact-write'),
|
|
103
101
|
descriptor('internal', 'task-review', 'finalize-summary', 'task-bound', 'progress'),
|
|
104
102
|
descriptor('internal', 'task-event', 'event', 'task-bound', 'progress'),
|
|
105
103
|
descriptor('internal', 'task-invalidation', 'reconcile', 'task-bound', 'progress'),
|
|
@@ -176,7 +174,7 @@ export const TASK_OPERATION_DESCRIPTORS = Object.freeze([
|
|
|
176
174
|
...PUBLIC_OPERATION_DESCRIPTORS
|
|
177
175
|
]);
|
|
178
176
|
export const INTERNAL_DISPATCHER_ROUTES = Object.freeze([
|
|
179
|
-
'task-create', 'task-qualification', 'sandbox-control', '
|
|
177
|
+
'task-create', 'task-qualification', 'sandbox-control', 'agent-client', 'codex-lifecycle', 'codex-sandbox-controller',
|
|
180
178
|
'git-workflow', 'task-delivery', 'release-workflow', 'platform-release-notes', 'platform-security', 'platform-metadata', 'platform-context',
|
|
181
179
|
'platform-comment', 'platform-issue', 'platform-pr', 'platform-pr-review', 'pr-review-grade',
|
|
182
180
|
'platform-checks', 'task-context', 'task-ledger', 'task-warning', 'task-activity', 'task-artifact',
|
|
@@ -288,28 +286,39 @@ function taskMarkerState(env) {
|
|
|
288
286
|
return 'incomplete';
|
|
289
287
|
}
|
|
290
288
|
export function resolveSandboxControlTransport(env = process.env, options = {}) {
|
|
291
|
-
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
292
|
-
const fixedStatusDir = options.statusMountPath
|
|
293
|
-
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
294
289
|
const hasAnyMarker = TASK_MARKER_KEYS.some((key) => Boolean(env[key]))
|
|
295
290
|
|| Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING)
|
|
296
291
|
|| Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
297
|
-
const
|
|
292
|
+
const fixedStatusDir = options.statusMountPath
|
|
293
|
+
?? SANDBOX_CONTROL_STATUS_MOUNT;
|
|
294
|
+
const fixedStatusProbe = path.isAbsolute(fixedStatusDir)
|
|
298
295
|
? nativeDirectoryProbe(fixedStatusDir) : 'absent';
|
|
299
296
|
if (fixedStatusProbe === 'unknown') {
|
|
300
297
|
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
301
298
|
}
|
|
302
299
|
const fixedStatusMounted = fixedStatusProbe === 'present';
|
|
300
|
+
// A fixed status mount is installed only in a task-bound sandbox. Its
|
|
301
|
+
// presence is not mutable by the sandbox process, so marker removal must
|
|
302
|
+
// never make that process look like a direct host invocation.
|
|
303
|
+
if (!hasAnyMarker) {
|
|
304
|
+
return fixedStatusMounted
|
|
305
|
+
? { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' }
|
|
306
|
+
: { kind: 'direct-host', reasonCode: null };
|
|
307
|
+
}
|
|
308
|
+
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
309
|
+
if (!hasCompleteConfig) {
|
|
310
|
+
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_CONFIGURATION_INCOMPLETE' };
|
|
311
|
+
}
|
|
312
|
+
const configuredStatusDir = env.AGENT_INFRA_CONTROL_STATUS_DIR;
|
|
303
313
|
const configuredStatusProbe = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
304
314
|
? nativeDirectoryProbe(configuredStatusDir) : 'absent';
|
|
305
315
|
if (configuredStatusProbe === 'unknown') {
|
|
306
316
|
return { kind: 'fail-closed', reasonCode: 'SANDBOX_CONTROL_IDENTITY_UNAVAILABLE' };
|
|
307
317
|
}
|
|
308
|
-
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
309
|
-
? configuredStatusDir
|
|
318
|
+
const statusDir = configuredStatusDir && path.isAbsolute(configuredStatusDir)
|
|
319
|
+
? configuredStatusProbe === 'present' ? configuredStatusDir : null
|
|
310
320
|
: fixedStatusMounted ? fixedStatusDir : null;
|
|
311
321
|
const statusMounted = statusDir !== null;
|
|
312
|
-
const hasCompleteConfig = TASK_CONTROL_CONFIG_KEYS.every((key) => Boolean(env[key]));
|
|
313
322
|
if (env.AGENT_INFRA_EXECUTOR_MANIFEST || env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING) {
|
|
314
323
|
return { kind: 'fail-closed', reasonCode: 'TASK_CONTROL_TRANSPORT_INVALID' };
|
|
315
324
|
}
|
|
@@ -16,12 +16,12 @@ function parseFailure(error) {
|
|
|
16
16
|
return message.replace(/^TASK_CONTROL_OPERATION_INVALID: /u, '');
|
|
17
17
|
}
|
|
18
18
|
async function taskOrchestration(args = []) {
|
|
19
|
-
if (!ensureInternalHandlerRoute('task-orchestration', args))
|
|
20
|
-
return;
|
|
21
19
|
if (args[0] === '--help' || args[0] === '-h') {
|
|
22
20
|
process.stdout.write(USAGE);
|
|
23
21
|
return;
|
|
24
22
|
}
|
|
23
|
+
if (!ensureInternalHandlerRoute('task-orchestration', args))
|
|
24
|
+
return;
|
|
25
25
|
const route = internalHandlerRoute('task-orchestration', 'status', args[1] ?? '')
|
|
26
26
|
? 'status'
|
|
27
27
|
: internalHandlerRoute('task-orchestration', 'progress', args[1] ? 'progress' : '')
|