@fitlab-ai/agent-infra 0.9.13 → 0.9.14
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/bin/cli.ts +26 -9
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +42 -21
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +152 -0
- package/dist/lib/internal/codex-lifecycle.js +9 -6
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/platform-checks.js +16 -4
- package/dist/lib/internal/platform-comment.js +16 -4
- package/dist/lib/internal/platform-context.js +4 -1
- package/dist/lib/internal/platform-issue.js +12 -4
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +16 -3
- package/dist/lib/internal/platform-pr.js +24 -10
- package/dist/lib/internal/platform-release-notes.js +6 -3
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +14 -3
- package/dist/lib/internal/release-workflow.js +19 -5
- package/dist/lib/internal/sandbox-control.js +22 -5
- package/dist/lib/internal/task-activity.js +13 -4
- package/dist/lib/internal/task-artifact.js +186 -27
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +48 -15
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +4 -1
- package/dist/lib/internal/task-finalization.js +4 -1
- package/dist/lib/internal/task-invalidation.js +4 -1
- package/dist/lib/internal/task-ledger.js +12 -1
- package/dist/lib/internal/task-lifecycle.js +4 -1
- package/dist/lib/internal/task-operation-registry.js +421 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +10 -3
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +4 -1
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +7 -2
- package/dist/lib/internal/task-verify.js +5 -1
- package/dist/lib/internal/task-warning.js +8 -1
- package/dist/lib/platform/comment-safety.js +226 -0
- package/dist/lib/platform/github-provider.js +233 -6
- package/dist/lib/platform/issue-comments.js +146 -34
- package/dist/lib/platform/pr-change-report.js +6 -12
- package/dist/lib/platform/pr-summary.js +28 -7
- package/dist/lib/platform/provider-contract.d.ts +67 -1
- package/dist/lib/platform/provider-contract.js +6 -2
- package/dist/lib/platform/provider-validation.js +76 -3
- package/dist/lib/platform/pull-requests.js +115 -170
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/verification-sync.js +33 -26
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/run/index.js +5 -0
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +32 -6
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +827 -120
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/client.js +83 -9
- package/dist/lib/sandbox/control/completed-reentry.js +85 -0
- package/dist/lib/sandbox/control/container-identity.js +28 -31
- package/dist/lib/sandbox/control/executor.js +149 -14
- package/dist/lib/sandbox/control/lifecycle.js +621 -127
- package/dist/lib/sandbox/control/native-file-lock.js +177 -0
- package/dist/lib/sandbox/control/server.js +305 -35
- package/dist/lib/sandbox/control/state.js +5 -3
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +5 -0
- package/dist/lib/sandbox/recovery.js +21 -1
- package/dist/lib/sandbox/workspace-view.js +2 -1
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/task/artifact-lifecycle.js +42 -13
- package/dist/lib/task/artifact-operations.js +520 -0
- package/dist/lib/task/artifact-repair-intent.js +59 -0
- package/dist/lib/task/artifact-schema.js +135 -0
- package/dist/lib/task/capabilities.js +72 -1
- package/dist/lib/task/control-authority.js +4 -0
- package/dist/lib/task/create-service.js +103 -4
- package/dist/lib/task/create.js +25 -0
- package/dist/lib/task/events.js +186 -67
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/local-artifact-finalization.js +75 -150
- package/dist/lib/task/orchestration.js +192 -52
- package/dist/lib/task/qualification-audit.js +490 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +22 -23
- package/dist/lib/task/review-finalization.js +121 -4
- package/dist/lib/task/sections.js +6 -4
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +54 -4
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +140 -0
- package/lib/internal/codex-lifecycle.ts +8 -6
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/platform-checks.ts +12 -4
- package/lib/internal/platform-comment.ts +12 -4
- package/lib/internal/platform-context.ts +3 -1
- package/lib/internal/platform-issue.ts +11 -4
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +14 -3
- package/lib/internal/platform-pr.ts +23 -10
- package/lib/internal/platform-release-notes.ts +5 -3
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +10 -3
- package/lib/internal/release-workflow.ts +17 -5
- package/lib/internal/sandbox-control.ts +19 -5
- package/lib/internal/task-activity.ts +12 -4
- package/lib/internal/task-artifact.ts +164 -25
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +76 -15
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +3 -1
- package/lib/internal/task-finalization.ts +3 -1
- package/lib/internal/task-invalidation.ts +3 -1
- package/lib/internal/task-ledger.ts +11 -1
- package/lib/internal/task-lifecycle.ts +3 -1
- package/lib/internal/task-operation-registry.ts +482 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +9 -3
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +3 -1
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +6 -2
- package/lib/internal/task-verify.ts +4 -1
- package/lib/internal/task-warning.ts +7 -1
- package/lib/platform/comment-safety.ts +250 -0
- package/lib/platform/github-provider.ts +229 -7
- package/lib/platform/issue-comments.ts +160 -35
- package/lib/platform/pr-change-report.ts +6 -12
- package/lib/platform/pr-summary.ts +27 -6
- package/lib/platform/provider-contract.ts +78 -3
- package/lib/platform/provider-validation.ts +85 -3
- package/lib/platform/pull-requests.ts +126 -178
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/verification-sync.ts +32 -28
- package/lib/process-data/index.ts +7 -0
- package/lib/run/index.ts +5 -0
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +46 -6
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +1000 -87
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/client.ts +103 -9
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +177 -12
- package/lib/sandbox/control/lifecycle.ts +629 -17
- package/lib/sandbox/control/native-file-lock.ts +209 -0
- package/lib/sandbox/control/protocol.ts +5 -2
- package/lib/sandbox/control/server.ts +319 -37
- package/lib/sandbox/control/state.ts +6 -3
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +7 -0
- package/lib/sandbox/recovery.ts +21 -1
- package/lib/sandbox/workspace-view.ts +9 -3
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/task/artifact-lifecycle.ts +38 -16
- package/lib/task/artifact-operations.ts +626 -0
- package/lib/task/artifact-repair-intent.ts +71 -0
- package/lib/task/artifact-schema.ts +192 -0
- package/lib/task/capabilities.ts +70 -1
- package/lib/task/control-authority.ts +11 -1
- package/lib/task/create-service.ts +131 -6
- package/lib/task/create.ts +27 -0
- package/lib/task/events.ts +161 -65
- package/lib/task/index.ts +16 -3
- package/lib/task/local-artifact-finalization.ts +87 -218
- package/lib/task/orchestration.ts +172 -49
- package/lib/task/qualification-audit.ts +599 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -22
- package/lib/task/review-finalization.ts +133 -4
- package/lib/task/sections.ts +6 -4
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +55 -4
- package/package.json +6 -1
- package/templates/.agents/rules/README.en.md +2 -0
- package/templates/.agents/rules/README.zh-CN.md +2 -0
- package/templates/.agents/rules/decision-qualification.en.md +22 -0
- package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
- package/templates/.agents/rules/evidence-reporting.en.md +28 -0
- package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
- package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
- package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
- package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
- package/templates/.agents/rules/security-alerts.en.md +8 -8
- package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
- package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
- package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
- package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
- package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
- package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
- package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -1
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +9 -22
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +9 -22
- package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
- package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
- package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
- package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
- package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
- package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
- package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
- package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -1
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +13 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/templates/task.en.md +6 -0
- package/templates/.agents/templates/task.zh-CN.md +6 -0
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
|
@@ -7,6 +7,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.js';
|
|
|
7
7
|
import { resolveTools, toolConfigDirCandidates } from '../tools.js';
|
|
8
8
|
import { detectEngine } from '../engine.js';
|
|
9
9
|
import { resolveSandboxTarget } from '../workspace-identity.js';
|
|
10
|
+
import { readSandboxControlStatusForRow } from './control-status.js';
|
|
10
11
|
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
|
|
11
12
|
|
|
12
13
|
Shows one sandbox's worktree path and per-tool state paths (Claude Code,
|
|
@@ -62,6 +63,21 @@ export function show(args = []) {
|
|
|
62
63
|
p.log.step('Workspace identity');
|
|
63
64
|
process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
|
|
64
65
|
process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
|
|
66
|
+
p.log.step('Control status');
|
|
67
|
+
const controlStatus = container
|
|
68
|
+
? readSandboxControlStatusForRow(config, container)
|
|
69
|
+
: null;
|
|
70
|
+
if (!controlStatus) {
|
|
71
|
+
p.log.warn(' Control status unavailable');
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
process.stdout.write(` Health: ${controlStatus.state}\n`);
|
|
75
|
+
process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
|
|
76
|
+
process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
|
|
77
|
+
if (controlStatus.taskView.reasonCode) {
|
|
78
|
+
process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
65
81
|
for (const tool of detail.toolStates) {
|
|
66
82
|
p.log.step(`${tool.name} state`);
|
|
67
83
|
if (tool.entries.length === 0) {
|
|
@@ -4,7 +4,54 @@ import { randomUUID } from 'node:crypto';
|
|
|
4
4
|
import { SANDBOX_CONTROL_ADMISSION_WINDOW_MS, SANDBOX_CONTROL_MAX_BYTES, SANDBOX_CONTROL_STATUS_STALE_MS, isSandboxControlFamily } from './protocol.js';
|
|
5
5
|
import { normalizeAgentToken } from '../../agent-clients/tokens.js';
|
|
6
6
|
import { readSandboxControlPayload, readSandboxControlStatus } from './state.js';
|
|
7
|
+
import { accessSandboxTaskView, taskViewFromStatus } from './task-view.js';
|
|
7
8
|
const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
|
|
9
|
+
export function classifySandboxControlEnvironment(env = process.env) {
|
|
10
|
+
const controlKeys = [
|
|
11
|
+
'AGENT_INFRA_CONTROL_TOKEN',
|
|
12
|
+
'AGENT_INFRA_CONTROL_GENERATION',
|
|
13
|
+
'AGENT_INFRA_CONTROL_DIR',
|
|
14
|
+
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
15
|
+
];
|
|
16
|
+
const hasControlMarker = controlKeys.some((key) => Boolean(env[key]));
|
|
17
|
+
const hasTaskIdentity = Boolean(env.AGENT_INFRA_TASK_ID);
|
|
18
|
+
const hasRuntime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
|
|
19
|
+
const hasExecutorMarker = Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
20
|
+
const hasControllerBinding = Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING);
|
|
21
|
+
const hasAnyMarker = hasControlMarker || hasTaskIdentity || hasRuntime || hasExecutorMarker || hasControllerBinding;
|
|
22
|
+
const hasCompleteControl = controlKeys.every((key) => Boolean(env[key]));
|
|
23
|
+
if (!hasAnyMarker)
|
|
24
|
+
return { kind: 'direct' };
|
|
25
|
+
if (hasExecutorMarker) {
|
|
26
|
+
return {
|
|
27
|
+
kind: 'invalid',
|
|
28
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
29
|
+
message: 'executor context is only valid for sandbox-control execute'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (hasControllerBinding) {
|
|
33
|
+
return {
|
|
34
|
+
kind: 'invalid',
|
|
35
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
36
|
+
message: 'sandbox client control configuration is incomplete or conflicting'
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (!hasCompleteControl) {
|
|
40
|
+
return {
|
|
41
|
+
kind: 'invalid',
|
|
42
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
43
|
+
message: 'sandbox client control configuration is incomplete or conflicting'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (hasTaskIdentity !== hasRuntime) {
|
|
47
|
+
return {
|
|
48
|
+
kind: 'invalid',
|
|
49
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
50
|
+
message: 'sandbox task identity and runtime binding must be provided together'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return { kind: 'controlled', mode: hasTaskIdentity ? 'task-bound' : 'branch-only' };
|
|
54
|
+
}
|
|
8
55
|
export class SandboxControlClientError extends Error {
|
|
9
56
|
detail;
|
|
10
57
|
accepted;
|
|
@@ -23,7 +70,12 @@ function sleep(milliseconds) {
|
|
|
23
70
|
function clientError(code, message, retryable, accepted = false, requestId = null) {
|
|
24
71
|
throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
|
|
25
72
|
}
|
|
26
|
-
function
|
|
73
|
+
function hasTaskBoundMarker(env = process.env) {
|
|
74
|
+
return Boolean(env.AGENT_INFRA_TASK_ID)
|
|
75
|
+
&& Boolean(env.AGENT_INFRA_CONTROL_TOKEN || env.AGENT_INFRA_CONTROL_GENERATION
|
|
76
|
+
|| env.AGENT_INFRA_CONTROL_STATUS_DIR || env.AGENT_INFRA_RUNTIME_DIR || env.AGENT_INFRA_CONTROL_DIR);
|
|
77
|
+
}
|
|
78
|
+
function preflight(statusDir, generation, taskViewEffect = null, now = Date.now(), env = process.env) {
|
|
27
79
|
let status;
|
|
28
80
|
try {
|
|
29
81
|
status = readSandboxControlStatus(statusDir);
|
|
@@ -45,8 +97,25 @@ function preflight(statusDir, generation, now = Date.now()) {
|
|
|
45
97
|
}
|
|
46
98
|
if (status.state !== 'healthy')
|
|
47
99
|
clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
|
|
100
|
+
if (taskViewEffect && hasTaskBoundMarker(env)) {
|
|
101
|
+
const view = taskViewFromStatus(status);
|
|
102
|
+
if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
|
|
103
|
+
clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
|
|
104
|
+
}
|
|
105
|
+
const access = accessSandboxTaskView(view, taskViewEffect);
|
|
106
|
+
if (!access.allowed) {
|
|
107
|
+
clientError(access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED', access.message ?? 'task view denied', false);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function taskViewEffectForRequest(request) {
|
|
112
|
+
if (request.family === 'task-lifecycle' || request.family === 'task-finalization')
|
|
113
|
+
return 'progress';
|
|
114
|
+
if (request.family === 'task-orchestration')
|
|
115
|
+
return request.args[1] === 'status' ? 'diagnostic' : 'progress';
|
|
116
|
+
return null;
|
|
48
117
|
}
|
|
49
|
-
function parseResponse(raw, id) {
|
|
118
|
+
function parseResponse(raw, id, accepted = false) {
|
|
50
119
|
const response = JSON.parse(raw);
|
|
51
120
|
if (response.version !== 2 || response.id !== id
|
|
52
121
|
|| !['completed', 'rejected'].includes(response.phase)
|
|
@@ -54,12 +123,12 @@ function parseResponse(raw, id) {
|
|
|
54
123
|
|| (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
|
|
55
124
|
|| (response.outputState === 'available' && !response.payload)
|
|
56
125
|
|| (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
|
|
57
|
-
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false,
|
|
126
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
|
|
58
127
|
}
|
|
59
128
|
return response;
|
|
60
129
|
}
|
|
61
|
-
function readPublishedResponse(raw, id, channelDir) {
|
|
62
|
-
const response = parseResponse(raw, id);
|
|
130
|
+
function readPublishedResponse(raw, id, channelDir, accepted = false) {
|
|
131
|
+
const response = parseResponse(raw, id, accepted);
|
|
63
132
|
if (response.outputState !== 'available')
|
|
64
133
|
return response;
|
|
65
134
|
const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
|
|
@@ -92,7 +161,8 @@ function cancelPendingRequest(requestPath) {
|
|
|
92
161
|
function exchangeSandboxControl(request, params) {
|
|
93
162
|
const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
|
|
94
163
|
const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
|
|
95
|
-
|
|
164
|
+
const taskViewEffect = taskViewEffectForRequest(request);
|
|
165
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
96
166
|
const encoded = `${JSON.stringify(request)}\n`;
|
|
97
167
|
if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
|
|
98
168
|
clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
|
|
@@ -127,7 +197,9 @@ function exchangeSandboxControl(request, params) {
|
|
|
127
197
|
}
|
|
128
198
|
let response;
|
|
129
199
|
try {
|
|
130
|
-
|
|
200
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
201
|
+
accepted = true;
|
|
202
|
+
response = readPublishedResponse(raw, request.id, channelDir, accepted);
|
|
131
203
|
malformedResponseRaw = null;
|
|
132
204
|
}
|
|
133
205
|
catch (error) {
|
|
@@ -153,7 +225,7 @@ function exchangeSandboxControl(request, params) {
|
|
|
153
225
|
}
|
|
154
226
|
if (!accepted) {
|
|
155
227
|
try {
|
|
156
|
-
preflight(statusDir, request.generation);
|
|
228
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
157
229
|
}
|
|
158
230
|
catch (error) {
|
|
159
231
|
if (cancelPendingRequest(requestPath))
|
|
@@ -196,7 +268,9 @@ export function recoverSandboxControl(requestId, params = {}) {
|
|
|
196
268
|
}
|
|
197
269
|
let response;
|
|
198
270
|
try {
|
|
199
|
-
|
|
271
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
272
|
+
accepted = true;
|
|
273
|
+
response = readPublishedResponse(raw, requestId, channelDir, accepted);
|
|
200
274
|
malformedResponseRaw = null;
|
|
201
275
|
}
|
|
202
276
|
catch (error) {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveTaskWorkspace } from '../task-resolver.js';
|
|
4
|
+
import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
|
|
5
|
+
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
|
|
6
|
+
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
7
|
+
import { inspectSandboxControlContainer } from './container-identity.js';
|
|
8
|
+
import { atomicWriteJson, readSandboxControlStatus } from './state.js';
|
|
9
|
+
import { taskViewForManifest } from './task-view.js';
|
|
10
|
+
function evidencePath(manifest) {
|
|
11
|
+
return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
|
|
12
|
+
}
|
|
13
|
+
function invalid() {
|
|
14
|
+
return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
|
|
15
|
+
}
|
|
16
|
+
export async function prepareCompletedReentry(manifest, inspect = inspectSandboxControlContainer) {
|
|
17
|
+
if (manifest.mode !== 'task-bound' || !manifest.taskId)
|
|
18
|
+
throw invalid();
|
|
19
|
+
assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
|
|
20
|
+
const container = await inspect(manifest);
|
|
21
|
+
if (container.state !== 'found' || container.id !== manifest.containerIdentity.id)
|
|
22
|
+
throw invalid();
|
|
23
|
+
const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
|
|
24
|
+
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
|
|
25
|
+
if (task.state !== 'completed' || task.branch !== manifest.branch
|
|
26
|
+
|| metadata.id !== manifest.taskId || metadata.status !== 'completed')
|
|
27
|
+
throw invalid();
|
|
28
|
+
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
29
|
+
if (!receipt || receipt.taskId !== manifest.taskId)
|
|
30
|
+
throw invalid();
|
|
31
|
+
const view = taskViewForManifest({ ...manifest, receipt });
|
|
32
|
+
if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt)
|
|
33
|
+
throw invalid();
|
|
34
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
35
|
+
const previous = status.taskView;
|
|
36
|
+
if (status.generation !== manifest.generation || status.activeRequestId !== null
|
|
37
|
+
|| previous.taskId !== manifest.taskId || !previous.receipt
|
|
38
|
+
|| previous.receipt.receiptId !== view.receipt.receiptId
|
|
39
|
+
|| previous.receipt.generation !== view.receipt.generation
|
|
40
|
+
|| previous.receipt.requestId !== view.receipt.requestId
|
|
41
|
+
|| previous.receipt.revision > view.receipt.revision)
|
|
42
|
+
throw invalid();
|
|
43
|
+
const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
|
|
44
|
+
const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
|
|
45
|
+
if (source !== path.join(completedRoot, manifest.taskId))
|
|
46
|
+
throw invalid();
|
|
47
|
+
const stat = fs.statSync(source, { bigint: true });
|
|
48
|
+
return {
|
|
49
|
+
taskId: manifest.taskId, generation: manifest.generation,
|
|
50
|
+
containerId: container.id, branch: manifest.branch,
|
|
51
|
+
worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
|
|
52
|
+
source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export function publishCompletedReentry(manifest, evidence) {
|
|
56
|
+
atomicWriteJson(evidencePath(manifest), evidence);
|
|
57
|
+
}
|
|
58
|
+
export async function completedReentryView(manifest, inspect = inspectSandboxControlContainer) {
|
|
59
|
+
const file = evidencePath(manifest);
|
|
60
|
+
if (!fs.existsSync(file))
|
|
61
|
+
return null;
|
|
62
|
+
const evidence = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
63
|
+
const current = await prepareCompletedReentry(manifest, inspect);
|
|
64
|
+
if (JSON.stringify(evidence) !== JSON.stringify(current))
|
|
65
|
+
throw invalid();
|
|
66
|
+
return {
|
|
67
|
+
state: 'current', taskId: current.taskId, observedSource: 'completed',
|
|
68
|
+
receipt: current.receipt, reasonCode: null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export async function waitForCompletedReentry(manifest, evidence) {
|
|
72
|
+
const deadline = Date.now() + 10_000;
|
|
73
|
+
while (Date.now() < deadline) {
|
|
74
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
75
|
+
if (status.generation !== evidence.generation)
|
|
76
|
+
throw invalid();
|
|
77
|
+
if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
|
|
78
|
+
&& status.taskView.taskId === evidence.taskId
|
|
79
|
+
&& JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt))
|
|
80
|
+
return;
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
82
|
+
}
|
|
83
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=completed-reentry.js.map
|
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return /(?:no such (?:container|object)|container .* not found|cannot find container)/i.test(message);
|
|
5
|
-
}
|
|
1
|
+
import { runProbe } from '../shell.js';
|
|
2
|
+
import { captureSandboxAuthority, isSandboxAuthorityEvidence, verifySandboxAuthority } from '../engines/authority.js';
|
|
3
|
+
import { discoverExactContainer } from '../engines/docker-exact-discovery.js';
|
|
6
4
|
export function classifySandboxContainerInspection(expected, inspection) {
|
|
7
5
|
if ('notFound' in inspection)
|
|
8
6
|
return { state: 'absent', id: expected.id };
|
|
9
7
|
if ('error' in inspection) {
|
|
10
|
-
return
|
|
11
|
-
? { state: 'absent', id: expected.id }
|
|
12
|
-
: { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
|
|
8
|
+
return { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
|
|
13
9
|
}
|
|
14
10
|
const response = inspection;
|
|
15
11
|
if (typeof response.id !== 'string' || response.id !== expected.id
|
|
@@ -30,32 +26,33 @@ export function classifySandboxContainerInspection(expected, inspection) {
|
|
|
30
26
|
};
|
|
31
27
|
}
|
|
32
28
|
export async function inspectSandboxControlContainer(manifest, options = {}) {
|
|
29
|
+
if (!isSandboxAuthorityEvidence(manifest.authorityEvidence)) {
|
|
30
|
+
return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
|
|
31
|
+
}
|
|
33
32
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '';
|
|
44
|
-
return classifySandboxContainerInspection(manifest.containerIdentity, {
|
|
45
|
-
error: new Error(stderr || `container inspection exited with ${result.status ?? 'unknown'}`)
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
raw = typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '';
|
|
33
|
+
const authority = captureSandboxAuthority(manifest.engine, {
|
|
34
|
+
probe: options.probe,
|
|
35
|
+
timeoutMs: options.timeoutMs,
|
|
36
|
+
lockDomain: manifest.authorityEvidence.lockDomain,
|
|
37
|
+
route: manifest.authorityEvidence
|
|
38
|
+
});
|
|
39
|
+
const authorityState = verifySandboxAuthority(manifest.authorityEvidence, authority);
|
|
40
|
+
if (authorityState.state !== 'verified') {
|
|
41
|
+
return { state: 'unknown', reason: authorityState.reason ?? 'SANDBOX_AUTHORITY_UNAVAILABLE' };
|
|
49
42
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return classifySandboxContainerInspection(manifest.containerIdentity, {
|
|
55
|
-
id: value.Id,
|
|
56
|
-
running: value.State?.Running,
|
|
57
|
-
labels: value.Config?.Labels
|
|
43
|
+
const discovered = discoverExactContainer(manifest.engine, manifest.containerIdentity.id, {
|
|
44
|
+
probe: options.probe,
|
|
45
|
+
timeoutMs: options.timeoutMs,
|
|
46
|
+
authority: manifest.authorityEvidence
|
|
58
47
|
});
|
|
48
|
+
if (discovered.state === 'absent')
|
|
49
|
+
return { state: 'absent', id: discovered.id };
|
|
50
|
+
if (discovered.state === 'conflict') {
|
|
51
|
+
return { state: 'unknown', reason: `SANDBOX_CONTROL_CONTAINER_CONFLICT: ${discovered.reason}` };
|
|
52
|
+
}
|
|
53
|
+
if (discovered.state === 'unknown')
|
|
54
|
+
return discovered;
|
|
55
|
+
return classifySandboxContainerInspection(manifest.containerIdentity, discovered);
|
|
59
56
|
}
|
|
60
57
|
catch (error) {
|
|
61
58
|
return classifySandboxContainerInspection(manifest.containerIdentity, { error });
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
5
5
|
import { getProcessStartTime } from '../../server/process-state.js';
|
|
6
|
-
import { createTask } from '../../task/create-service.js';
|
|
6
|
+
import { createTask, projectTaskCreateResult, taskCreateExitCode, taskCreateFailure } from '../../task/create-service.js';
|
|
7
7
|
import { applyTaskFinalization } from '../../task/finalization.js';
|
|
8
8
|
import { bindSandboxControlTask, validateSandboxControlRequest } from './protocol.js';
|
|
9
|
-
import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
|
|
9
|
+
import { appendSandboxControlAudit, atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
|
|
10
10
|
import { createSandboxExecutorExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../../task/control-authority.js';
|
|
11
11
|
import { assertSandboxControlBrokerOwner, readSandboxControlManifest } from './lifecycle.js';
|
|
12
12
|
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
@@ -30,6 +30,57 @@ export function assertSandboxControlExecutorAuthority(manifest, gateOwner) {
|
|
|
30
30
|
function safeEnv(env) {
|
|
31
31
|
return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
|
|
32
32
|
}
|
|
33
|
+
function safeRealpath(value) {
|
|
34
|
+
try {
|
|
35
|
+
return fs.realpathSync.native(value);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function appendExecutorAudit(manifest, event, fields = {}) {
|
|
42
|
+
try {
|
|
43
|
+
appendSandboxControlAudit(manifest, event, { source: 'executor', ...fields });
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Diagnostics must never change the control protocol.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function requestAuditFields(manifest, manifestPath, request) {
|
|
50
|
+
const args = 'args' in request ? request.args : [];
|
|
51
|
+
const encodedArgs = JSON.stringify(args);
|
|
52
|
+
return {
|
|
53
|
+
requestId: request.id,
|
|
54
|
+
requestFamily: request.family,
|
|
55
|
+
sandboxTaskId: manifest.taskId,
|
|
56
|
+
requestGeneration: request.generation,
|
|
57
|
+
requestIssuedAt: request.issuedAt,
|
|
58
|
+
requestExpiresAt: request.expiresAt,
|
|
59
|
+
requestArgCount: args.length,
|
|
60
|
+
requestArgsSha256: createHash('sha256').update(encodedArgs, 'utf8').digest('hex'),
|
|
61
|
+
requestTaskRef: args[0] ?? null,
|
|
62
|
+
requestCommand: args[1] ?? null,
|
|
63
|
+
controllerProofPresent: request.controllerProof !== null,
|
|
64
|
+
hostCwd: process.cwd(),
|
|
65
|
+
manifestPath,
|
|
66
|
+
manifestPathRealpath: safeRealpath(manifestPath),
|
|
67
|
+
repoRoot: manifest.repoRoot,
|
|
68
|
+
repoRootRealpath: safeRealpath(manifest.repoRoot),
|
|
69
|
+
worktreeRoot: manifest.worktreeRoot,
|
|
70
|
+
worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
|
|
71
|
+
runtimeDir: manifest.runtimeDir,
|
|
72
|
+
runtimeDirRealpath: safeRealpath(manifest.runtimeDir)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function resultAuditFields(result) {
|
|
76
|
+
return {
|
|
77
|
+
exitCode: result.exitCode,
|
|
78
|
+
stdoutBytes: Buffer.byteLength(result.stdout, 'utf8'),
|
|
79
|
+
stderrBytes: Buffer.byteLength(result.stderr, 'utf8'),
|
|
80
|
+
stdoutSha256: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
|
|
81
|
+
stderrSha256: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
|
|
82
|
+
};
|
|
83
|
+
}
|
|
33
84
|
export function nodeEntryArgs(entry, args) {
|
|
34
85
|
return path.extname(entry) === '.ts'
|
|
35
86
|
? ['--experimental-strip-types', '--no-warnings', entry, ...args]
|
|
@@ -174,6 +225,20 @@ function lifecycleFailure(code, message) {
|
|
|
174
225
|
stderr: ''
|
|
175
226
|
};
|
|
176
227
|
}
|
|
228
|
+
function taskCreateExecutionResult(result) {
|
|
229
|
+
return {
|
|
230
|
+
exitCode: taskCreateExitCode(result),
|
|
231
|
+
stdout: `${JSON.stringify(result)}\n`,
|
|
232
|
+
stderr: ''
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function taskCreateExecutionFailure(requestId, code, message) {
|
|
236
|
+
return taskCreateExecutionResult(taskCreateFailure({ code, message, retryable: false }, {
|
|
237
|
+
requestId,
|
|
238
|
+
accepted: true,
|
|
239
|
+
recovery: 'inspect-domain-state'
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
177
242
|
function finalizationResult(result) {
|
|
178
243
|
const payload = {
|
|
179
244
|
version: 1,
|
|
@@ -189,14 +254,21 @@ function finalizationResult(result) {
|
|
|
189
254
|
stderr: ''
|
|
190
255
|
};
|
|
191
256
|
}
|
|
192
|
-
|
|
257
|
+
async function executeRequestInner(manifest, manifestPath, request, options = {}) {
|
|
193
258
|
if (request.family === 'task-create') {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
259
|
+
try {
|
|
260
|
+
const result = await createTask(request.candidate, { repoRoot: manifest.repoRoot });
|
|
261
|
+
return taskCreateExecutionResult(projectTaskCreateResult(result, {
|
|
262
|
+
requestId: request.id,
|
|
263
|
+
accepted: true,
|
|
264
|
+
recovery: 'none'
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
269
|
+
const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'TASK_CREATE_EXECUTION_FAILED';
|
|
270
|
+
return taskCreateExecutionFailure(request.id, code, detail);
|
|
271
|
+
}
|
|
200
272
|
}
|
|
201
273
|
if (request.family === 'codex-controller') {
|
|
202
274
|
try {
|
|
@@ -239,6 +311,16 @@ export async function executeRequest(manifest, manifestPath, request, options =
|
|
|
239
311
|
return { exitCode: 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
240
312
|
}
|
|
241
313
|
catch (error) {
|
|
314
|
+
const errorCode = error instanceof CodexControllerRegistrationError
|
|
315
|
+
? error.code
|
|
316
|
+
: /^([A-Z][A-Z0-9_]+)/u.exec(error instanceof Error ? error.message : String(error))?.[1]
|
|
317
|
+
?? 'CODEX_SANDBOX_CONTROLLER_FAILED';
|
|
318
|
+
appendExecutorAudit(manifest, 'controller-operation-failed', {
|
|
319
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
320
|
+
controllerCommand: request.command,
|
|
321
|
+
errorCode,
|
|
322
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
323
|
+
});
|
|
242
324
|
return controllerFailure(error);
|
|
243
325
|
}
|
|
244
326
|
}
|
|
@@ -300,6 +382,14 @@ export async function executeRequest(manifest, manifestPath, request, options =
|
|
|
300
382
|
return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'Controller proof is only accepted for canonical Codex prepare');
|
|
301
383
|
}
|
|
302
384
|
}
|
|
385
|
+
const diagnosticLog = (event, fields) => {
|
|
386
|
+
appendExecutorAudit(manifest, event.startsWith('orchestration-') ? event : `orchestration-${event}`, {
|
|
387
|
+
requestId: request.id,
|
|
388
|
+
requestFamily: request.family,
|
|
389
|
+
sandboxTaskId: manifest.taskId,
|
|
390
|
+
...fields
|
|
391
|
+
});
|
|
392
|
+
};
|
|
303
393
|
const context = createSandboxExecutorExecutionContext({
|
|
304
394
|
repoRoot: manifest.repoRoot,
|
|
305
395
|
worktreeRoot: manifest.worktreeRoot,
|
|
@@ -308,6 +398,7 @@ export async function executeRequest(manifest, manifestPath, request, options =
|
|
|
308
398
|
generation: manifest.generation,
|
|
309
399
|
manifestPath,
|
|
310
400
|
requestId: request.id,
|
|
401
|
+
diagnosticLog,
|
|
311
402
|
...(controllerBinding ? { controllerBinding } : {})
|
|
312
403
|
});
|
|
313
404
|
const format = (value) => {
|
|
@@ -327,6 +418,29 @@ export async function executeRequest(manifest, manifestPath, request, options =
|
|
|
327
418
|
throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
|
|
328
419
|
return format(await dispatchTaskControlOperation(context, operation));
|
|
329
420
|
}
|
|
421
|
+
export async function executeRequest(manifest, manifestPath, request, options = {}) {
|
|
422
|
+
const fields = requestAuditFields(manifest, manifestPath, request);
|
|
423
|
+
appendExecutorAudit(manifest, 'executor-request-start', fields);
|
|
424
|
+
try {
|
|
425
|
+
const result = await executeRequestInner(manifest, manifestPath, request, options);
|
|
426
|
+
appendExecutorAudit(manifest, 'executor-request-finished', {
|
|
427
|
+
requestId: request.id,
|
|
428
|
+
requestFamily: request.family,
|
|
429
|
+
sandboxTaskId: manifest.taskId,
|
|
430
|
+
...resultAuditFields(result)
|
|
431
|
+
});
|
|
432
|
+
return result;
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
appendExecutorAudit(manifest, 'executor-request-failed', {
|
|
436
|
+
requestId: request.id,
|
|
437
|
+
requestFamily: request.family,
|
|
438
|
+
sandboxTaskId: manifest.taskId,
|
|
439
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
440
|
+
});
|
|
441
|
+
throw error;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
330
444
|
export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
331
445
|
const gateOwner = await waitForGate(nonce);
|
|
332
446
|
process.disconnect?.();
|
|
@@ -335,14 +449,29 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
335
449
|
throw new Error('SANDBOX_CONTROL_EXECUTOR_MANIFEST_MISSING');
|
|
336
450
|
const manifest = readSandboxControlManifest(manifestPath);
|
|
337
451
|
const root = fs.realpathSync.native(process.cwd());
|
|
338
|
-
|
|
452
|
+
const expectedRoot = safeRealpath(manifest.repoRoot);
|
|
453
|
+
if (expectedRoot !== root) {
|
|
454
|
+
appendExecutorAudit(manifest, 'executor-root-validation-failed', {
|
|
455
|
+
hostCwd: process.cwd(),
|
|
456
|
+
observedRoot: root,
|
|
457
|
+
expectedRoot
|
|
458
|
+
});
|
|
339
459
|
throw new Error('SANDBOX_CONTROL_EXECUTOR_ROOT_INVALID');
|
|
460
|
+
}
|
|
340
461
|
const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8'));
|
|
341
462
|
const request = validateSandboxControlRequest(raw, manifest);
|
|
463
|
+
appendExecutorAudit(manifest, 'executor-request-validated', requestAuditFields(manifest, manifestPath, request));
|
|
342
464
|
const requestDirectory = path.resolve(path.dirname(requestPath));
|
|
343
465
|
const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
|
|
344
466
|
const channelRequestDirectory = path.resolve(path.join(manifest.channelDir, 'requests'));
|
|
345
467
|
if (requestDirectory !== processingDirectory && requestDirectory !== channelRequestDirectory) {
|
|
468
|
+
appendExecutorAudit(manifest, 'executor-request-path-validation-failed', {
|
|
469
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
470
|
+
requestPath,
|
|
471
|
+
requestDirectory,
|
|
472
|
+
processingDirectory,
|
|
473
|
+
channelRequestDirectory
|
|
474
|
+
});
|
|
346
475
|
throw new Error('SANDBOX_CONTROL_EXECUTOR_REQUEST_INVALID');
|
|
347
476
|
}
|
|
348
477
|
let result;
|
|
@@ -351,6 +480,10 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
351
480
|
result = await executeRequest(manifest, manifestPath, request);
|
|
352
481
|
}
|
|
353
482
|
catch (error) {
|
|
483
|
+
appendExecutorAudit(manifest, 'executor-authority-or-dispatch-failed', {
|
|
484
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
485
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
486
|
+
});
|
|
354
487
|
const detail = error instanceof Error ? error.message : String(error);
|
|
355
488
|
const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'SANDBOX_CONTROL_EXECUTOR_FAILED';
|
|
356
489
|
result = request.family === 'task-finalization'
|
|
@@ -362,9 +495,11 @@ export async function runSandboxControlExecutor(requestPath, nonce) {
|
|
|
362
495
|
})}\n`,
|
|
363
496
|
stderr: ''
|
|
364
497
|
}
|
|
365
|
-
: request.family === 'task-
|
|
366
|
-
?
|
|
367
|
-
:
|
|
498
|
+
: request.family === 'task-create'
|
|
499
|
+
? taskCreateExecutionFailure(request.id, code, detail)
|
|
500
|
+
: request.family === 'task-lifecycle'
|
|
501
|
+
? lifecycleFailure(code, detail)
|
|
502
|
+
: orchestrationFailure(code, detail);
|
|
368
503
|
}
|
|
369
504
|
process.stdout.write(result.stdout);
|
|
370
505
|
process.stderr.write(result.stderr);
|