@fitlab-ai/agent-infra 0.9.13 → 0.9.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/README.zh-CN.md +10 -0
- package/bin/cli.ts +26 -9
- package/bin/internal-cli.sh +45 -0
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +126 -42
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
- package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
- package/dist/lib/defaults.json +103 -103
- package/dist/lib/fs/durable-write.js +37 -0
- package/dist/lib/fs/file-lock.js +111 -0
- package/dist/lib/host-control/audit.js +19 -0
- package/dist/lib/host-control/client.js +142 -0
- package/dist/lib/host-control/command.js +49 -0
- package/dist/lib/host-control/path.js +160 -0
- package/dist/lib/host-control/secure-fs.js +140 -0
- package/dist/lib/host-control/server.js +159 -0
- package/dist/lib/host-control/service.js +112 -0
- package/dist/lib/init.js +11 -2
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +154 -0
- package/dist/lib/internal/codex-lifecycle.js +15 -12
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/demo-promotion.js +322 -0
- package/dist/lib/internal/demo-transcript.js +454 -0
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/host-control.js +28 -0
- package/dist/lib/internal/manual-validation.js +322 -0
- 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 +42 -14
- 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 +82 -89
- package/dist/lib/internal/sandbox-control.js +171 -7
- package/dist/lib/internal/task-activity.js +13 -4
- package/dist/lib/internal/task-artifact.js +19 -107
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +48 -14
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +38 -35
- 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 +519 -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 +10 -52
- 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 +37 -12
- package/dist/lib/internal/task-verify.js +5 -1
- package/dist/lib/internal/task-warning.js +8 -1
- package/dist/lib/owned-path.js +33 -0
- package/dist/lib/platform/check-status.js +11 -0
- package/dist/lib/platform/comment-safety.js +229 -0
- package/dist/lib/platform/completion-backfill.js +1 -1
- package/dist/lib/platform/github-data.js +241 -0
- package/dist/lib/platform/github-provider.js +248 -26
- package/dist/lib/platform/issue-comments.js +146 -34
- package/dist/lib/platform/issues.js +1 -179
- package/dist/lib/platform/pr-change-report.js +6 -12
- package/dist/lib/platform/pr-checks.js +12 -65
- package/dist/lib/platform/pr-summary.js +83 -10
- 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 +117 -275
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/snapshots.js +2 -0
- package/dist/lib/platform/verification-sync.js +154 -162
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/release/workflow.js +1 -1
- 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 +39 -8
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +3 -807
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/audit.js +203 -0
- package/dist/lib/sandbox/control/client.js +125 -12
- 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 +233 -34
- package/dist/lib/sandbox/control/finalization-response.js +27 -0
- package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
- package/dist/lib/sandbox/control/lifecycle.js +656 -128
- package/dist/lib/sandbox/control/native-file-lock.js +85 -0
- package/dist/lib/sandbox/control/protocol.js +55 -4
- package/dist/lib/sandbox/control/server.js +720 -79
- package/dist/lib/sandbox/control/state.js +63 -3
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/control/task-workflow.js +74 -0
- package/dist/lib/sandbox/control/workflow-executor.js +99 -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 +7 -2
- package/dist/lib/sandbox/recovery.js +49 -5
- package/dist/lib/sandbox/removal.js +1717 -0
- package/dist/lib/sandbox/task-cleanup.js +217 -0
- package/dist/lib/sandbox/workspace-view.js +15 -15
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/task/activity-intent.js +2 -1
- package/dist/lib/task/activity-log.js +30 -24
- package/dist/lib/task/artifact-command.js +101 -0
- package/dist/lib/task/artifact-lifecycle.js +71 -40
- package/dist/lib/task/artifact-name.js +36 -0
- package/dist/lib/task/artifact-operations.js +474 -0
- package/dist/lib/task/artifact-receipts.js +3 -15
- package/dist/lib/task/artifact-repair-intent.js +130 -0
- package/dist/lib/task/artifact-schema.js +127 -0
- package/dist/lib/task/capabilities.js +83 -19
- package/dist/lib/task/codex-orchestration.js +71 -25
- package/dist/lib/task/commit-identity.js +28 -0
- package/dist/lib/task/commit-operation.js +2 -16
- package/dist/lib/task/control-authority.js +375 -5
- package/dist/lib/task/control-recovery.js +128 -0
- package/dist/lib/task/create-service.js +103 -4
- package/dist/lib/task/create.js +80 -2
- package/dist/lib/task/decision-details.js +2 -46
- package/dist/lib/task/events.js +306 -76
- package/dist/lib/task/finalization-state.js +9 -0
- package/dist/lib/task/human-override.js +3 -6
- package/dist/lib/task/implementation-inputs.js +3 -3
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/intermediate-cleanup.js +383 -0
- package/dist/lib/task/invalidation.js +18 -65
- package/dist/lib/task/ledger-intents.js +7 -22
- package/dist/lib/task/ledger.js +1 -3
- package/dist/lib/task/lifecycle.js +19 -1
- package/dist/lib/task/local-artifact-finalization.js +190 -290
- package/dist/lib/task/manual-validation-completion.js +26 -0
- package/dist/lib/task/manual-validation-evidence.js +130 -0
- package/dist/lib/task/manual-validation-receipt.js +101 -0
- package/dist/lib/task/manual-validation-shared.js +40 -0
- package/dist/lib/task/manual-validation-transaction.js +243 -0
- package/dist/lib/task/markdown.js +47 -0
- package/dist/lib/task/orchestration.js +206 -69
- package/dist/lib/task/qualification-audit.js +476 -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 +23 -40
- package/dist/lib/task/review-command.js +25 -0
- package/dist/lib/task/review-finalization.js +146 -97
- package/dist/lib/task/review-fingerprint.js +2 -2
- package/dist/lib/task/sections.js +34 -54
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +170 -327
- package/dist/lib/task/verification.js +1 -1
- package/dist/lib/task/workflow-command.js +17 -0
- package/dist/lib/task/workflow-warning-intents.js +0 -16
- package/dist/lib/task/workflow-warnings.js +28 -2
- package/dist/lib/update.js +2 -0
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
- package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
- package/lib/fs/durable-write.ts +29 -0
- package/lib/fs/file-lock.ts +113 -0
- package/lib/host-control/audit.ts +22 -0
- package/lib/host-control/client.ts +172 -0
- package/lib/host-control/command.ts +60 -0
- package/lib/host-control/path.ts +167 -0
- package/lib/host-control/secure-fs.ts +135 -0
- package/lib/host-control/server.ts +180 -0
- package/lib/host-control/service.ts +133 -0
- package/lib/init.ts +15 -2
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +142 -0
- package/lib/internal/codex-lifecycle.ts +14 -12
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/demo-promotion.ts +354 -0
- package/lib/internal/demo-transcript.ts +419 -0
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/host-control.ts +28 -0
- package/lib/internal/manual-validation.ts +306 -0
- 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 +38 -14
- 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 +91 -87
- package/lib/internal/sandbox-control.ts +181 -6
- package/lib/internal/task-activity.ts +12 -4
- package/lib/internal/task-artifact.ts +15 -79
- 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 +21 -10
- 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 +590 -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 +7 -50
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +32 -12
- package/lib/internal/task-verify.ts +4 -1
- package/lib/internal/task-warning.ts +7 -1
- package/lib/owned-path.ts +26 -0
- package/lib/platform/check-status.ts +9 -0
- package/lib/platform/comment-safety.ts +255 -0
- package/lib/platform/completion-backfill.ts +1 -1
- package/lib/platform/github-data.ts +322 -0
- package/lib/platform/github-provider.ts +244 -27
- package/lib/platform/issue-comments.ts +159 -35
- package/lib/platform/issues.ts +4 -245
- package/lib/platform/pr-change-report.ts +6 -12
- package/lib/platform/pr-checks.ts +13 -77
- package/lib/platform/pr-summary.ts +88 -11
- package/lib/platform/provider-contract.ts +78 -3
- package/lib/platform/provider-validation.ts +85 -3
- package/lib/platform/pull-requests.ts +129 -348
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/snapshots.ts +53 -0
- package/lib/platform/verification-sync.ts +171 -177
- package/lib/process-data/index.ts +7 -0
- package/lib/release/workflow.ts +3 -3
- package/lib/run/index.ts +5 -0
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +53 -12
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +3 -996
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/audit.ts +253 -0
- package/lib/sandbox/control/client.ts +150 -13
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +262 -37
- package/lib/sandbox/control/finalization-response.ts +29 -0
- package/lib/sandbox/control/identity-sentinel.ts +137 -0
- package/lib/sandbox/control/lifecycle.ts +672 -18
- package/lib/sandbox/control/native-file-lock.ts +108 -0
- package/lib/sandbox/control/protocol.ts +80 -8
- package/lib/sandbox/control/server.ts +779 -82
- package/lib/sandbox/control/state.ts +84 -3
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/control/task-workflow.ts +95 -0
- package/lib/sandbox/control/workflow-executor.ts +102 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +9 -2
- package/lib/sandbox/recovery.ts +49 -6
- package/lib/sandbox/removal.ts +2144 -0
- package/lib/sandbox/task-cleanup.ts +209 -0
- package/lib/sandbox/workspace-view.ts +26 -18
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/task/activity-intent.ts +2 -2
- package/lib/task/activity-log.ts +27 -19
- package/lib/task/artifact-command.ts +104 -0
- package/lib/task/artifact-lifecycle.ts +66 -39
- package/lib/task/artifact-name.ts +55 -0
- package/lib/task/artifact-operations.ts +580 -0
- package/lib/task/artifact-receipts.ts +3 -14
- package/lib/task/artifact-repair-intent.ts +152 -0
- package/lib/task/artifact-schema.ts +163 -0
- package/lib/task/capabilities.ts +82 -22
- package/lib/task/codex-orchestration.ts +85 -24
- package/lib/task/commit-identity.ts +31 -0
- package/lib/task/commit-operation.ts +2 -16
- package/lib/task/control-authority.ts +513 -6
- package/lib/task/control-recovery.ts +174 -0
- package/lib/task/create-service.ts +131 -6
- package/lib/task/create.ts +83 -1
- package/lib/task/decision-details.ts +3 -71
- package/lib/task/events.ts +292 -75
- package/lib/task/finalization-state.ts +11 -0
- package/lib/task/human-override.ts +3 -8
- package/lib/task/implementation-inputs.ts +3 -3
- package/lib/task/index.ts +16 -3
- package/lib/task/intermediate-cleanup.ts +455 -0
- package/lib/task/invalidation.ts +18 -62
- package/lib/task/ledger-intents.ts +7 -24
- package/lib/task/ledger.ts +1 -3
- package/lib/task/lifecycle.ts +17 -1
- package/lib/task/local-artifact-finalization.ts +223 -388
- package/lib/task/manual-validation-completion.ts +40 -0
- package/lib/task/manual-validation-evidence.ts +209 -0
- package/lib/task/manual-validation-receipt.ts +152 -0
- package/lib/task/manual-validation-shared.ts +50 -0
- package/lib/task/manual-validation-transaction.ts +304 -0
- package/lib/task/markdown.ts +70 -0
- package/lib/task/orchestration.ts +191 -68
- package/lib/task/qualification-audit.ts +588 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -41
- package/lib/task/review-command.ts +23 -0
- package/lib/task/review-finalization.ts +154 -153
- package/lib/task/review-fingerprint.ts +2 -2
- package/lib/task/sections.ts +33 -52
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +165 -370
- package/lib/task/verification-types.ts +17 -0
- package/lib/task/verification.ts +1 -1
- package/lib/task/workflow-command.ts +18 -0
- package/lib/task/workflow-warning-intents.ts +0 -13
- package/lib/task/workflow-warnings.ts +21 -2
- package/lib/update.ts +6 -0
- package/package.json +7 -2
- package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
- 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/rules/testing-discipline.en.md +2 -0
- package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
- package/templates/.agents/scripts/package.json +3 -0
- 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 +37 -21
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
- package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
- package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
- 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/release/SKILL.en.md +7 -3
- package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
- 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 +25 -4
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
- package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/test/SKILL.en.md +29 -7
- package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
- 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/.claude/commands/run-manual-validation.en.md +1 -1
- package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
- package/dist/lib/platform/adapters.js +0 -56
- package/lib/platform/adapters.ts +0 -197
- 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,6 @@
|
|
|
1
|
+
import { finalizationTerminalResponse } from './finalization-response.js';
|
|
2
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
3
|
+
import { completedReentryView } from './completed-reentry.js';
|
|
1
4
|
import fs from 'node:fs';
|
|
2
5
|
import path from 'node:path';
|
|
3
6
|
import { createHash, randomUUID } from 'node:crypto';
|
|
@@ -5,13 +8,126 @@ import { getProcessStartTime } from '../../server/process-state.js';
|
|
|
5
8
|
import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
|
|
6
9
|
import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES } from './protocol.js';
|
|
7
10
|
import { prepareSandboxControlExecution } from './executor.js';
|
|
8
|
-
import {
|
|
11
|
+
import { atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, readSandboxControlTerminalResult, createSandboxControlTerminalResult, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, writeSandboxControlTerminalResult, appendSandboxControlAudit, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
|
|
9
12
|
import { validateSandboxControlRequest } from './protocol.js';
|
|
13
|
+
import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
|
|
14
|
+
import { parseTaskControlOperation } from '../../task/control-authority.js';
|
|
15
|
+
import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
|
|
16
|
+
import { readRun } from '../../task/orchestration.js';
|
|
17
|
+
import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
|
|
18
|
+
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
|
|
19
|
+
import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
|
|
20
|
+
import { resolveTaskRef } from '../../task/resolve-ref.js';
|
|
21
|
+
import { loadShortIdByTaskId } from '../../task/short-id.js';
|
|
10
22
|
import { inspectSandboxControlContainer } from './container-identity.js';
|
|
11
23
|
import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
|
|
12
24
|
import { nextSandboxControlBackoff } from './timing.js';
|
|
13
25
|
import { readTaskFinalizationReceipt } from '../../task/finalization.js';
|
|
14
|
-
import {
|
|
26
|
+
import { readCodexControllerRegistration } from './controller-registration.js';
|
|
27
|
+
import { validateSandboxControlIdentity } from './identity-sentinel.js';
|
|
28
|
+
import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
|
|
29
|
+
import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
|
|
30
|
+
import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, recoverLifecycleRecoveryOperation } from '../../task/control-authority.js';
|
|
31
|
+
import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.js';
|
|
32
|
+
import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
|
|
33
|
+
function consumeLifecycleAuthorityForResult(manifest, execution) {
|
|
34
|
+
const authority = execution.lifecycleAuthority?.attestation;
|
|
35
|
+
if (!authority || execution.result?.exitCode !== 0)
|
|
36
|
+
return true;
|
|
37
|
+
try {
|
|
38
|
+
consumeLifecycleRecoveryAttestation(authority);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
|
|
43
|
+
...requestAuditFields(manifest, execution.request),
|
|
44
|
+
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
|
|
45
|
+
});
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function lifecycleAuthorityForRequest(manifest, manifestPath, request) {
|
|
50
|
+
const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
|
|
51
|
+
? request.authority
|
|
52
|
+
: undefined;
|
|
53
|
+
if (!selector)
|
|
54
|
+
return undefined;
|
|
55
|
+
let controllerBinding = null;
|
|
56
|
+
try {
|
|
57
|
+
const registration = readCodexControllerRegistration(manifestPath);
|
|
58
|
+
controllerBinding = {
|
|
59
|
+
instanceDigest: registration.controllerInstanceDigest,
|
|
60
|
+
controlGeneration: registration.controlGeneration
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// The issuer reports the stable unavailable result; the broker never trusts
|
|
65
|
+
// a controller binding supplied by the request.
|
|
66
|
+
}
|
|
67
|
+
return issueLifecycleRecoveryAttestation(selector, {
|
|
68
|
+
controllerBinding,
|
|
69
|
+
capabilityStore: createCodexCapabilityStore(),
|
|
70
|
+
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function appendBrokerAudit(manifest, event, fields = {}) {
|
|
74
|
+
appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
|
|
75
|
+
}
|
|
76
|
+
function operationKey(request, output) {
|
|
77
|
+
if (request.family === 'task-finalization')
|
|
78
|
+
return request.operation;
|
|
79
|
+
if (request.family === 'task-create')
|
|
80
|
+
return 'create';
|
|
81
|
+
if (request.family === 'codex-controller')
|
|
82
|
+
return request.command;
|
|
83
|
+
if (request.family === 'task-workflow')
|
|
84
|
+
return request.workflow.operation;
|
|
85
|
+
if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
|
|
86
|
+
return null;
|
|
87
|
+
if (request.family === 'task-orchestration' && request.args[1] === 'route') {
|
|
88
|
+
if (!output)
|
|
89
|
+
return 'route';
|
|
90
|
+
const result = parseControlOutput(output);
|
|
91
|
+
return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const operation = parseTaskControlOperation(request.family, request.args);
|
|
95
|
+
return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function recoveryOperationKey(request, terminalResult, output) {
|
|
102
|
+
const operation = operationKey(request, output);
|
|
103
|
+
if (operation !== 'route')
|
|
104
|
+
return operation;
|
|
105
|
+
const digest = terminalResult?.intentDigest;
|
|
106
|
+
for (const candidate of ['route.read', 'route.clean-completion']) {
|
|
107
|
+
const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
|
|
108
|
+
if (digest === expected)
|
|
109
|
+
return candidate;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
function terminalResultMatchesRequest(request, terminalResult) {
|
|
114
|
+
const operation = operationKey(request);
|
|
115
|
+
const candidates = operation === 'route'
|
|
116
|
+
? ['route.read', 'route.clean-completion']
|
|
117
|
+
: operation ? [operation] : [];
|
|
118
|
+
return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
|
|
119
|
+
}
|
|
120
|
+
function criticalRequestPhase(manifest, request, phase, outcome, reference) {
|
|
121
|
+
const context = createSandboxControlAuditContext(manifest, {
|
|
122
|
+
requestId: request.id,
|
|
123
|
+
family: request.family,
|
|
124
|
+
operation: operationKey(request),
|
|
125
|
+
phase,
|
|
126
|
+
outcome
|
|
127
|
+
});
|
|
128
|
+
appendCriticalAudit(manifest, context, { reference: reference ?? null });
|
|
129
|
+
writeSandboxControlTransition(manifest, { requestId: request.id, phase, reference });
|
|
130
|
+
}
|
|
15
131
|
function assertRealDirectory(directory, parent) {
|
|
16
132
|
const stat = fs.lstatSync(directory);
|
|
17
133
|
if (!stat.isDirectory() || stat.isSymbolicLink())
|
|
@@ -92,33 +208,30 @@ function finalizationRecoveryResponse(manifest, requestId, exitCode) {
|
|
|
92
208
|
catch {
|
|
93
209
|
return { status: 'deferred' };
|
|
94
210
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
stderr: '', error: null
|
|
120
|
-
}
|
|
121
|
-
};
|
|
211
|
+
return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
|
|
212
|
+
}
|
|
213
|
+
function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
|
|
214
|
+
let view;
|
|
215
|
+
try {
|
|
216
|
+
const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
|
|
217
|
+
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
|
|
218
|
+
view = resolved.ok && resolved.state === 'completed'
|
|
219
|
+
? taskViewAfterFinalization({
|
|
220
|
+
taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
|
|
221
|
+
})
|
|
222
|
+
: {
|
|
223
|
+
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
224
|
+
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
view = {
|
|
229
|
+
state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
|
|
230
|
+
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
|
|
234
|
+
return view;
|
|
122
235
|
}
|
|
123
236
|
function payloadReference(payload) {
|
|
124
237
|
return {
|
|
@@ -143,29 +256,260 @@ function outputMatchesEvidence(output, bytes, sha256) {
|
|
|
143
256
|
return Buffer.byteLength(output, 'utf8') === bytes
|
|
144
257
|
&& createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
|
|
145
258
|
}
|
|
146
|
-
function genericRecoveryResponse(
|
|
259
|
+
function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
|
|
260
|
+
const outputUnavailable = request.family === 'task-create' && !payload;
|
|
147
261
|
return {
|
|
148
262
|
version: 2,
|
|
149
|
-
id:
|
|
263
|
+
id: request.id,
|
|
150
264
|
phase: 'completed',
|
|
151
|
-
exitCode
|
|
152
|
-
stdout: '',
|
|
153
|
-
stderr: payload ? '' :
|
|
265
|
+
exitCode,
|
|
266
|
+
stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
|
|
267
|
+
stderr: payload ? '' : cause === 'publish'
|
|
268
|
+
? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
|
|
269
|
+
: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
|
|
154
270
|
error: null,
|
|
155
271
|
outputState: payload ? 'available' : 'unavailable',
|
|
156
272
|
payload: payload ? payloadReference(payload) : null
|
|
157
273
|
};
|
|
158
274
|
}
|
|
159
|
-
function
|
|
275
|
+
function taskCreateDomainEvidence(manifest, output) {
|
|
276
|
+
if (!output || !['applied', 'no-op', 'degraded'].includes(String(output.status))) {
|
|
277
|
+
return { consistent: false };
|
|
278
|
+
}
|
|
279
|
+
const task = output.task && typeof output.task === 'object' && !Array.isArray(output.task)
|
|
280
|
+
? output.task : null;
|
|
281
|
+
const taskId = typeof task?.id === 'string' ? task.id : null;
|
|
282
|
+
const shortId = typeof task?.shortId === 'string' ? task.shortId : null;
|
|
283
|
+
if (!taskId || !shortId)
|
|
284
|
+
return { consistent: false };
|
|
285
|
+
try {
|
|
286
|
+
const resolved = resolveTaskRef(taskId, { repoRoot: manifest.repoRoot });
|
|
287
|
+
const shortIds = loadShortIdByTaskId(manifest.repoRoot);
|
|
288
|
+
return {
|
|
289
|
+
consistent: resolved.ok && resolved.taskId === taskId && resolved.state === 'active' && shortIds.get(taskId) === shortId,
|
|
290
|
+
taskId,
|
|
291
|
+
shortId
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return { consistent: false };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function controllerDomainEvidence(manifest, manifestPath, request, output) {
|
|
299
|
+
if (!output || output.error !== null || typeof output.status !== 'string')
|
|
300
|
+
return { consistent: false };
|
|
301
|
+
try {
|
|
302
|
+
const registration = readCodexControllerRegistration(manifestPath);
|
|
303
|
+
if (request.family !== 'codex-controller')
|
|
304
|
+
return { consistent: false };
|
|
305
|
+
if (request.command === 'open') {
|
|
306
|
+
const lease = output.lease && typeof output.lease === 'object' && !Array.isArray(output.lease)
|
|
307
|
+
? output.lease : null;
|
|
308
|
+
return {
|
|
309
|
+
consistent: output.status === 'opened' && output.changed === true
|
|
310
|
+
&& lease?.taskId === registration.taskId
|
|
311
|
+
&& lease.controlGeneration === registration.controlGeneration
|
|
312
|
+
&& lease.controllerInstanceDigest === registration.controllerInstanceDigest
|
|
313
|
+
&& registration.taskId === manifest.taskId
|
|
314
|
+
&& registration.controlGeneration === manifest.generation
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
if (request.command === 'verify') {
|
|
318
|
+
const binding = output.binding && typeof output.binding === 'object' && !Array.isArray(output.binding)
|
|
319
|
+
? output.binding : null;
|
|
320
|
+
return {
|
|
321
|
+
consistent: output.status === 'verified' && output.changed === false
|
|
322
|
+
&& binding?.taskId === registration.taskId
|
|
323
|
+
&& binding.controlGeneration === registration.controlGeneration
|
|
324
|
+
&& binding.controllerInstanceDigest === registration.controllerInstanceDigest
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return { consistent: false };
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
return { consistent: request.family === 'codex-controller' && request.command === 'close'
|
|
331
|
+
&& error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
|
|
332
|
+
&& output?.status === 'closed' && typeof output.changed === 'boolean' };
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function orchestrationDomainEvidence(operation, terminalResult, output, run) {
|
|
336
|
+
if (!operation || !output)
|
|
337
|
+
return { consistent: false };
|
|
338
|
+
const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
|
|
339
|
+
? output.run : null;
|
|
340
|
+
const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
|
|
341
|
+
if (operation.class === 'read-only') {
|
|
342
|
+
return {
|
|
343
|
+
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
344
|
+
snapshotValid: true,
|
|
345
|
+
status: run?.status ?? terminalResult.status,
|
|
346
|
+
pendingDelegation: run?.pendingDelegation ?? null
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
if (!run || !outputRun)
|
|
350
|
+
return { consistent: false };
|
|
351
|
+
return {
|
|
352
|
+
consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
|
|
353
|
+
status: run.status,
|
|
354
|
+
pendingDelegation: run.pendingDelegation
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
|
|
358
|
+
if (!operation)
|
|
359
|
+
return { domain: null };
|
|
360
|
+
const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
|
|
361
|
+
? manifest.taskId
|
|
362
|
+
: 'args' in request ? request.args[0] ?? null : manifest.taskId;
|
|
363
|
+
const output = parseControlOutput(payloadOutput);
|
|
364
|
+
if (!taskRef && operation.family !== 'task-create' && operation.family !== 'codex-controller') {
|
|
365
|
+
return { domain: null };
|
|
366
|
+
}
|
|
367
|
+
if (operation.family === 'task-lifecycle') {
|
|
368
|
+
if (!taskRef || !terminalResult.targetState)
|
|
369
|
+
return { domain: null };
|
|
370
|
+
const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
|
|
371
|
+
try {
|
|
372
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
|
|
373
|
+
const shortIds = loadShortIdByTaskId(manifest.repoRoot);
|
|
374
|
+
const shortIdMatches = terminalResult.targetState === 'active'
|
|
375
|
+
? shortIds.has(resolved.ok ? resolved.taskId : taskRef)
|
|
376
|
+
: !shortIds.has(resolved.ok ? resolved.taskId : taskRef);
|
|
377
|
+
return {
|
|
378
|
+
domain: { consistent: resolved.ok && resolved.state === terminalResult.targetState && shortIdMatches },
|
|
379
|
+
journal
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return { domain: { consistent: false }, journal };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (operation.family === 'task-orchestration') {
|
|
387
|
+
if (!taskRef)
|
|
388
|
+
return { domain: null };
|
|
389
|
+
try {
|
|
390
|
+
const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
|
|
391
|
+
if (!resolved.ok)
|
|
392
|
+
return { domain: { consistent: false } };
|
|
393
|
+
const run = readRun(resolved.taskDir);
|
|
394
|
+
if (!run && operation.class !== 'read-only')
|
|
395
|
+
return { domain: { consistent: false } };
|
|
396
|
+
if (operation.class === 'route.clean-completion') {
|
|
397
|
+
const snapshot = captureRepositorySnapshot(manifest.repoRoot);
|
|
398
|
+
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
|
|
399
|
+
const completion = run?.completionEvidence ?? null;
|
|
400
|
+
const consistent = run !== null && completion !== null
|
|
401
|
+
&& terminalResult.completionEvidence !== null
|
|
402
|
+
&& isDeepStrictEqual(completion, terminalResult.completionEvidence);
|
|
403
|
+
return {
|
|
404
|
+
domain: {
|
|
405
|
+
consistent,
|
|
406
|
+
status: run?.status ?? null,
|
|
407
|
+
pendingDelegation: run?.pendingDelegation ?? null,
|
|
408
|
+
completionEvidence: completion,
|
|
409
|
+
snapshot,
|
|
410
|
+
lastReviewedCommit: metadata.last_reviewed_commit
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
return { domain: { consistent: false } };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
if (operation.family === 'task-create') {
|
|
421
|
+
return { domain: taskCreateDomainEvidence(manifest, output) };
|
|
422
|
+
}
|
|
423
|
+
if (operation.family === 'codex-controller') {
|
|
424
|
+
return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
|
|
425
|
+
}
|
|
426
|
+
if (operation.family === 'task-workflow') {
|
|
427
|
+
return {
|
|
428
|
+
domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
return { domain: null };
|
|
432
|
+
}
|
|
433
|
+
function readCommittedCriticalPhases(manifest, requestId) {
|
|
434
|
+
return SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES.filter((phase) => {
|
|
435
|
+
try {
|
|
436
|
+
readSandboxControlTransition(manifest, requestId, phase);
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
catch {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
|
|
445
|
+
const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
|
|
446
|
+
const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
|
|
447
|
+
if (!operation)
|
|
448
|
+
return null;
|
|
449
|
+
if (payload) {
|
|
450
|
+
const payloadTerminal = createSandboxControlTerminalResult(manifest, {
|
|
451
|
+
id: request.id, family: request.family, operation: operationName
|
|
452
|
+
}, payload.stdout);
|
|
453
|
+
if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
|
|
454
|
+
return unknown(request.id);
|
|
455
|
+
}
|
|
456
|
+
const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
|
|
457
|
+
if (evidence.exitCode === 0 && finalization?.status === 'deferred')
|
|
458
|
+
return null;
|
|
459
|
+
if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
|
|
460
|
+
try {
|
|
461
|
+
const registration = readCodexControllerRegistration(manifestPath);
|
|
462
|
+
const recovered = recoverLifecycleRecoveryOperation(request.authority, {
|
|
463
|
+
repoRoot: manifest.repoRoot,
|
|
464
|
+
capabilityStore: createCodexCapabilityStore(),
|
|
465
|
+
buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
|
|
466
|
+
controllerBinding: {
|
|
467
|
+
instanceDigest: registration.controllerInstanceDigest,
|
|
468
|
+
controlGeneration: registration.controlGeneration
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
if (recovered.status !== 'committed')
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
|
|
476
|
+
...requestAuditFields(manifest, request),
|
|
477
|
+
errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
|
|
478
|
+
});
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const recovery = finalization
|
|
483
|
+
? { domain: { consistent: finalization.status === 'matched' } }
|
|
484
|
+
: readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
|
|
485
|
+
const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
|
|
486
|
+
const decision = classifySandboxControlRecovery({
|
|
487
|
+
operation,
|
|
488
|
+
binding,
|
|
489
|
+
startedCommitted: true,
|
|
490
|
+
terminalResult,
|
|
491
|
+
criticalPhases: readCommittedCriticalPhases(manifest, request.id),
|
|
492
|
+
domain: recovery.domain,
|
|
493
|
+
journal: recovery.journal
|
|
494
|
+
});
|
|
495
|
+
if (decision.outcome === 'unknown' || decision.outcome === 'in-progress')
|
|
496
|
+
return unknown(request.id);
|
|
497
|
+
if (decision.outcome === 'not-executed')
|
|
498
|
+
return notExecuted(request.id);
|
|
499
|
+
if (decision.outcome !== 'success' && decision.outcome !== 'failure')
|
|
500
|
+
return null;
|
|
160
501
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
161
|
-
|
|
162
|
-
|
|
502
|
+
if (finalization?.status !== 'matched')
|
|
503
|
+
return null;
|
|
504
|
+
return finalization.response ?? null;
|
|
163
505
|
}
|
|
164
|
-
return genericRecoveryResponse(request
|
|
506
|
+
return genericRecoveryResponse(request, evidence.exitCode, payload);
|
|
165
507
|
}
|
|
166
|
-
function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid) {
|
|
508
|
+
function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
|
|
167
509
|
if (request.family === 'task-finalization' && evidence.exitCode === 0) {
|
|
168
|
-
|
|
510
|
+
if (!terminalResult)
|
|
511
|
+
return { valid: false, payloadReferenced: false };
|
|
512
|
+
const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
|
|
169
513
|
return {
|
|
170
514
|
valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
|
|
171
515
|
payloadReferenced: false
|
|
@@ -192,8 +536,10 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
|
|
|
192
536
|
};
|
|
193
537
|
}
|
|
194
538
|
if (response.outputState === 'unavailable') {
|
|
195
|
-
const
|
|
196
|
-
|
|
539
|
+
const causes = ['recovery', 'publish'];
|
|
540
|
+
const valid = causes.some((cause) => JSON.stringify(response)
|
|
541
|
+
=== JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
|
|
542
|
+
return { valid, payloadReferenced: false };
|
|
197
543
|
}
|
|
198
544
|
return {
|
|
199
545
|
valid: response.payload === undefined
|
|
@@ -202,8 +548,18 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
|
|
|
202
548
|
payloadReferenced: false
|
|
203
549
|
};
|
|
204
550
|
}
|
|
205
|
-
function publishExecutionResult(manifest, request, result, brokerOwns) {
|
|
551
|
+
function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
|
|
206
552
|
const normalized = sanitizeSandboxControlResult(manifest, result);
|
|
553
|
+
writeSandboxControlTerminalResult(manifest, {
|
|
554
|
+
id: request.id,
|
|
555
|
+
family: request.family,
|
|
556
|
+
operation: operationKey(request, normalized.stdout)
|
|
557
|
+
}, normalized.stdout);
|
|
558
|
+
if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
559
|
+
criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
560
|
+
criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
561
|
+
criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
562
|
+
}
|
|
207
563
|
let terminal = null;
|
|
208
564
|
if (request.family === 'task-finalization') {
|
|
209
565
|
const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
|
|
@@ -234,22 +590,21 @@ function publishExecutionResult(manifest, request, result, brokerOwns) {
|
|
|
234
590
|
catch {
|
|
235
591
|
payload = null;
|
|
236
592
|
}
|
|
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
|
-
};
|
|
593
|
+
terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
|
|
248
594
|
}
|
|
249
595
|
}
|
|
250
596
|
if (!brokerOwns())
|
|
251
597
|
return false;
|
|
252
|
-
|
|
598
|
+
if (request.family === 'task-finalization' && normalized.exitCode === 0) {
|
|
599
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
|
|
600
|
+
if (view.state === 'unknown')
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
const committed = writeSandboxControlResponse(manifest, terminal);
|
|
604
|
+
if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
|
|
605
|
+
criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
|
|
606
|
+
}
|
|
607
|
+
return committed;
|
|
253
608
|
}
|
|
254
609
|
function writeAcceptedResponse(manifest, response) {
|
|
255
610
|
atomicWriteJsonNoReplace(acceptedResponsePath(manifest, response.id), response);
|
|
@@ -309,7 +664,20 @@ function bindingReason(manifest) {
|
|
|
309
664
|
return 'SANDBOX_WORKTREE_BINDING_LOST';
|
|
310
665
|
}
|
|
311
666
|
}
|
|
312
|
-
function
|
|
667
|
+
function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
|
|
668
|
+
const result = validateSandboxControlIdentity({
|
|
669
|
+
publicStatusDir: manifest.publicStatusDir,
|
|
670
|
+
root: path.dirname(path.resolve(manifestPath)),
|
|
671
|
+
mode: manifest.mode,
|
|
672
|
+
taskId: manifest.taskId,
|
|
673
|
+
generation: manifest.generation,
|
|
674
|
+
controlRootId: manifest.controlRootId
|
|
675
|
+
});
|
|
676
|
+
if (result.state !== 'valid') {
|
|
677
|
+
throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
|
|
313
681
|
for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
|
|
314
682
|
if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
|
|
315
683
|
continue;
|
|
@@ -318,14 +686,19 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
318
686
|
const descriptor = executionPath(manifest, entry.name);
|
|
319
687
|
const existingResponse = responsePath(manifest, entry.name);
|
|
320
688
|
const processingDirectory = path.join(manifest.processingDir, entry.name);
|
|
689
|
+
const transitionsDirectory = path.join(processingDirectory, 'transitions');
|
|
690
|
+
const transitionProtocolActive = fs.existsSync(transitionsDirectory);
|
|
691
|
+
const startedCommitted = fs.existsSync(path.join(transitionsDirectory, 'started-committed.json'));
|
|
321
692
|
let payloadReferenced = false;
|
|
322
693
|
if (fs.existsSync(descriptor)) {
|
|
323
694
|
const execution = readExecution(descriptor);
|
|
324
695
|
let resultEvidence = null;
|
|
696
|
+
let terminalResult = null;
|
|
325
697
|
let payload = null;
|
|
326
698
|
let payloadInvalid = false;
|
|
327
699
|
let request = null;
|
|
328
700
|
let existingTerminalResponse = null;
|
|
701
|
+
let terminalReconciled = false;
|
|
329
702
|
try {
|
|
330
703
|
const rawRequest = JSON.parse(fs.readFileSync(path.join(processingDirectory, 'request.json'), 'utf8'));
|
|
331
704
|
request = validateSandboxControlRequest(rawRequest, manifest, {
|
|
@@ -347,6 +720,19 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
347
720
|
resultEvidence = null;
|
|
348
721
|
}
|
|
349
722
|
}
|
|
723
|
+
const terminalResultPathValue = path.join(processingDirectory, 'terminal-result.json');
|
|
724
|
+
if (fs.existsSync(terminalResultPathValue)) {
|
|
725
|
+
try {
|
|
726
|
+
terminalResult = readSandboxControlTerminalResult(terminalResultPathValue);
|
|
727
|
+
if (terminalResult.requestId !== entry.name || terminalResult.generation !== manifest.generation
|
|
728
|
+
|| !request || !terminalResultMatchesRequest(request, terminalResult)) {
|
|
729
|
+
terminalResult = null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
catch {
|
|
733
|
+
terminalResult = null;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
350
736
|
const publishedPayloadPath = payloadPath(manifest, entry.name);
|
|
351
737
|
if (fs.existsSync(publishedPayloadPath)) {
|
|
352
738
|
try {
|
|
@@ -360,12 +746,24 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
360
746
|
payloadInvalid = true;
|
|
361
747
|
}
|
|
362
748
|
}
|
|
749
|
+
if (transitionProtocolActive && startedCommitted && !terminalResult) {
|
|
750
|
+
if (!brokerOwns())
|
|
751
|
+
return false;
|
|
752
|
+
if (!terminateSandboxControlExecution(execution)) {
|
|
753
|
+
throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
|
|
754
|
+
}
|
|
755
|
+
appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
|
|
756
|
+
writeSandboxControlResponse(manifest, unknown(entry.name));
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
if (transitionProtocolActive && !startedCommitted && terminalResult)
|
|
760
|
+
continue;
|
|
363
761
|
if (!terminateSandboxControlExecution(execution)) {
|
|
364
762
|
throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
|
|
365
763
|
}
|
|
366
764
|
if (!brokerOwns())
|
|
367
765
|
return false;
|
|
368
|
-
|
|
766
|
+
appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
|
|
369
767
|
let terminal = false;
|
|
370
768
|
if (fs.existsSync(existingResponse)) {
|
|
371
769
|
try {
|
|
@@ -385,7 +783,7 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
385
783
|
throw new Error('payload missing or mismatched');
|
|
386
784
|
payloadReferenced = true;
|
|
387
785
|
}
|
|
388
|
-
else if (terminal && (response.outputState !== undefined
|
|
786
|
+
else if (terminal && ((response.outputState !== undefined && response.outputState !== 'unavailable')
|
|
389
787
|
|| response.payload !== undefined && response.payload !== null)) {
|
|
390
788
|
throw new Error('payload state invalid');
|
|
391
789
|
}
|
|
@@ -399,26 +797,66 @@ function recoverProcessing(manifest, brokerOwns) {
|
|
|
399
797
|
if (terminal && (!request || !resultEvidence))
|
|
400
798
|
continue;
|
|
401
799
|
if (terminal && request && resultEvidence) {
|
|
402
|
-
|
|
800
|
+
if (!terminalResult && request.family !== 'task-create')
|
|
801
|
+
continue;
|
|
802
|
+
const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
|
|
403
803
|
if (!reconciliation.valid)
|
|
404
804
|
continue;
|
|
805
|
+
terminalReconciled = true;
|
|
405
806
|
payloadReferenced = reconciliation.payloadReferenced;
|
|
807
|
+
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
808
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
809
|
+
if (view.state === 'unknown')
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (!terminalResult && !terminalReconciled) {
|
|
814
|
+
if (terminal)
|
|
815
|
+
continue;
|
|
816
|
+
if (!brokerOwns())
|
|
817
|
+
return false;
|
|
818
|
+
if (!resultEvidence || !request || payloadInvalid)
|
|
819
|
+
continue;
|
|
820
|
+
if (request.family === 'task-create' && resultEvidence.exitCode === 0 && payload === null) {
|
|
821
|
+
writeSandboxControlResponse(manifest, genericRecoveryResponse(request, resultEvidence.exitCode, null));
|
|
822
|
+
terminalReconciled = true;
|
|
823
|
+
terminal = true;
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
writeSandboxControlResponse(manifest, unknown(entry.name));
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
406
829
|
}
|
|
407
830
|
if (!terminal) {
|
|
408
831
|
if (!brokerOwns())
|
|
409
832
|
return false;
|
|
410
833
|
if (!resultEvidence || !request || payloadInvalid)
|
|
411
834
|
continue;
|
|
412
|
-
const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
|
|
835
|
+
const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
|
|
413
836
|
if (!recovered)
|
|
414
837
|
continue;
|
|
838
|
+
if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
|
|
839
|
+
const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
|
|
840
|
+
if (view.state === 'unknown')
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
415
843
|
writeSandboxControlResponse(manifest, recovered);
|
|
844
|
+
const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
|
|
845
|
+
if (transitionProtocolActive && !preserveRecoveryEvidence) {
|
|
846
|
+
writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
|
|
847
|
+
}
|
|
848
|
+
if (preserveRecoveryEvidence)
|
|
849
|
+
continue;
|
|
416
850
|
payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
|
|
417
851
|
}
|
|
418
852
|
}
|
|
419
853
|
else {
|
|
420
854
|
if (!brokerOwns())
|
|
421
855
|
return false;
|
|
856
|
+
if (transitionProtocolActive && startedCommitted) {
|
|
857
|
+
writeSandboxControlResponse(manifest, unknown(entry.name));
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
422
860
|
writeSandboxControlResponse(manifest, notExecuted(entry.name));
|
|
423
861
|
}
|
|
424
862
|
if (!brokerOwns())
|
|
@@ -434,7 +872,13 @@ function delay(ms) {
|
|
|
434
872
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
435
873
|
}
|
|
436
874
|
export function sandboxControlSafeEnv(env = process.env) {
|
|
437
|
-
return Object.fromEntries(Object.entries(env).filter(([key]) =>
|
|
875
|
+
return Object.fromEntries(Object.entries(env).filter(([key]) => {
|
|
876
|
+
const normalized = key.toUpperCase();
|
|
877
|
+
return !normalized.startsWith('AGENT_INFRA_CONTROL_')
|
|
878
|
+
&& normalized !== 'AGENT_INFRA_TASK_ID'
|
|
879
|
+
&& normalized !== 'AGENT_INFRA_RUNTIME_DIR'
|
|
880
|
+
&& normalized !== 'AGENT_INFRA_EXECUTOR_MANIFEST';
|
|
881
|
+
}));
|
|
438
882
|
}
|
|
439
883
|
export async function serveSandboxControl(manifestPath, signal = new AbortController().signal, options = {}) {
|
|
440
884
|
const timing = options.timing ?? DEFAULT_SANDBOX_CONTROL_TIMING;
|
|
@@ -443,6 +887,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
443
887
|
const prepareExecution = options.prepareExecution ?? prepareSandboxControlExecution;
|
|
444
888
|
const manifest = readSandboxControlManifest(manifestPath);
|
|
445
889
|
const root = path.dirname(manifestPath);
|
|
890
|
+
assertCurrentSandboxControlIdentity(manifest, manifestPath);
|
|
446
891
|
const startTime = getProcessStartTime(process.pid);
|
|
447
892
|
if (!startTime)
|
|
448
893
|
throw new Error('SANDBOX_CONTROL_BROKER_IDENTITY_UNAVAILABLE');
|
|
@@ -490,6 +935,28 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
490
935
|
releaseStartup();
|
|
491
936
|
}
|
|
492
937
|
let active = null;
|
|
938
|
+
let startupReceipt = null;
|
|
939
|
+
if (manifest.taskId) {
|
|
940
|
+
try {
|
|
941
|
+
startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
942
|
+
}
|
|
943
|
+
catch {
|
|
944
|
+
// Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
|
|
945
|
+
startupReceipt = {};
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
let taskView = taskViewForManifest({
|
|
949
|
+
...manifest,
|
|
950
|
+
receipt: startupReceipt
|
|
951
|
+
});
|
|
952
|
+
try {
|
|
953
|
+
const previous = readSandboxControlStatus(manifest.publicStatusDir);
|
|
954
|
+
if (previous.generation === manifest.generation)
|
|
955
|
+
taskView = mergeSandboxTaskView(taskView, previous.taskView);
|
|
956
|
+
}
|
|
957
|
+
catch {
|
|
958
|
+
// A fresh control root has no previous task-view projection.
|
|
959
|
+
}
|
|
493
960
|
let lastState = '';
|
|
494
961
|
let lastStatusAt = 0;
|
|
495
962
|
let nextBindingCheckAt = 0;
|
|
@@ -501,12 +968,36 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
501
968
|
try {
|
|
502
969
|
if (!brokerOwns())
|
|
503
970
|
return;
|
|
504
|
-
|
|
971
|
+
if (taskView.state === 'current' && taskView.observedSource === 'completed') {
|
|
972
|
+
// Recheck durable re-entry evidence before publishing a trusted startup view.
|
|
973
|
+
const unverified = { ...taskView, state: 'unknown',
|
|
974
|
+
reasonCode: 'SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID' };
|
|
975
|
+
try {
|
|
976
|
+
taskView = await completedReentryView(manifest, inspectContainer) ?? unverified;
|
|
977
|
+
}
|
|
978
|
+
catch {
|
|
979
|
+
taskView = unverified;
|
|
980
|
+
}
|
|
981
|
+
if (!brokerOwns())
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
writeSandboxControlStatus(manifest, broker, 'starting', null, null, Date.now(), taskView);
|
|
505
985
|
if (!brokerOwns())
|
|
506
986
|
return;
|
|
507
|
-
|
|
508
|
-
|
|
987
|
+
appendBrokerAudit(manifest, 'broker-start', {
|
|
988
|
+
pid: broker.pid,
|
|
989
|
+
brokerId: broker.brokerId
|
|
990
|
+
});
|
|
991
|
+
if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
|
|
509
992
|
return;
|
|
993
|
+
try {
|
|
994
|
+
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
995
|
+
if (published.generation === manifest.generation)
|
|
996
|
+
taskView = published.taskView;
|
|
997
|
+
}
|
|
998
|
+
catch {
|
|
999
|
+
// The regular heartbeat will publish an invalid or missing projection as unknown.
|
|
1000
|
+
}
|
|
510
1001
|
while (!signal.aborted) {
|
|
511
1002
|
let settledExecution = null;
|
|
512
1003
|
if (!brokerOwns())
|
|
@@ -514,6 +1005,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
514
1005
|
let current;
|
|
515
1006
|
try {
|
|
516
1007
|
current = readSandboxControlManifest(manifestPath);
|
|
1008
|
+
assertCurrentSandboxControlIdentity(current, manifestPath);
|
|
517
1009
|
}
|
|
518
1010
|
catch {
|
|
519
1011
|
break;
|
|
@@ -550,7 +1042,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
550
1042
|
}
|
|
551
1043
|
else if (observation.state === 'unknown') {
|
|
552
1044
|
containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_UNKNOWN';
|
|
553
|
-
|
|
1045
|
+
appendDiagnosticAudit(manifest, 'container-heartbeat-unknown', { reason: observation.reason });
|
|
554
1046
|
const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
|
|
555
1047
|
nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
|
|
556
1048
|
containerBackoffMs = backoff.nextDelayMs;
|
|
@@ -569,7 +1061,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
569
1061
|
if (!brokerOwns())
|
|
570
1062
|
break;
|
|
571
1063
|
containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_ABSENT_GC_FAILED';
|
|
572
|
-
|
|
1064
|
+
appendDiagnosticAudit(manifest, 'container-gc-failed', { reason: controlError(error).code });
|
|
573
1065
|
const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
|
|
574
1066
|
nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
|
|
575
1067
|
containerBackoffMs = backoff.nextDelayMs;
|
|
@@ -589,7 +1081,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
589
1081
|
if (cleanupStaleSandboxControlLease(manifest)) {
|
|
590
1082
|
if (!brokerOwns())
|
|
591
1083
|
break;
|
|
592
|
-
|
|
1084
|
+
appendBrokerAudit(manifest, 'lease-stale-cleanup');
|
|
593
1085
|
}
|
|
594
1086
|
if (readActiveLease(manifest))
|
|
595
1087
|
reasonCode = 'SANDBOX_CONTROL_HANDOFF_ACTIVE';
|
|
@@ -612,14 +1104,25 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
612
1104
|
}
|
|
613
1105
|
reasonCode ??= containerReasonCode;
|
|
614
1106
|
reasonCode ??= bindingReasonCode;
|
|
1107
|
+
if (!reasonCode && !active && taskView.state !== 'current') {
|
|
1108
|
+
try {
|
|
1109
|
+
const reentered = await completedReentryView(manifest, inspectContainer);
|
|
1110
|
+
if (reentered && brokerOwns() && !readActiveLease(manifest) && !bindingCheck(manifest)) {
|
|
1111
|
+
taskView = reentered;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
// Invalid or changed re-entry evidence never clears the existing task view.
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
615
1118
|
const state = reasonCode ? 'parked' : active ? 'busy' : 'healthy';
|
|
616
1119
|
const stateKey = `${state}:${reasonCode ?? ''}:${active?.request.id ?? ''}`;
|
|
617
1120
|
if (brokerOwns() && (stateKey !== lastState || now - lastStatusAt >= timing.controlTickMs)) {
|
|
618
|
-
writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now);
|
|
1121
|
+
writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now, taskView);
|
|
619
1122
|
lastStatusAt = now;
|
|
620
1123
|
}
|
|
621
1124
|
if (brokerOwns() && stateKey !== lastState) {
|
|
622
|
-
|
|
1125
|
+
appendBrokerAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
|
|
623
1126
|
lastState = stateKey;
|
|
624
1127
|
}
|
|
625
1128
|
if (settledExecution) {
|
|
@@ -627,13 +1130,33 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
627
1130
|
break;
|
|
628
1131
|
let terminalCommitted = false;
|
|
629
1132
|
if (settledExecution.result && settledExecution.resultEvidenceWritten) {
|
|
630
|
-
|
|
1133
|
+
if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
|
|
1134
|
+
active = settledExecution;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
|
|
1138
|
+
if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
|
|
1139
|
+
try {
|
|
1140
|
+
const published = readSandboxControlStatus(manifest.publicStatusDir);
|
|
1141
|
+
if (published.generation === manifest.generation)
|
|
1142
|
+
taskView = published.taskView;
|
|
1143
|
+
}
|
|
1144
|
+
catch {
|
|
1145
|
+
taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
631
1148
|
}
|
|
632
1149
|
else {
|
|
633
1150
|
terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
|
|
634
1151
|
}
|
|
635
1152
|
if (!terminalCommitted)
|
|
636
1153
|
return;
|
|
1154
|
+
appendBrokerAudit(manifest, 'executor-result-published', {
|
|
1155
|
+
...requestAuditFields(manifest, settledExecution.request),
|
|
1156
|
+
resultAvailable: settledExecution.result !== null,
|
|
1157
|
+
resultEvidenceWritten: settledExecution.resultEvidenceWritten,
|
|
1158
|
+
...(settledExecution.result ? resultAuditFields(settledExecution.result) : {})
|
|
1159
|
+
});
|
|
637
1160
|
if (!brokerOwns())
|
|
638
1161
|
break;
|
|
639
1162
|
removeAcceptedResponse(manifest, settledExecution.request.id);
|
|
@@ -646,12 +1169,20 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
646
1169
|
const id = name.slice(0, -5);
|
|
647
1170
|
const source = path.join(requestsDir, name);
|
|
648
1171
|
let claimed = null;
|
|
1172
|
+
let validatedRequest = null;
|
|
1173
|
+
let prepared = null;
|
|
649
1174
|
try {
|
|
650
1175
|
if (!brokerOwns()) {
|
|
651
1176
|
retiring = true;
|
|
652
1177
|
break;
|
|
653
1178
|
}
|
|
654
1179
|
claimed = claimRequest(manifest, source, id);
|
|
1180
|
+
appendBrokerAudit(manifest, 'request-claimed', {
|
|
1181
|
+
requestId: id,
|
|
1182
|
+
requestSourcePath: source,
|
|
1183
|
+
requestClaimedPath: claimed,
|
|
1184
|
+
requestProcessingDir: path.dirname(claimed)
|
|
1185
|
+
});
|
|
655
1186
|
if (!brokerOwns()) {
|
|
656
1187
|
retiring = true;
|
|
657
1188
|
break;
|
|
@@ -666,30 +1197,98 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
666
1197
|
if (active)
|
|
667
1198
|
throw new Error('SANDBOX_CONTROL_BUSY');
|
|
668
1199
|
const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
|
|
1200
|
+
validatedRequest = request;
|
|
1201
|
+
criticalRequestPhase(manifest, request, 'validated', 'in-progress');
|
|
1202
|
+
appendBrokerAudit(manifest, 'request-validated', {
|
|
1203
|
+
...requestAuditFields(manifest, request),
|
|
1204
|
+
requestPath: claimed
|
|
1205
|
+
});
|
|
669
1206
|
if (bindingCheck(manifest))
|
|
670
1207
|
throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
|
|
671
1208
|
if (readActiveLease(manifest))
|
|
672
1209
|
throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
|
|
1210
|
+
criticalRequestPhase(manifest, request, 'gated', 'in-progress');
|
|
1211
|
+
appendBrokerAudit(manifest, 'request-gates-passed', {
|
|
1212
|
+
...requestAuditFields(manifest, request),
|
|
1213
|
+
requestPath: claimed,
|
|
1214
|
+
bindingChecked: true,
|
|
1215
|
+
handoffLeaseChecked: true
|
|
1216
|
+
});
|
|
1217
|
+
criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
|
|
673
1218
|
writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
|
|
674
|
-
|
|
1219
|
+
appendBrokerAudit(manifest, 'executor-reservation-written', {
|
|
1220
|
+
...requestAuditFields(manifest, request),
|
|
1221
|
+
requestPath: claimed,
|
|
1222
|
+
executionPath: executionPath(manifest, request.id)
|
|
1223
|
+
});
|
|
1224
|
+
appendBrokerAudit(manifest, 'executor-prepare-start', {
|
|
1225
|
+
...requestAuditFields(manifest, request),
|
|
1226
|
+
requestPath: claimed,
|
|
1227
|
+
executorCwd: manifest.repoRoot,
|
|
1228
|
+
executorEntry: options.internalCliPath ?? process.argv[1] ?? null
|
|
1229
|
+
});
|
|
1230
|
+
const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
|
|
1231
|
+
if (lifecycleAuthority?.status === 'rejected') {
|
|
1232
|
+
throw Object.assign(new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'), { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' });
|
|
1233
|
+
}
|
|
1234
|
+
prepared = await prepareExecution({
|
|
675
1235
|
manifest, manifestPath, request, requestPath: claimed,
|
|
676
|
-
internalCliPath: options.internalCliPath ?? process.argv[1]
|
|
1236
|
+
internalCliPath: options.internalCliPath ?? process.argv[1],
|
|
1237
|
+
...(lifecycleAuthority ? { lifecycleAuthority } : {})
|
|
1238
|
+
});
|
|
1239
|
+
const preparedExecution = prepared;
|
|
1240
|
+
if (!preparedExecution)
|
|
1241
|
+
throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
|
|
1242
|
+
criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
|
|
1243
|
+
appendBrokerAudit(manifest, 'executor-prepared', {
|
|
1244
|
+
...requestAuditFields(manifest, request),
|
|
1245
|
+
requestPath: claimed,
|
|
1246
|
+
executorCwd: manifest.repoRoot,
|
|
1247
|
+
childPid: preparedExecution.execution.child.pid,
|
|
1248
|
+
childStartTime: preparedExecution.execution.child.startTime,
|
|
1249
|
+
childProcessGroupId: preparedExecution.execution.child.processGroupId,
|
|
1250
|
+
executionPath: executionPath(manifest, request.id)
|
|
677
1251
|
});
|
|
678
1252
|
const execution = {
|
|
679
|
-
request, prepared
|
|
1253
|
+
request, prepared: preparedExecution, lifecycleAuthority, result: null,
|
|
1254
|
+
resultEvidenceWritten: false, failure: null, settled: false
|
|
680
1255
|
};
|
|
681
1256
|
active = execution;
|
|
682
|
-
|
|
1257
|
+
preparedExecution.completion.then((result) => {
|
|
683
1258
|
execution.result = sanitizeSandboxControlResult(manifest, result);
|
|
1259
|
+
appendBrokerAudit(manifest, 'executor-completed', {
|
|
1260
|
+
...requestAuditFields(manifest, request),
|
|
1261
|
+
...resultAuditFields(execution.result),
|
|
1262
|
+
childPid: preparedExecution.execution.child.pid,
|
|
1263
|
+
childStartTime: preparedExecution.execution.child.startTime
|
|
1264
|
+
});
|
|
684
1265
|
try {
|
|
685
1266
|
writeSandboxControlResultEvidence(manifest, request.id, execution.result);
|
|
686
1267
|
execution.resultEvidenceWritten = true;
|
|
687
1268
|
execution.settled = true;
|
|
1269
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-written', {
|
|
1270
|
+
...requestAuditFields(manifest, request),
|
|
1271
|
+
resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
|
|
1272
|
+
...resultAuditFields(execution.result)
|
|
1273
|
+
});
|
|
688
1274
|
}
|
|
689
1275
|
catch (error) {
|
|
690
1276
|
execution.failure = error;
|
|
1277
|
+
appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
|
|
1278
|
+
...requestAuditFields(manifest, request),
|
|
1279
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1280
|
+
});
|
|
691
1281
|
}
|
|
692
|
-
}, (error) => {
|
|
1282
|
+
}, (error) => {
|
|
1283
|
+
execution.failure = error;
|
|
1284
|
+
execution.settled = true;
|
|
1285
|
+
appendBrokerAudit(manifest, 'executor-failed', {
|
|
1286
|
+
...requestAuditFields(manifest, request),
|
|
1287
|
+
childPid: preparedExecution.execution.child.pid,
|
|
1288
|
+
childStartTime: preparedExecution.execution.child.startTime,
|
|
1289
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1290
|
+
});
|
|
1291
|
+
});
|
|
693
1292
|
if (!brokerOwns()) {
|
|
694
1293
|
prepared.terminate(false);
|
|
695
1294
|
active = null;
|
|
@@ -702,9 +1301,16 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
702
1301
|
retiring = true;
|
|
703
1302
|
continue;
|
|
704
1303
|
}
|
|
1304
|
+
criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
|
|
705
1305
|
writeAcceptedResponse(manifest, {
|
|
706
1306
|
version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
|
|
707
1307
|
});
|
|
1308
|
+
writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
|
|
1309
|
+
appendBrokerAudit(manifest, 'request-accepted', {
|
|
1310
|
+
...requestAuditFields(manifest, request),
|
|
1311
|
+
acceptedPath: acceptedResponsePath(manifest, id),
|
|
1312
|
+
executionPhase: 'prepared'
|
|
1313
|
+
});
|
|
708
1314
|
if (!brokerOwns()) {
|
|
709
1315
|
prepared.terminate(false);
|
|
710
1316
|
active = null;
|
|
@@ -712,9 +1318,20 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
712
1318
|
break;
|
|
713
1319
|
}
|
|
714
1320
|
try {
|
|
1321
|
+
criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
|
|
1322
|
+
appendBrokerAudit(manifest, 'executor-start', {
|
|
1323
|
+
...requestAuditFields(manifest, request),
|
|
1324
|
+
childPid: prepared.execution.child.pid,
|
|
1325
|
+
childStartTime: prepared.execution.child.startTime,
|
|
1326
|
+
executionPath: executionPath(manifest, request.id)
|
|
1327
|
+
});
|
|
715
1328
|
prepared.start(brokerOwns);
|
|
716
1329
|
}
|
|
717
1330
|
catch (error) {
|
|
1331
|
+
appendBrokerAudit(manifest, 'executor-start-failed', {
|
|
1332
|
+
...requestAuditFields(manifest, request),
|
|
1333
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1334
|
+
});
|
|
718
1335
|
const owned = brokerOwns();
|
|
719
1336
|
prepared.terminate(owned);
|
|
720
1337
|
if (!owned || !brokerOwns()) {
|
|
@@ -736,20 +1353,39 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
736
1353
|
retiring = true;
|
|
737
1354
|
break;
|
|
738
1355
|
}
|
|
739
|
-
|
|
1356
|
+
appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
|
|
740
1357
|
continue;
|
|
741
1358
|
}
|
|
742
1359
|
}
|
|
743
1360
|
catch (error) {
|
|
1361
|
+
if (prepared) {
|
|
1362
|
+
try {
|
|
1363
|
+
prepared.terminate(brokerOwns());
|
|
1364
|
+
}
|
|
1365
|
+
catch { /* cleanup is best effort after a failed admission */ }
|
|
1366
|
+
prepared = null;
|
|
1367
|
+
active = null;
|
|
1368
|
+
}
|
|
744
1369
|
if (!brokerOwns()) {
|
|
745
1370
|
retiring = true;
|
|
746
1371
|
break;
|
|
747
1372
|
}
|
|
1373
|
+
const detail = controlError(error);
|
|
1374
|
+
appendBrokerAudit(manifest, 'request-rejected', {
|
|
1375
|
+
requestId: id,
|
|
1376
|
+
requestFamily: validatedRequest?.family ?? null,
|
|
1377
|
+
sandboxTaskId: manifest.taskId,
|
|
1378
|
+
requestPath: claimed,
|
|
1379
|
+
errorCode: detail.code,
|
|
1380
|
+
errorRetryable: detail.retryable,
|
|
1381
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
1382
|
+
});
|
|
748
1383
|
writeSandboxControlResponse(manifest, rejected(id, error));
|
|
749
1384
|
if (!brokerOwns()) {
|
|
750
1385
|
retiring = true;
|
|
751
1386
|
break;
|
|
752
1387
|
}
|
|
1388
|
+
removeAcceptedResponse(manifest, id);
|
|
753
1389
|
if (claimed)
|
|
754
1390
|
fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
|
|
755
1391
|
}
|
|
@@ -775,7 +1411,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
775
1411
|
}
|
|
776
1412
|
}
|
|
777
1413
|
if (owned && active.result && active.resultEvidenceWritten) {
|
|
778
|
-
if (
|
|
1414
|
+
if (consumeLifecycleAuthorityForResult(manifest, active)
|
|
1415
|
+
&& publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
|
|
779
1416
|
if (brokerOwns()) {
|
|
780
1417
|
removeAcceptedResponse(manifest, active.request.id);
|
|
781
1418
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -787,7 +1424,11 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
787
1424
|
const terminationConfirmed = active.prepared.terminate(owned);
|
|
788
1425
|
if (owned && brokerOwns() && active.request.family === 'task-finalization') {
|
|
789
1426
|
const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
|
|
790
|
-
|
|
1427
|
+
const view = recovered.status === 'matched'
|
|
1428
|
+
? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
|
|
1429
|
+
: null;
|
|
1430
|
+
if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
|
|
1431
|
+
&& writeSandboxControlResponse(manifest, recovered.response)) {
|
|
791
1432
|
if (terminationConfirmed && brokerOwns()) {
|
|
792
1433
|
removeAcceptedResponse(manifest, active.request.id);
|
|
793
1434
|
fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
|
|
@@ -801,7 +1442,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
|
|
|
801
1442
|
}
|
|
802
1443
|
}
|
|
803
1444
|
if (brokerOwns()) {
|
|
804
|
-
|
|
1445
|
+
appendDiagnosticAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });
|
|
805
1446
|
try {
|
|
806
1447
|
if (fs.readFileSync(brokerPath, 'utf8') === brokerRecord)
|
|
807
1448
|
fs.unlinkSync(brokerPath);
|