@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
|
@@ -16,6 +16,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.ts';
|
|
|
16
16
|
import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
|
|
17
17
|
import { detectEngine } from '../engine.ts';
|
|
18
18
|
import { resolveSandboxTarget } from '../workspace-identity.ts';
|
|
19
|
+
import { readSandboxControlStatusForRow } from './control-status.ts';
|
|
19
20
|
|
|
20
21
|
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
|
|
21
22
|
|
|
@@ -93,6 +94,21 @@ export function show(args: string[] = []): void {
|
|
|
93
94
|
process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
|
|
94
95
|
process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
|
|
95
96
|
|
|
97
|
+
p.log.step('Control status');
|
|
98
|
+
const controlStatus = container
|
|
99
|
+
? readSandboxControlStatusForRow(config, container)
|
|
100
|
+
: null;
|
|
101
|
+
if (!controlStatus) {
|
|
102
|
+
p.log.warn(' Control status unavailable');
|
|
103
|
+
} else {
|
|
104
|
+
process.stdout.write(` Health: ${controlStatus.state}\n`);
|
|
105
|
+
process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
|
|
106
|
+
process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
|
|
107
|
+
if (controlStatus.taskView.reasonCode) {
|
|
108
|
+
process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
for (const tool of detail.toolStates) {
|
|
97
113
|
p.log.step(`${tool.name} state`);
|
|
98
114
|
if (tool.entries.length === 0) {
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import type { SandboxControlManifest, SandboxControlFamily, SandboxControlRequest } from './protocol.ts';
|
|
5
|
+
import { identityDigest as digestIdentity } from './identity-sentinel.ts';
|
|
6
|
+
import { acquireSandboxResourceLock } from './native-file-lock.ts';
|
|
7
|
+
import { fsyncDirectory, writeDurableFile } from '../../fs/durable-write.ts';
|
|
8
|
+
|
|
9
|
+
export const SANDBOX_CONTROL_AUDIT_PHASES = Object.freeze([
|
|
10
|
+
'validated', 'gated', 'reserved', 'prepared', 'accepted-authorized', 'accepted-committed',
|
|
11
|
+
'start-authorized', 'started-committed', 'completed', 'evidence-written',
|
|
12
|
+
'publish-authorized', 'published-committed', 'rejected', 'recovered'
|
|
13
|
+
] as const);
|
|
14
|
+
export type SandboxControlAuditPhase = typeof SANDBOX_CONTROL_AUDIT_PHASES[number];
|
|
15
|
+
export type SandboxControlAuditOutcome = 'not-executed' | 'in-progress' | 'success' | 'failure' | 'unknown' | 'rejected';
|
|
16
|
+
|
|
17
|
+
export type SandboxControlAuditContext = Readonly<{
|
|
18
|
+
requestId: string;
|
|
19
|
+
taskId: string | null;
|
|
20
|
+
family: SandboxControlFamily | null;
|
|
21
|
+
operation: string | null;
|
|
22
|
+
phase: SandboxControlAuditPhase;
|
|
23
|
+
outcome: SandboxControlAuditOutcome;
|
|
24
|
+
generation: string;
|
|
25
|
+
identityDigest: string;
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
export type SandboxControlTransition = Readonly<{
|
|
29
|
+
version: 1;
|
|
30
|
+
requestId: string;
|
|
31
|
+
generation: string;
|
|
32
|
+
phase: SandboxControlAuditPhase;
|
|
33
|
+
at: number;
|
|
34
|
+
identityDigest: string;
|
|
35
|
+
reference: string | null;
|
|
36
|
+
}>;
|
|
37
|
+
|
|
38
|
+
const REQUEST_ID = /^[a-f0-9-]{16,64}$/u;
|
|
39
|
+
const SAFE_OPERATION = /^[a-z][a-z0-9.-]{0,96}$/u;
|
|
40
|
+
const AUDIT_MAX_BYTES = 1024 * 1024;
|
|
41
|
+
|
|
42
|
+
export function requestAuditFields(manifest: SandboxControlManifest, request: SandboxControlRequest) {
|
|
43
|
+
return {
|
|
44
|
+
requestId: request.id, requestFamily: request.family, sandboxTaskId: manifest.taskId,
|
|
45
|
+
requestGeneration: request.generation, requestIssuedAt: request.issuedAt, requestExpiresAt: request.expiresAt
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resultAuditFields(result: Readonly<{ exitCode: number; stdout: string; stderr: string }>) {
|
|
50
|
+
return {
|
|
51
|
+
exitCode: result.exitCode,
|
|
52
|
+
outputBytes: Buffer.byteLength(result.stdout, 'utf8'), errorBytes: Buffer.byteLength(result.stderr, 'utf8'),
|
|
53
|
+
outputDigest: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
|
|
54
|
+
errorDigest: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function auditRoot(manifest: SandboxControlManifest): string {
|
|
59
|
+
return path.dirname(path.resolve(manifest.publicStatusDir));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function auditPath(manifest: SandboxControlManifest): string {
|
|
63
|
+
return path.join(auditRoot(manifest), 'audit.ndjson');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function manifestIdentityDigest(manifest: SandboxControlManifest): string {
|
|
67
|
+
return digestIdentity({
|
|
68
|
+
version: 1,
|
|
69
|
+
mode: manifest.mode,
|
|
70
|
+
taskId: manifest.taskId,
|
|
71
|
+
generation: manifest.generation,
|
|
72
|
+
controlRootId: manifest.controlRootId
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function createSandboxControlAuditContext(
|
|
77
|
+
manifest: SandboxControlManifest,
|
|
78
|
+
params: Readonly<{
|
|
79
|
+
requestId: string;
|
|
80
|
+
family: SandboxControlFamily | null;
|
|
81
|
+
operation?: string | null;
|
|
82
|
+
phase: SandboxControlAuditPhase;
|
|
83
|
+
outcome: SandboxControlAuditOutcome;
|
|
84
|
+
}>
|
|
85
|
+
): SandboxControlAuditContext {
|
|
86
|
+
if (!REQUEST_ID.test(params.requestId)) throw new Error('SANDBOX_CONTROL_AUDIT_REQUEST_INVALID');
|
|
87
|
+
if (params.operation !== null && params.operation !== undefined && !SAFE_OPERATION.test(params.operation)) {
|
|
88
|
+
throw new Error('SANDBOX_CONTROL_AUDIT_OPERATION_INVALID');
|
|
89
|
+
}
|
|
90
|
+
if (!SANDBOX_CONTROL_AUDIT_PHASES.includes(params.phase)) throw new Error('SANDBOX_CONTROL_AUDIT_PHASE_INVALID');
|
|
91
|
+
return {
|
|
92
|
+
requestId: params.requestId,
|
|
93
|
+
taskId: manifest.taskId,
|
|
94
|
+
family: params.family,
|
|
95
|
+
operation: params.operation ?? null,
|
|
96
|
+
phase: params.phase,
|
|
97
|
+
outcome: params.outcome,
|
|
98
|
+
generation: manifest.generation,
|
|
99
|
+
identityDigest: manifestIdentityDigest(manifest)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function safeFields(fields: Readonly<Record<string, string | number | boolean | null>>): Record<string, string | number | boolean | null> {
|
|
104
|
+
const result: Record<string, string | number | boolean | null> = {};
|
|
105
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
106
|
+
if (!/^[a-z][a-zA-Z0-9]{0,63}$/u.test(key)) continue;
|
|
107
|
+
if (/(token|secret|credential|proof|password|path|root|cwd|stdout|stderr|args|command|taskref)/iu.test(key)) continue;
|
|
108
|
+
if (typeof value === 'string' && value.length > 256) continue;
|
|
109
|
+
result[key] = value;
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function requestHasTerminalTransition(directory: string): boolean {
|
|
115
|
+
const transitions = path.join(directory, 'transitions');
|
|
116
|
+
if (!fs.existsSync(transitions)) return false;
|
|
117
|
+
return ['published-committed', 'recovered'].some((phase) => fs.existsSync(path.join(transitions, `${phase}.json`)));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function hasActiveRequest(manifest: SandboxControlManifest): boolean {
|
|
121
|
+
if (!fs.existsSync(manifest.processingDir)) return false;
|
|
122
|
+
return fs.readdirSync(manifest.processingDir, { withFileTypes: true })
|
|
123
|
+
.some((entry) => entry.isDirectory() && REQUEST_ID.test(entry.name)
|
|
124
|
+
&& !requestHasTerminalTransition(path.join(manifest.processingDir, entry.name)));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function rotateAuditUnderLock(manifest: SandboxControlManifest, filePath: string): void {
|
|
128
|
+
if (!fs.existsSync(filePath) || fs.statSync(filePath).size < AUDIT_MAX_BYTES) return;
|
|
129
|
+
if (hasActiveRequest(manifest)) return;
|
|
130
|
+
fs.rmSync(`${filePath}.1`, { force: true });
|
|
131
|
+
fs.renameSync(filePath, `${filePath}.1`);
|
|
132
|
+
fsyncDirectory(auditRoot(manifest));
|
|
133
|
+
const descriptor = fs.openSync(filePath, 'a', 0o600);
|
|
134
|
+
try {
|
|
135
|
+
fs.fsyncSync(descriptor);
|
|
136
|
+
} finally {
|
|
137
|
+
fs.closeSync(descriptor);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function appendAuditRecord(
|
|
142
|
+
manifest: SandboxControlManifest,
|
|
143
|
+
record: Readonly<Record<string, unknown>>,
|
|
144
|
+
critical: boolean
|
|
145
|
+
): void {
|
|
146
|
+
const root = auditRoot(manifest);
|
|
147
|
+
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
148
|
+
const lock = acquireSandboxResourceLock(`sandbox-control-audit:${root}`);
|
|
149
|
+
try {
|
|
150
|
+
const filePath = auditPath(manifest);
|
|
151
|
+
rotateAuditUnderLock(manifest, filePath);
|
|
152
|
+
const descriptor = fs.openSync(filePath, 'a', 0o600);
|
|
153
|
+
try {
|
|
154
|
+
fs.writeSync(descriptor, `${JSON.stringify(record)}\n`, undefined, 'utf8');
|
|
155
|
+
if (critical) fs.fsyncSync(descriptor);
|
|
156
|
+
} finally {
|
|
157
|
+
fs.closeSync(descriptor);
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (critical) throw new Error(`SANDBOX_CONTROL_AUDIT_UNAVAILABLE: ${error instanceof Error ? error.message : String(error)}`);
|
|
161
|
+
} finally {
|
|
162
|
+
lock.release();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function appendCriticalAudit(
|
|
167
|
+
manifest: SandboxControlManifest,
|
|
168
|
+
context: SandboxControlAuditContext,
|
|
169
|
+
fields: Readonly<Record<string, string | number | boolean | null>> = {},
|
|
170
|
+
now = Date.now()
|
|
171
|
+
): void {
|
|
172
|
+
appendAuditRecord(manifest, {
|
|
173
|
+
version: 2,
|
|
174
|
+
at: now,
|
|
175
|
+
event: context.phase,
|
|
176
|
+
...context,
|
|
177
|
+
...safeFields(fields)
|
|
178
|
+
}, true);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function appendDiagnosticAudit(
|
|
182
|
+
manifest: SandboxControlManifest,
|
|
183
|
+
event: string,
|
|
184
|
+
fields: Readonly<Record<string, string | number | boolean | null>> = {},
|
|
185
|
+
now = Date.now()
|
|
186
|
+
): void {
|
|
187
|
+
try {
|
|
188
|
+
appendAuditRecord(manifest, {
|
|
189
|
+
version: 2,
|
|
190
|
+
at: now,
|
|
191
|
+
event,
|
|
192
|
+
generation: manifest.generation,
|
|
193
|
+
identityDigest: manifestIdentityDigest(manifest),
|
|
194
|
+
...safeFields(fields)
|
|
195
|
+
}, false);
|
|
196
|
+
} catch {
|
|
197
|
+
// Diagnostics are deliberately best effort.
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function transitionPath(manifest: SandboxControlManifest, requestId: string, phase: SandboxControlAuditPhase): string {
|
|
202
|
+
if (!REQUEST_ID.test(requestId)) throw new Error('SANDBOX_CONTROL_TRANSITION_REQUEST_INVALID');
|
|
203
|
+
return path.join(manifest.processingDir, requestId, 'transitions', `${phase}.json`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function writeSandboxControlTransition(
|
|
207
|
+
manifest: SandboxControlManifest,
|
|
208
|
+
params: Readonly<{
|
|
209
|
+
requestId: string;
|
|
210
|
+
phase: SandboxControlAuditPhase;
|
|
211
|
+
reference?: string | null;
|
|
212
|
+
now?: number;
|
|
213
|
+
}>
|
|
214
|
+
): SandboxControlTransition {
|
|
215
|
+
const transition: SandboxControlTransition = {
|
|
216
|
+
version: 1,
|
|
217
|
+
requestId: params.requestId,
|
|
218
|
+
generation: manifest.generation,
|
|
219
|
+
phase: params.phase,
|
|
220
|
+
at: params.now ?? Date.now(),
|
|
221
|
+
identityDigest: manifestIdentityDigest(manifest),
|
|
222
|
+
reference: params.reference ?? null
|
|
223
|
+
};
|
|
224
|
+
const target = transitionPath(manifest, params.requestId, params.phase);
|
|
225
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
226
|
+
try {
|
|
227
|
+
writeDurableFile(target, `${JSON.stringify(transition)}\n`, { mode: 0o600, replace: false });
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
230
|
+
const existing = readSandboxControlTransition(manifest, params.requestId, params.phase);
|
|
231
|
+
if (JSON.stringify(existing) !== JSON.stringify(transition)) throw new Error('SANDBOX_CONTROL_TRANSITION_CONFLICT');
|
|
232
|
+
}
|
|
233
|
+
return transition;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function readSandboxControlTransition(
|
|
237
|
+
manifest: SandboxControlManifest,
|
|
238
|
+
requestId: string,
|
|
239
|
+
phase: SandboxControlAuditPhase
|
|
240
|
+
): SandboxControlTransition {
|
|
241
|
+
const filePath = transitionPath(manifest, requestId, phase);
|
|
242
|
+
if (!fs.existsSync(filePath)) throw new Error('SANDBOX_CONTROL_TRANSITION_MISSING');
|
|
243
|
+
let value: unknown;
|
|
244
|
+
try { value = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { throw new Error('SANDBOX_CONTROL_TRANSITION_INVALID'); }
|
|
245
|
+
const candidate = value as Partial<SandboxControlTransition> | null;
|
|
246
|
+
if (!candidate || candidate.version !== 1 || candidate.requestId !== requestId
|
|
247
|
+
|| candidate.generation !== manifest.generation || candidate.phase !== phase
|
|
248
|
+
|| !Number.isSafeInteger(candidate.at) || typeof candidate.identityDigest !== 'string'
|
|
249
|
+
|| (candidate.reference !== null && typeof candidate.reference !== 'string')) {
|
|
250
|
+
throw new Error('SANDBOX_CONTROL_TRANSITION_INVALID');
|
|
251
|
+
}
|
|
252
|
+
return candidate as SandboxControlTransition;
|
|
253
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import { hasTaskBoundMarker } from '../../internal/task-operation-registry.ts';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { randomUUID } from 'node:crypto';
|
|
4
5
|
import {
|
|
@@ -12,7 +13,8 @@ import {
|
|
|
12
13
|
type SandboxTaskCreateRequest,
|
|
13
14
|
type SandboxTaskCommandRequest,
|
|
14
15
|
type SandboxTaskFinalizationRequest,
|
|
15
|
-
type SandboxCodexControllerRequest
|
|
16
|
+
type SandboxCodexControllerRequest,
|
|
17
|
+
type SandboxTaskWorkflowRequest
|
|
16
18
|
} from './protocol.ts';
|
|
17
19
|
import type {
|
|
18
20
|
CodexControllerLeaseProofV1,
|
|
@@ -22,6 +24,11 @@ import type { ProcessIdentity } from '../../server/process-state.ts';
|
|
|
22
24
|
import { normalizeAgentToken } from '../../agent-clients/tokens.ts';
|
|
23
25
|
import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts';
|
|
24
26
|
import type { TaskCreateCandidateV1 } from '../../task/create.ts';
|
|
27
|
+
import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
|
|
28
|
+
import { readSandboxControlIdentitySentinel } from './identity-sentinel.ts';
|
|
29
|
+
import type { TaskWorkflowRequest } from './task-workflow.ts';
|
|
30
|
+
import type { LifecycleAuthorityRequestV1 } from '../../task/control-authority.ts';
|
|
31
|
+
import { configuredShortIdLength, resolveShortIdReadOnly } from '../../task/short-id.ts';
|
|
25
32
|
|
|
26
33
|
const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
|
|
27
34
|
|
|
@@ -52,7 +59,30 @@ function clientError(
|
|
|
52
59
|
throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
|
|
53
60
|
}
|
|
54
61
|
|
|
55
|
-
function preflight(
|
|
62
|
+
function preflight(
|
|
63
|
+
statusDir: string,
|
|
64
|
+
generation: string,
|
|
65
|
+
taskViewEffect: TaskViewAccessEffect | null = null,
|
|
66
|
+
now = Date.now(),
|
|
67
|
+
env: NodeJS.ProcessEnv = process.env
|
|
68
|
+
): void {
|
|
69
|
+
if (fs.existsSync(statusDir)) {
|
|
70
|
+
let identity;
|
|
71
|
+
try {
|
|
72
|
+
identity = readSandboxControlIdentitySentinel(statusDir);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
const code = error instanceof Error && error.message.endsWith('MISSING')
|
|
75
|
+
? 'SANDBOX_CONTROL_IDENTITY_MISSING'
|
|
76
|
+
: 'SANDBOX_CONTROL_IDENTITY_MALFORMED';
|
|
77
|
+
clientError(code, 'sandbox control identity is missing or invalid', false);
|
|
78
|
+
}
|
|
79
|
+
if (identity.generation !== generation) {
|
|
80
|
+
clientError('SANDBOX_CONTROL_IDENTITY_GENERATION_MISMATCH', 'sandbox control identity generation does not match the request', false);
|
|
81
|
+
}
|
|
82
|
+
if (env.AGENT_INFRA_CONTROL_ROOT_ID && identity.controlRootId !== env.AGENT_INFRA_CONTROL_ROOT_ID) {
|
|
83
|
+
clientError('SANDBOX_CONTROL_IDENTITY_ROOT_ID_MISMATCH', 'sandbox control identity root does not match the client configuration', false);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
56
86
|
let status;
|
|
57
87
|
try {
|
|
58
88
|
status = readSandboxControlStatus(statusDir);
|
|
@@ -71,9 +101,30 @@ function preflight(statusDir: string, generation: string, now = Date.now()): voi
|
|
|
71
101
|
clientError(code, 'broker is parked until the host restores a valid binding', true);
|
|
72
102
|
}
|
|
73
103
|
if (status.state !== 'healthy') clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
|
|
104
|
+
if (taskViewEffect && hasTaskBoundMarker(env)) {
|
|
105
|
+
const view = taskViewFromStatus(status);
|
|
106
|
+
if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
|
|
107
|
+
clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
|
|
108
|
+
}
|
|
109
|
+
const access = accessSandboxTaskView(view, taskViewEffect);
|
|
110
|
+
if (!access.allowed) {
|
|
111
|
+
clientError(
|
|
112
|
+
access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED',
|
|
113
|
+
access.message ?? 'task view denied',
|
|
114
|
+
false
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
|
|
121
|
+
if (request.family === 'task-lifecycle' || request.family === 'task-finalization') return 'progress';
|
|
122
|
+
if (request.family === 'task-orchestration') return request.args[1] === 'status' ? 'diagnostic' : 'progress';
|
|
123
|
+
if (request.family === 'task-workflow') return request.workflow.operation === 'artifact-inspect' || request.workflow.operation === 'decision-next-id' ? 'diagnostic' : 'progress';
|
|
124
|
+
return null;
|
|
74
125
|
}
|
|
75
126
|
|
|
76
|
-
function parseResponse(raw: string, id: string): SandboxControlResponse {
|
|
127
|
+
function parseResponse(raw: string, id: string, accepted = false): SandboxControlResponse {
|
|
77
128
|
const response = JSON.parse(raw) as SandboxControlResponse;
|
|
78
129
|
if (response.version !== 2 || response.id !== id
|
|
79
130
|
|| !['completed', 'rejected'].includes(response.phase)
|
|
@@ -81,13 +132,13 @@ function parseResponse(raw: string, id: string): SandboxControlResponse {
|
|
|
81
132
|
|| (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
|
|
82
133
|
|| (response.outputState === 'available' && !response.payload)
|
|
83
134
|
|| (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
|
|
84
|
-
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false,
|
|
135
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
|
|
85
136
|
}
|
|
86
137
|
return response;
|
|
87
138
|
}
|
|
88
139
|
|
|
89
|
-
function readPublishedResponse(raw: string, id: string, channelDir: string): SandboxControlResponse {
|
|
90
|
-
const response = parseResponse(raw, id);
|
|
140
|
+
function readPublishedResponse(raw: string, id: string, channelDir: string, accepted = false): SandboxControlResponse {
|
|
141
|
+
const response = parseResponse(raw, id, accepted);
|
|
91
142
|
if (response.outputState !== 'available') return response;
|
|
92
143
|
const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
|
|
93
144
|
let payload;
|
|
@@ -115,6 +166,44 @@ function cancelPendingRequest(requestPath: string): boolean {
|
|
|
115
166
|
}
|
|
116
167
|
}
|
|
117
168
|
|
|
169
|
+
const CONTROL_TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
|
|
170
|
+
const CONTROL_SHORT_ID_RE = /^\d+$/;
|
|
171
|
+
|
|
172
|
+
type CanonicalRequestTask =
|
|
173
|
+
| Readonly<{ kind: 'none' }>
|
|
174
|
+
| Readonly<{ kind: 'task'; taskId: string }>
|
|
175
|
+
| Readonly<{ kind: 'unresolved' }>;
|
|
176
|
+
|
|
177
|
+
// Use the current short-id contract for the first visible active registry.
|
|
178
|
+
export function resolveVisibleActiveShortId(ref: string): string | null {
|
|
179
|
+
let dir = process.cwd();
|
|
180
|
+
for (let depth = 0; depth < 64; depth += 1) {
|
|
181
|
+
const registryPath = path.join(dir, '.agents', 'workspace', 'active', '.short-ids.json');
|
|
182
|
+
if (fs.existsSync(registryPath)) {
|
|
183
|
+
const resolved = resolveShortIdReadOnly(ref, dir, { shortIdLength: configuredShortIdLength(dir) });
|
|
184
|
+
return resolved.ok ? resolved.taskId : null;
|
|
185
|
+
}
|
|
186
|
+
const parent = path.dirname(dir);
|
|
187
|
+
if (parent === dir) break;
|
|
188
|
+
dir = parent;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// A request's leading argument is only a task reference for some families and
|
|
194
|
+
// may be a documented short id. Compare canonical ids so a bound sandbox keeps
|
|
195
|
+
// refusing other tasks without rejecting its own task's short id.
|
|
196
|
+
function canonicalRequestTask(request: SandboxControlRequest): CanonicalRequestTask {
|
|
197
|
+
const ref = request.family === 'task-workflow'
|
|
198
|
+
? request.workflow.taskId
|
|
199
|
+
: 'args' in request ? request.args[0] ?? null : null;
|
|
200
|
+
if (!ref) return { kind: 'none' };
|
|
201
|
+
if (CONTROL_TASK_ID_RE.test(ref)) return { kind: 'task', taskId: ref };
|
|
202
|
+
if (!CONTROL_SHORT_ID_RE.test(ref)) return { kind: 'none' };
|
|
203
|
+
const resolved = resolveVisibleActiveShortId(ref);
|
|
204
|
+
return resolved ? { kind: 'task', taskId: resolved } : { kind: 'unresolved' };
|
|
205
|
+
}
|
|
206
|
+
|
|
118
207
|
function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly<{
|
|
119
208
|
channelDir?: string;
|
|
120
209
|
statusDir?: string;
|
|
@@ -122,7 +211,22 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
122
211
|
}>): SandboxControlResponse {
|
|
123
212
|
const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
|
|
124
213
|
const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
|
|
125
|
-
|
|
214
|
+
const taskViewEffect = taskViewEffectForRequest(request);
|
|
215
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
216
|
+
const identityPath = path.join(statusDir, 'identity.json');
|
|
217
|
+
if (fs.existsSync(statusDir)) {
|
|
218
|
+
const identity = readSandboxControlIdentitySentinel(statusDir);
|
|
219
|
+
const requestTask = canonicalRequestTask(request);
|
|
220
|
+
if (identity.mode === 'task-bound'
|
|
221
|
+
&& ((process.env.AGENT_INFRA_TASK_ID && process.env.AGENT_INFRA_TASK_ID !== identity.taskId)
|
|
222
|
+
|| requestTask.kind === 'unresolved'
|
|
223
|
+
|| (requestTask.kind === 'task' && requestTask.taskId !== identity.taskId))) {
|
|
224
|
+
clientError('SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH', 'request task does not match the sandbox identity', false);
|
|
225
|
+
}
|
|
226
|
+
if (identity.mode === 'branch-only' && (request.family === 'task-finalization' || request.family === 'task-workflow')) {
|
|
227
|
+
clientError('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks', false);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
126
230
|
const encoded = `${JSON.stringify(request)}\n`;
|
|
127
231
|
if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
|
|
128
232
|
clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
|
|
@@ -154,7 +258,9 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
154
258
|
}
|
|
155
259
|
let response: SandboxControlResponse;
|
|
156
260
|
try {
|
|
157
|
-
|
|
261
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
262
|
+
accepted = true;
|
|
263
|
+
response = readPublishedResponse(raw, request.id, channelDir, accepted);
|
|
158
264
|
malformedResponseRaw = null;
|
|
159
265
|
} catch (error) {
|
|
160
266
|
if (!(error instanceof SyntaxError)) throw error;
|
|
@@ -176,7 +282,7 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
176
282
|
}
|
|
177
283
|
if (!accepted) {
|
|
178
284
|
try {
|
|
179
|
-
preflight(statusDir, request.generation);
|
|
285
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
180
286
|
} catch (error) {
|
|
181
287
|
if (cancelPendingRequest(requestPath)) throw error;
|
|
182
288
|
clientError(
|
|
@@ -236,7 +342,9 @@ export function recoverSandboxControl(requestId: string, params: Readonly<{
|
|
|
236
342
|
}
|
|
237
343
|
let response: SandboxControlResponse;
|
|
238
344
|
try {
|
|
239
|
-
|
|
345
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
346
|
+
accepted = true;
|
|
347
|
+
response = readPublishedResponse(raw, requestId, channelDir, accepted);
|
|
240
348
|
malformedResponseRaw = null;
|
|
241
349
|
} catch (error) {
|
|
242
350
|
if (!(error instanceof SyntaxError)) throw error;
|
|
@@ -276,7 +384,7 @@ export function requestSandboxControl(params: Readonly<{
|
|
|
276
384
|
token?: string; generation?: string; timeoutMs?: number;
|
|
277
385
|
}>): SandboxControlResponse {
|
|
278
386
|
if (!isSandboxControlFamily(params.family)) clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
|
|
279
|
-
if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization') {
|
|
387
|
+
if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization' || params.family === 'task-workflow') {
|
|
280
388
|
clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
|
|
281
389
|
}
|
|
282
390
|
const auth = authority(params);
|
|
@@ -295,6 +403,7 @@ export function requestSandboxTaskControl(params: Readonly<{
|
|
|
295
403
|
family: 'task-lifecycle' | 'task-orchestration';
|
|
296
404
|
args: string[];
|
|
297
405
|
controllerProof: CodexControllerLeaseProofV1 | null;
|
|
406
|
+
authority?: LifecycleAuthorityRequestV1;
|
|
298
407
|
channelDir?: string;
|
|
299
408
|
statusDir?: string;
|
|
300
409
|
token?: string;
|
|
@@ -305,14 +414,15 @@ export function requestSandboxTaskControl(params: Readonly<{
|
|
|
305
414
|
const issuedAt = Date.now();
|
|
306
415
|
const request: SandboxTaskCommandRequest = {
|
|
307
416
|
version: 3,
|
|
308
|
-
id: randomUUID(),
|
|
417
|
+
id: params.authority?.requestId ?? randomUUID(),
|
|
309
418
|
...auth,
|
|
310
419
|
issuedAt,
|
|
311
420
|
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
312
421
|
family: params.family,
|
|
313
422
|
args: params.args,
|
|
314
423
|
controllerProcess: null,
|
|
315
|
-
controllerProof: params.controllerProof
|
|
424
|
+
controllerProof: params.authority ? null : params.controllerProof,
|
|
425
|
+
...(params.authority ? { authority: params.authority } : {})
|
|
316
426
|
};
|
|
317
427
|
return exchangeSandboxControl(request, params);
|
|
318
428
|
}
|
|
@@ -345,6 +455,33 @@ export function requestSandboxTaskFinalization(params: Readonly<{
|
|
|
345
455
|
return exchangeSandboxControl(request, params);
|
|
346
456
|
}
|
|
347
457
|
|
|
458
|
+
export function requestSandboxTaskWorkflow(params: Readonly<{
|
|
459
|
+
workflow: TaskWorkflowRequest;
|
|
460
|
+
channelDir?: string;
|
|
461
|
+
statusDir?: string;
|
|
462
|
+
token?: string;
|
|
463
|
+
generation?: string;
|
|
464
|
+
timeoutMs?: number;
|
|
465
|
+
authority?: LifecycleAuthorityRequestV1;
|
|
466
|
+
}>): SandboxControlResponse {
|
|
467
|
+
const auth = authority(params);
|
|
468
|
+
const issuedAt = Date.now();
|
|
469
|
+
const request: SandboxTaskWorkflowRequest = {
|
|
470
|
+
version: 3,
|
|
471
|
+
id: params.workflow.id,
|
|
472
|
+
...auth,
|
|
473
|
+
issuedAt,
|
|
474
|
+
expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
|
|
475
|
+
family: 'task-workflow',
|
|
476
|
+
args: [],
|
|
477
|
+
controllerProcess: null,
|
|
478
|
+
controllerProof: null,
|
|
479
|
+
workflow: params.workflow,
|
|
480
|
+
...(params.authority ? { authority: params.authority } : {})
|
|
481
|
+
};
|
|
482
|
+
return exchangeSandboxControl(request, params);
|
|
483
|
+
}
|
|
484
|
+
|
|
348
485
|
type CodexControllerClosed = Readonly<{
|
|
349
486
|
version: 1;
|
|
350
487
|
status: 'closed';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveTaskWorkspace } from '../task-resolver.ts';
|
|
4
|
+
import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
|
|
5
|
+
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
|
|
6
|
+
import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
|
|
7
|
+
import { inspectSandboxControlContainer } from './container-identity.ts';
|
|
8
|
+
import type { SandboxControlManifest } from './protocol.ts';
|
|
9
|
+
import { atomicWriteJson, readSandboxControlStatus } from './state.ts';
|
|
10
|
+
import { taskViewForManifest, type SandboxTaskView } from './task-view.ts';
|
|
11
|
+
|
|
12
|
+
type ReentryEvidence = Readonly<{
|
|
13
|
+
taskId: string;
|
|
14
|
+
generation: string;
|
|
15
|
+
containerId: string;
|
|
16
|
+
branch: string;
|
|
17
|
+
worktreeRoot: string;
|
|
18
|
+
source: string;
|
|
19
|
+
sourceDevice: string;
|
|
20
|
+
sourceInode: string;
|
|
21
|
+
receipt: NonNullable<SandboxTaskView['receipt']>;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
function evidencePath(manifest: SandboxControlManifest): string {
|
|
25
|
+
return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function invalid(): Error {
|
|
29
|
+
return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function prepareCompletedReentry(
|
|
33
|
+
manifest: SandboxControlManifest,
|
|
34
|
+
inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
|
|
35
|
+
): Promise<ReentryEvidence> {
|
|
36
|
+
if (manifest.mode !== 'task-bound' || !manifest.taskId) throw invalid();
|
|
37
|
+
assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
|
|
38
|
+
const container = await inspect(manifest);
|
|
39
|
+
if (container.state !== 'found' || container.id !== manifest.containerIdentity.id) throw invalid();
|
|
40
|
+
const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
|
|
41
|
+
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
|
|
42
|
+
if (task.state !== 'completed' || task.branch !== manifest.branch
|
|
43
|
+
|| metadata.id !== manifest.taskId || metadata.status !== 'completed') throw invalid();
|
|
44
|
+
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
45
|
+
if (!receipt || receipt.taskId !== manifest.taskId) throw invalid();
|
|
46
|
+
const view = taskViewForManifest({ ...manifest, receipt });
|
|
47
|
+
if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt) throw invalid();
|
|
48
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
49
|
+
const previous = status.taskView;
|
|
50
|
+
if (status.generation !== manifest.generation || status.activeRequestId !== null
|
|
51
|
+
|| previous.taskId !== manifest.taskId || !previous.receipt
|
|
52
|
+
|| previous.receipt.receiptId !== view.receipt.receiptId
|
|
53
|
+
|| previous.receipt.generation !== view.receipt.generation
|
|
54
|
+
|| previous.receipt.requestId !== view.receipt.requestId
|
|
55
|
+
|| previous.receipt.revision > view.receipt.revision) throw invalid();
|
|
56
|
+
const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
|
|
57
|
+
const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
|
|
58
|
+
if (source !== path.join(completedRoot, manifest.taskId)) throw invalid();
|
|
59
|
+
const stat = fs.statSync(source, { bigint: true });
|
|
60
|
+
return {
|
|
61
|
+
taskId: manifest.taskId, generation: manifest.generation,
|
|
62
|
+
containerId: container.id, branch: manifest.branch,
|
|
63
|
+
worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
|
|
64
|
+
source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function publishCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): void {
|
|
69
|
+
atomicWriteJson(evidencePath(manifest), evidence);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function completedReentryView(
|
|
73
|
+
manifest: SandboxControlManifest,
|
|
74
|
+
inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
|
|
75
|
+
): Promise<SandboxTaskView | null> {
|
|
76
|
+
const file = evidencePath(manifest);
|
|
77
|
+
if (!fs.existsSync(file)) return null;
|
|
78
|
+
const evidence: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
79
|
+
const current = await prepareCompletedReentry(manifest, inspect);
|
|
80
|
+
if (JSON.stringify(evidence) !== JSON.stringify(current)) throw invalid();
|
|
81
|
+
return {
|
|
82
|
+
state: 'current', taskId: current.taskId, observedSource: 'completed',
|
|
83
|
+
receipt: current.receipt, reasonCode: null
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function waitForCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): Promise<void> {
|
|
88
|
+
const deadline = Date.now() + 10_000;
|
|
89
|
+
while (Date.now() < deadline) {
|
|
90
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
91
|
+
if (status.generation !== evidence.generation) throw invalid();
|
|
92
|
+
if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
|
|
93
|
+
&& status.taskView.taskId === evidence.taskId
|
|
94
|
+
&& JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt)) return;
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
96
|
+
}
|
|
97
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
|
|
98
|
+
}
|