@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,5 @@
|
|
|
1
|
+
import { isCompletionEvidence, type CleanCompletionEvidence } from '../../task/orchestration.ts';
|
|
2
|
+
import { parseControlOutput } from '../../task/control-recovery.ts';
|
|
1
3
|
import { execFileSync } from 'node:child_process';
|
|
2
4
|
import fs from 'node:fs';
|
|
3
5
|
import path from 'node:path';
|
|
@@ -23,6 +25,7 @@ import {
|
|
|
23
25
|
SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
|
|
24
26
|
SANDBOX_CONTROL_RESERVATION_BYTES
|
|
25
27
|
} from './protocol.ts';
|
|
28
|
+
import { parseSandboxTaskView, taskViewForManifest, type SandboxTaskView } from './task-view.ts';
|
|
26
29
|
|
|
27
30
|
export const SANDBOX_CONTROL_AUDIT_MAX_BYTES = 1024 * 1024;
|
|
28
31
|
export const SANDBOX_CONTROL_EXECUTION_STOP_MS = 2_000;
|
|
@@ -100,6 +103,82 @@ export function reservationPath(manifest: SandboxControlManifest, requestId: str
|
|
|
100
103
|
return path.join(manifest.processingDir, requestId, 'reservation.json');
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
export type SandboxControlTerminalResult = Readonly<{
|
|
107
|
+
version: 1;
|
|
108
|
+
requestId: string;
|
|
109
|
+
generation: string;
|
|
110
|
+
taskId: string | null;
|
|
111
|
+
intentDigest: string;
|
|
112
|
+
targetState: string | null;
|
|
113
|
+
status: string;
|
|
114
|
+
changed: boolean | null;
|
|
115
|
+
completionEvidence: CleanCompletionEvidence | null;
|
|
116
|
+
}>;
|
|
117
|
+
|
|
118
|
+
export function terminalResultPath(manifest: SandboxControlManifest, requestId: string): string {
|
|
119
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
120
|
+
return path.join(manifest.processingDir, requestId, 'terminal-result.json');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function createSandboxControlTerminalResult(
|
|
124
|
+
manifest: SandboxControlManifest,
|
|
125
|
+
request: Readonly<{ id: string; family: string; operation?: string | null }>,
|
|
126
|
+
output: string
|
|
127
|
+
): SandboxControlTerminalResult {
|
|
128
|
+
const nested = parseControlOutput(output) ?? {};
|
|
129
|
+
const run = nested.run && typeof nested.run === 'object' && !Array.isArray(nested.run)
|
|
130
|
+
? nested.run as Record<string, unknown> : null;
|
|
131
|
+
const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
|
|
132
|
+
const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
|
|
133
|
+
return {
|
|
134
|
+
version: 1,
|
|
135
|
+
requestId: request.id,
|
|
136
|
+
generation: manifest.generation,
|
|
137
|
+
taskId: manifest.taskId,
|
|
138
|
+
intentDigest: createHash('sha256').update(`${request.family}\0${operation}`, 'utf8').digest('hex'),
|
|
139
|
+
targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
|
|
140
|
+
status: typeof nested.status === 'string' ? nested.status : 'completed',
|
|
141
|
+
changed: typeof nested.changed === 'boolean' ? nested.changed : null,
|
|
142
|
+
completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function writeSandboxControlTerminalResult(
|
|
147
|
+
manifest: SandboxControlManifest,
|
|
148
|
+
request: Readonly<{ id: string; family: string; operation?: string | null }>,
|
|
149
|
+
output: string
|
|
150
|
+
): SandboxControlTerminalResult {
|
|
151
|
+
const result = createSandboxControlTerminalResult(manifest, request, output);
|
|
152
|
+
const filePath = terminalResultPath(manifest, request.id);
|
|
153
|
+
try {
|
|
154
|
+
atomicWriteJsonNoReplace(filePath, result);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (!(error instanceof Error) || error.message !== 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS') throw error;
|
|
157
|
+
const existing = readSandboxControlTerminalResult(filePath);
|
|
158
|
+
if (JSON.stringify(existing) !== JSON.stringify(result)) throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_CONFLICT');
|
|
159
|
+
}
|
|
160
|
+
return readSandboxControlTerminalResult(filePath);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function readSandboxControlTerminalResult(filePath: string): SandboxControlTerminalResult {
|
|
164
|
+
const value = readJsonFile(filePath) as Partial<SandboxControlTerminalResult> | null;
|
|
165
|
+
if (!value || value.version !== 1 || typeof value.requestId !== 'string'
|
|
166
|
+
|| typeof value.generation !== 'string' || (value.taskId !== null && typeof value.taskId !== 'string')
|
|
167
|
+
|| typeof value.intentDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(value.intentDigest)
|
|
168
|
+
|| (value.targetState !== null && typeof value.targetState !== 'string')
|
|
169
|
+
|| typeof value.status !== 'string'
|
|
170
|
+
|| (value.changed !== null && typeof value.changed !== 'boolean')) {
|
|
171
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
172
|
+
}
|
|
173
|
+
if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
|
|
174
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
175
|
+
}
|
|
176
|
+
if (value.changed === undefined) {
|
|
177
|
+
throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
|
|
178
|
+
}
|
|
179
|
+
return value as SandboxControlTerminalResult;
|
|
180
|
+
}
|
|
181
|
+
|
|
103
182
|
export function readSandboxControlResultEvidence(filePath: string): SandboxControlResultEvidence {
|
|
104
183
|
assertRegularFile(filePath);
|
|
105
184
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
@@ -296,10 +375,11 @@ export function writeSandboxControlStatus(
|
|
|
296
375
|
state: SandboxControlStatus['state'],
|
|
297
376
|
reasonCode: string | null,
|
|
298
377
|
activeRequestId: string | null,
|
|
299
|
-
now = Date.now()
|
|
378
|
+
now = Date.now(),
|
|
379
|
+
taskView: SandboxTaskView = taskViewForManifest(manifest)
|
|
300
380
|
): SandboxControlStatus {
|
|
301
381
|
const status: SandboxControlStatus = {
|
|
302
|
-
version:
|
|
382
|
+
version: 3, generation: manifest.generation, broker, state, reasonCode, activeRequestId, updatedAt: now, taskView
|
|
303
383
|
};
|
|
304
384
|
atomicWriteJson(statusPath(manifest), status, 0o600, false);
|
|
305
385
|
return status;
|
|
@@ -307,7 +387,7 @@ export function writeSandboxControlStatus(
|
|
|
307
387
|
|
|
308
388
|
export function parseSandboxControlStatus(value: unknown): SandboxControlStatus {
|
|
309
389
|
const status = value as Partial<SandboxControlStatus> | null;
|
|
310
|
-
if (!status || status.version !==
|
|
390
|
+
if (!status || status.version !== 3 || typeof status.generation !== 'string'
|
|
311
391
|
|| !status.broker || !Number.isSafeInteger(status.broker.pid) || typeof status.broker.startTime !== 'number'
|
|
312
392
|
|| !Number.isSafeInteger(status.broker.startTime) || typeof status.broker.brokerId !== 'string'
|
|
313
393
|
|| status.broker.brokerId.length === 0
|
|
@@ -315,6 +395,7 @@ export function parseSandboxControlStatus(value: unknown): SandboxControlStatus
|
|
|
315
395
|
|| (status.reasonCode !== null && typeof status.reasonCode !== 'string')
|
|
316
396
|
|| (status.activeRequestId !== null && typeof status.activeRequestId !== 'string')
|
|
317
397
|
|| !Number.isSafeInteger(status.updatedAt)) throw new Error('SANDBOX_CONTROL_STATUS_INVALID');
|
|
398
|
+
parseSandboxTaskView(status.taskView);
|
|
318
399
|
return status as SandboxControlStatus;
|
|
319
400
|
}
|
|
320
401
|
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const SANDBOX_TASK_VIEW_STATES = ['not-applicable', 'current', 'finalized-stale', 'unknown'] as const;
|
|
5
|
+
export const SANDBOX_TASK_VIEW_SOURCES = ['active', 'completed', 'unknown'] as const;
|
|
6
|
+
|
|
7
|
+
export type SandboxTaskViewState = typeof SANDBOX_TASK_VIEW_STATES[number];
|
|
8
|
+
export type SandboxTaskViewSource = typeof SANDBOX_TASK_VIEW_SOURCES[number];
|
|
9
|
+
export type SandboxTaskViewReceipt = Readonly<{
|
|
10
|
+
receiptId: string;
|
|
11
|
+
revision: number;
|
|
12
|
+
generation: string;
|
|
13
|
+
requestId: string;
|
|
14
|
+
}>;
|
|
15
|
+
export type SandboxTaskView = Readonly<{
|
|
16
|
+
state: SandboxTaskViewState;
|
|
17
|
+
taskId: string | null;
|
|
18
|
+
observedSource: SandboxTaskViewSource | null;
|
|
19
|
+
receipt: SandboxTaskViewReceipt | null;
|
|
20
|
+
reasonCode: string | null;
|
|
21
|
+
}>;
|
|
22
|
+
|
|
23
|
+
export type TaskViewProjectionInput = Readonly<{
|
|
24
|
+
mode: 'task-bound' | 'branch-only';
|
|
25
|
+
taskId: string | null;
|
|
26
|
+
generation: string;
|
|
27
|
+
source: SandboxTaskViewSource;
|
|
28
|
+
sourceMatches: boolean;
|
|
29
|
+
receipt?: unknown;
|
|
30
|
+
requestId?: string;
|
|
31
|
+
}>;
|
|
32
|
+
|
|
33
|
+
export type TaskViewAccessEffect =
|
|
34
|
+
| 'diagnostic'
|
|
35
|
+
| 'progress'
|
|
36
|
+
| 'artifact-write'
|
|
37
|
+
| 'terminal-verdict'
|
|
38
|
+
| 'recovery'
|
|
39
|
+
| 'cleanup'
|
|
40
|
+
| 'remote-write';
|
|
41
|
+
|
|
42
|
+
export type TaskViewAccess = Readonly<{
|
|
43
|
+
allowed: boolean;
|
|
44
|
+
state: SandboxTaskViewState;
|
|
45
|
+
reasonCode: string | null;
|
|
46
|
+
message: string | null;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
function invalidView(message: string): Error {
|
|
50
|
+
return new Error(`SANDBOX_TASK_VIEW_INVALID: ${message}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function receiptIdentity(value: unknown, generation: string, requestId?: string): SandboxTaskViewReceipt | null {
|
|
54
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
55
|
+
const receipt = value as Record<string, unknown>;
|
|
56
|
+
const binding = receipt.controlBinding;
|
|
57
|
+
if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return null;
|
|
58
|
+
const controlBinding = binding as Record<string, unknown>;
|
|
59
|
+
if (receipt.version !== 2 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
|
|
60
|
+
|| !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
|
|
61
|
+
|| receipt.lifecycle !== 'done'
|
|
62
|
+
|| controlBinding.generation !== generation
|
|
63
|
+
|| typeof controlBinding.requestId !== 'string'
|
|
64
|
+
|| !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
|
|
65
|
+
|| (requestId !== undefined && controlBinding.requestId !== requestId)) return null;
|
|
66
|
+
return {
|
|
67
|
+
receiptId: receipt.receiptId,
|
|
68
|
+
revision: receipt.revision as number,
|
|
69
|
+
generation,
|
|
70
|
+
requestId: controlBinding.requestId
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function parseSandboxTaskView(value: unknown): SandboxTaskView {
|
|
75
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalidView('taskView must be an object');
|
|
76
|
+
const view = value as Record<string, unknown>;
|
|
77
|
+
const receipt = view.receipt === null ? null : parseSandboxTaskViewReceipt(view.receipt);
|
|
78
|
+
if (!SANDBOX_TASK_VIEW_STATES.includes(view.state as SandboxTaskViewState)
|
|
79
|
+
|| (view.taskId !== null && typeof view.taskId !== 'string')
|
|
80
|
+
|| (view.observedSource !== null && !SANDBOX_TASK_VIEW_SOURCES.includes(view.observedSource as SandboxTaskViewSource))
|
|
81
|
+
|| (view.reasonCode !== null && typeof view.reasonCode !== 'string')
|
|
82
|
+
|| (view.receipt !== null && receipt === null)) {
|
|
83
|
+
throw invalidView('taskView schema is invalid');
|
|
84
|
+
}
|
|
85
|
+
if (view.state === 'not-applicable' && (view.taskId !== null || view.observedSource !== null || view.receipt !== null)) {
|
|
86
|
+
throw invalidView('not-applicable taskView cannot carry task evidence');
|
|
87
|
+
}
|
|
88
|
+
if (view.state === 'current' && (view.taskId === null || view.observedSource === null)) {
|
|
89
|
+
throw invalidView('current taskView requires taskId and observedSource');
|
|
90
|
+
}
|
|
91
|
+
if (view.state === 'current' && view.observedSource === 'completed' && view.receipt === null) {
|
|
92
|
+
throw invalidView('completed current taskView requires receipt');
|
|
93
|
+
}
|
|
94
|
+
if (view.state === 'finalized-stale' && (view.taskId === null || view.receipt === null)) {
|
|
95
|
+
throw invalidView('finalized-stale taskView requires taskId and receipt');
|
|
96
|
+
}
|
|
97
|
+
return view as SandboxTaskView;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseSandboxTaskViewReceipt(value: unknown): SandboxTaskViewReceipt | null {
|
|
101
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
102
|
+
const receipt = value as Record<string, unknown>;
|
|
103
|
+
if (Object.keys(receipt).sort().join(',') !== 'generation,receiptId,requestId,revision'
|
|
104
|
+
|| typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
|
|
105
|
+
|| !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
|
|
106
|
+
|| typeof receipt.generation !== 'string' || receipt.generation.length === 0
|
|
107
|
+
|| typeof receipt.requestId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(receipt.requestId)) return null;
|
|
108
|
+
return receipt as SandboxTaskViewReceipt;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function taskViewForManifest(params: Readonly<{
|
|
112
|
+
repoRoot: string;
|
|
113
|
+
mode: 'task-bound' | 'branch-only';
|
|
114
|
+
taskId: string | null;
|
|
115
|
+
generation: string;
|
|
116
|
+
receipt?: unknown;
|
|
117
|
+
}>): SandboxTaskView {
|
|
118
|
+
if (params.mode !== 'task-bound' || !params.taskId) {
|
|
119
|
+
return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
|
|
120
|
+
}
|
|
121
|
+
const active = path.join(params.repoRoot, '.agents', 'workspace', 'active', params.taskId, 'task.md');
|
|
122
|
+
const completed = path.join(params.repoRoot, '.agents', 'workspace', 'completed', params.taskId, 'task.md');
|
|
123
|
+
const activeExists = fs.existsSync(active);
|
|
124
|
+
const completedExists = fs.existsSync(completed);
|
|
125
|
+
if (activeExists === completedExists) {
|
|
126
|
+
return {
|
|
127
|
+
state: 'unknown', taskId: params.taskId,
|
|
128
|
+
observedSource: activeExists ? 'unknown' : null,
|
|
129
|
+
receipt: null, reasonCode: activeExists ? 'SANDBOX_TASK_VIEW_SOURCE_CONFLICT' : 'SANDBOX_TASK_VIEW_SOURCE_MISSING'
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const source = completedExists ? 'completed' : 'active';
|
|
133
|
+
const receipt = receiptIdentity(params.receipt, params.generation);
|
|
134
|
+
const receiptProvided = params.receipt !== undefined && params.receipt !== null;
|
|
135
|
+
if (source === 'completed' && receipt) {
|
|
136
|
+
return { state: 'current', taskId: params.taskId, observedSource: source, receipt, reasonCode: null };
|
|
137
|
+
}
|
|
138
|
+
if (source === 'active' && receipt) {
|
|
139
|
+
return {
|
|
140
|
+
state: 'finalized-stale', taskId: params.taskId, observedSource: source, receipt,
|
|
141
|
+
reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_STALE'
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
if (receiptProvided) {
|
|
145
|
+
return {
|
|
146
|
+
state: 'unknown', taskId: params.taskId, observedSource: source, receipt: null,
|
|
147
|
+
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (source === 'active') {
|
|
151
|
+
return { state: 'current', taskId: params.taskId, observedSource: source, receipt: null, reasonCode: null };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
state: 'unknown', taskId: params.taskId, observedSource: source, receipt: null,
|
|
155
|
+
reasonCode: 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function projectSandboxTaskView(input: TaskViewProjectionInput): SandboxTaskView {
|
|
160
|
+
if (input.mode !== 'task-bound' || !input.taskId) {
|
|
161
|
+
return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
|
|
162
|
+
}
|
|
163
|
+
const receipt = receiptIdentity(input.receipt, input.generation, input.requestId);
|
|
164
|
+
if (input.source === 'completed' && receipt && input.sourceMatches) {
|
|
165
|
+
return { state: 'current', taskId: input.taskId, observedSource: 'completed', receipt, reasonCode: null };
|
|
166
|
+
}
|
|
167
|
+
if (receipt && input.source === 'active' && !input.sourceMatches) {
|
|
168
|
+
return { state: 'finalized-stale', taskId: input.taskId, observedSource: 'active', receipt, reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_STALE' };
|
|
169
|
+
}
|
|
170
|
+
if (!receipt && input.source === 'active' && input.sourceMatches) {
|
|
171
|
+
return { state: 'current', taskId: input.taskId, observedSource: 'active', receipt: null, reasonCode: null };
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
state: 'unknown',
|
|
175
|
+
taskId: input.taskId,
|
|
176
|
+
observedSource: input.source,
|
|
177
|
+
receipt,
|
|
178
|
+
reasonCode: receipt ? 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED' : 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function taskViewAfterFinalization(params: Readonly<{
|
|
183
|
+
taskId: string;
|
|
184
|
+
generation: string;
|
|
185
|
+
requestId: string;
|
|
186
|
+
receipt: unknown;
|
|
187
|
+
}>): SandboxTaskView {
|
|
188
|
+
const receipt = receiptIdentity(params.receipt, params.generation, params.requestId);
|
|
189
|
+
if (!receipt) {
|
|
190
|
+
return {
|
|
191
|
+
state: 'unknown', taskId: params.taskId, observedSource: 'unknown', receipt: null,
|
|
192
|
+
reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
state: 'finalized-stale', taskId: params.taskId, observedSource: 'active', receipt,
|
|
197
|
+
reasonCode: 'SANDBOX_TASK_VIEW_FINALIZED'
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function mergeSandboxTaskView(
|
|
202
|
+
canonical: SandboxTaskView,
|
|
203
|
+
previous: SandboxTaskView
|
|
204
|
+
): SandboxTaskView {
|
|
205
|
+
if (previous.state !== 'finalized-stale' && previous.state !== 'unknown') return canonical;
|
|
206
|
+
return previous;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function accessSandboxTaskView(view: SandboxTaskView, effect: TaskViewAccessEffect): TaskViewAccess {
|
|
210
|
+
if (effect === 'diagnostic' || effect === 'cleanup' || effect === 'recovery') {
|
|
211
|
+
return { allowed: true, state: view.state, reasonCode: view.reasonCode, message: null };
|
|
212
|
+
}
|
|
213
|
+
if (view.state === 'current' && view.observedSource === 'active') {
|
|
214
|
+
return { allowed: true, state: view.state, reasonCode: null, message: null };
|
|
215
|
+
}
|
|
216
|
+
const reasonCode = view.state === 'current'
|
|
217
|
+
? 'SANDBOX_TASK_VIEW_COMPLETED_READ_ONLY'
|
|
218
|
+
: view.reasonCode ?? `SANDBOX_TASK_VIEW_${view.state.toUpperCase().replaceAll('-', '_')}`;
|
|
219
|
+
return {
|
|
220
|
+
allowed: false,
|
|
221
|
+
state: view.state,
|
|
222
|
+
reasonCode,
|
|
223
|
+
message: view.state === 'unknown'
|
|
224
|
+
? 'task view cannot be confirmed; inspect status, recover the original request, or explicitly clean up the sandbox'
|
|
225
|
+
: view.state === 'finalized-stale'
|
|
226
|
+
? 'task view is finalized/stale; exit or clean up the sandbox, or use a validated re-entry'
|
|
227
|
+
: 'completed task views are read-only'
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function taskViewFromStatus(value: unknown): SandboxTaskView {
|
|
232
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalidView('status must be an object');
|
|
233
|
+
const status = value as Record<string, unknown>;
|
|
234
|
+
if (status.version !== 3) throw invalidView('status version is unsupported');
|
|
235
|
+
const taskView = parseSandboxTaskView(status.taskView);
|
|
236
|
+
if (taskView.receipt && taskView.receipt.generation !== status.generation) {
|
|
237
|
+
throw invalidView('taskView receipt generation does not match status generation');
|
|
238
|
+
}
|
|
239
|
+
return taskView;
|
|
240
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
import { readStableFile, SecureFileError, writeAtomicFile } from '../../host-control/secure-fs.ts';
|
|
6
|
+
|
|
7
|
+
import { TASK_WORKFLOW_COMMANDS, TASK_WORKFLOW_OPERATIONS, type TaskWorkflowOperation, type WorkflowCommand } from '../../task/workflow-command.ts';
|
|
8
|
+
|
|
9
|
+
export type TaskWorkflowRequest = Readonly<{
|
|
10
|
+
version: 1;
|
|
11
|
+
id: string;
|
|
12
|
+
taskId: string;
|
|
13
|
+
generation: string;
|
|
14
|
+
operation: TaskWorkflowOperation;
|
|
15
|
+
args: readonly string[];
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export function validateTaskWorkflowRequest(value: unknown): TaskWorkflowRequest {
|
|
19
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
20
|
+
const request = value as TaskWorkflowRequest;
|
|
21
|
+
if (Object.keys(request).sort().join(',') !== 'args,generation,id,operation,taskId,version'
|
|
22
|
+
|| request.version !== 1 || typeof request.id !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(request.id)
|
|
23
|
+
|| typeof request.taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/u.test(request.taskId)
|
|
24
|
+
|| typeof request.generation !== 'string' || !request.generation
|
|
25
|
+
|| !Object.hasOwn(TASK_WORKFLOW_COMMANDS, request.operation)
|
|
26
|
+
|| !Array.isArray(request.args) || request.args.length < 2 || request.args.length > 128
|
|
27
|
+
|| !request.args.every((arg) => typeof arg === 'string' && !/[\r\n]/u.test(arg))
|
|
28
|
+
|| request.args[0] !== request.taskId) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
29
|
+
const [, selector] = TASK_WORKFLOW_COMMANDS[request.operation];
|
|
30
|
+
if (selector !== null && request.args[1] !== selector) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
|
|
31
|
+
return request;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Preserve argv verbatim; the domain command owns option parsing and validation. */
|
|
35
|
+
export function createTaskWorkflowRequest(
|
|
36
|
+
command: WorkflowCommand | undefined,
|
|
37
|
+
args: readonly string[],
|
|
38
|
+
taskId: string,
|
|
39
|
+
generation: string
|
|
40
|
+
): TaskWorkflowRequest {
|
|
41
|
+
const operation = TASK_WORKFLOW_OPERATIONS.find((key) => {
|
|
42
|
+
const [name, selector] = TASK_WORKFLOW_COMMANDS[key];
|
|
43
|
+
return name === command && (selector === null || selector === args[1]);
|
|
44
|
+
});
|
|
45
|
+
if (!operation) throw new Error('TASK_WORKFLOW_OPERATION_UNSUPPORTED');
|
|
46
|
+
return validateTaskWorkflowRequest({ version: 1, id: randomUUID(), taskId, generation, operation, args: [...args] });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function canonicalArtifactName(value: string): boolean {
|
|
50
|
+
return /^(?:analysis|plan|code|review-analysis|review-plan|review-code)(?:-r[1-9]\d*)?\.md$/u.test(value)
|
|
51
|
+
&& path.basename(value) === value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function assertTaskArtifactDirectory(directory: string): string {
|
|
55
|
+
const resolved = path.resolve(directory);
|
|
56
|
+
let stat: fs.Stats;
|
|
57
|
+
try {
|
|
58
|
+
stat = fs.lstatSync(resolved);
|
|
59
|
+
} catch {
|
|
60
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory is unavailable');
|
|
61
|
+
}
|
|
62
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
63
|
+
throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory must be a real directory');
|
|
64
|
+
}
|
|
65
|
+
return resolved;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function readTaskArtifact(
|
|
69
|
+
taskDir: string,
|
|
70
|
+
input: Readonly<{
|
|
71
|
+
artifact: string;
|
|
72
|
+
expectedSha256?: string;
|
|
73
|
+
}>
|
|
74
|
+
): Promise<Readonly<{ artifact: string; bytes: Buffer; sha256: string }>> {
|
|
75
|
+
if (!canonicalArtifactName(input.artifact)) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
|
|
76
|
+
const root = assertTaskArtifactDirectory(taskDir);
|
|
77
|
+
const candidate = path.join(root, input.artifact);
|
|
78
|
+
if (path.dirname(candidate) !== root) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
|
|
79
|
+
const stable = await readStableFile(candidate, {
|
|
80
|
+
maxBytes: 1024 * 1024,
|
|
81
|
+
...(input.expectedSha256 ? { expectedSha256: input.expectedSha256 } : {})
|
|
82
|
+
});
|
|
83
|
+
return { artifact: input.artifact, bytes: stable.bytes, sha256: stable.sha256 };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function writeTaskArtifact(
|
|
87
|
+
taskDir: string,
|
|
88
|
+
artifact: Readonly<{ artifact: string; bytes: Buffer; expectedSha256?: string }>
|
|
89
|
+
): Promise<void> {
|
|
90
|
+
if (!canonicalArtifactName(artifact.artifact)) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
|
|
91
|
+
const root = assertTaskArtifactDirectory(taskDir);
|
|
92
|
+
const target = path.join(root, artifact.artifact);
|
|
93
|
+
if (path.dirname(target) !== root) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
|
|
94
|
+
await writeAtomicFile(target, artifact.bytes, 0o600, artifact.expectedSha256);
|
|
95
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { TASK_WORKFLOW_COMMANDS } from '../../task/workflow-command.ts';
|
|
2
|
+
|
|
3
|
+
import { parseArtifactCommand, executeArtifactCommand } from '../../task/artifact-command.ts';
|
|
4
|
+
import { parseReviewCommand } from '../../task/review-command.ts';
|
|
5
|
+
import { prepareLocalArtifact, commitLocalArtifactProvenance } from '../../task/local-artifact-finalization.ts';
|
|
6
|
+
import { prepareReviewSummaryCandidate, commitReviewSummaryProvenance } from '../../task/review-finalization.ts';
|
|
7
|
+
import { withTaskExecutionLock } from '../../task/task-execution-lock.ts';
|
|
8
|
+
import { dispatchHostControlCommand } from '../../host-control/command.ts';
|
|
9
|
+
import { assertSandboxTaskSource } from '../workspace-view.ts';
|
|
10
|
+
import { appendDiagnosticAudit } from './audit.ts';
|
|
11
|
+
import {
|
|
12
|
+
readTaskArtifact, writeTaskArtifact, type TaskWorkflowRequest
|
|
13
|
+
} from './task-workflow.ts';
|
|
14
|
+
import type { SandboxControlManifest } from './protocol.ts';
|
|
15
|
+
import type { SandboxControlExecutionResult } from './executor.ts';
|
|
16
|
+
import type { LifecycleRecoveryAttestationV1 } from '../../task/control-authority.ts';
|
|
17
|
+
import { applyTaskEvent } from '../../task/events.ts';
|
|
18
|
+
import { parseTaskEventRequest } from '../../internal/task-event.ts';
|
|
19
|
+
|
|
20
|
+
function executionResult(result: Record<string, unknown>): SandboxControlExecutionResult {
|
|
21
|
+
return { exitCode: result.status === 'failed' || result.status === 'refused' ? 1 : 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** The broker already authorized this host executor; domain operations own validation. */
|
|
25
|
+
export async function executeTaskWorkflow(
|
|
26
|
+
manifest: SandboxControlManifest,
|
|
27
|
+
request: TaskWorkflowRequest,
|
|
28
|
+
lifecycleRecoveryAttestation: LifecycleRecoveryAttestationV1 | null = null
|
|
29
|
+
): Promise<SandboxControlExecutionResult> {
|
|
30
|
+
let publicationStarted = false;
|
|
31
|
+
try {
|
|
32
|
+
const taskDir = assertSandboxTaskSource(manifest.repoRoot, request.taskId);
|
|
33
|
+
const [command] = TASK_WORKFLOW_COMMANDS[request.operation];
|
|
34
|
+
if (request.operation === 'event') {
|
|
35
|
+
const eventRequest = parseTaskEventRequest(request.args);
|
|
36
|
+
const boundEventRequest = lifecycleRecoveryAttestation && eventRequest.requestId === undefined
|
|
37
|
+
? { ...eventRequest, requestId: lifecycleRecoveryAttestation.lifecycleRequestId }
|
|
38
|
+
: eventRequest;
|
|
39
|
+
const result = applyTaskEvent(boundEventRequest, {
|
|
40
|
+
repoRoot: manifest.repoRoot,
|
|
41
|
+
lifecycleRecoveryAttestation,
|
|
42
|
+
deferLifecycleRecoveryConsumption: true
|
|
43
|
+
});
|
|
44
|
+
return executionResult(result);
|
|
45
|
+
}
|
|
46
|
+
if (command !== 'task-artifact' && command !== 'task-review') {
|
|
47
|
+
publicationStarted = true;
|
|
48
|
+
// Inherit this executor's process group so its existing recovery owns the worker too.
|
|
49
|
+
const result = await dispatchHostControlCommand({
|
|
50
|
+
operation: command, payload: { workingDirectory: manifest.repoRoot, args: request.args }
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
const input = command === 'task-artifact' ? parseArtifactCommand(request.args) : parseReviewCommand(request.args);
|
|
55
|
+
if (command === 'task-artifact' && 'operation' in input && input.operation === 'inspect') {
|
|
56
|
+
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot }));
|
|
57
|
+
}
|
|
58
|
+
if ('operation' in input && (input.operation === 'init' || input.operation === 'repair')) {
|
|
59
|
+
return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot, artifactDir: taskDir }));
|
|
60
|
+
}
|
|
61
|
+
return await withTaskExecutionLock(manifest.repoRoot, request.taskId, `sandbox-control.${request.operation}`, async () => {
|
|
62
|
+
const artifact = await readTaskArtifact(taskDir, { artifact: input.artifact });
|
|
63
|
+
const content = artifact.bytes.toString('utf8');
|
|
64
|
+
let result: Record<string, unknown>;
|
|
65
|
+
if ('operation' in input) {
|
|
66
|
+
const { family } = input;
|
|
67
|
+
if (family !== 'analysis' && family !== 'plan' && family !== 'code') throw new Error('ARTIFACT_IDENTITY_INVALID');
|
|
68
|
+
const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot } as const;
|
|
69
|
+
const prepared = prepareLocalArtifact(local, content, lifecycleRecoveryAttestation ?? undefined);
|
|
70
|
+
if (prepared.result.status === 'failed') return executionResult(commitLocalArtifactProvenance(prepared));
|
|
71
|
+
publicationStarted = true;
|
|
72
|
+
await writeTaskArtifact(taskDir, {
|
|
73
|
+
artifact: artifact.artifact,
|
|
74
|
+
bytes: artifact.bytes,
|
|
75
|
+
expectedSha256: artifact.sha256
|
|
76
|
+
});
|
|
77
|
+
result = commitLocalArtifactProvenance(prepared);
|
|
78
|
+
} else {
|
|
79
|
+
if (input.overrideTicket) throw new Error('TASK_WORKFLOW_OVERRIDE_UNSUPPORTED');
|
|
80
|
+
const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot });
|
|
81
|
+
if (input.dryRun || prepared.result.status === 'planned') return executionResult(prepared.result);
|
|
82
|
+
if (prepared.result.status === 'failed') return executionResult(commitReviewSummaryProvenance(prepared, manifest.repoRoot));
|
|
83
|
+
publicationStarted = true;
|
|
84
|
+
await writeTaskArtifact(taskDir, {
|
|
85
|
+
artifact: input.artifact,
|
|
86
|
+
bytes: Buffer.from(prepared.content, 'utf8'),
|
|
87
|
+
expectedSha256: artifact.sha256
|
|
88
|
+
});
|
|
89
|
+
result = commitReviewSummaryProvenance(prepared, manifest.repoRoot);
|
|
90
|
+
}
|
|
91
|
+
if (result.status !== 'failed') appendDiagnosticAudit(manifest, 'task-workflow-artifact-finalized', {
|
|
92
|
+
requestId: request.id, sandboxTaskId: request.taskId, workflowOperation: request.operation,
|
|
93
|
+
artifact: input.artifact, sha256: result.artifactSha256 as string, semanticDigest: result.semanticDigest as string
|
|
94
|
+
});
|
|
95
|
+
return executionResult({ ...result, changed: true });
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
const code = (error as { code?: string })?.code ?? /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_WORKFLOW_REQUEST_INVALID';
|
|
100
|
+
return executionResult({ status: 'failed', changed: publicationStarted ? null : false, error: { code, message } });
|
|
101
|
+
}
|
|
102
|
+
}
|