@fitlab-ai/agent-infra 0.9.12 → 0.9.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/README.zh-CN.md +11 -11
- package/bin/cli.ts +26 -9
- package/dist/bin/cli.js +173 -157
- package/dist/bin/internal-cli.js +66 -40
- package/dist/lib/agent-client.js +5 -0
- package/dist/lib/agent-clients/next-steps.js +6 -1
- package/dist/lib/decide.js +3 -3
- package/dist/lib/internal/agent-client.js +5 -2
- package/dist/lib/internal/cli-route-inventory.js +152 -0
- package/dist/lib/internal/codex-lifecycle.js +9 -6
- package/dist/lib/internal/codex-sandbox-controller.js +8 -2
- package/dist/lib/internal/git-workflow.js +10 -6
- package/dist/lib/internal/platform-checks.js +19 -7
- package/dist/lib/internal/platform-comment.js +25 -13
- package/dist/lib/internal/platform-context.js +6 -3
- package/dist/lib/internal/platform-issue.js +20 -13
- package/dist/lib/internal/platform-metadata.js +88 -0
- package/dist/lib/internal/platform-pr-review.js +26 -13
- package/dist/lib/internal/platform-pr.js +84 -31
- package/dist/lib/internal/platform-release-notes.js +9 -6
- package/dist/lib/internal/platform-security.js +93 -0
- package/dist/lib/internal/pr-review-grade.js +25 -12
- package/dist/lib/internal/release-workflow.js +23 -9
- package/dist/lib/internal/sandbox-control.js +50 -7
- package/dist/lib/internal/task-activity.js +16 -7
- package/dist/lib/internal/task-artifact.js +235 -24
- package/dist/lib/internal/task-context.js +4 -1
- package/dist/lib/internal/task-create.js +50 -17
- package/dist/lib/internal/task-delivery.js +4 -1
- package/dist/lib/internal/task-event.js +10 -4
- package/dist/lib/internal/task-finalization.js +6 -3
- package/dist/lib/internal/task-invalidation.js +63 -0
- package/dist/lib/internal/task-ledger.js +20 -6
- package/dist/lib/internal/task-lifecycle.js +6 -3
- package/dist/lib/internal/task-operation-registry.js +421 -0
- package/dist/lib/internal/task-orchestration.js +12 -0
- package/dist/lib/internal/task-override.js +14 -7
- package/dist/lib/internal/task-qualification.js +90 -0
- package/dist/lib/internal/task-review.js +7 -4
- package/dist/lib/internal/task-short-id.js +11 -1
- package/dist/lib/internal/task-snapshot.js +4 -1
- package/dist/lib/internal/task-validate.js +7 -2
- package/dist/lib/internal/task-verify.js +11 -7
- package/dist/lib/internal/task-warning.js +11 -4
- package/dist/lib/platform/adapters.js +1 -3
- package/dist/lib/platform/change-request-git-evidence.js +27 -7
- package/dist/lib/platform/comment-safety.js +226 -0
- package/dist/lib/platform/completion-backfill.js +6 -5
- package/dist/lib/platform/context.js +70 -130
- package/dist/lib/platform/github-client.js +1 -1
- package/dist/lib/platform/github-provider.js +1130 -0
- package/dist/lib/platform/in-label-sync.js +143 -0
- package/dist/lib/platform/issue-comments.js +237 -72
- package/dist/lib/platform/issues.js +335 -189
- package/dist/lib/platform/metadata-labels.js +6 -2
- package/dist/lib/platform/none-provider.js +28 -0
- package/dist/lib/platform/pr-change-report.js +545 -0
- package/dist/lib/platform/pr-checks.js +119 -104
- package/dist/lib/platform/pr-review.js +88 -72
- package/dist/lib/platform/pr-summary.js +541 -50
- package/dist/lib/platform/provider-bridge.js +45 -0
- package/dist/lib/platform/provider-contract.d.ts +553 -0
- package/dist/lib/platform/provider-contract.js +99 -0
- package/dist/lib/platform/provider-loader.js +251 -0
- package/dist/lib/platform/provider-validation.js +577 -0
- package/dist/lib/platform/pull-request-metadata.js +8 -6
- package/dist/lib/platform/pull-requests.js +734 -543
- package/dist/lib/platform/release-notes.js +78 -55
- package/dist/lib/platform/releases.js +79 -50
- package/dist/lib/platform/repository-metadata.js +377 -0
- package/dist/lib/platform/resource-identity.d.ts +26 -0
- package/dist/lib/platform/resource-identity.js +92 -0
- package/dist/lib/platform/security-alerts.js +155 -0
- package/dist/lib/platform/task-identities.js +31 -0
- package/dist/lib/platform/verification-required.js +11 -12
- package/dist/lib/platform/verification-sync.js +129 -248
- package/dist/lib/pr-review/evidence-grading.js +19 -6
- package/dist/lib/process-data/index.js +7 -0
- package/dist/lib/run/index.js +68 -23
- package/dist/lib/sandbox/commands/control-status.js +22 -0
- package/dist/lib/sandbox/commands/create.js +32 -6
- package/dist/lib/sandbox/commands/ls.js +5 -0
- package/dist/lib/sandbox/commands/rm.js +827 -120
- package/dist/lib/sandbox/commands/show.js +16 -0
- package/dist/lib/sandbox/control/client.js +174 -19
- 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 +151 -16
- package/dist/lib/sandbox/control/lifecycle.js +621 -127
- package/dist/lib/sandbox/control/native-file-lock.js +177 -0
- package/dist/lib/sandbox/control/protocol.js +77 -0
- package/dist/lib/sandbox/control/server.js +677 -34
- package/dist/lib/sandbox/control/state.js +209 -4
- package/dist/lib/sandbox/control/task-view.js +183 -0
- package/dist/lib/sandbox/engines/authority.js +289 -0
- package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
- package/dist/lib/sandbox/index.js +5 -0
- package/dist/lib/sandbox/recovery.js +21 -1
- package/dist/lib/sandbox/workspace-view.js +2 -1
- package/dist/lib/sandbox/worktree-safety.js +4 -1
- package/dist/lib/server/index.js +8 -0
- package/dist/lib/server/protocol.js +38 -1
- package/dist/lib/task/activity-log.js +8 -1
- package/dist/lib/task/artifact-lifecycle.js +75 -18
- package/dist/lib/task/artifact-operations.js +520 -0
- package/dist/lib/task/artifact-repair-intent.js +59 -0
- package/dist/lib/task/artifact-schema.js +135 -0
- package/dist/lib/task/capabilities.js +310 -0
- package/dist/lib/task/commands/status.js +32 -0
- package/dist/lib/task/commit-operation.js +12 -1
- package/dist/lib/task/control-authority.js +12 -5
- package/dist/lib/task/create-service.js +111 -12
- package/dist/lib/task/create.js +25 -0
- package/dist/lib/task/current-contract.js +5 -1
- package/dist/lib/task/decision-details.js +1 -1
- package/dist/lib/task/events.js +416 -17
- package/dist/lib/task/finalization.js +37 -13
- package/dist/lib/task/human-override.js +58 -36
- package/dist/lib/task/index.js +11 -3
- package/dist/lib/task/invalidation-command.js +86 -0
- package/dist/lib/task/invalidation.js +260 -0
- package/dist/lib/task/ledger-intents.js +37 -0
- package/dist/lib/task/local-artifact-finalization.js +299 -0
- package/dist/lib/task/orchestration.js +240 -144
- package/dist/lib/task/pr-delivery-fact.js +102 -101
- package/dist/lib/task/qualification-audit.js +490 -0
- package/dist/lib/task/qualification-confirmation.js +109 -0
- package/dist/lib/task/qualification-intents.js +148 -0
- package/dist/lib/task/review-artifacts.js +22 -23
- package/dist/lib/task/review-finalization.js +121 -4
- package/dist/lib/task/review-fingerprint.js +24 -2
- package/dist/lib/task/rework-intent.js +138 -0
- package/dist/lib/task/sections.js +6 -4
- package/dist/lib/task/skill-scope.js +23 -0
- package/dist/lib/task/task-execution-lock.js +27 -17
- package/dist/lib/task/verification-config.js +11 -0
- package/dist/lib/task/verification-engine.js +93 -16
- package/dist/lib/task/verification.js +3 -3
- package/dist/lib/task/workflow-warning-intents.js +1 -1
- package/dist/lib/task/write.js +11 -0
- package/dist/lib/version.js +11 -1
- package/dist/lifecycle-build-manifest.json +3 -3
- package/lib/agent-client.ts +9 -0
- package/lib/agent-clients/next-steps.ts +6 -1
- package/lib/decide.ts +3 -3
- package/lib/init.ts +1 -1
- package/lib/internal/agent-client.ts +4 -2
- package/lib/internal/cli-route-inventory.ts +140 -0
- package/lib/internal/codex-lifecycle.ts +8 -6
- package/lib/internal/codex-sandbox-controller.ts +7 -2
- package/lib/internal/git-workflow.ts +9 -6
- package/lib/internal/platform-checks.ts +14 -6
- package/lib/internal/platform-comment.ts +20 -12
- package/lib/internal/platform-context.ts +5 -3
- package/lib/internal/platform-issue.ts +17 -11
- package/lib/internal/platform-metadata.ts +70 -0
- package/lib/internal/platform-pr-review.ts +23 -12
- package/lib/internal/platform-pr.ts +65 -27
- package/lib/internal/platform-release-notes.ts +8 -6
- package/lib/internal/platform-security.ts +83 -0
- package/lib/internal/pr-review-grade.ts +20 -11
- package/lib/internal/release-workflow.ts +21 -9
- package/lib/internal/sandbox-control.ts +44 -6
- package/lib/internal/task-activity.ts +15 -7
- package/lib/internal/task-artifact.ts +204 -22
- package/lib/internal/task-context.ts +3 -1
- package/lib/internal/task-create.ts +78 -17
- package/lib/internal/task-delivery.ts +3 -1
- package/lib/internal/task-event.ts +9 -4
- package/lib/internal/task-finalization.ts +6 -4
- package/lib/internal/task-invalidation.ts +41 -0
- package/lib/internal/task-ledger.ts +21 -8
- package/lib/internal/task-lifecycle.ts +5 -3
- package/lib/internal/task-operation-registry.ts +482 -0
- package/lib/internal/task-orchestration.ts +12 -0
- package/lib/internal/task-override.ts +15 -9
- package/lib/internal/task-qualification.ts +61 -0
- package/lib/internal/task-review.ts +6 -4
- package/lib/internal/task-short-id.ts +10 -1
- package/lib/internal/task-snapshot.ts +3 -1
- package/lib/internal/task-validate.ts +6 -2
- package/lib/internal/task-verify.ts +10 -7
- package/lib/internal/task-warning.ts +10 -4
- package/lib/platform/adapters.ts +3 -2
- package/lib/platform/change-request-git-evidence.ts +36 -9
- package/lib/platform/comment-safety.ts +250 -0
- package/lib/platform/completion-backfill.ts +9 -8
- package/lib/platform/context.ts +97 -134
- package/lib/platform/github-client.ts +2 -2
- package/lib/platform/github-provider.ts +1132 -0
- package/lib/platform/in-label-sync.ts +208 -0
- package/lib/platform/issue-comments.ts +255 -80
- package/lib/platform/issues.ts +344 -174
- package/lib/platform/metadata-labels.ts +5 -2
- package/lib/platform/none-provider.ts +35 -0
- package/lib/platform/pr-change-report.ts +639 -0
- package/lib/platform/pr-checks.ts +126 -106
- package/lib/platform/pr-review.ts +88 -85
- package/lib/platform/pr-summary.ts +581 -53
- package/lib/platform/provider-bridge.ts +82 -0
- package/lib/platform/provider-contract.ts +611 -0
- package/lib/platform/provider-loader.ts +307 -0
- package/lib/platform/provider-validation.ts +611 -0
- package/lib/platform/pull-request-metadata.ts +10 -7
- package/lib/platform/pull-requests.ts +781 -550
- package/lib/platform/release-notes.ts +76 -59
- package/lib/platform/releases.ts +77 -43
- package/lib/platform/repository-metadata.ts +453 -0
- package/lib/platform/resource-identity.ts +121 -0
- package/lib/platform/security-alerts.ts +191 -0
- package/lib/platform/task-identities.ts +40 -0
- package/lib/platform/types.ts +4 -2
- package/lib/platform/verification-required.ts +11 -13
- package/lib/platform/verification-sync.ts +128 -304
- package/lib/pr-review/evidence-grading.ts +25 -8
- package/lib/process-data/index.ts +7 -0
- package/lib/run/index.ts +62 -22
- package/lib/sandbox/commands/control-status.ts +26 -0
- package/lib/sandbox/commands/create.ts +46 -6
- package/lib/sandbox/commands/ls.ts +7 -0
- package/lib/sandbox/commands/rm.ts +1000 -87
- package/lib/sandbox/commands/show.ts +16 -0
- package/lib/sandbox/control/client.ts +203 -19
- package/lib/sandbox/control/completed-reentry.ts +98 -0
- package/lib/sandbox/control/container-identity.ts +27 -43
- package/lib/sandbox/control/executor.ts +180 -15
- package/lib/sandbox/control/lifecycle.ts +629 -17
- package/lib/sandbox/control/native-file-lock.ts +209 -0
- package/lib/sandbox/control/protocol.ts +118 -2
- package/lib/sandbox/control/server.ts +724 -34
- package/lib/sandbox/control/state.ts +257 -4
- package/lib/sandbox/control/task-view.ts +240 -0
- package/lib/sandbox/engines/authority.ts +365 -0
- package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
- package/lib/sandbox/index.ts +7 -0
- package/lib/sandbox/recovery.ts +21 -1
- package/lib/sandbox/workspace-view.ts +9 -3
- package/lib/sandbox/worktree-safety.ts +4 -1
- package/lib/server/index.ts +10 -0
- package/lib/server/protocol.ts +36 -1
- package/lib/task/activity-log.ts +8 -0
- package/lib/task/artifact-lifecycle.ts +70 -22
- package/lib/task/artifact-operations.ts +626 -0
- package/lib/task/artifact-repair-intent.ts +71 -0
- package/lib/task/artifact-schema.ts +192 -0
- package/lib/task/capabilities.ts +331 -0
- package/lib/task/commands/status.ts +38 -0
- package/lib/task/commit-operation.ts +12 -1
- package/lib/task/control-authority.ts +22 -9
- package/lib/task/create-service.ts +139 -14
- package/lib/task/create.ts +27 -0
- package/lib/task/current-contract.ts +6 -1
- package/lib/task/decision-details.ts +6 -2
- package/lib/task/events.ts +392 -16
- package/lib/task/finalization.ts +50 -14
- package/lib/task/human-override.ts +70 -46
- package/lib/task/index.ts +16 -3
- package/lib/task/invalidation-command.ts +86 -0
- package/lib/task/invalidation.ts +332 -0
- package/lib/task/ledger-intents.ts +35 -1
- package/lib/task/local-artifact-finalization.ts +432 -0
- package/lib/task/orchestration.ts +223 -133
- package/lib/task/pr-delivery-fact.ts +131 -148
- package/lib/task/qualification-audit.ts +599 -0
- package/lib/task/qualification-confirmation.ts +117 -0
- package/lib/task/qualification-intents.ts +150 -0
- package/lib/task/review-artifacts.ts +21 -22
- package/lib/task/review-finalization.ts +133 -4
- package/lib/task/review-fingerprint.ts +23 -2
- package/lib/task/rework-intent.ts +165 -0
- package/lib/task/sections.ts +6 -4
- package/lib/task/skill-scope.ts +24 -0
- package/lib/task/task-execution-lock.ts +23 -14
- package/lib/task/verification-config.ts +12 -0
- package/lib/task/verification-engine.ts +103 -17
- package/lib/task/verification.ts +4 -4
- package/lib/task/workflow-warning-intents.ts +1 -1
- package/lib/task/write.ts +19 -0
- package/lib/update.ts +1 -1
- package/lib/version.ts +15 -1
- package/package.json +12 -1
- package/templates/.agents/rules/README.en.md +3 -1
- package/templates/.agents/rules/README.zh-CN.md +3 -1
- 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/issue-pr-commands.en.md +1 -1
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/issue-sync.en.md +2 -0
- package/templates/.agents/rules/issue-sync.zh-CN.md +2 -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 +13 -2
- package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
- package/templates/.agents/rules/pr-sync.en.md +10 -4
- package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
- 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/task-short-id.en.md +1 -4
- package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
- package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
- 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/check-task/SKILL.en.md +16 -37
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
- 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 +41 -9
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
- package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -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/commit/reference/issue-metadata-sync.en.md +2 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
- package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/create-pr/config/verify.json +1 -0
- package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
- package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
- 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 +27 -4
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
- package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
- package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
- 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 +18 -5
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
- 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 +17 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
- 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 +9 -3
- package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
- package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
- package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
- package/templates/.agents/skills/test/SKILL.en.md +13 -2
- package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
- package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
- package/templates/.agents/templates/task.en.md +24 -1
- package/templates/.agents/templates/task.zh-CN.md +24 -1
- package/templates/.github/workflows/pr-label.yml +7 -26
- package/templates/.agents/scripts/security-alerts.github.sh +0 -151
- package/templates/.agents/scripts/security-alerts.sh +0 -31
- package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
- package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
- package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
|
@@ -16,6 +16,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.ts';
|
|
|
16
16
|
import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
|
|
17
17
|
import { detectEngine } from '../engine.ts';
|
|
18
18
|
import { resolveSandboxTarget } from '../workspace-identity.ts';
|
|
19
|
+
import { readSandboxControlStatusForRow } from './control-status.ts';
|
|
19
20
|
|
|
20
21
|
const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
|
|
21
22
|
|
|
@@ -93,6 +94,21 @@ export function show(args: string[] = []): void {
|
|
|
93
94
|
process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
|
|
94
95
|
process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
|
|
95
96
|
|
|
97
|
+
p.log.step('Control status');
|
|
98
|
+
const controlStatus = container
|
|
99
|
+
? readSandboxControlStatusForRow(config, container)
|
|
100
|
+
: null;
|
|
101
|
+
if (!controlStatus) {
|
|
102
|
+
p.log.warn(' Control status unavailable');
|
|
103
|
+
} else {
|
|
104
|
+
process.stdout.write(` Health: ${controlStatus.state}\n`);
|
|
105
|
+
process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
|
|
106
|
+
process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
|
|
107
|
+
if (controlStatus.taskView.reasonCode) {
|
|
108
|
+
process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
for (const tool of detail.toolStates) {
|
|
97
113
|
p.log.step(`${tool.name} state`);
|
|
98
114
|
if (tool.entries.length === 0) {
|
|
@@ -20,19 +20,78 @@ import type {
|
|
|
20
20
|
} from './controller-registration.ts';
|
|
21
21
|
import type { ProcessIdentity } from '../../server/process-state.ts';
|
|
22
22
|
import { normalizeAgentToken } from '../../agent-clients/tokens.ts';
|
|
23
|
-
import { readSandboxControlStatus } from './state.ts';
|
|
23
|
+
import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts';
|
|
24
24
|
import type { TaskCreateCandidateV1 } from '../../task/create.ts';
|
|
25
|
+
import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
|
|
25
26
|
|
|
26
27
|
const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
|
|
27
28
|
|
|
29
|
+
type SandboxControlEnvironmentClassification = Readonly<{
|
|
30
|
+
kind: 'direct' | 'controlled' | 'invalid';
|
|
31
|
+
mode?: 'task-bound' | 'branch-only';
|
|
32
|
+
code?: 'TASK_CONTROL_TRANSPORT_INVALID';
|
|
33
|
+
message?: string;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export function classifySandboxControlEnvironment(
|
|
37
|
+
env: NodeJS.ProcessEnv = process.env
|
|
38
|
+
): SandboxControlEnvironmentClassification {
|
|
39
|
+
const controlKeys = [
|
|
40
|
+
'AGENT_INFRA_CONTROL_TOKEN',
|
|
41
|
+
'AGENT_INFRA_CONTROL_GENERATION',
|
|
42
|
+
'AGENT_INFRA_CONTROL_DIR',
|
|
43
|
+
'AGENT_INFRA_CONTROL_STATUS_DIR'
|
|
44
|
+
] as const;
|
|
45
|
+
const hasControlMarker = controlKeys.some((key) => Boolean(env[key]));
|
|
46
|
+
const hasTaskIdentity = Boolean(env.AGENT_INFRA_TASK_ID);
|
|
47
|
+
const hasRuntime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
|
|
48
|
+
const hasExecutorMarker = Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
|
|
49
|
+
const hasControllerBinding = Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING);
|
|
50
|
+
const hasAnyMarker = hasControlMarker || hasTaskIdentity || hasRuntime || hasExecutorMarker || hasControllerBinding;
|
|
51
|
+
const hasCompleteControl = controlKeys.every((key) => Boolean(env[key]));
|
|
52
|
+
|
|
53
|
+
if (!hasAnyMarker) return { kind: 'direct' };
|
|
54
|
+
if (hasExecutorMarker) {
|
|
55
|
+
return {
|
|
56
|
+
kind: 'invalid',
|
|
57
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
58
|
+
message: 'executor context is only valid for sandbox-control execute'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (hasControllerBinding) {
|
|
62
|
+
return {
|
|
63
|
+
kind: 'invalid',
|
|
64
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
65
|
+
message: 'sandbox client control configuration is incomplete or conflicting'
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (!hasCompleteControl) {
|
|
69
|
+
return {
|
|
70
|
+
kind: 'invalid',
|
|
71
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
72
|
+
message: 'sandbox client control configuration is incomplete or conflicting'
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (hasTaskIdentity !== hasRuntime) {
|
|
76
|
+
return {
|
|
77
|
+
kind: 'invalid',
|
|
78
|
+
code: 'TASK_CONTROL_TRANSPORT_INVALID',
|
|
79
|
+
message: 'sandbox task identity and runtime binding must be provided together'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { kind: 'controlled', mode: hasTaskIdentity ? 'task-bound' : 'branch-only' };
|
|
83
|
+
}
|
|
84
|
+
|
|
28
85
|
export class SandboxControlClientError extends Error {
|
|
29
86
|
readonly detail: SandboxControlError;
|
|
30
87
|
readonly accepted: boolean;
|
|
31
|
-
|
|
88
|
+
readonly requestId: string | null;
|
|
89
|
+
constructor(detail: SandboxControlError, accepted = false, requestId: string | null = null) {
|
|
32
90
|
super(detail.message);
|
|
33
91
|
this.name = 'SandboxControlClientError';
|
|
34
92
|
this.detail = detail;
|
|
35
93
|
this.accepted = accepted;
|
|
94
|
+
this.requestId = requestId;
|
|
36
95
|
}
|
|
37
96
|
}
|
|
38
97
|
|
|
@@ -40,11 +99,29 @@ function sleep(milliseconds: number): void {
|
|
|
40
99
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
|
|
41
100
|
}
|
|
42
101
|
|
|
43
|
-
function clientError(
|
|
44
|
-
|
|
102
|
+
function clientError(
|
|
103
|
+
code: string,
|
|
104
|
+
message: string,
|
|
105
|
+
retryable: boolean,
|
|
106
|
+
accepted = false,
|
|
107
|
+
requestId: string | null = null
|
|
108
|
+
): never {
|
|
109
|
+
throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hasTaskBoundMarker(env: NodeJS.ProcessEnv = process.env): boolean {
|
|
113
|
+
return Boolean(env.AGENT_INFRA_TASK_ID)
|
|
114
|
+
&& Boolean(env.AGENT_INFRA_CONTROL_TOKEN || env.AGENT_INFRA_CONTROL_GENERATION
|
|
115
|
+
|| env.AGENT_INFRA_CONTROL_STATUS_DIR || env.AGENT_INFRA_RUNTIME_DIR || env.AGENT_INFRA_CONTROL_DIR);
|
|
45
116
|
}
|
|
46
117
|
|
|
47
|
-
function preflight(
|
|
118
|
+
function preflight(
|
|
119
|
+
statusDir: string,
|
|
120
|
+
generation: string,
|
|
121
|
+
taskViewEffect: TaskViewAccessEffect | null = null,
|
|
122
|
+
now = Date.now(),
|
|
123
|
+
env: NodeJS.ProcessEnv = process.env
|
|
124
|
+
): void {
|
|
48
125
|
let status;
|
|
49
126
|
try {
|
|
50
127
|
status = readSandboxControlStatus(statusDir);
|
|
@@ -63,18 +140,60 @@ function preflight(statusDir: string, generation: string, now = Date.now()): voi
|
|
|
63
140
|
clientError(code, 'broker is parked until the host restores a valid binding', true);
|
|
64
141
|
}
|
|
65
142
|
if (status.state !== 'healthy') clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
|
|
143
|
+
if (taskViewEffect && hasTaskBoundMarker(env)) {
|
|
144
|
+
const view = taskViewFromStatus(status);
|
|
145
|
+
if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
|
|
146
|
+
clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
|
|
147
|
+
}
|
|
148
|
+
const access = accessSandboxTaskView(view, taskViewEffect);
|
|
149
|
+
if (!access.allowed) {
|
|
150
|
+
clientError(
|
|
151
|
+
access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED',
|
|
152
|
+
access.message ?? 'task view denied',
|
|
153
|
+
false
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
|
|
160
|
+
if (request.family === 'task-lifecycle' || request.family === 'task-finalization') return 'progress';
|
|
161
|
+
if (request.family === 'task-orchestration') return request.args[1] === 'status' ? 'diagnostic' : 'progress';
|
|
162
|
+
return null;
|
|
66
163
|
}
|
|
67
164
|
|
|
68
|
-
function parseResponse(raw: string, id: string): SandboxControlResponse {
|
|
165
|
+
function parseResponse(raw: string, id: string, accepted = false): SandboxControlResponse {
|
|
69
166
|
const response = JSON.parse(raw) as SandboxControlResponse;
|
|
70
167
|
if (response.version !== 2 || response.id !== id
|
|
71
|
-
|| !['
|
|
72
|
-
|| (response.phase === 'completed' && !Number.isInteger(response.exitCode))
|
|
73
|
-
|
|
168
|
+
|| !['completed', 'rejected'].includes(response.phase)
|
|
169
|
+
|| (response.phase === 'completed' && !Number.isInteger(response.exitCode))
|
|
170
|
+
|| (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
|
|
171
|
+
|| (response.outputState === 'available' && !response.payload)
|
|
172
|
+
|| (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
|
|
173
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
|
|
74
174
|
}
|
|
75
175
|
return response;
|
|
76
176
|
}
|
|
77
177
|
|
|
178
|
+
function readPublishedResponse(raw: string, id: string, channelDir: string, accepted = false): SandboxControlResponse {
|
|
179
|
+
const response = parseResponse(raw, id, accepted);
|
|
180
|
+
if (response.outputState !== 'available') return response;
|
|
181
|
+
const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
|
|
182
|
+
let payload;
|
|
183
|
+
try {
|
|
184
|
+
payload = readSandboxControlPayload(filePath);
|
|
185
|
+
} catch {
|
|
186
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload is missing or invalid', false, true, id);
|
|
187
|
+
}
|
|
188
|
+
if (!response.payload || response.payload.version !== payload.version
|
|
189
|
+
|| response.payload.id !== payload.id || response.payload.generation !== payload.generation
|
|
190
|
+
|| response.payload.stdoutBytes !== payload.stdoutBytes || response.payload.stderrBytes !== payload.stderrBytes
|
|
191
|
+
|| response.payload.stdoutSha256 !== payload.stdoutSha256 || response.payload.stderrSha256 !== payload.stderrSha256) {
|
|
192
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload reference is invalid', false, true, id);
|
|
193
|
+
}
|
|
194
|
+
return { ...response, stdout: payload.stdout, stderr: payload.stderr };
|
|
195
|
+
}
|
|
196
|
+
|
|
78
197
|
function cancelPendingRequest(requestPath: string): boolean {
|
|
79
198
|
try {
|
|
80
199
|
fs.unlinkSync(requestPath);
|
|
@@ -92,16 +211,20 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
92
211
|
}>): SandboxControlResponse {
|
|
93
212
|
const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
|
|
94
213
|
const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
|
|
95
|
-
|
|
214
|
+
const taskViewEffect = taskViewEffectForRequest(request);
|
|
215
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
96
216
|
const encoded = `${JSON.stringify(request)}\n`;
|
|
97
217
|
if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
|
|
98
|
-
clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false);
|
|
218
|
+
clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
|
|
99
219
|
}
|
|
100
220
|
const requestsDir = path.join(channelDir, 'requests');
|
|
101
221
|
const responsesDir = path.join(channelDir, 'responses');
|
|
102
222
|
const temporary = path.join(requestsDir, `.${request.id}.tmp`);
|
|
103
223
|
const requestPath = path.join(requestsDir, `${request.id}.json`);
|
|
104
224
|
const responsePath = path.join(responsesDir, `${request.id}.json`);
|
|
225
|
+
if (fs.existsSync(responsePath)) {
|
|
226
|
+
clientError('SANDBOX_CONTROL_REQUEST_REPLAYED', 'request id already has a terminal response', false, false, request.id);
|
|
227
|
+
}
|
|
105
228
|
fs.writeFileSync(temporary, encoded, { mode: 0o600, flag: 'wx' });
|
|
106
229
|
fs.renameSync(temporary, requestPath);
|
|
107
230
|
const deadline = Date.now() + (params.timeoutMs ?? 30_000);
|
|
@@ -109,6 +232,8 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
109
232
|
let malformedResponseRaw: string | null = null;
|
|
110
233
|
let malformedResponseObservedAt = 0;
|
|
111
234
|
while (Date.now() < deadline) {
|
|
235
|
+
const acceptedPath = path.join(responsesDir, `${request.id}.accepted.json`);
|
|
236
|
+
if (fs.existsSync(acceptedPath)) accepted = true;
|
|
112
237
|
if (fs.existsSync(responsePath)) {
|
|
113
238
|
let raw: string;
|
|
114
239
|
try {
|
|
@@ -119,7 +244,9 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
119
244
|
}
|
|
120
245
|
let response: SandboxControlResponse;
|
|
121
246
|
try {
|
|
122
|
-
|
|
247
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
248
|
+
accepted = true;
|
|
249
|
+
response = readPublishedResponse(raw, request.id, channelDir, accepted);
|
|
123
250
|
malformedResponseRaw = null;
|
|
124
251
|
} catch (error) {
|
|
125
252
|
if (!(error instanceof SyntaxError)) throw error;
|
|
@@ -128,7 +255,7 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
128
255
|
malformedResponseRaw = raw;
|
|
129
256
|
malformedResponseObservedAt = now;
|
|
130
257
|
} else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
|
|
131
|
-
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted);
|
|
258
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, request.id);
|
|
132
259
|
}
|
|
133
260
|
sleep(25);
|
|
134
261
|
continue;
|
|
@@ -136,20 +263,20 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
136
263
|
if (response.phase === 'accepted') {
|
|
137
264
|
accepted = true;
|
|
138
265
|
} else {
|
|
139
|
-
fs.rmSync(responsePath, { force: true });
|
|
140
266
|
return response;
|
|
141
267
|
}
|
|
142
268
|
}
|
|
143
269
|
if (!accepted) {
|
|
144
270
|
try {
|
|
145
|
-
preflight(statusDir, request.generation);
|
|
271
|
+
preflight(statusDir, request.generation, taskViewEffect);
|
|
146
272
|
} catch (error) {
|
|
147
273
|
if (cancelPendingRequest(requestPath)) throw error;
|
|
148
274
|
clientError(
|
|
149
275
|
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
150
276
|
'request was claimed before broker availability was lost; inspect domain state before retrying',
|
|
151
277
|
false,
|
|
152
|
-
true
|
|
278
|
+
true,
|
|
279
|
+
request.id
|
|
153
280
|
);
|
|
154
281
|
}
|
|
155
282
|
}
|
|
@@ -160,7 +287,8 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
160
287
|
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
161
288
|
'accepted request did not produce a final result; inspect domain state before retrying',
|
|
162
289
|
false,
|
|
163
|
-
true
|
|
290
|
+
true,
|
|
291
|
+
request.id
|
|
164
292
|
);
|
|
165
293
|
}
|
|
166
294
|
if (!cancelPendingRequest(requestPath)) {
|
|
@@ -168,10 +296,66 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
|
|
|
168
296
|
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
169
297
|
'request was claimed but no acceptance result was observed; inspect domain state before retrying',
|
|
170
298
|
false,
|
|
171
|
-
true
|
|
299
|
+
true,
|
|
300
|
+
request.id
|
|
172
301
|
);
|
|
173
302
|
}
|
|
174
|
-
clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true);
|
|
303
|
+
clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true, false, request.id);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function recoverSandboxControl(requestId: string, params: Readonly<{
|
|
307
|
+
channelDir?: string;
|
|
308
|
+
timeoutMs?: number;
|
|
309
|
+
}> = {}): SandboxControlResponse {
|
|
310
|
+
if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) {
|
|
311
|
+
clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'request id is invalid', false, true);
|
|
312
|
+
}
|
|
313
|
+
const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
|
|
314
|
+
const responsePath = path.join(channelDir, 'responses', `${requestId}.json`);
|
|
315
|
+
const deadline = Date.now() + (params.timeoutMs ?? 30_000);
|
|
316
|
+
let accepted = false;
|
|
317
|
+
let malformedResponseRaw: string | null = null;
|
|
318
|
+
let malformedResponseObservedAt = 0;
|
|
319
|
+
while (Date.now() < deadline) {
|
|
320
|
+
if (fs.existsSync(path.join(channelDir, 'responses', `${requestId}.accepted.json`))) accepted = true;
|
|
321
|
+
if (fs.existsSync(responsePath)) {
|
|
322
|
+
let raw: string;
|
|
323
|
+
try {
|
|
324
|
+
raw = fs.readFileSync(responsePath, 'utf8');
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue;
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
let response: SandboxControlResponse;
|
|
330
|
+
try {
|
|
331
|
+
// The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
|
|
332
|
+
accepted = true;
|
|
333
|
+
response = readPublishedResponse(raw, requestId, channelDir, accepted);
|
|
334
|
+
malformedResponseRaw = null;
|
|
335
|
+
} catch (error) {
|
|
336
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
337
|
+
const now = Date.now();
|
|
338
|
+
if (malformedResponseRaw !== raw) {
|
|
339
|
+
malformedResponseRaw = raw;
|
|
340
|
+
malformedResponseObservedAt = now;
|
|
341
|
+
} else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
|
|
342
|
+
clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, requestId);
|
|
343
|
+
}
|
|
344
|
+
sleep(25);
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (response.phase === 'accepted') accepted = true;
|
|
348
|
+
else return response;
|
|
349
|
+
}
|
|
350
|
+
sleep(25);
|
|
351
|
+
}
|
|
352
|
+
clientError(
|
|
353
|
+
'SANDBOX_CONTROL_RESULT_UNKNOWN',
|
|
354
|
+
'request did not produce a final result; inspect domain state before retrying',
|
|
355
|
+
false,
|
|
356
|
+
accepted,
|
|
357
|
+
requestId
|
|
358
|
+
);
|
|
175
359
|
}
|
|
176
360
|
|
|
177
361
|
function authority(params: { token?: string; generation?: string }): { token: string; generation: string } {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { resolveTaskWorkspace } from '../task-resolver.ts';
|
|
4
|
+
import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
|
|
5
|
+
import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
|
|
6
|
+
import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
|
|
7
|
+
import { inspectSandboxControlContainer } from './container-identity.ts';
|
|
8
|
+
import type { SandboxControlManifest } from './protocol.ts';
|
|
9
|
+
import { atomicWriteJson, readSandboxControlStatus } from './state.ts';
|
|
10
|
+
import { taskViewForManifest, type SandboxTaskView } from './task-view.ts';
|
|
11
|
+
|
|
12
|
+
type ReentryEvidence = Readonly<{
|
|
13
|
+
taskId: string;
|
|
14
|
+
generation: string;
|
|
15
|
+
containerId: string;
|
|
16
|
+
branch: string;
|
|
17
|
+
worktreeRoot: string;
|
|
18
|
+
source: string;
|
|
19
|
+
sourceDevice: string;
|
|
20
|
+
sourceInode: string;
|
|
21
|
+
receipt: NonNullable<SandboxTaskView['receipt']>;
|
|
22
|
+
}>;
|
|
23
|
+
|
|
24
|
+
function evidencePath(manifest: SandboxControlManifest): string {
|
|
25
|
+
return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function invalid(): Error {
|
|
29
|
+
return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function prepareCompletedReentry(
|
|
33
|
+
manifest: SandboxControlManifest,
|
|
34
|
+
inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
|
|
35
|
+
): Promise<ReentryEvidence> {
|
|
36
|
+
if (manifest.mode !== 'task-bound' || !manifest.taskId) throw invalid();
|
|
37
|
+
assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
|
|
38
|
+
const container = await inspect(manifest);
|
|
39
|
+
if (container.state !== 'found' || container.id !== manifest.containerIdentity.id) throw invalid();
|
|
40
|
+
const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
|
|
41
|
+
const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
|
|
42
|
+
if (task.state !== 'completed' || task.branch !== manifest.branch
|
|
43
|
+
|| metadata.id !== manifest.taskId || metadata.status !== 'completed') throw invalid();
|
|
44
|
+
const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
|
|
45
|
+
if (!receipt || receipt.taskId !== manifest.taskId) throw invalid();
|
|
46
|
+
const view = taskViewForManifest({ ...manifest, receipt });
|
|
47
|
+
if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt) throw invalid();
|
|
48
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
49
|
+
const previous = status.taskView;
|
|
50
|
+
if (status.generation !== manifest.generation || status.activeRequestId !== null
|
|
51
|
+
|| previous.taskId !== manifest.taskId || !previous.receipt
|
|
52
|
+
|| previous.receipt.receiptId !== view.receipt.receiptId
|
|
53
|
+
|| previous.receipt.generation !== view.receipt.generation
|
|
54
|
+
|| previous.receipt.requestId !== view.receipt.requestId
|
|
55
|
+
|| previous.receipt.revision > view.receipt.revision) throw invalid();
|
|
56
|
+
const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
|
|
57
|
+
const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
|
|
58
|
+
if (source !== path.join(completedRoot, manifest.taskId)) throw invalid();
|
|
59
|
+
const stat = fs.statSync(source, { bigint: true });
|
|
60
|
+
return {
|
|
61
|
+
taskId: manifest.taskId, generation: manifest.generation,
|
|
62
|
+
containerId: container.id, branch: manifest.branch,
|
|
63
|
+
worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
|
|
64
|
+
source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function publishCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): void {
|
|
69
|
+
atomicWriteJson(evidencePath(manifest), evidence);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function completedReentryView(
|
|
73
|
+
manifest: SandboxControlManifest,
|
|
74
|
+
inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
|
|
75
|
+
): Promise<SandboxTaskView | null> {
|
|
76
|
+
const file = evidencePath(manifest);
|
|
77
|
+
if (!fs.existsSync(file)) return null;
|
|
78
|
+
const evidence: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
79
|
+
const current = await prepareCompletedReentry(manifest, inspect);
|
|
80
|
+
if (JSON.stringify(evidence) !== JSON.stringify(current)) throw invalid();
|
|
81
|
+
return {
|
|
82
|
+
state: 'current', taskId: current.taskId, observedSource: 'completed',
|
|
83
|
+
receipt: current.receipt, reasonCode: null
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function waitForCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): Promise<void> {
|
|
88
|
+
const deadline = Date.now() + 10_000;
|
|
89
|
+
while (Date.now() < deadline) {
|
|
90
|
+
const status = readSandboxControlStatus(manifest.publicStatusDir);
|
|
91
|
+
if (status.generation !== evidence.generation) throw invalid();
|
|
92
|
+
if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
|
|
93
|
+
&& status.taskView.taskId === evidence.taskId
|
|
94
|
+
&& JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt)) return;
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
96
|
+
}
|
|
97
|
+
throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
|
|
98
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { SandboxControlManifest } from './protocol.ts';
|
|
2
|
-
import {
|
|
2
|
+
import { runProbe } from '../shell.ts';
|
|
3
|
+
import { captureSandboxAuthority, isSandboxAuthorityEvidence, verifySandboxAuthority } from '../engines/authority.ts';
|
|
4
|
+
import { discoverExactContainer } from '../engines/docker-exact-discovery.ts';
|
|
3
5
|
|
|
4
6
|
export type ContainerObservation =
|
|
5
7
|
| Readonly<{ state: 'found'; id: string; running: boolean; labels: Readonly<Record<string, string>> }>
|
|
@@ -12,20 +14,13 @@ export type ContainerInspection = Readonly<{
|
|
|
12
14
|
labels?: unknown;
|
|
13
15
|
}>;
|
|
14
16
|
|
|
15
|
-
function isAuthoritativeNotFound(error: unknown): boolean {
|
|
16
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
17
|
-
return /(?:no such (?:container|object)|container .* not found|cannot find container)/i.test(message);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
17
|
export function classifySandboxContainerInspection(
|
|
21
18
|
expected: SandboxControlManifest['containerIdentity'],
|
|
22
19
|
inspection: ContainerInspection | { notFound: true } | { error: unknown }
|
|
23
20
|
): ContainerObservation {
|
|
24
21
|
if ('notFound' in inspection) return { state: 'absent', id: expected.id };
|
|
25
22
|
if ('error' in inspection) {
|
|
26
|
-
return
|
|
27
|
-
? { state: 'absent', id: expected.id }
|
|
28
|
-
: { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
|
|
23
|
+
return { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
|
|
29
24
|
}
|
|
30
25
|
const response = inspection as ContainerInspection;
|
|
31
26
|
if (typeof response.id !== 'string' || response.id !== expected.id
|
|
@@ -49,44 +44,33 @@ export function classifySandboxContainerInspection(
|
|
|
49
44
|
|
|
50
45
|
export async function inspectSandboxControlContainer(
|
|
51
46
|
manifest: SandboxControlManifest,
|
|
52
|
-
options: {
|
|
47
|
+
options: { probe?: typeof runProbe; timeoutMs?: number } = {}
|
|
53
48
|
): Promise<ContainerObservation> {
|
|
49
|
+
if (!isSandboxAuthorityEvidence(manifest.authorityEvidence)) {
|
|
50
|
+
return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
|
|
51
|
+
}
|
|
54
52
|
try {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs }
|
|
65
|
-
);
|
|
66
|
-
if (result.status !== 0) {
|
|
67
|
-
const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '';
|
|
68
|
-
return classifySandboxContainerInspection(manifest.containerIdentity, {
|
|
69
|
-
error: new Error(stderr || `container inspection exited with ${result.status ?? 'unknown'}`)
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
raw = typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '';
|
|
53
|
+
const authority = captureSandboxAuthority(manifest.engine, {
|
|
54
|
+
probe: options.probe,
|
|
55
|
+
timeoutMs: options.timeoutMs,
|
|
56
|
+
lockDomain: manifest.authorityEvidence.lockDomain,
|
|
57
|
+
route: manifest.authorityEvidence
|
|
58
|
+
});
|
|
59
|
+
const authorityState = verifySandboxAuthority(manifest.authorityEvidence, authority);
|
|
60
|
+
if (authorityState.state !== 'verified') {
|
|
61
|
+
return { state: 'unknown', reason: authorityState.reason ?? 'SANDBOX_AUTHORITY_UNAVAILABLE' };
|
|
73
62
|
}
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} | Array<{
|
|
79
|
-
Id?: unknown;
|
|
80
|
-
State?: { Running?: unknown };
|
|
81
|
-
Config?: { Labels?: unknown };
|
|
82
|
-
}>;
|
|
83
|
-
const value = Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed;
|
|
84
|
-
if (!value || Array.isArray(value)) return { state: 'unknown', reason: 'container inspection response is incomplete' };
|
|
85
|
-
return classifySandboxContainerInspection(manifest.containerIdentity, {
|
|
86
|
-
id: value.Id,
|
|
87
|
-
running: value.State?.Running,
|
|
88
|
-
labels: value.Config?.Labels
|
|
63
|
+
const discovered = discoverExactContainer(manifest.engine, manifest.containerIdentity.id, {
|
|
64
|
+
probe: options.probe,
|
|
65
|
+
timeoutMs: options.timeoutMs,
|
|
66
|
+
authority: manifest.authorityEvidence
|
|
89
67
|
});
|
|
68
|
+
if (discovered.state === 'absent') return { state: 'absent', id: discovered.id };
|
|
69
|
+
if (discovered.state === 'conflict') {
|
|
70
|
+
return { state: 'unknown', reason: `SANDBOX_CONTROL_CONTAINER_CONFLICT: ${discovered.reason}` };
|
|
71
|
+
}
|
|
72
|
+
if (discovered.state === 'unknown') return discovered;
|
|
73
|
+
return classifySandboxContainerInspection(manifest.containerIdentity, discovered);
|
|
90
74
|
} catch (error) {
|
|
91
75
|
return classifySandboxContainerInspection(manifest.containerIdentity, { error });
|
|
92
76
|
}
|