@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
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { SpawnSyncReturns } from 'node:child_process';
|
|
3
|
+
import { runProbe } from '../shell.ts';
|
|
4
|
+
import { getAdapter } from './index.ts';
|
|
5
|
+
|
|
6
|
+
export type SandboxAuthorityEvidenceV1 = Readonly<{
|
|
7
|
+
version: 1;
|
|
8
|
+
provider: string;
|
|
9
|
+
lockDomain: string;
|
|
10
|
+
routeKind: 'default' | 'context' | 'endpoint' | 'wsl2';
|
|
11
|
+
routeSelector: Readonly<Record<string, string>>;
|
|
12
|
+
normalizedEndpoint: string;
|
|
13
|
+
endpointFingerprint: string;
|
|
14
|
+
daemonIdentity: Readonly<{
|
|
15
|
+
kind: 'docker-server-id';
|
|
16
|
+
fingerprint: string;
|
|
17
|
+
}>;
|
|
18
|
+
apiVersion: Readonly<{ major: number; minor: number }>;
|
|
19
|
+
authorityFingerprint: string;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
export type AuthorityProbe = (cmd: string, args: string[], options?: { timeout?: number }) => SpawnSyncReturns<string | Buffer>;
|
|
23
|
+
|
|
24
|
+
export type AuthorityCaptureOptions = Readonly<{
|
|
25
|
+
probe?: AuthorityProbe;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
env?: NodeJS.ProcessEnv;
|
|
28
|
+
lockDomain?: string;
|
|
29
|
+
route?: SandboxAuthorityEvidenceV1;
|
|
30
|
+
}>;
|
|
31
|
+
|
|
32
|
+
export type AuthorityVerification = Readonly<{
|
|
33
|
+
state: 'verified' | 'unknown' | 'conflict';
|
|
34
|
+
reason?: string;
|
|
35
|
+
}>;
|
|
36
|
+
|
|
37
|
+
function digest(value: string): string {
|
|
38
|
+
return createHash('sha256').update(value).digest('hex');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function canonical(value: unknown): string {
|
|
42
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;
|
|
43
|
+
if (!value || typeof value !== 'object') return JSON.stringify(value) ?? 'null';
|
|
44
|
+
return `{${Object.entries(value as Record<string, unknown>)
|
|
45
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
46
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${canonical(entry)}`)
|
|
47
|
+
.join(',')}}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function text(value: string | Buffer | null | undefined): string {
|
|
51
|
+
return typeof value === 'string' ? value : value?.toString('utf8') ?? '';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function safeEndpoint(value: string): string {
|
|
55
|
+
if (!value) return 'docker-default';
|
|
56
|
+
try {
|
|
57
|
+
const parsed = new URL(value);
|
|
58
|
+
const hasUnreplayableParts = Boolean(parsed.username || parsed.password || parsed.search || parsed.hash);
|
|
59
|
+
parsed.username = '';
|
|
60
|
+
parsed.password = '';
|
|
61
|
+
parsed.search = '';
|
|
62
|
+
parsed.hash = '';
|
|
63
|
+
const normalized = parsed.toString().replace(/\/$/u, '');
|
|
64
|
+
return hasUnreplayableParts
|
|
65
|
+
? `${normalized}#<redacted>`
|
|
66
|
+
: normalized;
|
|
67
|
+
} catch {
|
|
68
|
+
const normalized = value
|
|
69
|
+
.replace(/\/\/[^/@]+@/u, '//<redacted>@')
|
|
70
|
+
.replace(/[?&](?:token|password|secret|key)=[^&]*/giu, '');
|
|
71
|
+
return /[@?#]/u.test(value) ? `${normalized.replace(/[?#].*$/u, '')}#<redacted>` : normalized;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function lockDomainFor(engine: string, endpoint: string): string {
|
|
76
|
+
return digest(`${process.platform}\0${process.arch}\0${engine}\0${endpoint}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type SandboxAuthorityRoute = Readonly<{
|
|
80
|
+
kind: SandboxAuthorityEvidenceV1['routeKind'];
|
|
81
|
+
selector: Readonly<Record<string, string>>;
|
|
82
|
+
endpoint: string;
|
|
83
|
+
command: (args: string[]) => { cmd: string; args: string[] };
|
|
84
|
+
}>;
|
|
85
|
+
|
|
86
|
+
function contextRoute(context: string): SandboxAuthorityRoute {
|
|
87
|
+
return {
|
|
88
|
+
kind: 'context',
|
|
89
|
+
selector: { context },
|
|
90
|
+
endpoint: `docker-context://${context}`,
|
|
91
|
+
command: (args) => ({ cmd: 'docker', args: ['--context', context, ...args] })
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function endpointRoute(endpoint: string, rawEndpoint: string, unreplayable?: string): SandboxAuthorityRoute {
|
|
96
|
+
return {
|
|
97
|
+
kind: 'endpoint',
|
|
98
|
+
selector: {
|
|
99
|
+
source: 'DOCKER_HOST',
|
|
100
|
+
endpoint,
|
|
101
|
+
...(unreplayable ? { unreplayable } : {})
|
|
102
|
+
},
|
|
103
|
+
endpoint,
|
|
104
|
+
command: (args) => ({ cmd: 'docker', args: ['--host', rawEndpoint, ...args] })
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function tlsEnvironmentReason(env: NodeJS.ProcessEnv): string | undefined {
|
|
109
|
+
return env.DOCKER_TLS_VERIFY || env.DOCKER_CERT_PATH || env.DOCKER_TLS
|
|
110
|
+
? 'tls-environment'
|
|
111
|
+
: undefined;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function routeFor(engine: string, env: NodeJS.ProcessEnv): SandboxAuthorityRoute {
|
|
115
|
+
const context = getAdapter(engine).dockerContext;
|
|
116
|
+
if (context) {
|
|
117
|
+
return contextRoute(context);
|
|
118
|
+
}
|
|
119
|
+
if (env.DOCKER_CONTEXT) {
|
|
120
|
+
return contextRoute(env.DOCKER_CONTEXT);
|
|
121
|
+
}
|
|
122
|
+
if (env.DOCKER_HOST) {
|
|
123
|
+
return endpointRoute(safeEndpoint(env.DOCKER_HOST), env.DOCKER_HOST, tlsEnvironmentReason(env));
|
|
124
|
+
}
|
|
125
|
+
return contextRoute('default');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function validRouteName(value: string): boolean {
|
|
129
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.@/-]*$/u.test(value);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolveWslDistro(probe: AuthorityProbe, timeoutMs?: number, explicit?: string): string {
|
|
133
|
+
if (explicit) {
|
|
134
|
+
if (!validRouteName(explicit)) throw new Error('SANDBOX_AUTHORITY_ROUTE_CAPTURE_FAILED');
|
|
135
|
+
return explicit;
|
|
136
|
+
}
|
|
137
|
+
const result = probe(
|
|
138
|
+
'wsl.exe',
|
|
139
|
+
['--list', '--verbose'],
|
|
140
|
+
timeoutMs === undefined ? {} : { timeout: timeoutMs }
|
|
141
|
+
);
|
|
142
|
+
if (result.status !== 0) throw new Error('SANDBOX_AUTHORITY_ROUTE_CAPTURE_FAILED');
|
|
143
|
+
const output = text(result.stdout).replace(/\0/gu, '').replace(/\r/gu, '');
|
|
144
|
+
const defaultLine = output.split('\n').find((line) => /^\s*\*\s+/u.test(line));
|
|
145
|
+
const distro = defaultLine?.replace(/^\s*\*\s+/u, '').replace(/\s+(?:Running|Stopped)\s+\d+\s*$/iu, '').trim();
|
|
146
|
+
if (!distro || !validRouteName(distro)) throw new Error('SANDBOX_AUTHORITY_ROUTE_CAPTURE_FAILED');
|
|
147
|
+
return distro;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function wslRoute(
|
|
151
|
+
distro: string,
|
|
152
|
+
context: string | undefined,
|
|
153
|
+
env: NodeJS.ProcessEnv
|
|
154
|
+
): SandboxAuthorityRoute {
|
|
155
|
+
const endpoint = env.DOCKER_HOST
|
|
156
|
+
? safeEndpoint(env.DOCKER_HOST)
|
|
157
|
+
: `wsl://${distro}/${context ?? 'default'}`;
|
|
158
|
+
const unreplayable = tlsEnvironmentReason(env)
|
|
159
|
+
?? (endpoint.includes('<redacted>') ? 'ambient-endpoint-secret' : undefined);
|
|
160
|
+
return {
|
|
161
|
+
kind: 'wsl2',
|
|
162
|
+
selector: {
|
|
163
|
+
distro,
|
|
164
|
+
...(context ? { context } : {}),
|
|
165
|
+
...(unreplayable ? { unreplayable } : {}),
|
|
166
|
+
...(env.DOCKER_HOST ? { endpoint } : {})
|
|
167
|
+
},
|
|
168
|
+
endpoint,
|
|
169
|
+
command: (args) => ({
|
|
170
|
+
cmd: 'wsl.exe',
|
|
171
|
+
args: [
|
|
172
|
+
'--distribution', distro,
|
|
173
|
+
'--exec', 'docker',
|
|
174
|
+
...(context ? ['--context', context] : []),
|
|
175
|
+
...(env.DOCKER_HOST ? ['--host', env.DOCKER_HOST] : []),
|
|
176
|
+
...args
|
|
177
|
+
]
|
|
178
|
+
})
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function currentWslRoute(
|
|
183
|
+
env: NodeJS.ProcessEnv,
|
|
184
|
+
probe: AuthorityProbe,
|
|
185
|
+
timeoutMs?: number
|
|
186
|
+
): SandboxAuthorityRoute {
|
|
187
|
+
const distro = resolveWslDistro(probe, timeoutMs, env.WSL_DISTRO_NAME);
|
|
188
|
+
let context = env.DOCKER_CONTEXT;
|
|
189
|
+
if (!context && !env.DOCKER_HOST) {
|
|
190
|
+
const result = probe(
|
|
191
|
+
'wsl.exe',
|
|
192
|
+
['--distribution', distro, '--exec', 'docker', 'context', 'show'],
|
|
193
|
+
timeoutMs === undefined ? {} : { timeout: timeoutMs }
|
|
194
|
+
);
|
|
195
|
+
context = text(result.stdout).trim();
|
|
196
|
+
if (result.status !== 0 || !validRouteName(context)) {
|
|
197
|
+
throw new Error('SANDBOX_AUTHORITY_ROUTE_CAPTURE_FAILED');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return wslRoute(distro, context, env);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function routeFromEvidence(evidence: SandboxAuthorityEvidenceV1): SandboxAuthorityRoute {
|
|
204
|
+
return {
|
|
205
|
+
kind: evidence.routeKind,
|
|
206
|
+
selector: evidence.routeSelector,
|
|
207
|
+
endpoint: evidence.normalizedEndpoint,
|
|
208
|
+
command: (args) => commandForSandboxAuthority(evidence, 'docker', args)
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function currentContextRoute(
|
|
213
|
+
engine: string,
|
|
214
|
+
env: NodeJS.ProcessEnv,
|
|
215
|
+
probe: AuthorityProbe,
|
|
216
|
+
timeoutMs?: number
|
|
217
|
+
): SandboxAuthorityRoute {
|
|
218
|
+
const configured = routeFor(engine, env);
|
|
219
|
+
if (configured.kind !== 'context' || configured.selector.context !== 'default') return configured;
|
|
220
|
+
if (env.DOCKER_CONTEXT || env.DOCKER_HOST || getAdapter(engine).dockerContext) return configured;
|
|
221
|
+
const result = probe('docker', ['context', 'show'], timeoutMs === undefined ? {} : { timeout: timeoutMs });
|
|
222
|
+
const context = text(result.stdout).trim();
|
|
223
|
+
if (result.status !== 0 || !/^[A-Za-z0-9][A-Za-z0-9_.@/-]*$/u.test(context)) {
|
|
224
|
+
throw new Error('SANDBOX_AUTHORITY_ROUTE_CAPTURE_FAILED');
|
|
225
|
+
}
|
|
226
|
+
return contextRoute(context);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Build a command using only the route persisted with the sandbox authority. */
|
|
230
|
+
export function commandForSandboxAuthority(
|
|
231
|
+
evidence: SandboxAuthorityEvidenceV1,
|
|
232
|
+
cmd: string,
|
|
233
|
+
args: string[] = []
|
|
234
|
+
): { cmd: string; args: string[] } {
|
|
235
|
+
if (cmd !== 'docker') {
|
|
236
|
+
throw new Error('SANDBOX_AUTHORITY_ROUTE_UNSUPPORTED');
|
|
237
|
+
}
|
|
238
|
+
if (evidence.routeKind === 'context') {
|
|
239
|
+
const context = evidence.routeSelector.context;
|
|
240
|
+
if (!context) throw new Error('SANDBOX_AUTHORITY_ROUTE_INVALID');
|
|
241
|
+
return { cmd, args: ['--context', context, ...args] };
|
|
242
|
+
}
|
|
243
|
+
if (evidence.routeKind === 'endpoint') {
|
|
244
|
+
const endpoint = evidence.routeSelector.endpoint;
|
|
245
|
+
if (!endpoint || endpoint.includes('<redacted>') || evidence.routeSelector.unreplayable) {
|
|
246
|
+
throw new Error('SANDBOX_AUTHORITY_ROUTE_UNREPLAYABLE');
|
|
247
|
+
}
|
|
248
|
+
return { cmd, args: ['--host', endpoint, ...args] };
|
|
249
|
+
}
|
|
250
|
+
if (evidence.routeKind === 'wsl2') {
|
|
251
|
+
const distro = evidence.routeSelector.distro;
|
|
252
|
+
const context = evidence.routeSelector.context;
|
|
253
|
+
const endpoint = evidence.routeSelector.endpoint;
|
|
254
|
+
if (!distro || evidence.routeSelector.unreplayable || endpoint?.includes('<redacted>')) {
|
|
255
|
+
throw new Error('SANDBOX_AUTHORITY_ROUTE_UNREPLAYABLE');
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
cmd: 'wsl.exe',
|
|
259
|
+
args: [
|
|
260
|
+
'--distribution', distro,
|
|
261
|
+
'--exec', cmd,
|
|
262
|
+
...(context ? ['--context', context] : []),
|
|
263
|
+
...(endpoint && !endpoint.startsWith('wsl://') ? ['--host', endpoint] : []),
|
|
264
|
+
...args
|
|
265
|
+
]
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return { cmd, args: ['--context', 'default', ...args] };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function authorityVersionArgs(): string[] {
|
|
272
|
+
return ['version', '--format', '{{json .Server}}'];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function captureSandboxAuthority(
|
|
276
|
+
engine: string,
|
|
277
|
+
options: AuthorityCaptureOptions = {}
|
|
278
|
+
): SandboxAuthorityEvidenceV1 {
|
|
279
|
+
const env = options.env ?? process.env;
|
|
280
|
+
const probe = options.probe ?? runProbe;
|
|
281
|
+
const route = options.route
|
|
282
|
+
? routeFromEvidence(options.route)
|
|
283
|
+
: engine === 'wsl2'
|
|
284
|
+
? currentWslRoute(env, probe, options.timeoutMs)
|
|
285
|
+
: currentContextRoute(engine, env, probe, options.timeoutMs);
|
|
286
|
+
function probeJson(args: string[]): unknown {
|
|
287
|
+
const command = options.route
|
|
288
|
+
? commandForSandboxAuthority(options.route, 'docker', args)
|
|
289
|
+
: route.command(args);
|
|
290
|
+
try {
|
|
291
|
+
const response = probe(
|
|
292
|
+
command.cmd,
|
|
293
|
+
command.args,
|
|
294
|
+
options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs }
|
|
295
|
+
);
|
|
296
|
+
if (response.status !== 0) throw new Error('SANDBOX_AUTHORITY_CAPTURE_FAILED');
|
|
297
|
+
return JSON.parse(text(response.stdout).trim());
|
|
298
|
+
} catch {
|
|
299
|
+
throw new Error('SANDBOX_AUTHORITY_CAPTURE_FAILED');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const server = probeJson(authorityVersionArgs());
|
|
303
|
+
if (!server || typeof server !== 'object' || Array.isArray(server)) {
|
|
304
|
+
throw new Error('SANDBOX_AUTHORITY_CAPTURE_FAILED');
|
|
305
|
+
}
|
|
306
|
+
const record = server as Record<string, unknown>;
|
|
307
|
+
const api = typeof record.ApiVersion === 'string' ? /^(\d+)\.(\d+)$/u.exec(record.ApiVersion) : null;
|
|
308
|
+
if (!api) throw new Error('SANDBOX_AUTHORITY_CAPTURE_FAILED');
|
|
309
|
+
// Docker version exposes API metadata; the daemon ID belongs to Docker info.
|
|
310
|
+
const serverId = probeJson(['info', '--format', '{{json .ID}}']);
|
|
311
|
+
if (typeof serverId !== 'string' || !serverId.trim()) throw new Error('SANDBOX_AUTHORITY_CAPTURE_FAILED');
|
|
312
|
+
const normalizedEndpoint = route.endpoint;
|
|
313
|
+
const evidenceWithoutFingerprint = {
|
|
314
|
+
version: 1 as const,
|
|
315
|
+
provider: engine,
|
|
316
|
+
lockDomain: options.lockDomain ?? options.route?.lockDomain ?? lockDomainFor(engine, normalizedEndpoint),
|
|
317
|
+
routeKind: route.kind,
|
|
318
|
+
routeSelector: route.selector,
|
|
319
|
+
normalizedEndpoint,
|
|
320
|
+
endpointFingerprint: digest(normalizedEndpoint),
|
|
321
|
+
daemonIdentity: { kind: 'docker-server-id' as const, fingerprint: digest(serverId) },
|
|
322
|
+
apiVersion: { major: Number(api[1]), minor: Number(api[2]) }
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
...evidenceWithoutFingerprint,
|
|
326
|
+
authorityFingerprint: digest(canonical(evidenceWithoutFingerprint))
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function verifySandboxAuthority(
|
|
331
|
+
expected: SandboxAuthorityEvidenceV1 | null | undefined,
|
|
332
|
+
actual: SandboxAuthorityEvidenceV1 | null
|
|
333
|
+
): AuthorityVerification {
|
|
334
|
+
if (!expected) return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
|
|
335
|
+
if (!actual) return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_UNAVAILABLE' };
|
|
336
|
+
if (expected.authorityFingerprint !== actual.authorityFingerprint
|
|
337
|
+
|| expected.lockDomain !== actual.lockDomain
|
|
338
|
+
|| expected.routeKind !== actual.routeKind
|
|
339
|
+
|| expected.endpointFingerprint !== actual.endpointFingerprint
|
|
340
|
+
|| expected.daemonIdentity.fingerprint !== actual.daemonIdentity.fingerprint) {
|
|
341
|
+
return { state: 'conflict', reason: 'SANDBOX_AUTHORITY_DRIFT' };
|
|
342
|
+
}
|
|
343
|
+
return { state: 'verified' };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function isSandboxAuthorityEvidence(value: unknown): value is SandboxAuthorityEvidenceV1 {
|
|
347
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
348
|
+
const evidence = value as Record<string, unknown>;
|
|
349
|
+
const selector = evidence.routeSelector as Record<string, unknown> | null;
|
|
350
|
+
const daemon = evidence.daemonIdentity as Record<string, unknown> | null;
|
|
351
|
+
const api = evidence.apiVersion as Record<string, unknown> | null;
|
|
352
|
+
return evidence.version === 1
|
|
353
|
+
&& typeof evidence.provider === 'string'
|
|
354
|
+
&& /^[a-f0-9]{64}$/u.test(evidence.lockDomain as string)
|
|
355
|
+
&& ['default', 'context', 'endpoint', 'wsl2'].includes(evidence.routeKind as string)
|
|
356
|
+
&& !!selector && typeof selector === 'object' && !Array.isArray(selector)
|
|
357
|
+
&& Object.values(selector).every((entry) => typeof entry === 'string')
|
|
358
|
+
&& typeof evidence.normalizedEndpoint === 'string'
|
|
359
|
+
&& /^[a-f0-9]{64}$/u.test(evidence.endpointFingerprint as string)
|
|
360
|
+
&& !!daemon && daemon.kind === 'docker-server-id'
|
|
361
|
+
&& /^[a-f0-9]{64}$/u.test(daemon.fingerprint as string)
|
|
362
|
+
&& !!api && Number.isSafeInteger(api.major) && (api.major as number) >= 0
|
|
363
|
+
&& Number.isSafeInteger(api.minor) && (api.minor as number) >= 0
|
|
364
|
+
&& /^[a-f0-9]{64}$/u.test(evidence.authorityFingerprint as string);
|
|
365
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { SpawnSyncReturns } from 'node:child_process';
|
|
2
|
+
import { commandForEngine, runProbe } from '../shell.ts';
|
|
3
|
+
import { commandForSandboxAuthority, type SandboxAuthorityEvidenceV1 } from './authority.ts';
|
|
4
|
+
|
|
5
|
+
type ProbeResult = SpawnSyncReturns<string | Buffer>;
|
|
6
|
+
type Probe = (cmd: string, args: string[], options?: { timeout?: number }) => ProbeResult;
|
|
7
|
+
|
|
8
|
+
export type ExactContainerDiscovery =
|
|
9
|
+
| Readonly<{ state: 'absent'; id: string; evidence: 'exact-empty' }>
|
|
10
|
+
| Readonly<{
|
|
11
|
+
state: 'found';
|
|
12
|
+
id: string;
|
|
13
|
+
running: boolean;
|
|
14
|
+
labels: Readonly<Record<string, string>>;
|
|
15
|
+
evidence: 'exact-present';
|
|
16
|
+
}>
|
|
17
|
+
| Readonly<{ state: 'conflict'; reason: string }>
|
|
18
|
+
| Readonly<{ state: 'unknown'; reason: string }>;
|
|
19
|
+
|
|
20
|
+
export type ExactContainerDiscoveryOptions = Readonly<{
|
|
21
|
+
probe?: Probe;
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
authority?: SandboxAuthorityEvidenceV1;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
const FULL_CONTAINER_ID = /^[a-f0-9]{64}$/u;
|
|
27
|
+
|
|
28
|
+
function outputText(value: string | Buffer | null | undefined): string {
|
|
29
|
+
return typeof value === 'string' ? value : value?.toString('utf8') ?? '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function unknown(reason: string): ExactContainerDiscovery {
|
|
33
|
+
return { state: 'unknown', reason };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function buildExactContainerListArgs(id: string): string[] {
|
|
37
|
+
if (!FULL_CONTAINER_ID.test(id)) throw new Error('SANDBOX_CONTAINER_ID_NOT_FULL');
|
|
38
|
+
return ['container', 'ls', '--all', '--no-trunc', '--filter', `id=${id}`, '--format', '{{.ID}}'];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildExactContainerInspectArgs(id: string): string[] {
|
|
42
|
+
return ['container', 'inspect', '--format', '{{json .}}', id];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function runDockerProbe(
|
|
46
|
+
engine: string,
|
|
47
|
+
args: string[],
|
|
48
|
+
options: ExactContainerDiscoveryOptions
|
|
49
|
+
): ProbeResult {
|
|
50
|
+
const command = options.authority
|
|
51
|
+
? commandForSandboxAuthority(options.authority, 'docker', args)
|
|
52
|
+
: commandForEngine(engine, 'docker', args);
|
|
53
|
+
return (options.probe ?? runProbe)(
|
|
54
|
+
command.cmd,
|
|
55
|
+
command.args,
|
|
56
|
+
options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseExactList(stdout: string, expectedId: string): 'empty' | 'present' | 'conflict' | string {
|
|
61
|
+
const rows = stdout.split(/\r?\n/u).map((row) => row.trim()).filter(Boolean);
|
|
62
|
+
if (rows.length === 0) return 'empty';
|
|
63
|
+
if (rows.length !== 1) return 'exact container query returned multiple rows';
|
|
64
|
+
if (!FULL_CONTAINER_ID.test(rows[0]!)) return 'exact container query returned a malformed ID';
|
|
65
|
+
if (rows[0] !== expectedId) return 'conflict';
|
|
66
|
+
return 'present';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseExactInspect(stdout: string, expectedId: string): ExactContainerDiscovery {
|
|
70
|
+
let value: unknown;
|
|
71
|
+
try {
|
|
72
|
+
value = JSON.parse(stdout);
|
|
73
|
+
} catch {
|
|
74
|
+
return unknown('exact container inspection returned malformed JSON');
|
|
75
|
+
}
|
|
76
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
77
|
+
return unknown('exact container inspection returned an incomplete object');
|
|
78
|
+
}
|
|
79
|
+
const inspected = value as {
|
|
80
|
+
Id?: unknown;
|
|
81
|
+
State?: { Running?: unknown };
|
|
82
|
+
Config?: { Labels?: unknown };
|
|
83
|
+
};
|
|
84
|
+
if (inspected.Id !== expectedId || typeof inspected.State?.Running !== 'boolean'
|
|
85
|
+
|| !inspected.Config?.Labels || typeof inspected.Config.Labels !== 'object'
|
|
86
|
+
|| Array.isArray(inspected.Config.Labels)) {
|
|
87
|
+
return unknown('exact container inspection identity is incomplete');
|
|
88
|
+
}
|
|
89
|
+
const rawLabels = inspected.Config.Labels as Record<string, unknown>;
|
|
90
|
+
if (Object.values(rawLabels).some((value) => typeof value !== 'string')) {
|
|
91
|
+
return unknown('exact container inspection labels are malformed');
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
state: 'found',
|
|
95
|
+
id: expectedId,
|
|
96
|
+
running: inspected.State.Running,
|
|
97
|
+
labels: { ...rawLabels as Record<string, string> },
|
|
98
|
+
evidence: 'exact-present'
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function discoverExactContainer(
|
|
103
|
+
engine: string,
|
|
104
|
+
id: string,
|
|
105
|
+
options: ExactContainerDiscoveryOptions = {}
|
|
106
|
+
): ExactContainerDiscovery {
|
|
107
|
+
if (!FULL_CONTAINER_ID.test(id)) return unknown('container identity is not a full 64-character ID');
|
|
108
|
+
let listed: ProbeResult;
|
|
109
|
+
try {
|
|
110
|
+
listed = runDockerProbe(engine, buildExactContainerListArgs(id), options);
|
|
111
|
+
} catch {
|
|
112
|
+
return unknown('exact container query failed before producing a result');
|
|
113
|
+
}
|
|
114
|
+
if (listed.status !== 0) {
|
|
115
|
+
return unknown(`exact container query exited with ${listed.status ?? 'unknown status'}`);
|
|
116
|
+
}
|
|
117
|
+
const listState = parseExactList(outputText(listed.stdout), id);
|
|
118
|
+
if (listState === 'empty') return { state: 'absent', id, evidence: 'exact-empty' };
|
|
119
|
+
if (listState === 'conflict') return { state: 'conflict', reason: 'exact container query returned a different ID' };
|
|
120
|
+
if (listState !== 'present') return unknown(listState);
|
|
121
|
+
|
|
122
|
+
let inspected: ProbeResult;
|
|
123
|
+
try {
|
|
124
|
+
inspected = runDockerProbe(engine, buildExactContainerInspectArgs(id), options);
|
|
125
|
+
} catch {
|
|
126
|
+
return unknown('exact container inspection failed before producing a result');
|
|
127
|
+
}
|
|
128
|
+
if (inspected.status !== 0) {
|
|
129
|
+
return unknown(`exact container inspection exited with ${inspected.status ?? 'unknown status'}`);
|
|
130
|
+
}
|
|
131
|
+
return parseExactInspect(outputText(inspected.stdout).trim(), id);
|
|
132
|
+
}
|
package/lib/sandbox/index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PUBLIC_CLI_ROUTE_SELECTORS } from '../internal/cli-route-inventory.ts';
|
|
2
|
+
|
|
1
3
|
const USAGE = `Usage: ai sandbox <command> [options]
|
|
2
4
|
|
|
3
5
|
Commands:
|
|
@@ -18,14 +20,15 @@ Commands:
|
|
|
18
20
|
rm <branch | TASK-id> | --unbound | --purge
|
|
19
21
|
Remove one sandbox; use a full TASK-id for task-bound
|
|
20
22
|
cleanup and a branch for branch-only cleanup. --unbound
|
|
21
|
-
removes completed task-bound and branch-only sandboxes
|
|
22
|
-
active, blocked, and archive tasks are protected.
|
|
23
|
+
removes completed task-bound and branch-only sandboxes
|
|
24
|
+
plus verified auxiliary state; active, blocked, and archive tasks are protected.
|
|
23
25
|
start [--recreate] <branch | TASK-id | N>
|
|
24
26
|
Verify or recover an existing sandbox container;
|
|
25
27
|
optionally replace only the container on failure
|
|
26
28
|
vm status|start|stop Manage the sandbox VM (macOS) or check the backend (Windows)
|
|
27
29
|
|
|
28
30
|
Run 'ai sandbox <command> --help' for details.`;
|
|
31
|
+
const SANDBOX_OPERATIONS = PUBLIC_CLI_ROUTE_SELECTORS.sandbox;
|
|
29
32
|
|
|
30
33
|
export async function runSandbox(args: string[]): Promise<void> {
|
|
31
34
|
const [subcommand, ...rest] = args;
|
|
@@ -41,6 +44,10 @@ export async function runSandbox(args: string[]): Promise<void> {
|
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
if (!SANDBOX_OPERATIONS.includes(subcommand as typeof SANDBOX_OPERATIONS[number])) {
|
|
48
|
+
throw new Error(`Unknown sandbox command: ${subcommand}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
44
51
|
switch (subcommand) {
|
|
45
52
|
case 'create': {
|
|
46
53
|
const { create } = await import('./commands/create.ts');
|
package/lib/sandbox/recovery.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { prepareCompletedReentry, publishCompletedReentry, waitForCompletedReentry } from './control/completed-reentry.ts';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { spawn } from 'node:child_process';
|
|
@@ -59,9 +60,10 @@ import {
|
|
|
59
60
|
type BrokerOwner
|
|
60
61
|
} from './control/lifecycle.ts';
|
|
61
62
|
import {
|
|
62
|
-
appendSandboxControlAudit,
|
|
63
63
|
readSandboxControlStatus
|
|
64
64
|
} from './control/state.ts';
|
|
65
|
+
import { validateSandboxControlIdentity } from './control/identity-sentinel.ts';
|
|
66
|
+
import { appendDiagnosticAudit as appendControlDiagnosticAudit } from './control/audit.ts';
|
|
65
67
|
import { inspectSandboxControlContainer } from './control/container-identity.ts';
|
|
66
68
|
import {
|
|
67
69
|
SANDBOX_CONTROL_FUTURE_SKEW_MS,
|
|
@@ -223,6 +225,17 @@ export async function startSandboxControlBroker(repoRoot: string, manifestPath:
|
|
|
223
225
|
const internalCli = path.resolve(directory, '..', '..', 'bin', `internal-cli${extension}`);
|
|
224
226
|
const manifest = readSandboxControlManifest(manifestPath);
|
|
225
227
|
const root = path.dirname(manifestPath);
|
|
228
|
+
const identity = validateSandboxControlIdentity({
|
|
229
|
+
publicStatusDir: manifest.publicStatusDir,
|
|
230
|
+
root,
|
|
231
|
+
mode: manifest.mode,
|
|
232
|
+
taskId: manifest.taskId,
|
|
233
|
+
generation: manifest.generation,
|
|
234
|
+
controlRootId: manifest.controlRootId
|
|
235
|
+
});
|
|
236
|
+
if (identity.state !== 'valid') {
|
|
237
|
+
throw new Error(`SANDBOX_CONTROL_IDENTITY_${identity.state.replaceAll('-', '_').toUpperCase()}`);
|
|
238
|
+
}
|
|
226
239
|
if (isSandboxControlRootQuiescing(root)) throw new Error('SANDBOX_CONTROL_QUIESCING');
|
|
227
240
|
const brokerPath = path.join(root, 'broker.json');
|
|
228
241
|
let brokerSnapshot: string | null = null;
|
|
@@ -295,8 +308,8 @@ export async function startSandboxControlBroker(repoRoot: string, manifestPath:
|
|
|
295
308
|
}
|
|
296
309
|
}
|
|
297
310
|
if (replacedBrokerRecord) {
|
|
298
|
-
|
|
299
|
-
|
|
311
|
+
appendControlDiagnosticAudit(manifest, 'broker-observed-crash');
|
|
312
|
+
appendControlDiagnosticAudit(manifest, 'broker-restart');
|
|
300
313
|
}
|
|
301
314
|
child = spawn(
|
|
302
315
|
process.execPath,
|
|
@@ -337,6 +350,17 @@ async function ensureSandboxControlBroker(params: {
|
|
|
337
350
|
});
|
|
338
351
|
if (!fs.existsSync(control.manifestPath)) return;
|
|
339
352
|
const validatedManifest = readSandboxControlManifest(control.manifestPath);
|
|
353
|
+
const identity = validateSandboxControlIdentity({
|
|
354
|
+
publicStatusDir: validatedManifest.publicStatusDir,
|
|
355
|
+
root: control.root,
|
|
356
|
+
mode: validatedManifest.mode,
|
|
357
|
+
taskId: validatedManifest.taskId,
|
|
358
|
+
generation: validatedManifest.generation,
|
|
359
|
+
controlRootId: validatedManifest.controlRootId
|
|
360
|
+
});
|
|
361
|
+
if (identity.state !== 'valid') {
|
|
362
|
+
throw new Error(`SANDBOX_CONTROL_IDENTITY_${identity.state.replaceAll('-', '_').toUpperCase()}`);
|
|
363
|
+
}
|
|
340
364
|
const containerObservation = await inspectSandboxControlContainer(validatedManifest);
|
|
341
365
|
if (containerObservation.state === 'unknown') {
|
|
342
366
|
throw new Error(`SANDBOX_CONTROL_CONTAINER_UNKNOWN: ${containerObservation.reason}`);
|
|
@@ -653,7 +677,7 @@ function recoveryTaskSources(repoRoot: string, taskId: string): {
|
|
|
653
677
|
return { mountPaths, accessiblePaths: [completedSource] };
|
|
654
678
|
}
|
|
655
679
|
|
|
656
|
-
function startCompletedSandboxContainer(params: {
|
|
680
|
+
export function startCompletedSandboxContainer(params: {
|
|
657
681
|
config: SandboxConfig;
|
|
658
682
|
engine: string;
|
|
659
683
|
container: string;
|
|
@@ -748,7 +772,7 @@ function expectedMounts(params: {
|
|
|
748
772
|
? {}
|
|
749
773
|
: {
|
|
750
774
|
runtimeDir: control.runtimeDir,
|
|
751
|
-
taskSources: taskSources
|
|
775
|
+
taskSources: taskSources!.accessiblePaths,
|
|
752
776
|
taskId: taskId!
|
|
753
777
|
})
|
|
754
778
|
}).map((mount) => ({
|
|
@@ -757,7 +781,7 @@ function expectedMounts(params: {
|
|
|
757
781
|
hostPaths: mount.hostPaths,
|
|
758
782
|
...(taskSources !== null && taskId !== null
|
|
759
783
|
&& mount.containerPath === `/workspace/.agents/workspace/active/${taskId}`
|
|
760
|
-
? { sourceAccessiblePaths: taskSources
|
|
784
|
+
? { sourceAccessiblePaths: taskSources!.accessiblePaths }
|
|
761
785
|
: {}),
|
|
762
786
|
expectedRW: !mount.readOnly
|
|
763
787
|
}));
|
|
@@ -1250,7 +1274,17 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
1250
1274
|
throw new Error('Explicit container recreation requested.');
|
|
1251
1275
|
}
|
|
1252
1276
|
if (!params.row.running) {
|
|
1277
|
+
let completedReentry: { manifest: SandboxControlManifest; evidence: Awaited<ReturnType<typeof prepareCompletedReentry>> } | null = null;
|
|
1253
1278
|
if (params.reentry === 'completed' && params.workspace?.mode === 'task-bound') {
|
|
1279
|
+
const control = sandboxControlPaths({
|
|
1280
|
+
base: params.config.controlBase, project: params.config.project,
|
|
1281
|
+
container: params.row.name, identity: params.workspace
|
|
1282
|
+
});
|
|
1283
|
+
const manifest = readSandboxControlManifest(control.manifestPath);
|
|
1284
|
+
if (manifest.taskId !== params.workspace.taskId || manifest.branch !== params.branch) {
|
|
1285
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
|
|
1286
|
+
}
|
|
1287
|
+
completedReentry = { manifest, evidence: await prepareCompletedReentry(manifest) };
|
|
1254
1288
|
startCompletedSandboxContainer({
|
|
1255
1289
|
config: params.config,
|
|
1256
1290
|
engine: params.engine,
|
|
@@ -1315,6 +1349,15 @@ export async function ensureSandboxReady(params: EnsureSandboxReadyParams): Prom
|
|
|
1315
1349
|
if (final.findings.length > 0) {
|
|
1316
1350
|
throw new Error(describeFindings(final.findings));
|
|
1317
1351
|
}
|
|
1352
|
+
if (completedReentry) {
|
|
1353
|
+
const { manifest, evidence } = completedReentry;
|
|
1354
|
+
const current = await prepareCompletedReentry(manifest);
|
|
1355
|
+
if (JSON.stringify(current) !== JSON.stringify(evidence)) {
|
|
1356
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
|
|
1357
|
+
}
|
|
1358
|
+
publishCompletedReentry(manifest, evidence);
|
|
1359
|
+
await waitForCompletedReentry(manifest, evidence);
|
|
1360
|
+
}
|
|
1318
1361
|
return { container: params.row.name, path: 'recovered', warnings };
|
|
1319
1362
|
}
|
|
1320
1363
|
|