gsd-pi 2.76.0-dev.4100bd590 → 2.76.0-dev.479ad0e78
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/dist/claude-cli-check.js +32 -3
- package/dist/mcp-server.d.ts +7 -0
- package/dist/mcp-server.js +35 -1
- package/dist/onboarding.js +45 -0
- package/dist/resource-loader.d.ts +1 -1
- package/dist/resource-loader.js +2 -8
- package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +77 -17
- package/dist/resources/extensions/gsd/auto/loop.js +9 -0
- package/dist/resources/extensions/gsd/auto/phases.js +58 -5
- package/dist/resources/extensions/gsd/auto/run-unit.js +38 -2
- package/dist/resources/extensions/gsd/auto/session.js +22 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +14 -3
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +32 -1
- package/dist/resources/extensions/gsd/auto-start.js +58 -57
- package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +39 -9
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +93 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/compaction-snapshot.js +121 -0
- package/dist/resources/extensions/gsd/complexity-classifier.js +5 -3
- package/dist/resources/extensions/gsd/error-classifier.js +10 -3
- package/dist/resources/extensions/gsd/exec-history.js +120 -0
- package/dist/resources/extensions/gsd/exec-sandbox.js +258 -0
- package/dist/resources/extensions/gsd/gitignore.js +1 -0
- package/dist/resources/extensions/gsd/gsd-db.js +149 -31
- package/dist/resources/extensions/gsd/guided-flow.js +190 -1
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/init-wizard.js +15 -1
- package/dist/resources/extensions/gsd/key-manager.js +28 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +44 -9
- package/dist/resources/extensions/gsd/preferences-types.js +9 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +83 -0
- package/dist/resources/extensions/gsd/preferences.js +17 -17
- package/dist/resources/extensions/gsd/prompt-loader.js +22 -7
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +13 -5
- package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
- package/dist/resources/extensions/gsd/tools/exec-search-tool.js +59 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +126 -0
- package/dist/resources/extensions/gsd/tools/resume-tool.js +23 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +20 -3
- package/dist/resources/extensions/gsd/workflow-mcp.js +3 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/dist/resources/skills/write-docs/SKILL.md +2 -1
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +7 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +70 -8
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts +14 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +49 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -1
- package/packages/mcp-server/src/mcp-server.test.ts +67 -0
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +89 -14
- package/packages/mcp-server/src/session-manager.ts +43 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +146 -1
- package/packages/mcp-server/src/workflow-tools.ts +84 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/models/custom.d.ts +38 -0
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/custom.js +41 -0
- package/packages/pi-ai/dist/models/custom.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +60 -15
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts +17 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js +75 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.js.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js +41 -0
- package/packages/pi-ai/dist/providers/think-tag-parser.test.js.map +1 -0
- package/packages/pi-ai/src/models/custom.ts +42 -0
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +9 -3
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +57 -16
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/src/providers/think-tag-parser.test.ts +44 -0
- package/packages/pi-ai/src/providers/think-tag-parser.ts +94 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +7 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts +3 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.js +92 -12
- package/packages/pi-coding-agent/dist/core/model-discovery.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js +16 -1
- package/packages/pi-coding-agent/dist/core/model-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js +61 -1
- package/packages/pi-coding-agent/dist/core/model-registry-discovery.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +90 -10
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js +49 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js +67 -0
- package/packages/pi-coding-agent/dist/core/redact-secrets.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +10 -6
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +45 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +5 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +13 -7
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +29 -21
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +13 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +7 -0
- package/packages/pi-coding-agent/src/core/model-discovery.test.ts +19 -0
- package/packages/pi-coding-agent/src/core/model-discovery.ts +99 -12
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry-discovery.test.ts +75 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +102 -10
- package/packages/pi-coding-agent/src/core/redact-secrets.test.ts +86 -0
- package/packages/pi-coding-agent/src/core/redact-secrets.ts +58 -0
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +65 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +10 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +6 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +16 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +36 -22
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +78 -17
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +149 -5
- package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -0
- package/src/resources/extensions/gsd/auto/loop.ts +9 -0
- package/src/resources/extensions/gsd/auto/phases.ts +82 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +40 -2
- package/src/resources/extensions/gsd/auto/session.ts +35 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +17 -2
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +26 -1
- package/src/resources/extensions/gsd/auto-start.ts +60 -68
- package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +23 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +40 -9
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +109 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/compaction-snapshot.ts +165 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -3
- package/src/resources/extensions/gsd/error-classifier.ts +10 -3
- package/src/resources/extensions/gsd/exec-history.ts +153 -0
- package/src/resources/extensions/gsd/exec-sandbox.ts +326 -0
- package/src/resources/extensions/gsd/gitignore.ts +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +157 -33
- package/src/resources/extensions/gsd/guided-flow.ts +222 -1
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/init-wizard.ts +15 -1
- package/src/resources/extensions/gsd/journal.ts +2 -1
- package/src/resources/extensions/gsd/key-manager.ts +28 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/pre-execution-checks.ts +46 -10
- package/src/resources/extensions/gsd/preferences-types.ts +46 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +79 -0
- package/src/resources/extensions/gsd/preferences.ts +17 -17
- package/src/resources/extensions/gsd/prompt-loader.ts +30 -7
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +17 -4
- package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +33 -3
- package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +237 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +210 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +447 -1
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +356 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +110 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/prompt-loader-extension-dir.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +48 -0
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/save-gate-result-render.test.ts +95 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +3 -1
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
- package/src/resources/extensions/gsd/tools/exec-search-tool.ts +81 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +183 -0
- package/src/resources/extensions/gsd/tools/resume-tool.ts +40 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +26 -3
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/src/resources/skills/write-docs/SKILL.md +2 -1
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{YnUwu2WWaT0_hyTLUF4nq → JgU2F-5N9mTyB7kUSSk9A}/_ssgManifest.js +0 -0
|
@@ -3,10 +3,26 @@
|
|
|
3
3
|
* Tracks every bash command, file write, and file edit during a unit execution.
|
|
4
4
|
* Evidence is compared against LLM completion claims in evidence-cross-ref.ts.
|
|
5
5
|
*
|
|
6
|
+
* Evidence is persisted to .gsd/safety/evidence-<mid>-<sid>-<tid>.json so it
|
|
7
|
+
* survives session restarts (pause/resume, crash recovery). On unit start,
|
|
8
|
+
* call resetEvidence() then loadEvidenceFromDisk(). On every new tool call,
|
|
9
|
+
* saveEvidenceToDisk() is called automatically by recordToolCall/recordToolResult.
|
|
10
|
+
*
|
|
6
11
|
* Follows the same module-level Map pattern as auto-tool-tracking.ts.
|
|
7
12
|
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
8
13
|
*/
|
|
9
14
|
|
|
15
|
+
import {
|
|
16
|
+
existsSync,
|
|
17
|
+
mkdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
renameSync,
|
|
21
|
+
unlinkSync,
|
|
22
|
+
} from "node:fs";
|
|
23
|
+
import { join, dirname } from "node:path";
|
|
24
|
+
import { randomBytes } from "node:crypto";
|
|
25
|
+
|
|
10
26
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
11
27
|
|
|
12
28
|
export interface BashEvidence {
|
|
@@ -62,6 +78,109 @@ export function getFilePaths(): string[] {
|
|
|
62
78
|
.map(e => e.path);
|
|
63
79
|
}
|
|
64
80
|
|
|
81
|
+
// ─── Persistence (Bug #4385 — evidence must survive session restarts) ────────
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Build the path for the evidence JSON file for a given unit.
|
|
85
|
+
* Lives under .gsd/safety/ which is gitignored and session-scoped.
|
|
86
|
+
*/
|
|
87
|
+
function evidencePath(basePath: string, milestoneId: string, sliceId: string, taskId: string): string {
|
|
88
|
+
return join(basePath, ".gsd", "safety", `evidence-${milestoneId}-${sliceId}-${taskId}.json`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validate that a parsed value is an array of EvidenceEntry objects.
|
|
93
|
+
* Rejects corrupt / schema-mismatch data rather than letting it poison state.
|
|
94
|
+
*/
|
|
95
|
+
function isEvidenceArray(data: unknown): data is EvidenceEntry[] {
|
|
96
|
+
if (!Array.isArray(data)) return false;
|
|
97
|
+
return data.every((e) => {
|
|
98
|
+
if (e === null || typeof e !== "object") return false;
|
|
99
|
+
const rec = e as Record<string, unknown>;
|
|
100
|
+
if (typeof rec.toolCallId !== "string") return false;
|
|
101
|
+
if (typeof rec.timestamp !== "number") return false;
|
|
102
|
+
if (rec.kind === "bash") {
|
|
103
|
+
return (
|
|
104
|
+
typeof rec.command === "string" &&
|
|
105
|
+
typeof rec.exitCode === "number" &&
|
|
106
|
+
typeof rec.outputSnippet === "string"
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (rec.kind === "write" || rec.kind === "edit") {
|
|
110
|
+
return typeof rec.path === "string";
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Persist the current in-memory evidence to disk so it survives a session
|
|
118
|
+
* restart. Called from saveEvidenceToDisk after recordToolCall/recordToolResult.
|
|
119
|
+
* Non-fatal — persistence failures must never break unit execution.
|
|
120
|
+
*/
|
|
121
|
+
export function saveEvidenceToDisk(
|
|
122
|
+
basePath: string,
|
|
123
|
+
milestoneId: string,
|
|
124
|
+
sliceId: string,
|
|
125
|
+
taskId: string,
|
|
126
|
+
): void {
|
|
127
|
+
try {
|
|
128
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
129
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
130
|
+
const tmp = `${path}.tmp.${randomBytes(4).toString("hex")}`;
|
|
131
|
+
writeFileSync(tmp, JSON.stringify(unitEvidence, null, 2) + "\n", "utf-8");
|
|
132
|
+
renameSync(tmp, path);
|
|
133
|
+
} catch {
|
|
134
|
+
// Non-fatal — don't let persistence failures break unit execution
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Load persisted evidence from disk into the in-memory array.
|
|
140
|
+
* Call after resetEvidence() on session resume to restore context for a
|
|
141
|
+
* partially-executed unit. If the file does not exist (fresh unit), this
|
|
142
|
+
* is a no-op — getEvidence() will return [] which is correct.
|
|
143
|
+
*/
|
|
144
|
+
export function loadEvidenceFromDisk(
|
|
145
|
+
basePath: string,
|
|
146
|
+
milestoneId: string,
|
|
147
|
+
sliceId: string,
|
|
148
|
+
taskId: string,
|
|
149
|
+
): void {
|
|
150
|
+
try {
|
|
151
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
152
|
+
if (!existsSync(path)) return;
|
|
153
|
+
const raw = readFileSync(path, "utf-8");
|
|
154
|
+
const parsed = JSON.parse(raw);
|
|
155
|
+
if (isEvidenceArray(parsed)) {
|
|
156
|
+
unitEvidence = parsed;
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
// Non-fatal — corrupt / missing file is treated as empty evidence
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Delete the persisted evidence file for a unit after it has been fully
|
|
165
|
+
* processed. Prevents stale evidence from affecting future retries of
|
|
166
|
+
* the same unit ID.
|
|
167
|
+
*/
|
|
168
|
+
export function clearEvidenceFromDisk(
|
|
169
|
+
basePath: string,
|
|
170
|
+
milestoneId: string,
|
|
171
|
+
sliceId: string,
|
|
172
|
+
taskId: string,
|
|
173
|
+
): void {
|
|
174
|
+
try {
|
|
175
|
+
const path = evidencePath(basePath, milestoneId, sliceId, taskId);
|
|
176
|
+
if (existsSync(path)) {
|
|
177
|
+
unlinkSync(path);
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// Non-fatal
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
65
184
|
// ─── Recording (called from register-hooks.ts) ─────────────────────────────
|
|
66
185
|
|
|
67
186
|
/**
|
|
@@ -4,15 +4,23 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Uses tasks.expected_output (DB column, populated from per-task ## Expected Output)
|
|
6
6
|
* and tasks.files (from slice PLAN.md - Files: subline) as the expected set.
|
|
7
|
-
* Compares against git diff
|
|
7
|
+
* Compares against `git diff-tree --root --no-commit-id -r --name-only HEAD` after auto-commit.
|
|
8
|
+
* Using diff-tree --root handles initial commits, shallow clones, and merge commits correctly
|
|
9
|
+
* (Bug #4385 — git diff HEAD~1 failed on initial commits).
|
|
8
10
|
*
|
|
9
11
|
* Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
10
12
|
*/
|
|
11
13
|
|
|
14
|
+
import { createRequire } from "node:module";
|
|
12
15
|
import { execFileSync } from "node:child_process";
|
|
13
16
|
import { normalizePlannedFileReference } from "../files.js";
|
|
14
17
|
import { logWarning } from "../workflow-logger.js";
|
|
15
18
|
|
|
19
|
+
const _require = createRequire(import.meta.url);
|
|
20
|
+
type PicomatchMatcher = (input: string) => boolean;
|
|
21
|
+
type PicomatchFn = (pattern: string, opts?: { dot?: boolean }) => PicomatchMatcher;
|
|
22
|
+
const picomatch = _require("picomatch") as PicomatchFn;
|
|
23
|
+
|
|
16
24
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
17
25
|
|
|
18
26
|
export interface FileViolation {
|
|
@@ -43,6 +51,7 @@ export function validateFileChanges(
|
|
|
43
51
|
basePath: string,
|
|
44
52
|
expectedOutput: string[],
|
|
45
53
|
plannedFiles: string[],
|
|
54
|
+
fileChangeAllowlist: string[] = [],
|
|
46
55
|
): FileChangeAudit | null {
|
|
47
56
|
const allExpected = new Set([...expectedOutput, ...plannedFiles]);
|
|
48
57
|
|
|
@@ -63,8 +72,12 @@ export function validateFileChanges(
|
|
|
63
72
|
),
|
|
64
73
|
);
|
|
65
74
|
|
|
66
|
-
//
|
|
67
|
-
const
|
|
75
|
+
// Build allowlist matchers once (dot: true so patterns like `**/.hidden` work).
|
|
76
|
+
const allowlistMatchers = fileChangeAllowlist.map(p => picomatch(p, { dot: true }));
|
|
77
|
+
const isAllowlisted = (f: string) => allowlistMatchers.some(m => m(f));
|
|
78
|
+
|
|
79
|
+
// Compute symmetric difference, excluding allowlisted files
|
|
80
|
+
const unexpectedFiles = projectFiles.filter(f => !normalizedExpected.has(f) && !isAllowlisted(f));
|
|
68
81
|
const missingFiles = [...normalizedExpected].filter(f => !projectFiles.includes(f));
|
|
69
82
|
|
|
70
83
|
const violations: FileViolation[] = [];
|
|
@@ -100,7 +113,7 @@ function getChangedFilesFromLastCommit(basePath: string): string[] | null {
|
|
|
100
113
|
try {
|
|
101
114
|
const result = execFileSync(
|
|
102
115
|
"git",
|
|
103
|
-
["diff", "--
|
|
116
|
+
["diff-tree", "--root", "--no-commit-id", "-r", "--name-only", "HEAD"],
|
|
104
117
|
{ cwd: basePath, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
|
|
105
118
|
).trim();
|
|
106
119
|
return result ? result.split("\n").filter(Boolean) : [];
|
|
@@ -25,6 +25,8 @@ export interface SafetyHarnessConfig {
|
|
|
25
25
|
checkpoints: boolean;
|
|
26
26
|
auto_rollback: boolean;
|
|
27
27
|
timeout_scale_cap: number;
|
|
28
|
+
/** Glob patterns for files excluded from unexpected-change warnings (#4385). */
|
|
29
|
+
file_change_allowlist: string[];
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
// ─── Defaults ───────────────────────────────────────────────────────────────
|
|
@@ -39,6 +41,7 @@ const DEFAULTS: SafetyHarnessConfig = {
|
|
|
39
41
|
checkpoints: true,
|
|
40
42
|
auto_rollback: false,
|
|
41
43
|
timeout_scale_cap: 6,
|
|
44
|
+
file_change_allowlist: [],
|
|
42
45
|
};
|
|
43
46
|
|
|
44
47
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -62,6 +65,9 @@ export function resolveSafetyHarnessConfig(
|
|
|
62
65
|
checkpoints: typeof raw.checkpoints === "boolean" ? raw.checkpoints : DEFAULTS.checkpoints,
|
|
63
66
|
auto_rollback: typeof raw.auto_rollback === "boolean" ? raw.auto_rollback : DEFAULTS.auto_rollback,
|
|
64
67
|
timeout_scale_cap: typeof raw.timeout_scale_cap === "number" ? raw.timeout_scale_cap : DEFAULTS.timeout_scale_cap,
|
|
68
|
+
file_change_allowlist: Array.isArray(raw.file_change_allowlist)
|
|
69
|
+
? (raw.file_change_allowlist as unknown[]).filter((p): p is string => typeof p === "string")
|
|
70
|
+
: DEFAULTS.file_change_allowlist,
|
|
65
71
|
};
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -86,6 +92,9 @@ export {
|
|
|
86
92
|
getFilePaths,
|
|
87
93
|
recordToolCall,
|
|
88
94
|
recordToolResult,
|
|
95
|
+
saveEvidenceToDisk,
|
|
96
|
+
loadEvidenceFromDisk,
|
|
97
|
+
clearEvidenceFromDisk,
|
|
89
98
|
} from "./evidence-collector.js";
|
|
90
99
|
|
|
91
100
|
export type { EvidenceEntry, BashEvidence, FileWriteEvidence, FileEditEvidence } from "./evidence-collector.js";
|
|
@@ -35,12 +35,15 @@ function makeMockSession(opts?: {
|
|
|
35
35
|
newSessionDelayMs?: number;
|
|
36
36
|
onNewSessionStart?: (session: any) => void;
|
|
37
37
|
onNewSessionSettle?: (session: any) => void;
|
|
38
|
+
/** Called after the delay with the aborted state of any passed abortSignal.
|
|
39
|
+
* Used to verify that runUnit passes an aborted signal on late resolution (#3731). */
|
|
40
|
+
onSignalCheck?: (aborted: boolean) => void;
|
|
38
41
|
}) {
|
|
39
42
|
const session = {
|
|
40
43
|
active: true,
|
|
41
44
|
verbose: false,
|
|
42
45
|
cmdCtx: {
|
|
43
|
-
newSession: () => {
|
|
46
|
+
newSession: (options?: { abortSignal?: AbortSignal }) => {
|
|
44
47
|
opts?.onNewSessionStart?.(session);
|
|
45
48
|
if (opts?.newSessionThrows) {
|
|
46
49
|
return Promise.reject(new Error(opts.newSessionThrows));
|
|
@@ -50,11 +53,17 @@ function makeMockSession(opts?: {
|
|
|
50
53
|
if (delay > 0) {
|
|
51
54
|
return new Promise<{ cancelled: boolean }>((res) =>
|
|
52
55
|
setTimeout(() => {
|
|
56
|
+
// Simulate AgentSession.newSession() checking abortSignal after
|
|
57
|
+
// its internal async work (abort()) completes — this is where the
|
|
58
|
+
// real code captures process.cwd() and rebuilds the tool runtime.
|
|
59
|
+
// If the signal is aborted, the real code discards the session.
|
|
60
|
+
opts?.onSignalCheck?.(options?.abortSignal?.aborted ?? false);
|
|
53
61
|
opts?.onNewSessionSettle?.(session);
|
|
54
62
|
res(result);
|
|
55
63
|
}, delay),
|
|
56
64
|
);
|
|
57
65
|
}
|
|
66
|
+
opts?.onSignalCheck?.(options?.abortSignal?.aborted ?? false);
|
|
58
67
|
opts?.onNewSessionSettle?.(session);
|
|
59
68
|
return Promise.resolve(result);
|
|
60
69
|
},
|
|
@@ -349,6 +358,181 @@ test("runUnit cancels before dispatch when model restore fails after newSession"
|
|
|
349
358
|
]);
|
|
350
359
|
});
|
|
351
360
|
|
|
361
|
+
test("runUnit cancels before dispatch when provider is not request-ready (#4555)", async () => {
|
|
362
|
+
_resetPendingResolve();
|
|
363
|
+
|
|
364
|
+
const ctx = makeMockCtx();
|
|
365
|
+
ctx.model = { provider: "anthropic", id: "claude-opus-4-6" };
|
|
366
|
+
ctx.modelRegistry = {
|
|
367
|
+
isProviderRequestReady: (_provider: string) => false,
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const pi = makeMockPi();
|
|
371
|
+
const s = makeMockSession();
|
|
372
|
+
|
|
373
|
+
const result = await runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
374
|
+
|
|
375
|
+
assert.equal(result.status, "cancelled");
|
|
376
|
+
assert.equal(result.errorContext?.category, "provider");
|
|
377
|
+
assert.match(
|
|
378
|
+
result.errorContext?.message ?? "",
|
|
379
|
+
/Provider anthropic is not request-ready/,
|
|
380
|
+
);
|
|
381
|
+
assert.equal(pi.calls.length, 0, "sendMessage must not be called when provider is not ready");
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("runUnit cancels before dispatch using currentUnitModel provider when set (#4555)", async () => {
|
|
385
|
+
_resetPendingResolve();
|
|
386
|
+
|
|
387
|
+
const ctx = makeMockCtx();
|
|
388
|
+
// ctx.model uses "openai" which IS ready — if the code ignores currentUnitModel
|
|
389
|
+
// and falls back to ctx.model.provider, the unit would NOT be cancelled. The
|
|
390
|
+
// test therefore differentiates: only a bug (wrong provider lookup) would pass.
|
|
391
|
+
ctx.model = { provider: "openai", id: "gpt-4o" };
|
|
392
|
+
// modelRegistry says anthropic is not ready but openai is
|
|
393
|
+
ctx.modelRegistry = {
|
|
394
|
+
isProviderRequestReady: (provider: string) => provider === "openai",
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const pi = makeMockPi();
|
|
398
|
+
const s = makeMockSession();
|
|
399
|
+
// currentUnitModel overrides the provider used in the readiness check
|
|
400
|
+
s.currentUnitModel = { provider: "anthropic", id: "claude-opus-4-6" };
|
|
401
|
+
|
|
402
|
+
const result = await runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
403
|
+
|
|
404
|
+
assert.equal(result.status, "cancelled");
|
|
405
|
+
assert.equal(result.errorContext?.category, "provider");
|
|
406
|
+
assert.match(
|
|
407
|
+
result.errorContext?.message ?? "",
|
|
408
|
+
/Provider anthropic is not request-ready/,
|
|
409
|
+
);
|
|
410
|
+
assert.equal(pi.calls.length, 0, "sendMessage must not be called — anthropic (currentUnitModel) is not ready");
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("runUnit does not cancel before dispatch when provider is request-ready (#4555)", async () => {
|
|
414
|
+
_resetPendingResolve();
|
|
415
|
+
|
|
416
|
+
const ctx = makeMockCtx();
|
|
417
|
+
ctx.model = { provider: "anthropic", id: "claude-opus-4-6" };
|
|
418
|
+
ctx.modelRegistry = {
|
|
419
|
+
isProviderRequestReady: (_provider: string) => true,
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const pi = makeMockPi();
|
|
423
|
+
const s = makeMockSession();
|
|
424
|
+
|
|
425
|
+
const resultPromise = runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
426
|
+
|
|
427
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
428
|
+
resolveAgentEnd(makeEvent());
|
|
429
|
+
|
|
430
|
+
const result = await resultPromise;
|
|
431
|
+
assert.equal(result.status, "completed");
|
|
432
|
+
assert.equal(pi.calls.length, 1, "sendMessage must be called when provider is ready");
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test("runUnit proceeds when modelRegistry is absent (no readiness check available) (#4555)", async () => {
|
|
436
|
+
_resetPendingResolve();
|
|
437
|
+
|
|
438
|
+
const ctx = makeMockCtx();
|
|
439
|
+
ctx.model = { provider: "anthropic", id: "claude-opus-4-6" };
|
|
440
|
+
// No modelRegistry on ctx — pre-check should be skipped
|
|
441
|
+
|
|
442
|
+
const pi = makeMockPi();
|
|
443
|
+
const s = makeMockSession();
|
|
444
|
+
|
|
445
|
+
const resultPromise = runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
446
|
+
|
|
447
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
448
|
+
resolveAgentEnd(makeEvent());
|
|
449
|
+
|
|
450
|
+
const result = await resultPromise;
|
|
451
|
+
assert.equal(result.status, "completed");
|
|
452
|
+
assert.equal(pi.calls.length, 1);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
test("runUnit proceeds when isProviderRequestReady throws (defensive) (#4555)", async () => {
|
|
456
|
+
_resetPendingResolve();
|
|
457
|
+
|
|
458
|
+
const ctx = makeMockCtx();
|
|
459
|
+
ctx.model = { provider: "anthropic", id: "claude-opus-4-6" };
|
|
460
|
+
ctx.modelRegistry = {
|
|
461
|
+
isProviderRequestReady: (_provider: string) => {
|
|
462
|
+
throw new Error("registry error");
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const pi = makeMockPi();
|
|
467
|
+
const s = makeMockSession();
|
|
468
|
+
|
|
469
|
+
const result = await runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
470
|
+
|
|
471
|
+
// When the readyCheck throws, ready=false → unit cancelled
|
|
472
|
+
assert.equal(result.status, "cancelled");
|
|
473
|
+
assert.equal(result.errorContext?.category, "provider");
|
|
474
|
+
assert.equal(pi.calls.length, 0);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test("late-resolving newSession() after timeout receives aborted signal so tool runtime is not configured with root cwd (#3731)", async () => {
|
|
478
|
+
// When newSession() times out in runUnit(), auto-mode restores cwd to project
|
|
479
|
+
// root. If newSession() later resolves, it must NOT use process.cwd() to
|
|
480
|
+
// configure the tool runtime (which would give it root cwd, not worktree cwd).
|
|
481
|
+
//
|
|
482
|
+
// The fix: runUnit creates an AbortController, aborts it on timeout, and passes
|
|
483
|
+
// the signal to newSession(). AgentSession.newSession() checks the signal after
|
|
484
|
+
// its internal await this.abort() completes and returns early (discards) if aborted.
|
|
485
|
+
//
|
|
486
|
+
// This test uses mock.timers to control timing precisely.
|
|
487
|
+
_resetPendingResolve();
|
|
488
|
+
mock.timers.enable();
|
|
489
|
+
|
|
490
|
+
try {
|
|
491
|
+
let abortedWhenLateSessionSettled: boolean | null = null;
|
|
492
|
+
|
|
493
|
+
// newSession mock simulates AgentSession.newSession() behavior:
|
|
494
|
+
// after an internal delay (representing await this.abort()), it checks the
|
|
495
|
+
// abortSignal — that's where the real code would capture process.cwd() and
|
|
496
|
+
// call _buildRuntime. If aborted, the real code must discard the session.
|
|
497
|
+
const s = makeMockSession({
|
|
498
|
+
newSessionDelayMs: 200_000, // longer than NEW_SESSION_TIMEOUT_MS (120s)
|
|
499
|
+
onSignalCheck: (aborted) => {
|
|
500
|
+
abortedWhenLateSessionSettled = aborted;
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
const ctx = makeMockCtx();
|
|
505
|
+
const pi = makeMockPi();
|
|
506
|
+
|
|
507
|
+
const resultPromise = runUnit(ctx, pi, s, "task", "T01", "prompt");
|
|
508
|
+
|
|
509
|
+
// Tick past the 120s NEW_SESSION_TIMEOUT_MS — runUnit returns cancelled
|
|
510
|
+
mock.timers.tick(121_000);
|
|
511
|
+
await Promise.resolve();
|
|
512
|
+
|
|
513
|
+
const result = await resultPromise;
|
|
514
|
+
assert.equal(result.status, "cancelled", "runUnit must return cancelled on session timeout");
|
|
515
|
+
|
|
516
|
+
// Tick past the delayed newSession (200s total) — the late newSession resolves
|
|
517
|
+
mock.timers.tick(80_000);
|
|
518
|
+
// Drain microtask queue so the .finally() and setTimeout callbacks run
|
|
519
|
+
await Promise.resolve();
|
|
520
|
+
await Promise.resolve();
|
|
521
|
+
|
|
522
|
+
// The key assertion: when the late newSession() resolves, runUnit must have
|
|
523
|
+
// passed an aborted AbortSignal. Without the fix, no signal is passed and
|
|
524
|
+
// abortedWhenLateSessionSettled would be false (or null, if signal not passed at all).
|
|
525
|
+
assert.equal(
|
|
526
|
+
abortedWhenLateSessionSettled,
|
|
527
|
+
true,
|
|
528
|
+
"runUnit must pass an aborted AbortSignal to newSession() when it resolves after the session-creation timeout (#3731). " +
|
|
529
|
+
"Without this, AgentSession.newSession() captures root process.cwd() and rebuilds the tool runtime with wrong cwd.",
|
|
530
|
+
);
|
|
531
|
+
} finally {
|
|
532
|
+
mock.timers.reset();
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
|
|
352
536
|
// ─── Structural assertions ───────────────────────────────────────────────────
|
|
353
537
|
|
|
354
538
|
test("auto-loop.ts exports autoLoop, runUnit, resolveAgentEnd", async () => {
|
|
@@ -409,7 +593,7 @@ test("auto/phases.ts: selectAndApplyModel called exactly once and before updateP
|
|
|
409
593
|
// Extract the runUnitPhase function body
|
|
410
594
|
const fnStart = src.indexOf("export async function runUnitPhase");
|
|
411
595
|
assert.ok(fnStart > 0, "runUnitPhase should exist in phases.ts");
|
|
412
|
-
const fnBody = src.slice(fnStart, fnStart +
|
|
596
|
+
const fnBody = src.slice(fnStart, fnStart + 16000);
|
|
413
597
|
|
|
414
598
|
// selectAndApplyModel must appear exactly once
|
|
415
599
|
const allOccurrences = [...fnBody.matchAll(/selectAndApplyModel\(/g)];
|
|
@@ -497,6 +681,8 @@ function makeMockDeps(
|
|
|
497
681
|
autoWorktreeBranch: () => "auto/M001",
|
|
498
682
|
resolveMilestoneFile: () => null,
|
|
499
683
|
reconcileMergeState: () => "clean",
|
|
684
|
+
preflightCleanRoot: () => ({ stashPushed: false, summary: "" }),
|
|
685
|
+
postflightPopStash: () => {},
|
|
500
686
|
getLedger: () => null,
|
|
501
687
|
getProjectTotals: () => ({ cost: 0 }),
|
|
502
688
|
formatCost: (c: number) => `$${c.toFixed(2)}`,
|
|
@@ -341,6 +341,18 @@ test("dynamic routing passes provider-qualified model keys to the router", () =>
|
|
|
341
341
|
);
|
|
342
342
|
});
|
|
343
343
|
|
|
344
|
+
test("selectAndApplyModel re-applies captured thinking level after setModel success", () => {
|
|
345
|
+
const src = readFileSync(join(__dirname, "..", "auto-model-selection.ts"), "utf-8");
|
|
346
|
+
assert.ok(
|
|
347
|
+
src.includes("autoModeStartThinkingLevel?: ReturnType<ExtensionAPI[\"getThinkingLevel\"]> | null"),
|
|
348
|
+
"selectAndApplyModel should accept an autoModeStartThinkingLevel parameter",
|
|
349
|
+
);
|
|
350
|
+
assert.ok(
|
|
351
|
+
src.includes("reapplyThinkingLevel(pi, autoModeStartThinkingLevel)"),
|
|
352
|
+
"selectAndApplyModel should re-apply captured thinking level after model changes",
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
|
|
344
356
|
test("resolveModelId: anthropic wins over claude-code when session provider is not claude-code", () => {
|
|
345
357
|
const availableModels = [
|
|
346
358
|
{ id: "claude-sonnet-4-6", provider: "claude-code" },
|
|
@@ -39,6 +39,18 @@ test("auto.ts validates milestone before restoring paused session (#1664)", () =
|
|
|
39
39
|
source.includes('resolveMilestoneFile(base, meta.milestoneId, "SUMMARY")'),
|
|
40
40
|
"auto.ts must check for SUMMARY file to detect completed milestones",
|
|
41
41
|
);
|
|
42
|
+
|
|
43
|
+
// Resume path must sanitize paused session file metadata before unlink/recovery.
|
|
44
|
+
assert.ok(
|
|
45
|
+
source.includes("normalizeSessionFilePath(meta.sessionFile ?? null)"),
|
|
46
|
+
"auto.ts must sanitize paused-session metadata sessionFile before using it",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Pause path must sanitize live session file path before persisting metadata.
|
|
50
|
+
assert.ok(
|
|
51
|
+
source.includes("normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null)"),
|
|
52
|
+
"auto.ts must sanitize sessionManager getSessionFile output before persisting",
|
|
53
|
+
);
|
|
42
54
|
});
|
|
43
55
|
|
|
44
56
|
// ─── Filesystem validation unit tests ───────────────────────────────────────
|
|
@@ -775,6 +775,55 @@ test("#4414: parallel-research sentinel path does not collide with RESEARCH suff
|
|
|
775
775
|
}
|
|
776
776
|
});
|
|
777
777
|
|
|
778
|
+
test("#4068: verifyExpectedArtifact parallel-research treats PARALLEL-BLOCKER as terminal completion", () => {
|
|
779
|
+
// Regression: when a parallel-research unit times out and the timeout-recovery
|
|
780
|
+
// machinery writes a PARALLEL-BLOCKER placeholder, verifyExpectedArtifact must
|
|
781
|
+
// return true so the dispatch loop can advance. Previously it only returned
|
|
782
|
+
// true when every slice had a RESEARCH file — meaning a timeout always left
|
|
783
|
+
// verifyExpectedArtifact returning false, the unit was never cleared from
|
|
784
|
+
// unitDispatchCount, and the dispatch rule re-fired on the next iteration
|
|
785
|
+
// (infinite loop, issue #4068 / #4355).
|
|
786
|
+
const base = makeTmpBase();
|
|
787
|
+
try {
|
|
788
|
+
// Write a minimal roadmap
|
|
789
|
+
writeFileSync(
|
|
790
|
+
join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
|
|
791
|
+
[
|
|
792
|
+
"# M001: Timeout Test",
|
|
793
|
+
"",
|
|
794
|
+
"## Slices",
|
|
795
|
+
"",
|
|
796
|
+
"- [ ] **S01: Alpha** `risk:low` `depends:[]`",
|
|
797
|
+
"- [ ] **S02: Beta** `risk:low` `depends:[]`",
|
|
798
|
+
"",
|
|
799
|
+
].join("\n"),
|
|
800
|
+
"utf-8",
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
// No RESEARCH files written — subagents timed out
|
|
804
|
+
clearParseCache();
|
|
805
|
+
invalidateAllCaches();
|
|
806
|
+
|
|
807
|
+
// Simulate timeout-recovery writing the PARALLEL-BLOCKER placeholder
|
|
808
|
+
const blockerPath = resolveExpectedArtifactPath("research-slice", "M001/parallel-research", base);
|
|
809
|
+
assert.ok(blockerPath, "PARALLEL-BLOCKER path must resolve for parallel-research unit");
|
|
810
|
+
writeFileSync(blockerPath!, "# BLOCKER — timeout recovery\n\n**Reason**: hard timeout.\n", "utf-8");
|
|
811
|
+
|
|
812
|
+
clearParseCache();
|
|
813
|
+
invalidateAllCaches();
|
|
814
|
+
|
|
815
|
+
// After blocker is written, verifyExpectedArtifact must return true
|
|
816
|
+
// so the dispatch loop treats this unit as complete and moves on.
|
|
817
|
+
assert.equal(
|
|
818
|
+
verifyExpectedArtifact("research-slice", "M001/parallel-research", base),
|
|
819
|
+
true,
|
|
820
|
+
"#4068: PARALLEL-BLOCKER on disk must satisfy verifyExpectedArtifact so the loop does not re-dispatch",
|
|
821
|
+
);
|
|
822
|
+
} finally {
|
|
823
|
+
cleanup(base);
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
778
827
|
test("#4414: verifyExpectedArtifact parallel-research succeeds when all research-ready slices have RESEARCH", () => {
|
|
779
828
|
const base = makeTmpBase();
|
|
780
829
|
try {
|