@fitlab-ai/agent-infra 0.9.2 → 0.9.4
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 +12 -9
- package/README.zh-CN.md +12 -9
- package/bin/cli.ts +12 -0
- package/dist/bin/cli.js +14 -0
- package/dist/bin/internal-cli.js +134 -108
- package/dist/lib/agent-clients/adapter.js +137 -4
- package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
- package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
- package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
- package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
- package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
- package/dist/lib/agent-clients/adapters/codex.js +9 -0
- package/dist/lib/agent-clients/adapters/index.js +2 -2
- package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
- package/dist/lib/agent-clients/adapters/opencode.js +24 -3
- package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/dist/lib/agent-clients/config.js +42 -4
- package/dist/lib/agent-clients/next-steps.js +11 -2
- package/dist/lib/agent-clients/reconcile.js +2 -1
- package/dist/lib/agent-clients/registry.js +16 -2
- package/dist/lib/agent-clients/schema.js +18 -0
- package/dist/lib/agent-clients/tokens.js +49 -0
- package/dist/lib/agent-clients/types.js +1 -1
- package/dist/lib/defaults.json +1 -1
- package/dist/lib/git/workflow.js +54 -1
- package/dist/lib/internal/agent-client.js +61 -4
- package/dist/lib/internal/git-workflow.js +6 -1
- package/dist/lib/internal/platform-checks.js +3 -3
- package/dist/lib/internal/platform-comment.js +29 -2
- package/dist/lib/internal/platform-issue.js +7 -0
- package/dist/lib/internal/platform-pr-review.js +132 -0
- package/dist/lib/internal/platform-pr.js +20 -2
- package/dist/lib/internal/platform-release-notes.js +52 -16
- package/dist/lib/internal/pr-review-grade.js +140 -0
- package/dist/lib/internal/release-workflow.js +13 -6
- package/dist/lib/internal/sandbox-control.js +24 -0
- package/dist/lib/internal/task-activity.js +105 -0
- package/dist/lib/internal/task-event.js +17 -1
- package/dist/lib/internal/task-ledger.js +12 -3
- package/dist/lib/internal/task-lifecycle.js +26 -1
- package/dist/lib/internal/task-orchestration.js +167 -18
- package/dist/lib/internal/task-review.js +8 -3
- package/dist/lib/platform/adapters.js +8 -1
- package/dist/lib/platform/completion-backfill.js +122 -0
- package/dist/lib/platform/github-client.js +19 -2
- package/dist/lib/platform/issue-comments.js +3 -2
- package/dist/lib/platform/merged-pr-equivalence.js +115 -5
- package/dist/lib/platform/pr-checks.js +65 -42
- package/dist/lib/platform/pr-review.js +166 -0
- package/dist/lib/platform/pull-requests.js +371 -4
- package/dist/lib/platform/release-notes.js +85 -3
- package/dist/lib/platform/releases.js +13 -5
- package/dist/lib/pr-review/evidence-grading.js +229 -0
- package/dist/lib/process-data/index.js +239 -0
- package/dist/lib/process-data/normalize.js +198 -0
- package/dist/lib/process-data/privacy.js +33 -0
- package/dist/lib/process-data/reconcile.js +95 -0
- package/dist/lib/process-data/repair.js +58 -0
- package/dist/lib/process-data/sources.js +345 -0
- package/dist/lib/process-data/store.js +282 -0
- package/dist/lib/process-data/types.js +2 -0
- package/dist/lib/run/index.js +4 -3
- package/dist/lib/run/tui.js +10 -13
- package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
- package/dist/lib/sandbox/capture.js +7 -2
- package/dist/lib/sandbox/commands/create.js +109 -248
- package/dist/lib/sandbox/commands/enter.js +15 -47
- package/dist/lib/sandbox/commands/list-running.js +16 -6
- package/dist/lib/sandbox/commands/ls.js +10 -6
- package/dist/lib/sandbox/commands/rebuild.js +4 -3
- package/dist/lib/sandbox/commands/refresh.js +4 -84
- package/dist/lib/sandbox/commands/rm.js +24 -3
- package/dist/lib/sandbox/commands/show.js +13 -4
- package/dist/lib/sandbox/commands/start.js +7 -4
- package/dist/lib/sandbox/config.js +2 -0
- package/dist/lib/sandbox/constants.js +6 -0
- package/dist/lib/sandbox/control/client.js +44 -0
- package/dist/lib/sandbox/control/protocol.js +35 -0
- package/dist/lib/sandbox/control/server.js +109 -0
- package/dist/lib/sandbox/dockerfile.js +36 -13
- package/dist/lib/sandbox/mounts.js +17 -4
- package/dist/lib/sandbox/recovery.js +128 -5
- package/dist/lib/sandbox/redaction.js +12 -0
- package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/dist/lib/sandbox/workspace-identity.js +95 -0
- package/dist/lib/sandbox/workspace-view.js +103 -0
- package/dist/lib/task/activity-intent.js +307 -0
- package/dist/lib/task/artifact-lifecycle.js +2 -1
- package/dist/lib/task/commands/log.js +15 -14
- package/dist/lib/task/commit-finalization.js +267 -0
- package/dist/lib/task/commit-intent.js +165 -0
- package/dist/lib/task/decision-intents.js +31 -17
- package/dist/lib/task/delegation-receipts.js +25 -4
- package/dist/lib/task/events.js +67 -28
- package/dist/lib/task/finalization-artifacts.js +35 -0
- package/dist/lib/task/implementation-inputs.js +54 -7
- package/dist/lib/task/ledger-intents.js +35 -1
- package/dist/lib/task/lifecycle-execution.js +46 -0
- package/dist/lib/task/orchestration.js +753 -54
- package/dist/lib/task/resolve-ref.js +41 -1
- package/dist/lib/task/review-finalization.js +29 -9
- package/dist/lib/task/task-execution-lock.js +231 -0
- package/dist/lib/task/verification-checks.js +1 -1
- package/dist/lib/task/verification-engine.js +218 -2
- package/dist/lib/task/verification.js +4 -3
- package/dist/lib/task/workspace-snapshot.js +17 -5
- package/lib/agent-clients/adapter.ts +201 -4
- package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
- package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
- package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
- package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
- package/lib/agent-clients/adapters/claude-code.ts +31 -4
- package/lib/agent-clients/adapters/codex.ts +9 -0
- package/lib/agent-clients/adapters/index.ts +2 -2
- package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
- package/lib/agent-clients/adapters/opencode.ts +27 -3
- package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
- package/lib/agent-clients/config.ts +49 -4
- package/lib/agent-clients/next-steps.ts +15 -2
- package/lib/agent-clients/reconcile.ts +2 -1
- package/lib/agent-clients/registry.ts +19 -1
- package/lib/agent-clients/schema.ts +18 -0
- package/lib/agent-clients/tokens.ts +53 -0
- package/lib/agent-clients/types.ts +15 -1
- package/lib/defaults.json +1 -1
- package/lib/git/workflow.ts +66 -2
- package/lib/internal/agent-client.ts +59 -4
- package/lib/internal/git-workflow.ts +5 -1
- package/lib/internal/platform-checks.ts +4 -4
- package/lib/internal/platform-comment.ts +17 -2
- package/lib/internal/platform-issue.ts +4 -0
- package/lib/internal/platform-pr-review.ts +104 -0
- package/lib/internal/platform-pr.ts +14 -1
- package/lib/internal/platform-release-notes.ts +59 -16
- package/lib/internal/pr-review-grade.ts +130 -0
- package/lib/internal/release-workflow.ts +13 -6
- package/lib/internal/sandbox-control.ts +24 -0
- package/lib/internal/task-activity.ts +91 -0
- package/lib/internal/task-event.ts +9 -1
- package/lib/internal/task-ledger.ts +12 -3
- package/lib/internal/task-lifecycle.ts +26 -1
- package/lib/internal/task-orchestration.ts +123 -14
- package/lib/internal/task-review.ts +8 -3
- package/lib/platform/adapters.ts +29 -2
- package/lib/platform/completion-backfill.ts +134 -0
- package/lib/platform/github-client.ts +20 -2
- package/lib/platform/issue-comments.ts +3 -2
- package/lib/platform/merged-pr-equivalence.ts +127 -5
- package/lib/platform/pr-checks.ts +73 -46
- package/lib/platform/pr-review.ts +201 -0
- package/lib/platform/pull-requests.ts +444 -3
- package/lib/platform/release-notes.ts +100 -4
- package/lib/platform/releases.ts +16 -4
- package/lib/pr-review/evidence-grading.ts +318 -0
- package/lib/process-data/index.ts +224 -0
- package/lib/process-data/normalize.ts +202 -0
- package/lib/process-data/privacy.ts +41 -0
- package/lib/process-data/reconcile.ts +110 -0
- package/lib/process-data/repair.ts +64 -0
- package/lib/process-data/sources.ts +354 -0
- package/lib/process-data/store.ts +315 -0
- package/lib/process-data/types.ts +135 -0
- package/lib/run/index.ts +5 -3
- package/lib/run/tui.ts +14 -12
- package/lib/sandbox/agent-client-reconciler.ts +44 -2
- package/lib/sandbox/capture.ts +9 -2
- package/lib/sandbox/commands/create.ts +126 -280
- package/lib/sandbox/commands/enter.ts +13 -59
- package/lib/sandbox/commands/list-running.ts +23 -6
- package/lib/sandbox/commands/ls.ts +22 -6
- package/lib/sandbox/commands/rebuild.ts +4 -3
- package/lib/sandbox/commands/refresh.ts +4 -126
- package/lib/sandbox/commands/rm.ts +24 -3
- package/lib/sandbox/commands/show.ts +30 -4
- package/lib/sandbox/commands/start.ts +6 -4
- package/lib/sandbox/config.ts +4 -0
- package/lib/sandbox/constants.ts +8 -0
- package/lib/sandbox/control/client.ts +55 -0
- package/lib/sandbox/control/protocol.ts +75 -0
- package/lib/sandbox/control/server.ts +117 -0
- package/lib/sandbox/dockerfile.ts +66 -13
- package/lib/sandbox/mounts.ts +24 -4
- package/lib/sandbox/recovery.ts +163 -6
- package/lib/sandbox/redaction.ts +14 -0
- package/lib/sandbox/runtimes/base.dockerfile +24 -74
- package/lib/sandbox/tool-types.ts +16 -0
- package/lib/sandbox/workspace-identity.ts +123 -0
- package/lib/sandbox/workspace-view.ts +143 -0
- package/lib/server/server.schema.json +2 -2
- package/lib/task/activity-intent.ts +417 -0
- package/lib/task/artifact-lifecycle.ts +2 -1
- package/lib/task/commands/log.ts +15 -14
- package/lib/task/commit-finalization.ts +356 -0
- package/lib/task/commit-intent.ts +230 -0
- package/lib/task/decision-intents.ts +32 -12
- package/lib/task/delegation-receipts.ts +34 -5
- package/lib/task/events.ts +75 -30
- package/lib/task/finalization-artifacts.ts +58 -0
- package/lib/task/implementation-inputs.ts +62 -8
- package/lib/task/ledger-intents.ts +43 -3
- package/lib/task/lifecycle-execution.ts +106 -0
- package/lib/task/orchestration.ts +1019 -72
- package/lib/task/resolve-ref.ts +46 -1
- package/lib/task/review-finalization.ts +40 -9
- package/lib/task/task-execution-lock.ts +296 -0
- package/lib/task/verification-checks.ts +1 -1
- package/lib/task/verification-engine.ts +249 -2
- package/lib/task/verification.ts +5 -3
- package/lib/task/workspace-snapshot.ts +26 -5
- package/package.json +3 -3
- package/templates/.agents/QUICKSTART.en.md +25 -8
- package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
- package/templates/.agents/README.en.md +10 -7
- package/templates/.agents/README.zh-CN.md +10 -7
- package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
- package/templates/.agents/rules/README.en.md +1 -1
- package/templates/.agents/rules/README.zh-CN.md +1 -1
- package/templates/.agents/rules/create-issue.github.en.md +4 -2
- package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
- package/templates/.agents/rules/human-decision-context.en.md +11 -2
- package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
- package/templates/.agents/rules/issue-fields.github.en.md +3 -1
- package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
- package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
- package/templates/.agents/rules/issue-sync.github.en.md +7 -3
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
- package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
- package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
- package/templates/.agents/rules/next-step-output.en.md +3 -1
- package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
- package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
- package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
- package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
- package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
- package/templates/.agents/rules/pr-sync.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.en.md +11 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
- package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
- package/templates/.agents/rules/release-commands.en.md +1 -1
- package/templates/.agents/rules/release-commands.github.en.md +12 -2
- package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
- package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
- package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
- package/templates/.agents/rules/task-management.en.md +13 -0
- package/templates/.agents/rules/task-management.zh-CN.md +13 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
- package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
- package/templates/.agents/skills/commit/SKILL.en.md +23 -11
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
- package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
- package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
- package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
- package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
- package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
- package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
- package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
- package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
- package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
- package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
- package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
- package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
- package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/release/SKILL.en.md +16 -10
- package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
- package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
- package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
- package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
- package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
- package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
- package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
- package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
- package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
- package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
- package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
- package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
- package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
- package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
- package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
- package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
- package/templates/.agents/templates/task.en.md +2 -2
- package/templates/.agents/templates/task.zh-CN.md +2 -2
- package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
- package/templates/.agents/workflows/code-review.en.yaml +1 -1
- package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +4 -4
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
- package/templates/.agents/workflows/refactoring.en.yaml +3 -3
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
- package/templates/.claude/commands/post-release.en.md +1 -0
- package/templates/.claude/commands/post-release.zh-CN.md +1 -0
- package/templates/.claude/commands/review-pr.en.md +8 -0
- package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
- package/templates/.claude/commands/watch-pr.en.md +1 -1
- package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
- package/templates/.claude/rules/tool-preferences.en.md +20 -0
- package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
- package/templates/.opencode/commands/post-release.en.md +2 -0
- package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
- package/templates/.opencode/commands/review-pr.en.md +11 -0
- package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
- package/templates/.opencode/commands/watch-pr.en.md +1 -1
- package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
- package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
- package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
- package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
- package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
- package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
- package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
- package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
- package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
- package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
- package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
- package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/release.en.toml +0 -8
- package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
- package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
- package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
- package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/test.en.toml +0 -6
- package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
- package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
- package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
- package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
- package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
- package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
- package/templates/.gemini/settings.json +0 -3
|
@@ -2,32 +2,33 @@ import fs from 'node:fs';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
7
8
|
import * as p from '@clack/prompts';
|
|
8
9
|
import pc from 'picocolors';
|
|
9
10
|
import { listAgentClientAdapters } from '../../agent-clients/registry.js';
|
|
10
11
|
import { loadConfig } from '../config.js';
|
|
11
|
-
import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, sandboxRuntimeCapabilityLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
|
|
12
|
+
import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, sandboxRuntimeCapabilityLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
|
|
12
13
|
import { prepareDockerfile } from '../dockerfile.js';
|
|
13
14
|
import { assertBuildProxyCompatibility, buildProxyFailureHint, prepareBuildProxy, redactBuildProxyValues } from '../build-proxy.js';
|
|
14
15
|
import { detectEngine, ensureDocker } from '../engine.js';
|
|
15
16
|
import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSafe, runSafeEngine, runVerboseEngine } from '../shell.js';
|
|
16
|
-
import {
|
|
17
|
+
import { parseSandboxWorkspaceIdentity, resolveSandboxTarget, sameSandboxWorkspaceIdentity } from '../workspace-identity.js';
|
|
17
18
|
import { createSandboxCapabilityPlan, runSandboxHooks, runBoundedSandboxHookCommand } from '../agent-client-reconciler.js';
|
|
18
19
|
import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
|
|
19
20
|
import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts } from '../recovery.js';
|
|
20
21
|
import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
|
|
21
22
|
import { sandboxCoreBindMounts } from '../mounts.js';
|
|
23
|
+
import { assertSandboxTaskSource, materializeSandboxControl, materializeSandboxWorkspaceView } from '../workspace-view.js';
|
|
22
24
|
import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.js';
|
|
23
25
|
import { validateSelinuxDisableEnv } from '../engines/selinux.js';
|
|
24
26
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
|
|
25
27
|
import { ensureSandboxDiscoveryReadmes } from '../readme-scaffold.js';
|
|
26
28
|
import { removeDirRecursive } from '../../remove-dir.js';
|
|
27
|
-
import {
|
|
29
|
+
import { redactCommandError } from '../redaction.js';
|
|
28
30
|
import { detectHostTimezone } from '../host-timezone.js';
|
|
29
31
|
import { buildImageSignature, buildSandboxImageArgs, isRefreshDisabled, isRefreshDue, parseImageLabels, parseRefreshTimestamp } from '../image-build.js';
|
|
30
|
-
export { ensureCodexModelInheritance, ensureCodexWorkspaceTrust } from '../../agent-clients/adapters/codex-sandbox.js';
|
|
31
32
|
const SANDBOX_ALIAS_BLOCK_BEGIN = '# >>> agent-infra managed aliases >>>';
|
|
32
33
|
const SANDBOX_ALIAS_BLOCK_END = '# <<< agent-infra managed aliases <<<';
|
|
33
34
|
const CONTAINER_HOME = '/home/devuser';
|
|
@@ -64,6 +65,11 @@ const MANAGED_GITHUB_GIT_CONFIG = [
|
|
|
64
65
|
'[url "https://github.com/"]',
|
|
65
66
|
'\tinsteadOf = git@github.com:'
|
|
66
67
|
];
|
|
68
|
+
function internalCliPath() {
|
|
69
|
+
const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
70
|
+
const extension = path.extname(fileURLToPath(import.meta.url));
|
|
71
|
+
return path.resolve(directory, '..', '..', '..', 'bin', `internal-cli${extension}`);
|
|
72
|
+
}
|
|
67
73
|
function resolveToolDirs(config, tools, branch) {
|
|
68
74
|
return tools.map((tool) => {
|
|
69
75
|
const candidates = toolConfigDirCandidates(tool, config.project, branch);
|
|
@@ -544,207 +550,6 @@ export function assertBranchAvailable(repoRoot, branch, { allowedWorktrees = [],
|
|
|
544
550
|
}
|
|
545
551
|
}
|
|
546
552
|
}
|
|
547
|
-
function readHostJsonSafe(filePath) {
|
|
548
|
-
if (!filePath || !fs.existsSync(filePath)) {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
try {
|
|
552
|
-
const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
553
|
-
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
|
|
554
|
-
}
|
|
555
|
-
catch {
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
const CLAUDE_SETTINGS_INHERIT_TOP_LEVEL_KEYS = [
|
|
560
|
-
'model',
|
|
561
|
-
'fallbackModel',
|
|
562
|
-
'availableModels',
|
|
563
|
-
'modelOverrides',
|
|
564
|
-
'enforceAvailableModels',
|
|
565
|
-
'advisorModel',
|
|
566
|
-
'apiKeyHelper',
|
|
567
|
-
'effortLevel'
|
|
568
|
-
];
|
|
569
|
-
function isJsonObjectRecord(value) {
|
|
570
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
571
|
-
}
|
|
572
|
-
function mergeMissingStringEnvFields(target, source) {
|
|
573
|
-
if (!isJsonObjectRecord(source.env)) {
|
|
574
|
-
return false;
|
|
575
|
-
}
|
|
576
|
-
if (Object.hasOwn(target, 'env') && !isJsonObjectRecord(target.env)) {
|
|
577
|
-
return false;
|
|
578
|
-
}
|
|
579
|
-
let targetEnv = target.env;
|
|
580
|
-
let changed = false;
|
|
581
|
-
for (const [key, value] of Object.entries(source.env)) {
|
|
582
|
-
if (typeof value !== 'string' || value === '') {
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
if (!targetEnv) {
|
|
586
|
-
targetEnv = {};
|
|
587
|
-
target.env = targetEnv;
|
|
588
|
-
}
|
|
589
|
-
if (!Object.hasOwn(targetEnv, key)) {
|
|
590
|
-
targetEnv[key] = value;
|
|
591
|
-
changed = true;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
return changed;
|
|
595
|
-
}
|
|
596
|
-
function mergeMissingTopLevelSettings(target, source) {
|
|
597
|
-
let changed = false;
|
|
598
|
-
for (const key of CLAUDE_SETTINGS_INHERIT_TOP_LEVEL_KEYS) {
|
|
599
|
-
if (!Object.hasOwn(source, key) || Object.hasOwn(target, key)) {
|
|
600
|
-
continue;
|
|
601
|
-
}
|
|
602
|
-
const value = source[key];
|
|
603
|
-
if (value === null || value === undefined || value === '') {
|
|
604
|
-
continue;
|
|
605
|
-
}
|
|
606
|
-
target[key] = value;
|
|
607
|
-
changed = true;
|
|
608
|
-
}
|
|
609
|
-
return changed;
|
|
610
|
-
}
|
|
611
|
-
export function ensureClaudeOnboarding(toolDir, hostHomeDir) {
|
|
612
|
-
const claudeJsonPath = path.join(toolDir, '.claude.json');
|
|
613
|
-
let data = {};
|
|
614
|
-
if (fs.existsSync(claudeJsonPath)) {
|
|
615
|
-
try {
|
|
616
|
-
data = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
|
|
617
|
-
}
|
|
618
|
-
catch {
|
|
619
|
-
// malformed JSON, start fresh
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
let changed = false;
|
|
623
|
-
if (!data.hasCompletedOnboarding) {
|
|
624
|
-
data.hasCompletedOnboarding = true;
|
|
625
|
-
changed = true;
|
|
626
|
-
}
|
|
627
|
-
if (!data.projects) {
|
|
628
|
-
data.projects = {};
|
|
629
|
-
changed = true;
|
|
630
|
-
}
|
|
631
|
-
if (!data.projects['/workspace']) {
|
|
632
|
-
data.projects['/workspace'] = {};
|
|
633
|
-
changed = true;
|
|
634
|
-
}
|
|
635
|
-
if (!data.projects['/workspace'].hasTrustDialogAccepted) {
|
|
636
|
-
data.projects['/workspace'].hasTrustDialogAccepted = true;
|
|
637
|
-
changed = true;
|
|
638
|
-
}
|
|
639
|
-
if (hostHomeDir) {
|
|
640
|
-
const hostClaudeJson = readHostJsonSafe(path.join(hostHomeDir, '.claude.json'));
|
|
641
|
-
if (hostClaudeJson
|
|
642
|
-
&& typeof hostClaudeJson.model === 'string'
|
|
643
|
-
&& hostClaudeJson.model !== ''
|
|
644
|
-
&& !Object.hasOwn(data, 'model')) {
|
|
645
|
-
data.model = hostClaudeJson.model;
|
|
646
|
-
changed = true;
|
|
647
|
-
}
|
|
648
|
-
// Claude Code launch-pins a default effort per model generation (for
|
|
649
|
-
// example xhigh for Opus 4.7). The saved effortLevel is honored only after
|
|
650
|
-
// a top-level boolean `unpin*LaunchEffort: true` flag unlocks it, so mirror
|
|
651
|
-
// those flags here with the existing first-write semantics.
|
|
652
|
-
//
|
|
653
|
-
// Pattern matching avoids one patch per future model generation. If
|
|
654
|
-
// Anthropic changes the naming convention, this block will no-op and should
|
|
655
|
-
// be revisited.
|
|
656
|
-
if (hostClaudeJson) {
|
|
657
|
-
for (const key of Object.keys(hostClaudeJson)) {
|
|
658
|
-
if (/^unpin.*LaunchEffort$/.test(key)
|
|
659
|
-
&& hostClaudeJson[key] === true
|
|
660
|
-
&& !Object.hasOwn(data, key)) {
|
|
661
|
-
data[key] = true;
|
|
662
|
-
changed = true;
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
if (changed) {
|
|
668
|
-
fs.writeFileSync(claudeJsonPath, JSON.stringify(data, null, 4), 'utf8');
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
export function ensureClaudeSettings(toolDir, hostHomeDir) {
|
|
672
|
-
const settingsPath = path.join(toolDir, 'settings.json');
|
|
673
|
-
let data = {};
|
|
674
|
-
if (fs.existsSync(settingsPath)) {
|
|
675
|
-
try {
|
|
676
|
-
data = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
677
|
-
}
|
|
678
|
-
catch {
|
|
679
|
-
// malformed JSON, start fresh
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
let changed = false;
|
|
683
|
-
if (data.skipDangerousModePermissionPrompt !== true) {
|
|
684
|
-
data.skipDangerousModePermissionPrompt = true;
|
|
685
|
-
changed = true;
|
|
686
|
-
}
|
|
687
|
-
if (hostHomeDir) {
|
|
688
|
-
const hostSettings = readHostJsonSafe(path.join(hostHomeDir, '.claude', 'settings.json'));
|
|
689
|
-
if (hostSettings) {
|
|
690
|
-
changed = mergeMissingStringEnvFields(data, hostSettings) || changed;
|
|
691
|
-
changed = mergeMissingTopLevelSettings(data, hostSettings) || changed;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
if (changed) {
|
|
695
|
-
fs.writeFileSync(settingsPath, JSON.stringify(data, null, 4), 'utf8');
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
export function ensureOpenCodeModelInheritance(toolDir, hostHomeDir) {
|
|
699
|
-
if (!hostHomeDir) {
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
const hostConfigPath = path.join(hostHomeDir, '.config', 'opencode', 'opencode.json');
|
|
703
|
-
const hostJson = readHostJsonSafe(hostConfigPath);
|
|
704
|
-
if (!hostJson) {
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
const sandboxConfigPath = path.join(toolDir, 'opencode.json');
|
|
708
|
-
let sandboxJson = {};
|
|
709
|
-
if (fs.existsSync(sandboxConfigPath)) {
|
|
710
|
-
const existing = readHostJsonSafe(sandboxConfigPath);
|
|
711
|
-
if (!existing) {
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
sandboxJson = existing;
|
|
715
|
-
}
|
|
716
|
-
let changed = false;
|
|
717
|
-
for (const key of ['model', 'small_model']) {
|
|
718
|
-
if (Object.hasOwn(sandboxJson, key)) {
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
const value = hostJson[key];
|
|
722
|
-
if (typeof value !== 'string' || value === '') {
|
|
723
|
-
continue;
|
|
724
|
-
}
|
|
725
|
-
sandboxJson[key] = value;
|
|
726
|
-
changed = true;
|
|
727
|
-
}
|
|
728
|
-
if (changed) {
|
|
729
|
-
fs.writeFileSync(sandboxConfigPath, JSON.stringify(sandboxJson, null, 2), 'utf8');
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
export function ensureGeminiWorkspaceTrust(toolDir) {
|
|
733
|
-
const trustPath = path.join(toolDir, 'trustedFolders.json');
|
|
734
|
-
let data = {};
|
|
735
|
-
if (fs.existsSync(trustPath)) {
|
|
736
|
-
try {
|
|
737
|
-
data = JSON.parse(fs.readFileSync(trustPath, 'utf8'));
|
|
738
|
-
}
|
|
739
|
-
catch {
|
|
740
|
-
// malformed JSON, start fresh
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
if (data['/workspace'] !== 'TRUST_FOLDER') {
|
|
744
|
-
data['/workspace'] = 'TRUST_FOLDER';
|
|
745
|
-
fs.writeFileSync(trustPath, JSON.stringify(data, null, 2), 'utf8');
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
553
|
export function sandboxAliasesPath(home) {
|
|
749
554
|
return hostJoin(home, '.agent-infra', 'aliases', 'sandbox.sh');
|
|
750
555
|
}
|
|
@@ -864,10 +669,10 @@ export async function create(args) {
|
|
|
864
669
|
throw new Error(USAGE);
|
|
865
670
|
}
|
|
866
671
|
validateSelinuxDisableEnv();
|
|
867
|
-
validateClaudeCredentialsEnvOverride();
|
|
868
672
|
const config = loadConfig();
|
|
869
673
|
const [branchOrTaskId = '', base] = positionals;
|
|
870
|
-
const
|
|
674
|
+
const target = resolveSandboxTarget(branchOrTaskId, config.repoRoot);
|
|
675
|
+
const branch = target.branch;
|
|
871
676
|
assertValidBranchName(branch);
|
|
872
677
|
const effectiveConfig = {
|
|
873
678
|
...config,
|
|
@@ -882,39 +687,43 @@ export async function create(args) {
|
|
|
882
687
|
const capabilityPlan = createSandboxCapabilityPlan(effectiveConfig);
|
|
883
688
|
const tools = [...capabilityPlan.tools];
|
|
884
689
|
const resolvedTools = resolveToolDirs(effectiveConfig, tools, branch);
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
690
|
+
const prepareHookResults = await runSandboxHooks({
|
|
691
|
+
hooks: capabilityPlan.hooksByPhase.prepare,
|
|
692
|
+
phase: 'prepare',
|
|
693
|
+
context: {
|
|
694
|
+
config: effectiveConfig,
|
|
695
|
+
plan: capabilityPlan,
|
|
696
|
+
create: {
|
|
697
|
+
hostHome: effectiveConfig.home,
|
|
698
|
+
hostEnv: { ...process.env },
|
|
699
|
+
project: effectiveConfig.project,
|
|
700
|
+
resolvedTools
|
|
701
|
+
}
|
|
702
|
+
},
|
|
703
|
+
runCommand: runBoundedSandboxHookCommand
|
|
704
|
+
});
|
|
705
|
+
const prepareFailure = prepareHookResults.find((result) => result.status === 'fatal');
|
|
706
|
+
if (prepareFailure) {
|
|
707
|
+
throw new Error(prepareFailure.message ?? `Sandbox hook '${prepareFailure.hookId}' failed.`);
|
|
708
|
+
}
|
|
889
709
|
const effectiveResolvedTools = resolvedTools;
|
|
890
710
|
const container = containerName(effectiveConfig, branch);
|
|
891
711
|
const worktree = worktreeCandidates.find((candidate) => fs.existsSync(candidate)) ?? worktreeCandidates[0] ?? '';
|
|
892
712
|
const shareCommon = shareCommonDir(effectiveConfig);
|
|
893
713
|
const shareBranch = shareBranchDir(effectiveConfig, branch);
|
|
894
|
-
const preparedDockerfile = prepareDockerfile(effectiveConfig);
|
|
714
|
+
const preparedDockerfile = prepareDockerfile(effectiveConfig, capabilityPlan.image);
|
|
895
715
|
const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
|
|
896
716
|
const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
|
|
897
717
|
const engine = detectEngine(effectiveConfig);
|
|
898
718
|
let createdTmpfsSeedPlan = [];
|
|
899
719
|
p.intro(pc.cyan('AI Sandbox'));
|
|
900
720
|
p.log.info(`Project: ${pc.bold(effectiveConfig.project)} | Branch: ${pc.bold(branch)} | Base: ${pc.bold(baseBranch || 'HEAD')}`);
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
+ ' To enable it: run "claude" once on the host to complete login, then re-run "ai sandbox create".');
|
|
721
|
+
for (const result of prepareHookResults) {
|
|
722
|
+
if (result.status === 'warning' && result.message)
|
|
723
|
+
p.log.warn(result.message);
|
|
905
724
|
}
|
|
906
725
|
try {
|
|
907
726
|
p.log.step('Checking container engine...');
|
|
908
|
-
const prepareHookResults = await runSandboxHooks({
|
|
909
|
-
hooks: capabilityPlan.hooksByPhase.prepare,
|
|
910
|
-
phase: 'prepare',
|
|
911
|
-
context: { config: effectiveConfig, plan: capabilityPlan },
|
|
912
|
-
runCommand: runBoundedSandboxHookCommand
|
|
913
|
-
});
|
|
914
|
-
const prepareFailure = prepareHookResults.find((result) => result.status === 'fatal');
|
|
915
|
-
if (prepareFailure) {
|
|
916
|
-
throw new Error(prepareFailure.message ?? `Sandbox hook '${prepareFailure.hookId}' failed.`);
|
|
917
|
-
}
|
|
918
727
|
await ensureDocker(effectiveConfig, (detail) => {
|
|
919
728
|
p.log.info(` ${detail}`);
|
|
920
729
|
});
|
|
@@ -1064,6 +873,32 @@ export async function create(args) {
|
|
|
1064
873
|
const matchedContainers = containerNameCandidates(effectiveConfig, branch)
|
|
1065
874
|
.filter((name) => existing.includes(name));
|
|
1066
875
|
if (matchedContainers.length > 0) {
|
|
876
|
+
for (const name of matchedContainers) {
|
|
877
|
+
const rawLabels = runSafeEngine(engine, 'docker', [
|
|
878
|
+
'inspect', '--format', '{{json .Config.Labels}}', name
|
|
879
|
+
]).trim();
|
|
880
|
+
let labels = {};
|
|
881
|
+
try {
|
|
882
|
+
labels = JSON.parse(rawLabels);
|
|
883
|
+
}
|
|
884
|
+
catch {
|
|
885
|
+
labels = {};
|
|
886
|
+
}
|
|
887
|
+
const existingIdentity = parseSandboxWorkspaceIdentity(labels, {
|
|
888
|
+
mode: sandboxWorkspaceModeLabel(effectiveConfig),
|
|
889
|
+
taskId: sandboxTaskIdLabel(effectiveConfig)
|
|
890
|
+
});
|
|
891
|
+
if (!sameSandboxWorkspaceIdentity(existingIdentity, target.workspace)) {
|
|
892
|
+
const existingDescription = existingIdentity.mode === 'task-bound'
|
|
893
|
+
? `task-bound:${existingIdentity.taskId}`
|
|
894
|
+
: existingIdentity.mode;
|
|
895
|
+
const requestedDescription = target.workspace.mode === 'task-bound'
|
|
896
|
+
? `task-bound:${target.workspace.taskId}`
|
|
897
|
+
: target.workspace.mode;
|
|
898
|
+
throw new Error(`SANDBOX_WORKSPACE_IDENTITY_CONFLICT: container '${name}' is ${existingDescription}, `
|
|
899
|
+
+ `but this request is ${requestedDescription}. Run 'ai sandbox rm ${branch}' and retry.`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
1067
902
|
message('Removing old container instance...');
|
|
1068
903
|
for (const name of matchedContainers) {
|
|
1069
904
|
runSafeEngine(engine, 'docker', ['stop', name]);
|
|
@@ -1107,6 +942,8 @@ export async function create(args) {
|
|
|
1107
942
|
plan: capabilityPlan,
|
|
1108
943
|
create: {
|
|
1109
944
|
hostHome: effectiveConfig.home,
|
|
945
|
+
hostEnv: { ...process.env },
|
|
946
|
+
project: effectiveConfig.project,
|
|
1110
947
|
resolvedTools: effectiveResolvedTools
|
|
1111
948
|
}
|
|
1112
949
|
},
|
|
@@ -1117,26 +954,22 @@ export async function create(args) {
|
|
|
1117
954
|
throw new Error(beforeCreateFailure.message
|
|
1118
955
|
?? `Sandbox hook '${beforeCreateFailure.hookId}' failed.`);
|
|
1119
956
|
}
|
|
1120
|
-
const claudeCodeEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'claude-code');
|
|
1121
|
-
if (claudeCodeEntry) {
|
|
1122
|
-
ensureClaudeOnboarding(claudeCodeEntry.dir, effectiveConfig.home);
|
|
1123
|
-
ensureClaudeSettings(claudeCodeEntry.dir, effectiveConfig.home);
|
|
1124
|
-
// prepareClaudeCredentials wrote OAuth credentials or confirmed
|
|
1125
|
-
// provider/API settings are enough for Claude Code. If no usable
|
|
1126
|
-
// auth was present, the claude-code entry was removed above.
|
|
1127
|
-
}
|
|
1128
|
-
const geminiEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'gemini-cli');
|
|
1129
|
-
if (geminiEntry) {
|
|
1130
|
-
ensureGeminiWorkspaceTrust(geminiEntry.dir);
|
|
1131
|
-
}
|
|
1132
|
-
const opencodeEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'opencode');
|
|
1133
|
-
if (opencodeEntry) {
|
|
1134
|
-
// The TUI reads <toolDir>/opencode.json via OPENCODE_CONFIG pinned in tools.js.
|
|
1135
|
-
ensureOpenCodeModelInheritance(opencodeEntry.dir, effectiveConfig.home);
|
|
1136
|
-
}
|
|
1137
957
|
const toolVolumes = effectiveResolvedTools.flatMap(({ tool, dir }) => tool.tmpfs ? [] : ['-v', volumeArg(engine, dir, tool.containerMount)]);
|
|
1138
958
|
const tmpfsArgs = effectiveResolvedTools.flatMap(({ tool }) => tool.tmpfs ? buildTmpfsRunArgs(tool.containerMount, tool.tmpfs) : []);
|
|
1139
|
-
const
|
|
959
|
+
const workspaceView = materializeSandboxWorkspaceView({
|
|
960
|
+
base: effectiveConfig.workspaceViewBase,
|
|
961
|
+
project: effectiveConfig.project,
|
|
962
|
+
container,
|
|
963
|
+
identity: target.workspace
|
|
964
|
+
});
|
|
965
|
+
const control = materializeSandboxControl({
|
|
966
|
+
base: effectiveConfig.controlBase,
|
|
967
|
+
repoRoot: effectiveConfig.repoRoot,
|
|
968
|
+
project: effectiveConfig.project,
|
|
969
|
+
container,
|
|
970
|
+
branch,
|
|
971
|
+
identity: target.workspace
|
|
972
|
+
});
|
|
1140
973
|
hostShellConfig = prepareHostShellConfig({
|
|
1141
974
|
home: effectiveConfig.home,
|
|
1142
975
|
project: effectiveConfig.project,
|
|
@@ -1145,7 +978,15 @@ export async function create(args) {
|
|
|
1145
978
|
});
|
|
1146
979
|
const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
|
|
1147
980
|
worktree,
|
|
1148
|
-
shellConfigHostDir: hostShellConfig.hostDir
|
|
981
|
+
shellConfigHostDir: hostShellConfig.hostDir,
|
|
982
|
+
workspaceViewRoot: workspaceView.root,
|
|
983
|
+
controlDir: control.channelDir,
|
|
984
|
+
...(target.workspace.mode === 'task-bound'
|
|
985
|
+
? {
|
|
986
|
+
taskSource: assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId),
|
|
987
|
+
taskId: target.workspace.taskId
|
|
988
|
+
}
|
|
989
|
+
: {})
|
|
1149
990
|
}).flatMap(({ hostPaths, containerPath, readOnly }) => [
|
|
1150
991
|
'-v',
|
|
1151
992
|
volumeArg(engine, hostPaths[0], containerPath, readOnly ? ':ro' : '')
|
|
@@ -1177,7 +1018,6 @@ export async function create(args) {
|
|
|
1177
1018
|
'-v',
|
|
1178
1019
|
volumeArg(engine, hostPath, path.posix.join(tool.containerMount, containerSubpath))
|
|
1179
1020
|
]));
|
|
1180
|
-
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
1181
1021
|
fs.mkdirSync(shareCommon, { recursive: true });
|
|
1182
1022
|
fs.mkdirSync(shareBranch, { recursive: true });
|
|
1183
1023
|
fs.mkdirSync(clipboardHostDir(effectiveConfig.home), { recursive: true, mode: 0o700 });
|
|
@@ -1199,6 +1039,20 @@ export async function create(args) {
|
|
|
1199
1039
|
'--label',
|
|
1200
1040
|
`${sandboxBranchLabel(effectiveConfig)}=${branch}`,
|
|
1201
1041
|
'--label',
|
|
1042
|
+
`${sandboxWorkspaceModeLabel(effectiveConfig)}=${target.workspace.mode}`,
|
|
1043
|
+
...(target.workspace.mode === 'task-bound'
|
|
1044
|
+
? [
|
|
1045
|
+
'--label',
|
|
1046
|
+
`${sandboxTaskIdLabel(effectiveConfig)}=${target.workspace.taskId}`,
|
|
1047
|
+
'-e',
|
|
1048
|
+
`AGENT_INFRA_TASK_ID=${target.workspace.taskId}`
|
|
1049
|
+
]
|
|
1050
|
+
: []),
|
|
1051
|
+
'-e',
|
|
1052
|
+
`AGENT_INFRA_CONTROL_TOKEN=${control.token}`,
|
|
1053
|
+
'-e',
|
|
1054
|
+
'AGENT_INFRA_CONTROL_DIR=/run/agent-infra/control',
|
|
1055
|
+
'--label',
|
|
1202
1056
|
`${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
|
|
1203
1057
|
...coreVolumes,
|
|
1204
1058
|
...buildClipboardVolumeArgs(engine, effectiveConfig.home),
|
|
@@ -1215,6 +1069,12 @@ export async function create(args) {
|
|
|
1215
1069
|
'/workspace',
|
|
1216
1070
|
effectiveConfig.imageName
|
|
1217
1071
|
]);
|
|
1072
|
+
const broker = spawn(process.execPath, [internalCliPath(), 'sandbox-control', 'serve', '--manifest', control.manifestPath], { cwd: effectiveConfig.repoRoot, detached: true, stdio: 'ignore' });
|
|
1073
|
+
broker.once('error', () => {
|
|
1074
|
+
// Fresh readiness verification below remains the source of truth;
|
|
1075
|
+
// consume detached spawn errors so they cannot crash the command.
|
|
1076
|
+
});
|
|
1077
|
+
broker.unref();
|
|
1218
1078
|
createdTmpfsSeedPlan = tmpfsSeedPlan;
|
|
1219
1079
|
}
|
|
1220
1080
|
finally {
|
|
@@ -1297,10 +1157,11 @@ export async function create(args) {
|
|
|
1297
1157
|
preparedDockerfile.cleanup();
|
|
1298
1158
|
}
|
|
1299
1159
|
p.log.step('Verifying setup...');
|
|
1300
|
-
assertFreshSandboxReady({
|
|
1160
|
+
await assertFreshSandboxReady({
|
|
1301
1161
|
config: effectiveConfig,
|
|
1302
1162
|
engine,
|
|
1303
1163
|
branch,
|
|
1164
|
+
workspace: target.workspace,
|
|
1304
1165
|
container,
|
|
1305
1166
|
copiedEntries: createdTmpfsSeedPlan
|
|
1306
1167
|
});
|
|
@@ -3,12 +3,13 @@ import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, san
|
|
|
3
3
|
import { detectEngine } from '../engine.js';
|
|
4
4
|
import { ensureSandboxReady } from '../recovery.js';
|
|
5
5
|
import { createSandboxCapabilityPlan, runBoundedSandboxHookCommand, runSandboxHooks } from '../agent-client-reconciler.js';
|
|
6
|
-
import { formatCredentialWarnings, formatRemaining, hasClaudeProviderAuth, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
|
|
7
6
|
import { runInteractiveEngine } from '../shell.js';
|
|
8
7
|
import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
|
|
8
|
+
import { redactCommandError } from '../redaction.js';
|
|
9
9
|
import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
|
|
10
10
|
import { detectHostTimezone } from '../host-timezone.js';
|
|
11
|
-
import { fetchSandboxRows,
|
|
11
|
+
import { fetchSandboxRows, selectSandboxContainer, } from './list-running.js';
|
|
12
|
+
import { resolveSandboxTarget } from '../workspace-identity.js';
|
|
12
13
|
const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
|
|
13
14
|
|
|
14
15
|
N references an active task short id from
|
|
@@ -53,32 +54,6 @@ export function runSandboxInteractive(params) {
|
|
|
53
54
|
}
|
|
54
55
|
return runBridge({ engine, dockerArgs, container, home });
|
|
55
56
|
}
|
|
56
|
-
export function formatCredentialSyncStatus(result, isTTY = process.stderr.isTTY, providerAuthAvailable = false) {
|
|
57
|
-
if (providerAuthAvailable && (result.status === 'STALE_ACCESS' || result.status === 'MISSING')) {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
if (result.status === 'STALE_ACCESS') {
|
|
61
|
-
return 'Warning: Claude Code credentials on host appear stale. Run "ai sandbox refresh" or "claude /login" to renew.\n';
|
|
62
|
-
}
|
|
63
|
-
if (result.status === 'MISSING') {
|
|
64
|
-
return 'Warning: Claude Code credentials missing on host. Run "claude /login" to authenticate.\n';
|
|
65
|
-
}
|
|
66
|
-
if (result.status === 'KEYCHAIN_WRITE_FAILED') {
|
|
67
|
-
return `Warning: A sandbox refresh produced newer credentials but host Keychain write failed (${formatCredentialWarnings(result.warnings)}). Run "ai sandbox refresh" again or "claude /status" on the host to retry.\n`;
|
|
68
|
-
}
|
|
69
|
-
if (result.status === 'KEYCHAIN_LOCKED' || result.status === 'KEYCHAIN_ERROR') {
|
|
70
|
-
return 'Warning: Host keychain is unavailable; Claude credential sync skipped. Run "ai sandbox refresh" for details.\n';
|
|
71
|
-
}
|
|
72
|
-
if (result.status === 'OK' && result.authoritative !== 'host') {
|
|
73
|
-
const message = `Synced Claude Code credentials from sandbox refresh back to host (expires in ${formatRemaining(result.expiresAt)})`;
|
|
74
|
-
return isTTY ? `\x1b[2m${message}\x1b[0m\n` : `${message}\n`;
|
|
75
|
-
}
|
|
76
|
-
if (result.status === 'OK' && result.filesWritten.length > 0) {
|
|
77
|
-
const message = `Synced Claude Code credentials from host Keychain (expires in ${formatRemaining(result.expiresAt)})`;
|
|
78
|
-
return isTTY ? `\x1b[2m${message}\x1b[0m\n` : `${message}\n`;
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
57
|
export function parseEnterArgs(args) {
|
|
83
58
|
let recreate = false;
|
|
84
59
|
let index = 0;
|
|
@@ -101,10 +76,10 @@ export async function enter(args) {
|
|
|
101
76
|
return 0;
|
|
102
77
|
}
|
|
103
78
|
const config = loadConfig();
|
|
104
|
-
validateClaudeCredentialsEnvOverride();
|
|
105
79
|
const engine = detectEngine(config);
|
|
106
80
|
const parsed = parseEnterArgs(args);
|
|
107
|
-
const
|
|
81
|
+
const target = resolveSandboxTarget(parsed.target, config.repoRoot);
|
|
82
|
+
const branch = target.branch;
|
|
108
83
|
assertValidBranchName(branch);
|
|
109
84
|
const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
|
|
110
85
|
const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
|
|
@@ -115,11 +90,12 @@ export async function enter(args) {
|
|
|
115
90
|
config,
|
|
116
91
|
engine,
|
|
117
92
|
branch,
|
|
93
|
+
workspace: target.workspace,
|
|
118
94
|
row: found,
|
|
119
95
|
allowRecreate: parsed.recreate,
|
|
120
|
-
recreate: async (
|
|
96
|
+
recreate: async () => {
|
|
121
97
|
const { create } = await import('./create.js');
|
|
122
|
-
await create([
|
|
98
|
+
await create([target.requestedRef, '--no-refresh']);
|
|
123
99
|
}
|
|
124
100
|
});
|
|
125
101
|
const container = ready.container;
|
|
@@ -128,7 +104,11 @@ export async function enter(args) {
|
|
|
128
104
|
const enterHookResults = await runSandboxHooks({
|
|
129
105
|
hooks: capabilityPlan.hooksByPhase['before-enter'],
|
|
130
106
|
phase: 'before-enter',
|
|
131
|
-
context: {
|
|
107
|
+
context: {
|
|
108
|
+
config,
|
|
109
|
+
plan: capabilityPlan,
|
|
110
|
+
enter: { hostHome: config.home, hostEnv: { ...process.env } }
|
|
111
|
+
},
|
|
132
112
|
runCommand: runBoundedSandboxHookCommand
|
|
133
113
|
});
|
|
134
114
|
for (const result of enterHookResults) {
|
|
@@ -138,20 +118,8 @@ export async function enter(args) {
|
|
|
138
118
|
if (result.status === 'warning') {
|
|
139
119
|
process.stderr.write(`Warning: ${result.message ?? `Sandbox hook '${result.hookId}' did not complete.`}\n`);
|
|
140
120
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
try {
|
|
144
|
-
// Scan all projects so a refresh from a neighbouring sandbox can still flow back to the host.
|
|
145
|
-
const providerAuthAvailable = hasClaudeProviderAuth(config.home);
|
|
146
|
-
const result = reconcileClaudeCredentials(config.home);
|
|
147
|
-
const message = formatCredentialSyncStatus(result, process.stderr.isTTY, providerAuthAvailable);
|
|
148
|
-
if (message) {
|
|
149
|
-
process.stderr.write(message);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
catch (error) {
|
|
153
|
-
process.stderr.write(`Warning: Failed to sync Claude Code credentials: ${redactCommandError(error instanceof Error ? error.message : 'unknown error')}\n`);
|
|
154
|
-
}
|
|
121
|
+
if (result.message && result.status !== 'warning')
|
|
122
|
+
process.stderr.write(result.message);
|
|
155
123
|
}
|
|
156
124
|
const envFlags = [...terminalEnvFlags(), ...hostTimezoneEnvFlags()];
|
|
157
125
|
if (cmd.length === 0) {
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { runSafeEngine, runVerboseEngine } from '../shell.js';
|
|
5
5
|
import { resolveTaskBranch } from '../task-resolver.js';
|
|
6
6
|
import { isRemovedHashShortIdInput } from '../../task/short-id.js';
|
|
7
|
+
import { parseSandboxWorkspaceIdentity } from '../workspace-identity.js';
|
|
7
8
|
export function containerListFormat() {
|
|
8
9
|
return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
|
|
9
10
|
}
|
|
@@ -24,19 +25,28 @@ export function parseLabels(csv) {
|
|
|
24
25
|
}
|
|
25
26
|
return labels;
|
|
26
27
|
}
|
|
27
|
-
export function parseSandboxRows(rawOutput, branchKey) {
|
|
28
|
+
export function parseSandboxRows(rawOutput, branchKey, workspaceKeys) {
|
|
28
29
|
if (!rawOutput) {
|
|
29
30
|
return [];
|
|
30
31
|
}
|
|
31
32
|
return rawOutput.split('\n').map((line) => {
|
|
32
33
|
const [name = '', status = '', labelsCsv = ''] = line.split('\t');
|
|
33
|
-
const
|
|
34
|
+
const labels = parseLabels(labelsCsv);
|
|
35
|
+
const workspace = workspaceKeys
|
|
36
|
+
? parseSandboxWorkspaceIdentity(labels, workspaceKeys)
|
|
37
|
+
: null;
|
|
34
38
|
return {
|
|
35
39
|
name,
|
|
36
40
|
status,
|
|
37
|
-
branch,
|
|
41
|
+
branch: labels[branchKey] ?? '',
|
|
38
42
|
running: status.startsWith('Up '),
|
|
39
|
-
index: null
|
|
43
|
+
index: null,
|
|
44
|
+
...(workspace
|
|
45
|
+
? {
|
|
46
|
+
workspaceMode: workspace.mode,
|
|
47
|
+
taskId: workspace.mode === 'task-bound' ? workspace.taskId : null
|
|
48
|
+
}
|
|
49
|
+
: {})
|
|
40
50
|
};
|
|
41
51
|
});
|
|
42
52
|
}
|
|
@@ -58,7 +68,7 @@ export function sortAndIndexSandboxRows(rows) {
|
|
|
58
68
|
}));
|
|
59
69
|
return { running, nonRunning };
|
|
60
70
|
}
|
|
61
|
-
export function fetchSandboxRows(engine, label, branchKey) {
|
|
71
|
+
export function fetchSandboxRows(engine, label, branchKey, workspaceKeys) {
|
|
62
72
|
const raw = runSafeEngine(engine, 'docker', [
|
|
63
73
|
'ps',
|
|
64
74
|
'-a',
|
|
@@ -67,7 +77,7 @@ export function fetchSandboxRows(engine, label, branchKey) {
|
|
|
67
77
|
'--format',
|
|
68
78
|
containerListFormat()
|
|
69
79
|
]);
|
|
70
|
-
return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey));
|
|
80
|
+
return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey, workspaceKeys));
|
|
71
81
|
}
|
|
72
82
|
/**
|
|
73
83
|
* Returns true iff `arg` is a syntactically valid task short reference.
|