@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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { completedReentryView } from './completed-reentry.js';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { createHash, randomUUID } from 'node:crypto';
|
|
@@ -5,13 +6,66 @@ import { getProcessStartTime } from '../../server/process-state.js';
|
|
|
5
6
|
import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
|
|
6
7
|
import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES } from './protocol.js';
|
|
7
8
|
import { prepareSandboxControlExecution } from './executor.js';
|
|
8
|
-
import { appendSandboxControlAudit, atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, terminateSandboxControlExecution, writeSandboxControlStatus } from './state.js';
|
|
9
|
+
import { appendSandboxControlAudit, atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
|
|
9
10
|
import { validateSandboxControlRequest } from './protocol.js';
|
|
10
11
|
import { inspectSandboxControlContainer } from './container-identity.js';
|
|
11
12
|
import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
|
|
12
13
|
import { nextSandboxControlBackoff } from './timing.js';
|
|
13
14
|
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
14
15
|
import { resolveTaskRef } from '../../task/resolve-ref.js';
|
|
16
|
+
import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
|
|
17
|
+
import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
|
|
18
|
+
function safeRealpath(value) {
|
|
19
|
+
try {
|
|
20
|
+
return fs.realpathSync.native(value);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function appendBrokerAudit(manifest, event, fields = {}) {
|
|
27
|
+
try {
|
|
28
|
+
appendSandboxControlAudit(manifest, event, { source: 'broker', ...fields });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Diagnostics must never change the control protocol.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function requestAuditFields(manifest, manifestPath, request) {
|
|
35
|
+
const args = 'args' in request ? request.args : [];
|
|
36
|
+
const encodedArgs = JSON.stringify(args);
|
|
37
|
+
return {
|
|
38
|
+
requestId: request.id,
|
|
39
|
+
requestFamily: request.family,
|
|
40
|
+
sandboxTaskId: manifest.taskId,
|
|
41
|
+
requestGeneration: request.generation,
|
|
42
|
+
requestIssuedAt: request.issuedAt,
|
|
43
|
+
requestExpiresAt: request.expiresAt,
|
|
44
|
+
requestArgCount: args.length,
|
|
45
|
+
requestArgsSha256: createHash('sha256').update(encodedArgs, 'utf8').digest('hex'),
|
|
46
|
+
requestTaskRef: args[0] ?? null,
|
|
47
|
+
requestCommand: args[1] ?? null,
|
|
48
|
+
controllerProofPresent: request.controllerProof !== null,
|
|
49
|
+
hostCwd: process.cwd(),
|
|
50
|
+
manifestPath,
|
|
51
|
+
manifestPathRealpath: safeRealpath(manifestPath),
|
|
52
|
+
repoRoot: manifest.repoRoot,
|
|
53
|
+
repoRootRealpath: safeRealpath(manifest.repoRoot),
|
|
54
|
+
worktreeRoot: manifest.worktreeRoot,
|
|
55
|
+
worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
|
|
56
|
+
runtimeDir: manifest.runtimeDir,
|
|
57
|
+
runtimeDirRealpath: safeRealpath(manifest.runtimeDir)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function resultAuditFields(result) {
|
|
61
|
+
return {
|
|
62
|
+
exitCode: result.exitCode,
|
|
63
|
+
stdoutBytes: Buffer.byteLength(result.stdout, 'utf8'),
|
|
64
|
+
stderrBytes: Buffer.byteLength(result.stderr, 'utf8'),
|
|
65
|
+
stdoutSha256: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
|
|
66
|
+
stderrSha256: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
|
|
67
|
+
};
|
|
68
|
+
}
|
|
15
69
|
function assertRealDirectory(directory, parent) {
|
|
16
70
|
const stat = fs.lstatSync(directory);
|
|
17
71
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
@@ -120,6 +174,29 @@ function finalizationRecoveryResponse(manifest, requestId, exitCode) {
|
|
|
120
174
|
}
|
|
121
175
|
};
|
|
122
176
|
}
|
|
177
|
+
function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
|
|
178
|
+
let view;
|
|
179
|
+
try {
|
|
180
|
+
const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
|
|
181
|
+
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
|
|
182
|
+
view = resolved.ok && resolved.state === 'completed'
|
|
183
|
+
? taskViewAfterFinalization({
|
|
184
|
+
taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
|
|
185
|
+
})
|
|
186
|
+
: {
|
|
187
|
+
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
188
|
+
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
view = {
|
|
193
|
+
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
194
|
+
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
|
|
198
|
+
return view;
|
|
199
|
+
}
|
|
123
200
|
function payloadReference(payload) {
|
|
124
201
|
return {
|
|
125
202
|
version: payload.version,
|
|
@@ -143,14 +220,17 @@ function outputMatchesEvidence(output, bytes, sha256) {
|
|
|
143
220
|
return Buffer.byteLength(output, 'utf8') === bytes
|
|
144
221
|
&& createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
|
|
145
222
|
}
|
|
146
|
-
function genericRecoveryResponse(
|
|
223
|
+
function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
|
|
224
|
+
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
147
225
|
return {
|
|
148
226
|
version: 2,
|
|
149
|
-
id:
|
|
227
|
+
id: request.id,
|
|
150
228
|
phase: 'completed',
|
|
151
|
-
exitCode
|
|
152
|
-
stdout: '',
|
|
153
|
-
stderr: payload ? '' :
|
|
229
|
+
exitCode,
|
|
230
|
+
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
231
|
+
stderr: payload ? '' : cause === 'publish'
|
|
232
|
+
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
233
|
+
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
154
234
|
error: null,
|
|
155
235
|
outputState: payload ? 'available' : 'unavailable',
|
|
156
236
|
payload: payload ? payloadReference(payload) : null
|
|
@@ -161,7 +241,7 @@ function recoveryResponse(manifest, request, evidence, payload) {
|
|
|
161
241
|
const finalization = finalizationRecoveryResponse(manifest, request.id, evidence.exitCode);
|
|
162
242
|
return finalization.status === 'matched' ? finalization.response ?? null : null;
|
|
163
243
|
}
|
|
164
|
-
return genericRecoveryResponse(request
|
|
244
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
165
245
|
}
|
|
166
246
|
function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid) {
|
|
167
247
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
@@ -192,8 +272,10 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
|
|
|
192
272
|
};
|
|
193
273
|
}
|
|
194
274
|
if (response.outputState === 'unavailable') {
|
|
195
|
-
const
|
|
196
|
-
|
|
275
|
+
const causes = ['recovery', 'publish'];
|
|
276
|
+
const valid = causes.some((cause) => JSON.stringify(response)
|
|
277
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
278
|
+
return { valid, payloadReferenced: false };
|
|
197
279
|
}
|
|
198
280
|
return {
|
|
199
281
|
valid: response.payload === undefined
|
|
@@ -202,7 +284,7 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
|
|
|
202
284
|
payloadReferenced: false
|
|
203
285
|
};
|
|
204
286
|
}
|
|
205
|
-
function publishExecutionResult(manifest, request, result, brokerOwns) {
|
|
287
|
+
function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
206
288
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
207
289
|
let terminal = null;
|
|
208
290
|
if (request.family === 'task-finalization') {
|
|
@@ -234,21 +316,16 @@ function publishExecutionResult(manifest, request, result, brokerOwns) {
|
|
|
234
316
|
catch {
|
|
235
317
|
payload = null;
|
|
236
318
|
}
|
|
237
|
-
terminal =
|
|
238
|
-
version: 2,
|
|
239
|
-
id: request.id,
|
|
240
|
-
phase: 'completed',
|
|
241
|
-
exitCode: normalized.exitCode,
|
|
242
|
-
stdout: '',
|
|
243
|
-
stderr: payload ? '' : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n',
|
|
244
|
-
error: null,
|
|
245
|
-
outputState: payload ? 'available' : 'unavailable',
|
|
246
|
-
payload: payload ? payloadReference(payload) : null
|
|
247
|
-
};
|
|
319
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
248
320
|
}
|
|
249
321
|
}
|
|
250
322
|
if (!brokerOwns())
|
|
251
323
|
return false;
|
|
324
|
+
if (request.family === 'task-finalization' && normalized.exitCode === 0) {
|
|
325
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
|
|
326
|
+
if (view.state === 'unknown')
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
252
329
|
return writeSandboxControlResponse(manifest, terminal);
|
|
253
330
|
}
|
|
254
331
|
function writeAcceptedResponse(manifest, response) {
|
|
@@ -309,7 +386,7 @@ function bindingReason(manifest) {
|
|
|
309
386
|
return 'SANDBOX_WORKTREE_BINDING_LOST';
|
|
310
387
|
}
|
|
311
388
|
}
|
|
312
|
-
function recoverProcessing(manifest, brokerOwns) {
|
|
389
|
+
function recoverProcessing(manifest, broker, brokerOwns) {
|
|
313
390
|
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
314
391
|
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
|
|
315
392
|
continue;
|
|
@@ -385,7 +462,7 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
385
462
|
throw new Error('payload missing or mismatched');
|
|
386
463
|
payloadReferenced = true;
|
|
387
464
|
}
|
|
388
|
-
else if (terminal && (response.outputState !== undefined
|
|
465
|
+
else if (terminal && ((response.outputState !== undefined && response.outputState !== 'unavailable')
|
|
389
466
|
|| response.payload !== undefined && response.payload !== null)) {
|
|
390
467
|
throw new Error('payload state invalid');
|
|
391
468
|
}
|
|
@@ -403,6 +480,11 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
403
480
|
if (!reconciliation.valid)
|
|
404
481
|
continue;
|
|
405
482
|
payloadReferenced = reconciliation.payloadReferenced;
|
|
483
|
+
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
484
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
485
|
+
if (view.state === 'unknown')
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
406
488
|
}
|
|
407
489
|
if (!terminal) {
|
|
408
490
|
if (!brokerOwns())
|
|
@@ -412,6 +494,11 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
412
494
|
const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
|
|
413
495
|
if (!recovered)
|
|
414
496
|
continue;
|
|
497
|
+
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
498
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
499
|
+
if (view.state === 'unknown')
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
415
502
|
writeSandboxControlResponse(manifest, recovered);
|
|
416
503
|
payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
|
|
417
504
|
}
|
|
@@ -434,7 +521,13 @@ function delay(ms) {
|
|
|
434
521
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
435
522
|
}
|
|
436
523
|
export function sandboxControlSafeEnv(env = process.env) {
|
|
437
|
-
return Object.fromEntries(Object.entries(env).filter(([key]) =>
|
|
524
|
+
return Object.fromEntries(Object.entries(env).filter(([key]) => {
|
|
525
|
+
const normalized = key.toUpperCase();
|
|
526
|
+
return !normalized.startsWith('AGENT_INFRA_CONTROL_')
|
|
527
|
+
&& normalized !== 'AGENT_INFRA_TASK_ID'
|
|
528
|
+
&& normalized !== 'AGENT_INFRA_RUNTIME_DIR'
|
|
529
|
+
&& normalized !== 'AGENT_INFRA_EXECUTOR_MANIFEST';
|
|
530
|
+
}));
|
|
438
531
|
}
|
|
439
532
|
export async function serveSandboxControl(manifestPath, signal = new AbortController().signal, options = {}) {
|
|
440
533
|
const timing = options.timing ?? DEFAULT_SANDBOX_CONTROL_TIMING;
|
|
@@ -490,6 +583,28 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
490
583
|
releaseStartup();
|
|
491
584
|
}
|
|
492
585
|
let active = null;
|
|
586
|
+
let startupReceipt = null;
|
|
587
|
+
if (manifest.taskId) {
|
|
588
|
+
try {
|
|
589
|
+
startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
590
|
+
}
|
|
591
|
+
catch {
|
|
592
|
+
// Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
|
|
593
|
+
startupReceipt = {};
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
let taskView = taskViewForManifest({
|
|
597
|
+
...manifest,
|
|
598
|
+
receipt: startupReceipt
|
|
599
|
+
});
|
|
600
|
+
try {
|
|
601
|
+
const previous = readSandboxControlStatus(manifest.publicStatusDir);
|
|
602
|
+
if (previous.generation === manifest.generation)
|
|
603
|
+
taskView = mergeSandboxTaskView(taskView, previous.taskView);
|
|
604
|
+
}
|
|
605
|
+
catch {
|
|
606
|
+
// A fresh control root has no previous task-view projection.
|
|
607
|
+
}
|
|
493
608
|
let lastState = '';
|
|
494
609
|
let lastStatusAt = 0;
|
|
495
610
|
let nextBindingCheckAt = 0;
|
|
@@ -501,12 +616,49 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
501
616
|
try {
|
|
502
617
|
if (!brokerOwns())
|
|
503
618
|
return;
|
|
504
|
-
|
|
619
|
+
if (taskView.state === 'current' && taskView.observedSource === 'completed') {
|
|
620
|
+
// Recheck durable re-entry evidence before publishing a trusted startup view.
|
|
621
|
+
const unverified = { ...taskView, state: 'unknown',
|
|
622
|
+
reasonCode: 'SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID' };
|
|
623
|
+
try {
|
|
624
|
+
taskView = await completedReentryView(manifest, inspectContainer) ?? unverified;
|
|
625
|
+
}
|
|
626
|
+
catch {
|
|
627
|
+
taskView = unverified;
|
|
628
|
+
}
|
|
629
|
+
if (!brokerOwns())
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
writeSandboxControlStatus(manifest, broker, 'starting', null, null, Date.now(), taskView);
|
|
505
633
|
if (!brokerOwns())
|
|
506
634
|
return;
|
|
507
|
-
|
|
508
|
-
|
|
635
|
+
appendBrokerAudit(manifest, 'broker-start', {
|
|
636
|
+
pid: broker.pid,
|
|
637
|
+
brokerId: broker.brokerId,
|
|
638
|
+
hostCwd: process.cwd(),
|
|
639
|
+
manifestPath,
|
|
640
|
+
manifestPathRealpath: safeRealpath(manifestPath),
|
|
641
|
+
repoRoot: manifest.repoRoot,
|
|
642
|
+
repoRootRealpath: safeRealpath(manifest.repoRoot),
|
|
643
|
+
worktreeRoot: manifest.worktreeRoot,
|
|
644
|
+
worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
|
|
645
|
+
runtimeDir: manifest.runtimeDir,
|
|
646
|
+
runtimeDirRealpath: safeRealpath(manifest.runtimeDir),
|
|
647
|
+
channelDir: manifest.channelDir,
|
|
648
|
+
publicStatusDir: manifest.publicStatusDir,
|
|
649
|
+
processingDir: manifest.processingDir,
|
|
650
|
+
internalCliPath: options.internalCliPath ?? process.argv[1] ?? null
|
|
651
|
+
});
|
|
652
|
+
if (!recoverProcessing(manifest, broker, brokerOwns))
|
|
509
653
|
return;
|
|
654
|
+
try {
|
|
655
|
+
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
656
|
+
if (published.generation === manifest.generation)
|
|
657
|
+
taskView = published.taskView;
|
|
658
|
+
}
|
|
659
|
+
catch {
|
|
660
|
+
// The regular heartbeat will publish an invalid or missing projection as unknown.
|
|
661
|
+
}
|
|
510
662
|
while (!signal.aborted) {
|
|
511
663
|
let settledExecution = null;
|
|
512
664
|
if (!brokerOwns())
|
|
@@ -589,7 +741,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
589
741
|
if (cleanupStaleSandboxControlLease(manifest)) {
|
|
590
742
|
if (!brokerOwns())
|
|
591
743
|
break;
|
|
592
|
-
|
|
744
|
+
appendBrokerAudit(manifest, 'lease-stale-cleanup');
|
|
593
745
|
}
|
|
594
746
|
if (readActiveLease(manifest))
|
|
595
747
|
reasonCode = 'SANDBOX_CONTROL_HANDOFF_ACTIVE';
|
|
@@ -612,14 +764,25 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
612
764
|
}
|
|
613
765
|
reasonCode ??= containerReasonCode;
|
|
614
766
|
reasonCode ??= bindingReasonCode;
|
|
767
|
+
if (!reasonCode && !active && taskView.state !== 'current') {
|
|
768
|
+
try {
|
|
769
|
+
const reentered = await completedReentryView(manifest, inspectContainer);
|
|
770
|
+
if (reentered && brokerOwns() && !readActiveLease(manifest) && !bindingCheck(manifest)) {
|
|
771
|
+
taskView = reentered;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
// Invalid or changed re-entry evidence never clears the existing task view.
|
|
776
|
+
}
|
|
777
|
+
}
|
|
615
778
|
const state = reasonCode ? 'parked' : active ? 'busy' : 'healthy';
|
|
616
779
|
const stateKey = `${state}:${reasonCode ?? ''}:${active?.request.id ?? ''}`;
|
|
617
780
|
if (brokerOwns() && (stateKey !== lastState || now - lastStatusAt >= timing.controlTickMs)) {
|
|
618
|
-
writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now);
|
|
781
|
+
writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now, taskView);
|
|
619
782
|
lastStatusAt = now;
|
|
620
783
|
}
|
|
621
784
|
if (brokerOwns() && stateKey !== lastState) {
|
|
622
|
-
|
|
785
|
+
appendBrokerAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
|
|
623
786
|
lastState = stateKey;
|
|
624
787
|
}
|
|
625
788
|
if (settledExecution) {
|
|
@@ -627,13 +790,29 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
627
790
|
break;
|
|
628
791
|
let terminalCommitted = false;
|
|
629
792
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
630
|
-
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
|
|
793
|
+
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
|
|
794
|
+
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
795
|
+
try {
|
|
796
|
+
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
797
|
+
if (published.generation === manifest.generation)
|
|
798
|
+
taskView = published.taskView;
|
|
799
|
+
}
|
|
800
|
+
catch {
|
|
801
|
+
taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
|
|
802
|
+
}
|
|
803
|
+
}
|
|
631
804
|
}
|
|
632
805
|
else {
|
|
633
806
|
terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
|
|
634
807
|
}
|
|
635
808
|
if (!terminalCommitted)
|
|
636
809
|
return;
|
|
810
|
+
appendBrokerAudit(manifest, 'executor-result-published', {
|
|
811
|
+
...requestAuditFields(manifest, manifestPath, settledExecution.request),
|
|
812
|
+
resultAvailable: settledExecution.result !== null,
|
|
813
|
+
resultEvidenceWritten: settledExecution.resultEvidenceWritten,
|
|
814
|
+
...(settledExecution.result ? resultAuditFields(settledExecution.result) : {})
|
|
815
|
+
});
|
|
637
816
|
if (!brokerOwns())
|
|
638
817
|
break;
|
|
639
818
|
removeAcceptedResponse(manifest, settledExecution.request.id);
|
|
@@ -646,12 +825,19 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
646
825
|
const id = name.slice(0, -5);
|
|
647
826
|
const source = path.join(requestsDir, name);
|
|
648
827
|
let claimed = null;
|
|
828
|
+
let validatedRequest = null;
|
|
649
829
|
try {
|
|
650
830
|
if (!brokerOwns()) {
|
|
651
831
|
retiring = true;
|
|
652
832
|
break;
|
|
653
833
|
}
|
|
654
834
|
claimed = claimRequest(manifest, source, id);
|
|
835
|
+
appendBrokerAudit(manifest, 'request-claimed', {
|
|
836
|
+
requestId: id,
|
|
837
|
+
requestSourcePath: source,
|
|
838
|
+
requestClaimedPath: claimed,
|
|
839
|
+
requestProcessingDir: path.dirname(claimed)
|
|
840
|
+
});
|
|
655
841
|
if (!brokerOwns()) {
|
|
656
842
|
retiring = true;
|
|
657
843
|
break;
|
|
@@ -666,30 +852,85 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
666
852
|
if (active)
|
|
667
853
|
throw new Error('SANDBOX_CONTROL_BUSY');
|
|
668
854
|
const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
|
|
855
|
+
validatedRequest = request;
|
|
856
|
+
appendBrokerAudit(manifest, 'request-validated', {
|
|
857
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
858
|
+
requestPath: claimed
|
|
859
|
+
});
|
|
669
860
|
if (bindingCheck(manifest))
|
|
670
861
|
throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
|
|
671
862
|
if (readActiveLease(manifest))
|
|
672
863
|
throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
|
|
864
|
+
appendBrokerAudit(manifest, 'request-gates-passed', {
|
|
865
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
866
|
+
requestPath: claimed,
|
|
867
|
+
bindingChecked: true,
|
|
868
|
+
handoffLeaseChecked: true
|
|
869
|
+
});
|
|
673
870
|
writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
|
|
871
|
+
appendBrokerAudit(manifest, 'executor-reservation-written', {
|
|
872
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
873
|
+
requestPath: claimed,
|
|
874
|
+
executionPath: executionPath(manifest, request.id)
|
|
875
|
+
});
|
|
876
|
+
appendBrokerAudit(manifest, 'executor-prepare-start', {
|
|
877
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
878
|
+
requestPath: claimed,
|
|
879
|
+
executorCwd: manifest.repoRoot,
|
|
880
|
+
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
881
|
+
});
|
|
674
882
|
const prepared = await prepareExecution({
|
|
675
883
|
manifest, manifestPath, request, requestPath: claimed,
|
|
676
884
|
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
677
885
|
});
|
|
886
|
+
appendBrokerAudit(manifest, 'executor-prepared', {
|
|
887
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
888
|
+
requestPath: claimed,
|
|
889
|
+
executorCwd: manifest.repoRoot,
|
|
890
|
+
childPid: prepared.execution.child.pid,
|
|
891
|
+
childStartTime: prepared.execution.child.startTime,
|
|
892
|
+
childProcessGroupId: prepared.execution.child.processGroupId,
|
|
893
|
+
executionPath: executionPath(manifest, request.id)
|
|
894
|
+
});
|
|
678
895
|
const execution = {
|
|
679
896
|
request, prepared, result: null, resultEvidenceWritten: false, failure: null, settled: false
|
|
680
897
|
};
|
|
681
898
|
active = execution;
|
|
682
899
|
prepared.completion.then((result) => {
|
|
683
900
|
execution.result = sanitizeSandboxControlResult(manifest, result);
|
|
901
|
+
appendBrokerAudit(manifest, 'executor-completed', {
|
|
902
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
903
|
+
...resultAuditFields(execution.result),
|
|
904
|
+
childPid: prepared.execution.child.pid,
|
|
905
|
+
childStartTime: prepared.execution.child.startTime
|
|
906
|
+
});
|
|
684
907
|
try {
|
|
685
908
|
writeSandboxControlResultEvidence(manifest, request.id, execution.result);
|
|
686
909
|
execution.resultEvidenceWritten = true;
|
|
687
910
|
execution.settled = true;
|
|
911
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-written', {
|
|
912
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
913
|
+
resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
|
|
914
|
+
...resultAuditFields(execution.result)
|
|
915
|
+
});
|
|
688
916
|
}
|
|
689
917
|
catch (error) {
|
|
690
918
|
execution.failure = error;
|
|
919
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
|
|
920
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
921
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
922
|
+
});
|
|
691
923
|
}
|
|
692
|
-
}, (error) => {
|
|
924
|
+
}, (error) => {
|
|
925
|
+
execution.failure = error;
|
|
926
|
+
execution.settled = true;
|
|
927
|
+
appendBrokerAudit(manifest, 'executor-failed', {
|
|
928
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
929
|
+
childPid: prepared.execution.child.pid,
|
|
930
|
+
childStartTime: prepared.execution.child.startTime,
|
|
931
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
932
|
+
});
|
|
933
|
+
});
|
|
693
934
|
if (!brokerOwns()) {
|
|
694
935
|
prepared.terminate(false);
|
|
695
936
|
active = null;
|
|
@@ -705,6 +946,11 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
705
946
|
writeAcceptedResponse(manifest, {
|
|
706
947
|
version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
|
|
707
948
|
});
|
|
949
|
+
appendBrokerAudit(manifest, 'request-accepted', {
|
|
950
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
951
|
+
acceptedPath: acceptedResponsePath(manifest, id),
|
|
952
|
+
executionPhase: 'prepared'
|
|
953
|
+
});
|
|
708
954
|
if (!brokerOwns()) {
|
|
709
955
|
prepared.terminate(false);
|
|
710
956
|
active = null;
|
|
@@ -712,9 +958,19 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
712
958
|
break;
|
|
713
959
|
}
|
|
714
960
|
try {
|
|
961
|
+
appendBrokerAudit(manifest, 'executor-start', {
|
|
962
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
963
|
+
childPid: prepared.execution.child.pid,
|
|
964
|
+
childStartTime: prepared.execution.child.startTime,
|
|
965
|
+
executionPath: executionPath(manifest, request.id)
|
|
966
|
+
});
|
|
715
967
|
prepared.start(brokerOwns);
|
|
716
968
|
}
|
|
717
969
|
catch (error) {
|
|
970
|
+
appendBrokerAudit(manifest, 'executor-start-failed', {
|
|
971
|
+
...requestAuditFields(manifest, manifestPath, request),
|
|
972
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
973
|
+
});
|
|
718
974
|
const owned = brokerOwns();
|
|
719
975
|
prepared.terminate(owned);
|
|
720
976
|
if (!owned || !brokerOwns()) {
|
|
@@ -736,7 +992,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
736
992
|
retiring = true;
|
|
737
993
|
break;
|
|
738
994
|
}
|
|
739
|
-
|
|
995
|
+
appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
|
|
740
996
|
continue;
|
|
741
997
|
}
|
|
742
998
|
}
|
|
@@ -745,6 +1001,16 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
745
1001
|
retiring = true;
|
|
746
1002
|
break;
|
|
747
1003
|
}
|
|
1004
|
+
const detail = controlError(error);
|
|
1005
|
+
appendBrokerAudit(manifest, 'request-rejected', {
|
|
1006
|
+
requestId: id,
|
|
1007
|
+
requestFamily: validatedRequest?.family ?? null,
|
|
1008
|
+
sandboxTaskId: manifest.taskId,
|
|
1009
|
+
requestPath: claimed,
|
|
1010
|
+
errorCode: detail.code,
|
|
1011
|
+
errorRetryable: detail.retryable,
|
|
1012
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1013
|
+
});
|
|
748
1014
|
writeSandboxControlResponse(manifest, rejected(id, error));
|
|
749
1015
|
if (!brokerOwns()) {
|
|
750
1016
|
retiring = true;
|
|
@@ -775,7 +1041,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
775
1041
|
}
|
|
776
1042
|
}
|
|
777
1043
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
778
|
-
if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
|
|
1044
|
+
if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
779
1045
|
if (brokerOwns()) {
|
|
780
1046
|
removeAcceptedResponse(manifest, active.request.id);
|
|
781
1047
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -787,7 +1053,11 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
787
1053
|
const terminationConfirmed = active.prepared.terminate(owned);
|
|
788
1054
|
if (owned && brokerOwns() && active.request.family === 'task-finalization') {
|
|
789
1055
|
const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
|
|
790
|
-
|
|
1056
|
+
const view = recovered.status === 'matched'
|
|
1057
|
+
? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
|
|
1058
|
+
: null;
|
|
1059
|
+
if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
|
|
1060
|
+
&& writeSandboxControlResponse(manifest, recovered.response)) {
|
|
791
1061
|
if (terminationConfirmed && brokerOwns()) {
|
|
792
1062
|
removeAcceptedResponse(manifest, active.request.id);
|
|
793
1063
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -5,6 +5,7 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
5
5
|
import { buildProcessTreeStopCommand } from '../../server/process-control.js';
|
|
6
6
|
import { getProcessIdentityState } from '../../server/process-state.js';
|
|
7
7
|
import { parseSandboxControlResultEvidence, parseSandboxControlPayload, parseSandboxControlReservation, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_LOGICAL_RECORDS, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES, SANDBOX_CONTROL_RESERVATION_BYTES } from './protocol.js';
|
|
8
|
+
import { parseSandboxTaskView, taskViewForManifest } from './task-view.js';
|
|
8
9
|
export const SANDBOX_CONTROL_AUDIT_MAX_BYTES = 1024 * 1024;
|
|
9
10
|
export const SANDBOX_CONTROL_EXECUTION_STOP_MS = 2_000;
|
|
10
11
|
function assertRegularFile(filePath) {
|
|
@@ -233,16 +234,16 @@ export function sandboxControlGenerationUsage(manifest) {
|
|
|
233
234
|
export function statusPath(manifest) {
|
|
234
235
|
return path.join(manifest.publicStatusDir, 'status.json');
|
|
235
236
|
}
|
|
236
|
-
export function writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, now = Date.now()) {
|
|
237
|
+
export function writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, now = Date.now(), taskView = taskViewForManifest(manifest)) {
|
|
237
238
|
const status = {
|
|
238
|
-
version:
|
|
239
|
+
version: 3, generation: manifest.generation, broker, state, reasonCode, activeRequestId, updatedAt: now, taskView
|
|
239
240
|
};
|
|
240
241
|
atomicWriteJson(statusPath(manifest), status, 0o600, false);
|
|
241
242
|
return status;
|
|
242
243
|
}
|
|
243
244
|
export function parseSandboxControlStatus(value) {
|
|
244
245
|
const status = value;
|
|
245
|
-
if (!status || status.version !==
|
|
246
|
+
if (!status || status.version !== 3 || typeof status.generation !== 'string'
|
|
246
247
|
|| !status.broker || !Number.isSafeInteger(status.broker.pid) || typeof status.broker.startTime !== 'number'
|
|
247
248
|
|| !Number.isSafeInteger(status.broker.startTime) || typeof status.broker.brokerId !== 'string'
|
|
248
249
|
|| status.broker.brokerId.length === 0
|
|
@@ -251,6 +252,7 @@ export function parseSandboxControlStatus(value) {
|
|
|
251
252
|
|| (status.activeRequestId !== null && typeof status.activeRequestId !== 'string')
|
|
252
253
|
|| !Number.isSafeInteger(status.updatedAt))
|
|
253
254
|
throw new Error('SANDBOX_CONTROL_STATUS_INVALID');
|
|
255
|
+
parseSandboxTaskView(status.taskView);
|
|
254
256
|
return status;
|
|
255
257
|
}
|
|
256
258
|
export function readSandboxControlStatus(directory) {
|