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
|
@@ -34,6 +34,7 @@ const packageMap = {
|
|
|
34
34
|
'pi-coding-agent': { scope: '@gsd', name: 'pi-coding-agent' },
|
|
35
35
|
'pi-tui': { scope: '@gsd', name: 'pi-tui' },
|
|
36
36
|
'rpc-client': { scope: '@gsd-build', name: 'rpc-client' },
|
|
37
|
+
'mcp-server': { scope: '@gsd-build', name: 'mcp-server' },
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
for (const scopeDir of Object.values(scopeDirs)) {
|
|
@@ -21,10 +21,11 @@ const CLAUDE_COMMAND = process.platform === "win32" ? "claude.cmd" : "claude";
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Windows installs vary: some environments expose `claude.cmd` (npm shim),
|
|
24
|
-
*
|
|
25
|
-
* Try
|
|
24
|
+
* `claude.exe` (direct binary install), or a bare `claude` shim on PATH
|
|
25
|
+
* (for example Git Bash wrappers). Try all three to avoid false "not
|
|
26
|
+
* installed" results in readiness checks.
|
|
26
27
|
*/
|
|
27
|
-
const CLAUDE_COMMAND_CANDIDATES = process.platform === "win32" ? [CLAUDE_COMMAND, "claude"] : [CLAUDE_COMMAND];
|
|
28
|
+
const CLAUDE_COMMAND_CANDIDATES = process.platform === "win32" ? [CLAUDE_COMMAND, "claude.exe", "claude"] : [CLAUDE_COMMAND];
|
|
28
29
|
|
|
29
30
|
function execClaude(args: string[]): Buffer {
|
|
30
31
|
let lastError: unknown;
|
|
@@ -692,18 +692,16 @@ export function makeAbortedMessage(model: string, lastTextContent: string): Assi
|
|
|
692
692
|
/**
|
|
693
693
|
* Resolve the Claude Code permission mode for the current run.
|
|
694
694
|
*
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
* users opt into a stricter mode (`acceptEdits`, `default`, `plan`).
|
|
695
|
+
* Defaults to `acceptEdits`, which auto-approves file reads/edits but
|
|
696
|
+
* surfaces a permission dialog for dangerous operations (e.g. general Bash,
|
|
697
|
+
* Agent, WebFetch). This prevents tools outside the allowlist from being
|
|
698
|
+
* silently denied — the SDK emits an `extension_ui_request` event so the
|
|
699
|
+
* user sees a prompt instead of a silent refusal that Claude Code mistakes
|
|
700
|
+
* for user rejection (#4383).
|
|
702
701
|
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
* (#4099) is continuous approval fatigue that blocks real work.
|
|
702
|
+
* Set `GSD_CLAUDE_CODE_PERMISSION_MODE` to `bypassPermissions` to restore
|
|
703
|
+
* the old always-approve behaviour, or to `default` / `plan` for stricter
|
|
704
|
+
* modes.
|
|
707
705
|
*/
|
|
708
706
|
export async function resolveClaudePermissionMode(
|
|
709
707
|
env: NodeJS.ProcessEnv = process.env,
|
|
@@ -712,7 +710,7 @@ export async function resolveClaudePermissionMode(
|
|
|
712
710
|
if (override === "bypassPermissions" || override === "acceptEdits" || override === "default" || override === "plan") {
|
|
713
711
|
return override;
|
|
714
712
|
}
|
|
715
|
-
return "
|
|
713
|
+
return "acceptEdits";
|
|
716
714
|
}
|
|
717
715
|
|
|
718
716
|
// NOTE: These helpers intentionally mirror @gsd/pi-ai anthropic-shared
|
|
@@ -772,7 +770,7 @@ export function buildSdkOptions(
|
|
|
772
770
|
): Record<string, unknown> {
|
|
773
771
|
const { reasoning, ...sdkExtraOptions } = extraOptions;
|
|
774
772
|
const mcpServers = buildWorkflowMcpServers();
|
|
775
|
-
const permissionMode = overrides?.permissionMode ?? "
|
|
773
|
+
const permissionMode = overrides?.permissionMode ?? "acceptEdits";
|
|
776
774
|
const disallowedTools = ["AskUserQuestion"];
|
|
777
775
|
// Pre-authorize the safe built-ins and every registered workflow MCP
|
|
778
776
|
// server's tools. `acceptEdits` mode (the interactive default) only
|
|
@@ -867,6 +865,69 @@ function normalizeToolResultContent(content: unknown): ExternalToolResultContent
|
|
|
867
865
|
return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
|
|
868
866
|
}
|
|
869
867
|
|
|
868
|
+
/**
|
|
869
|
+
* Extract a `details` payload from an MCP tool-result block.
|
|
870
|
+
*
|
|
871
|
+
* MCP's `CallToolResult` carries structured data in `structuredContent` — the
|
|
872
|
+
* protocol's supported channel for non-text payloads. Claude Code's synthetic
|
|
873
|
+
* user message may surface that field in one of two shapes depending on SDK
|
|
874
|
+
* version: as a sibling on the `mcp_tool_result` block itself, or as a
|
|
875
|
+
* dedicated content sub-block with `type: "structuredContent"`. Snake-case
|
|
876
|
+
* (`structured_content`) is accepted defensively in case a transport hop
|
|
877
|
+
* rewrites casing. All other shapes fall back to an empty object so callers
|
|
878
|
+
* can rely on `details` being present.
|
|
879
|
+
*/
|
|
880
|
+
function extractStructuredDetailsFromBlock(block: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
881
|
+
const sibling = block.structuredContent ?? (block as Record<string, unknown>).structured_content;
|
|
882
|
+
if (sibling && typeof sibling === "object" && !Array.isArray(sibling)) {
|
|
883
|
+
return sibling as Record<string, unknown>;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (Array.isArray(block.content)) {
|
|
887
|
+
for (const item of block.content) {
|
|
888
|
+
if (!item || typeof item !== "object") continue;
|
|
889
|
+
const sub = item as Record<string, unknown>;
|
|
890
|
+
if (sub.type !== "structuredContent" && sub.type !== "structured_content") continue;
|
|
891
|
+
const payload = sub.structuredContent ?? sub.structured_content ?? sub.data ?? sub.value;
|
|
892
|
+
if (payload && typeof payload === "object" && !Array.isArray(payload)) {
|
|
893
|
+
return payload as Record<string, unknown>;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Return undefined (not {}) when no structured payload is present, matching
|
|
899
|
+
// the pre-#4477 contract where `details` was nullable. An empty-object
|
|
900
|
+
// sentinel is truthy and breaks downstream consumers that gate on
|
|
901
|
+
// `if (details)`. `undefined` matches the type of the field these results
|
|
902
|
+
// flow into (`Record<string, unknown> | undefined`).
|
|
903
|
+
return undefined;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* True for items that are MCP `structuredContent` pseudo-blocks living inside
|
|
908
|
+
* a tool-result `content[]` array. These blocks carry the structured payload
|
|
909
|
+
* (extracted separately by `extractStructuredDetailsFromBlock`) and must NOT
|
|
910
|
+
* leak into the visible content rendered to the user — otherwise the renderer
|
|
911
|
+
* stringifies the JSON pseudo-block and shows it next to the actual tool
|
|
912
|
+
* output. See PR #4477 review (CodeRabbit, post-fix-round).
|
|
913
|
+
*/
|
|
914
|
+
function isStructuredContentPseudoBlock(item: unknown): boolean {
|
|
915
|
+
if (!item || typeof item !== "object") return false;
|
|
916
|
+
const type = (item as Record<string, unknown>).type;
|
|
917
|
+
return type === "structuredContent" || type === "structured_content";
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* Strip `structuredContent` pseudo-blocks from a tool-result content array
|
|
922
|
+
* before normalization. The structured payload is extracted via the sibling
|
|
923
|
+
* `structuredContent` field (or a dedicated extractor pass on the raw block);
|
|
924
|
+
* the visible content path must not include the pseudo-block itself.
|
|
925
|
+
*/
|
|
926
|
+
function stripStructuredContentPseudoBlocks(content: unknown): unknown {
|
|
927
|
+
if (!Array.isArray(content)) return content;
|
|
928
|
+
return content.filter((item) => !isStructuredContentPseudoBlock(item));
|
|
929
|
+
}
|
|
930
|
+
|
|
870
931
|
/** Extract tool result payloads from an SDK synthetic user message, keyed by tool-use ID. */
|
|
871
932
|
export function extractToolResultsFromSdkUserMessage(message: SDKUserMessage): Array<{
|
|
872
933
|
toolUseId: string;
|
|
@@ -890,8 +951,8 @@ export function extractToolResultsFromSdkUserMessage(message: SDKUserMessage): A
|
|
|
890
951
|
extracted.push({
|
|
891
952
|
toolUseId,
|
|
892
953
|
result: {
|
|
893
|
-
content: normalizeToolResultContent(block.content),
|
|
894
|
-
details:
|
|
954
|
+
content: normalizeToolResultContent(stripStructuredContentPseudoBlocks(block.content)),
|
|
955
|
+
details: extractStructuredDetailsFromBlock(block),
|
|
895
956
|
isError: block.is_error === true,
|
|
896
957
|
},
|
|
897
958
|
});
|
|
@@ -906,8 +967,8 @@ export function extractToolResultsFromSdkUserMessage(message: SDKUserMessage): A
|
|
|
906
967
|
extracted.push({
|
|
907
968
|
toolUseId,
|
|
908
969
|
result: {
|
|
909
|
-
content: normalizeToolResultContent(toolResult.content),
|
|
910
|
-
details:
|
|
970
|
+
content: normalizeToolResultContent(stripStructuredContentPseudoBlocks(toolResult.content)),
|
|
971
|
+
details: extractStructuredDetailsFromBlock(toolResult),
|
|
911
972
|
isError: toolResult.is_error === true,
|
|
912
973
|
},
|
|
913
974
|
});
|
|
@@ -372,13 +372,146 @@ describe("stream-adapter — Claude Code external tool results", () => {
|
|
|
372
372
|
toolUseId: "tool-bash-1",
|
|
373
373
|
result: {
|
|
374
374
|
content: [{ type: "text", text: "line 1\nline 2" }],
|
|
375
|
-
|
|
375
|
+
// extractStructuredDetailsFromBlock returns undefined when no
|
|
376
|
+
// structured payload exists, restoring the pre-#4477 nullable
|
|
377
|
+
// contract (#4477 review feedback).
|
|
378
|
+
details: undefined,
|
|
376
379
|
isError: false,
|
|
377
380
|
},
|
|
378
381
|
},
|
|
379
382
|
]);
|
|
380
383
|
});
|
|
381
384
|
|
|
385
|
+
test("extractToolResultsFromSdkUserMessage reads structuredContent as a sibling field (#4472)", () => {
|
|
386
|
+
const message: SDKUserMessage = {
|
|
387
|
+
type: "user",
|
|
388
|
+
session_id: "sess-1",
|
|
389
|
+
parent_tool_use_id: "tool-mcp-1",
|
|
390
|
+
message: {
|
|
391
|
+
role: "user",
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: "mcp_tool_result",
|
|
395
|
+
tool_use_id: "tool-mcp-1",
|
|
396
|
+
content: [{ type: "text", text: "Gate Q3 result saved: verdict=pass" }],
|
|
397
|
+
is_error: false,
|
|
398
|
+
structuredContent: { gateId: "Q3", verdict: "pass" },
|
|
399
|
+
} as unknown as Record<string, unknown>,
|
|
400
|
+
],
|
|
401
|
+
},
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
405
|
+
assert.deepEqual(results[0].result.details, { gateId: "Q3", verdict: "pass" });
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("extractToolResultsFromSdkUserMessage reads structuredContent from a content sub-block (#4472)", () => {
|
|
409
|
+
const message: SDKUserMessage = {
|
|
410
|
+
type: "user",
|
|
411
|
+
session_id: "sess-1",
|
|
412
|
+
parent_tool_use_id: "tool-mcp-2",
|
|
413
|
+
message: {
|
|
414
|
+
role: "user",
|
|
415
|
+
content: [
|
|
416
|
+
{
|
|
417
|
+
type: "mcp_tool_result",
|
|
418
|
+
tool_use_id: "tool-mcp-2",
|
|
419
|
+
content: [
|
|
420
|
+
{ type: "text", text: "Gate Q4 result saved: verdict=flag" },
|
|
421
|
+
{ type: "structuredContent", structuredContent: { gateId: "Q4", verdict: "flag" } },
|
|
422
|
+
],
|
|
423
|
+
is_error: false,
|
|
424
|
+
} as unknown as Record<string, unknown>,
|
|
425
|
+
],
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
430
|
+
assert.deepEqual(results[0].result.details, { gateId: "Q4", verdict: "flag" });
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
test("#4477 extractToolResultsFromSdkUserMessage does NOT leak structuredContent pseudo-blocks into visible content", () => {
|
|
434
|
+
// Regression: when a content sub-block carries `type: "structuredContent"`,
|
|
435
|
+
// it carries the structured payload (extracted separately into `details`)
|
|
436
|
+
// and must NOT appear in the visible `content` array — otherwise the
|
|
437
|
+
// renderer stringifies the JSON pseudo-block and shows it next to the
|
|
438
|
+
// actual tool output. See PR #4477 review (CodeRabbit, post-fix-round).
|
|
439
|
+
const message: SDKUserMessage = {
|
|
440
|
+
type: "user",
|
|
441
|
+
session_id: "sess-1",
|
|
442
|
+
parent_tool_use_id: "tool-mcp-strip",
|
|
443
|
+
message: {
|
|
444
|
+
role: "user",
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "mcp_tool_result",
|
|
448
|
+
tool_use_id: "tool-mcp-strip",
|
|
449
|
+
content: [
|
|
450
|
+
{ type: "text", text: "Gate Q5 result saved: verdict=pass" },
|
|
451
|
+
{ type: "structuredContent", structuredContent: { gateId: "Q5", verdict: "pass" } },
|
|
452
|
+
{ type: "text", text: "second visible line" },
|
|
453
|
+
// snake_case variant — also a pseudo-block; also must be stripped
|
|
454
|
+
{ type: "structured_content", structured_content: { extra: "data" } },
|
|
455
|
+
],
|
|
456
|
+
is_error: false,
|
|
457
|
+
} as unknown as Record<string, unknown>,
|
|
458
|
+
],
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
463
|
+
assert.equal(results.length, 1, "should extract one result");
|
|
464
|
+
const result = results[0].result;
|
|
465
|
+
|
|
466
|
+
// The structured payload IS extracted to `details`.
|
|
467
|
+
assert.deepEqual(result.details, { gateId: "Q5", verdict: "pass" });
|
|
468
|
+
|
|
469
|
+
// The visible content has the two text blocks but NEITHER pseudo-block.
|
|
470
|
+
const visibleTexts = result.content.map((c: any) => c.text);
|
|
471
|
+
assert.deepEqual(
|
|
472
|
+
visibleTexts,
|
|
473
|
+
["Gate Q5 result saved: verdict=pass", "second visible line"],
|
|
474
|
+
"visible content must include only the two text blocks; both structuredContent variants must be stripped",
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
// Belt-and-suspenders: assert no rendered text shows the JSON serialization
|
|
478
|
+
// of a pseudo-block. We don't check for bare keys like "gateId" or "verdict"
|
|
479
|
+
// because those are legitimate words in the gate-result message text. The
|
|
480
|
+
// regression signature would be a JSON-shaped substring that could only
|
|
481
|
+
// appear via stringification.
|
|
482
|
+
const allText = visibleTexts.join("\n");
|
|
483
|
+
assert.ok(
|
|
484
|
+
!allText.includes('"structuredContent"'),
|
|
485
|
+
"rendered content must not include the pseudo-block type marker as JSON text",
|
|
486
|
+
);
|
|
487
|
+
assert.ok(
|
|
488
|
+
!allText.includes('"structured_content"'),
|
|
489
|
+
"rendered content must not include the snake_case pseudo-block type marker as JSON text",
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("extractToolResultsFromSdkUserMessage accepts snake_case structured_content defensively (#4472)", () => {
|
|
494
|
+
const message: SDKUserMessage = {
|
|
495
|
+
type: "user",
|
|
496
|
+
session_id: "sess-1",
|
|
497
|
+
parent_tool_use_id: "tool-mcp-3",
|
|
498
|
+
message: {
|
|
499
|
+
role: "user",
|
|
500
|
+
content: [
|
|
501
|
+
{
|
|
502
|
+
type: "mcp_tool_result",
|
|
503
|
+
tool_use_id: "tool-mcp-3",
|
|
504
|
+
content: [{ type: "text", text: "ok" }],
|
|
505
|
+
structured_content: { operation: "save_gate_result" },
|
|
506
|
+
} as unknown as Record<string, unknown>,
|
|
507
|
+
],
|
|
508
|
+
},
|
|
509
|
+
};
|
|
510
|
+
|
|
511
|
+
const results = extractToolResultsFromSdkUserMessage(message);
|
|
512
|
+
assert.deepEqual(results[0].result.details, { operation: "save_gate_result" });
|
|
513
|
+
});
|
|
514
|
+
|
|
382
515
|
test("extractToolResultsFromSdkUserMessage falls back to tool_use_result", () => {
|
|
383
516
|
const message: SDKUserMessage = {
|
|
384
517
|
type: "user",
|
|
@@ -398,7 +531,9 @@ describe("stream-adapter — Claude Code external tool results", () => {
|
|
|
398
531
|
toolUseId: "tool-read-1",
|
|
399
532
|
result: {
|
|
400
533
|
content: [{ type: "text", text: "file contents" }],
|
|
401
|
-
|
|
534
|
+
// undefined (not {}) per the restored nullable contract — see
|
|
535
|
+
// the analogous assertion in the tool_result test above.
|
|
536
|
+
details: undefined,
|
|
402
537
|
isError: true,
|
|
403
538
|
},
|
|
404
539
|
},
|
|
@@ -1081,11 +1216,15 @@ describe("stream-adapter — permission mode (F10)", () => {
|
|
|
1081
1216
|
}
|
|
1082
1217
|
}
|
|
1083
1218
|
|
|
1084
|
-
test("buildSdkOptions defaults to
|
|
1219
|
+
test("buildSdkOptions defaults to acceptEdits (#4383)", () => {
|
|
1085
1220
|
clearWorkflowMcpEnv();
|
|
1086
1221
|
const opts = buildSdkOptions("claude-sonnet-4-6", "test");
|
|
1087
|
-
assert.equal(opts.permissionMode, "
|
|
1088
|
-
assert.equal(
|
|
1222
|
+
assert.equal(opts.permissionMode, "acceptEdits");
|
|
1223
|
+
assert.equal(
|
|
1224
|
+
opts.allowDangerouslySkipPermissions,
|
|
1225
|
+
false,
|
|
1226
|
+
"allowDangerouslySkipPermissions must be false when permissionMode is acceptEdits",
|
|
1227
|
+
);
|
|
1089
1228
|
});
|
|
1090
1229
|
|
|
1091
1230
|
test("buildSdkOptions respects explicit acceptEdits override", () => {
|
|
@@ -1099,6 +1238,11 @@ describe("stream-adapter — permission mode (F10)", () => {
|
|
|
1099
1238
|
);
|
|
1100
1239
|
});
|
|
1101
1240
|
|
|
1241
|
+
test("resolveClaudePermissionMode defaults to acceptEdits when no env var is set (#4383)", async () => {
|
|
1242
|
+
const mode = await resolveClaudePermissionMode({});
|
|
1243
|
+
assert.equal(mode, "acceptEdits");
|
|
1244
|
+
});
|
|
1245
|
+
|
|
1102
1246
|
test("resolveClaudePermissionMode honours the GSD_CLAUDE_CODE_PERMISSION_MODE env override", async () => {
|
|
1103
1247
|
const env = { GSD_CLAUDE_CODE_PERMISSION_MODE: "acceptEdits" } as NodeJS.ProcessEnv;
|
|
1104
1248
|
const mode = await resolveClaudePermissionMode(env);
|
|
@@ -22,6 +22,7 @@ import type { CmuxLogLevel } from "../../cmux/index.js";
|
|
|
22
22
|
import type { JournalEntry } from "../journal.js";
|
|
23
23
|
import type { MergeReconcileResult } from "../auto-recovery.js";
|
|
24
24
|
import type { UokTurnObserver } from "../uok/contracts.js";
|
|
25
|
+
import type { PreflightResult } from "../clean-root-preflight.js";
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Dependencies injected by the caller (auto.ts startAuto) so autoLoop
|
|
@@ -122,6 +123,18 @@ export interface LoopDeps {
|
|
|
122
123
|
) => string | null;
|
|
123
124
|
reconcileMergeState: (basePath: string, ctx: ExtensionContext) => MergeReconcileResult;
|
|
124
125
|
|
|
126
|
+
// Clean-root preflight gate (#2909)
|
|
127
|
+
preflightCleanRoot: (
|
|
128
|
+
basePath: string,
|
|
129
|
+
milestoneId: string,
|
|
130
|
+
notify: (message: string, level: "info" | "warning" | "error") => void,
|
|
131
|
+
) => PreflightResult;
|
|
132
|
+
postflightPopStash: (
|
|
133
|
+
basePath: string,
|
|
134
|
+
milestoneId: string,
|
|
135
|
+
notify: (message: string, level: "info" | "warning" | "error") => void,
|
|
136
|
+
) => void;
|
|
137
|
+
|
|
125
138
|
// Budget/context/secrets
|
|
126
139
|
getLedger: () => unknown;
|
|
127
140
|
getProjectTotals: (units: unknown) => { cost: number };
|
|
@@ -213,6 +226,7 @@ export interface LoopDeps {
|
|
|
213
226
|
retryContext?: { isRetry: boolean; previousTier?: string },
|
|
214
227
|
isAutoMode?: boolean,
|
|
215
228
|
sessionModelOverride?: { provider: string; id: string } | null,
|
|
229
|
+
autoModeStartThinkingLevel?: ReturnType<ExtensionAPI["getThinkingLevel"]> | null,
|
|
216
230
|
) => Promise<{
|
|
217
231
|
routing: { tier: string; modelDowngraded: boolean } | null;
|
|
218
232
|
appliedModel: { provider: string; id: string } | null;
|
|
@@ -50,6 +50,13 @@ function stuckStatePath(basePath: string): string {
|
|
|
50
50
|
function loadStuckState(basePath: string): { recentUnits: Array<{ key: string }>; stuckRecoveryAttempts: number } {
|
|
51
51
|
try {
|
|
52
52
|
const data = JSON.parse(readFileSync(stuckStatePath(basePath), "utf-8"));
|
|
53
|
+
// Only load state written by a DIFFERENT process (real session restart).
|
|
54
|
+
// If the PID matches the current process, this state was written by an earlier
|
|
55
|
+
// autoLoop call in the same process (e.g., a test that completed before this
|
|
56
|
+
// one), not by a crashed session — skip it to prevent test state pollution.
|
|
57
|
+
if (data.pid === process.pid) {
|
|
58
|
+
return { recentUnits: [], stuckRecoveryAttempts: 0 };
|
|
59
|
+
}
|
|
53
60
|
return {
|
|
54
61
|
recentUnits: Array.isArray(data.recentUnits) ? data.recentUnits : [],
|
|
55
62
|
stuckRecoveryAttempts: typeof data.stuckRecoveryAttempts === "number" ? data.stuckRecoveryAttempts : 0,
|
|
@@ -65,6 +72,7 @@ function saveStuckState(basePath: string, state: LoopState): void {
|
|
|
65
72
|
const filePath = stuckStatePath(basePath);
|
|
66
73
|
mkdirSync(join(gsdRoot(basePath), "runtime"), { recursive: true });
|
|
67
74
|
writeFileSync(filePath, JSON.stringify({
|
|
75
|
+
pid: process.pid,
|
|
68
76
|
recentUnits: state.recentUnits.slice(-20), // keep last 20 entries
|
|
69
77
|
stuckRecoveryAttempts: state.stuckRecoveryAttempts,
|
|
70
78
|
updatedAt: new Date().toISOString(),
|
|
@@ -590,6 +598,7 @@ export async function autoLoop(
|
|
|
590
598
|
consecutiveCooldowns = 0;
|
|
591
599
|
recentErrorMessages.length = 0;
|
|
592
600
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
|
|
601
|
+
saveStuckState(s.basePath, loopState); // persist across session restarts (#4382)
|
|
593
602
|
debugLog("autoLoop", { phase: "iteration-complete", iteration });
|
|
594
603
|
finishTurn("completed");
|
|
595
604
|
} catch (loopErr) {
|
|
@@ -54,7 +54,8 @@ import type { MinimalModelRegistry } from "../context-budget.js";
|
|
|
54
54
|
import { ensurePlanV2Graph } from "../uok/plan-v2.js";
|
|
55
55
|
import { resolveUokFlags } from "../uok/flags.js";
|
|
56
56
|
import { UokGateRunner } from "../uok/gate-runner.js";
|
|
57
|
-
import { resetEvidence } from "../safety/evidence-collector.js";
|
|
57
|
+
import { resetEvidence, loadEvidenceFromDisk } from "../safety/evidence-collector.js";
|
|
58
|
+
import { parseUnitId } from "../unit-id.js";
|
|
58
59
|
import { createCheckpoint, cleanupCheckpoint, rollbackToCheckpoint } from "../safety/git-checkpoint.js";
|
|
59
60
|
import { resolveSafetyHarnessConfig } from "../safety/safety-harness.js";
|
|
60
61
|
import {
|
|
@@ -80,7 +81,11 @@ export function resetSessionTimeoutState(): void {
|
|
|
80
81
|
* Exported for testing as _resolveReportBasePath.
|
|
81
82
|
*/
|
|
82
83
|
export function _resolveReportBasePath(s: Pick<AutoSession, "originalBasePath" | "basePath">): string {
|
|
83
|
-
|
|
84
|
+
// Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
|
|
85
|
+
// originalBasePath is falsy — prevents reports landing in the worktree (#3729).
|
|
86
|
+
const resolved = s.originalBasePath || s.basePath;
|
|
87
|
+
const markerIdx = resolved.indexOf("/.gsd/worktrees/");
|
|
88
|
+
return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
/**
|
|
@@ -91,7 +96,12 @@ export function _resolveReportBasePath(s: Pick<AutoSession, "originalBasePath" |
|
|
|
91
96
|
export function _resolveDispatchGuardBasePath(
|
|
92
97
|
s: Pick<AutoSession, "originalBasePath" | "basePath">,
|
|
93
98
|
): string {
|
|
94
|
-
|
|
99
|
+
// Strip /.gsd/worktrees/ suffix when basePath is itself a worktree path and
|
|
100
|
+
// originalBasePath is falsy — prevents guard checks running against the
|
|
101
|
+
// worktree instead of the project root (#3729).
|
|
102
|
+
const resolved = s.originalBasePath || s.basePath;
|
|
103
|
+
const markerIdx = resolved.indexOf("/.gsd/worktrees/");
|
|
104
|
+
return markerIdx !== -1 ? resolved.slice(0, markerIdx) : resolved;
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
const PLAN_V2_GATE_PHASES: ReadonlySet<Phase> = new Set([
|
|
@@ -419,7 +429,7 @@ export async function runPreDispatch(
|
|
|
419
429
|
findings: reason,
|
|
420
430
|
milestoneId: state.activeMilestone?.id ?? undefined,
|
|
421
431
|
});
|
|
422
|
-
ctx.ui.notify(`Plan gate failed-closed: ${reason}`, "error");
|
|
432
|
+
ctx.ui.notify(`Plan gate failed-closed: ${reason}\n\nIf this keeps happening, try: /gsd doctor heal`, "error");
|
|
423
433
|
await deps.pauseAuto(ctx, pi);
|
|
424
434
|
return { action: "break", reason: "plan-v2-gate-failed" };
|
|
425
435
|
}
|
|
@@ -545,6 +555,12 @@ export async function runPreDispatch(
|
|
|
545
555
|
loopState.stuckRecoveryAttempts = 0;
|
|
546
556
|
|
|
547
557
|
// Worktree lifecycle on milestone transition — merge current, enter next
|
|
558
|
+
// #2909: preflight — warn + stash dirty working tree before merge
|
|
559
|
+
const preflightTransition = deps.preflightCleanRoot(
|
|
560
|
+
s.originalBasePath || s.basePath,
|
|
561
|
+
s.currentMilestoneId!,
|
|
562
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
563
|
+
);
|
|
548
564
|
try {
|
|
549
565
|
deps.resolver.mergeAndExit(s.currentMilestoneId!, ctx.ui);
|
|
550
566
|
} catch (mergeErr) {
|
|
@@ -566,6 +582,14 @@ export async function runPreDispatch(
|
|
|
566
582
|
await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
|
|
567
583
|
return { action: "break", reason: "merge-failed" };
|
|
568
584
|
}
|
|
585
|
+
// #2909: postflight — restore stashed changes after successful merge
|
|
586
|
+
if (preflightTransition.stashPushed) {
|
|
587
|
+
deps.postflightPopStash(
|
|
588
|
+
s.originalBasePath || s.basePath,
|
|
589
|
+
s.currentMilestoneId!,
|
|
590
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
591
|
+
);
|
|
592
|
+
}
|
|
569
593
|
|
|
570
594
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
571
595
|
|
|
@@ -644,6 +668,12 @@ export async function runPreDispatch(
|
|
|
644
668
|
if (incomplete.length === 0 && state.registry.length > 0) {
|
|
645
669
|
// All milestones complete — merge milestone branch before stopping
|
|
646
670
|
if (s.currentMilestoneId) {
|
|
671
|
+
// #2909: preflight — warn + stash dirty working tree before merge
|
|
672
|
+
const preflightAllComplete = deps.preflightCleanRoot(
|
|
673
|
+
s.originalBasePath || s.basePath,
|
|
674
|
+
s.currentMilestoneId,
|
|
675
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
676
|
+
);
|
|
647
677
|
try {
|
|
648
678
|
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
649
679
|
// Prevent stopAuto from attempting the same merge (#2645)
|
|
@@ -665,6 +695,14 @@ export async function runPreDispatch(
|
|
|
665
695
|
await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
|
|
666
696
|
return { action: "break", reason: "merge-failed" };
|
|
667
697
|
}
|
|
698
|
+
// #2909: postflight — restore stashed changes after successful merge
|
|
699
|
+
if (preflightAllComplete.stashPushed) {
|
|
700
|
+
deps.postflightPopStash(
|
|
701
|
+
s.originalBasePath || s.basePath,
|
|
702
|
+
s.currentMilestoneId,
|
|
703
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
704
|
+
);
|
|
705
|
+
}
|
|
668
706
|
|
|
669
707
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
670
708
|
}
|
|
@@ -758,6 +796,12 @@ export async function runPreDispatch(
|
|
|
758
796
|
if (state.phase === "complete") {
|
|
759
797
|
// Milestone merge on complete (before closeout so branch state is clean)
|
|
760
798
|
if (s.currentMilestoneId) {
|
|
799
|
+
// #2909: preflight — warn + stash dirty working tree before merge
|
|
800
|
+
const preflightComplete = deps.preflightCleanRoot(
|
|
801
|
+
s.originalBasePath || s.basePath,
|
|
802
|
+
s.currentMilestoneId,
|
|
803
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
804
|
+
);
|
|
761
805
|
try {
|
|
762
806
|
deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
|
|
763
807
|
// Prevent stopAuto from attempting the same merge (#2645)
|
|
@@ -779,6 +823,14 @@ export async function runPreDispatch(
|
|
|
779
823
|
await deps.stopAuto(ctx, pi, `Merge error on milestone ${s.currentMilestoneId}: ${String(mergeErr)}`);
|
|
780
824
|
return { action: "break", reason: "merge-failed" };
|
|
781
825
|
}
|
|
826
|
+
// #2909: postflight — restore stashed changes after successful merge
|
|
827
|
+
if (preflightComplete.stashPushed) {
|
|
828
|
+
deps.postflightPopStash(
|
|
829
|
+
s.originalBasePath || s.basePath,
|
|
830
|
+
s.currentMilestoneId,
|
|
831
|
+
ctx.ui.notify.bind(ctx.ui),
|
|
832
|
+
);
|
|
833
|
+
}
|
|
782
834
|
|
|
783
835
|
// PR creation (auto_pr) is handled inside mergeMilestoneToMain (#2302)
|
|
784
836
|
}
|
|
@@ -1385,6 +1437,14 @@ export async function runUnitPhase(
|
|
|
1385
1437
|
);
|
|
1386
1438
|
if (safetyConfig.enabled && safetyConfig.evidence_collection) {
|
|
1387
1439
|
resetEvidence();
|
|
1440
|
+
// Restore persisted evidence so session-restart resumes don't produce
|
|
1441
|
+
// false-positive "no bash calls" warnings (Bug #4385).
|
|
1442
|
+
if (s.basePath && unitType === "execute-task") {
|
|
1443
|
+
const { milestone: eMid, slice: eSid, task: eTid } = parseUnitId(unitId);
|
|
1444
|
+
if (eMid && eSid && eTid) {
|
|
1445
|
+
loadEvidenceFromDisk(s.basePath, eMid, eSid, eTid);
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1388
1448
|
}
|
|
1389
1449
|
// Only checkpoint code-executing units (not lifecycle/planning units)
|
|
1390
1450
|
if (safetyConfig.enabled && safetyConfig.checkpoints && unitType === "execute-task") {
|
|
@@ -1471,6 +1531,7 @@ export async function runUnitPhase(
|
|
|
1471
1531
|
sidecarItem ? undefined : { isRetry, previousTier },
|
|
1472
1532
|
undefined,
|
|
1473
1533
|
s.manualSessionModelOverride,
|
|
1534
|
+
s.autoModeStartThinkingLevel,
|
|
1474
1535
|
);
|
|
1475
1536
|
s.currentUnitRouting =
|
|
1476
1537
|
modelResult.routing as AutoSession["currentUnitRouting"];
|
|
@@ -1485,6 +1546,9 @@ export async function runUnitPhase(
|
|
|
1485
1546
|
if (match) {
|
|
1486
1547
|
const ok = await pi.setModel(match, { persist: false });
|
|
1487
1548
|
if (ok) {
|
|
1549
|
+
if (s.autoModeStartThinkingLevel) {
|
|
1550
|
+
pi.setThinkingLevel(s.autoModeStartThinkingLevel);
|
|
1551
|
+
}
|
|
1488
1552
|
s.currentUnitModel = match as AutoSession["currentUnitModel"];
|
|
1489
1553
|
ctx.ui.notify(`Hook model override: ${match.provider}/${match.id}`, "info");
|
|
1490
1554
|
} else {
|
|
@@ -1924,6 +1988,20 @@ export async function runFinalize(
|
|
|
1924
1988
|
debugLog("autoLoop", { phase: "sidecar-artifact-retry-skipped", iteration: ic.iteration });
|
|
1925
1989
|
} else {
|
|
1926
1990
|
// s.pendingVerificationRetry was set by postUnitPreVerification.
|
|
1991
|
+
// Emit a dedicated journal event so forensics can distinguish bounded
|
|
1992
|
+
// verification retries from genuine stuck-loop dispatch repetitions (#4540).
|
|
1993
|
+
const retryInfo = s.pendingVerificationRetry;
|
|
1994
|
+
deps.emitJournalEvent({
|
|
1995
|
+
ts: new Date().toISOString(),
|
|
1996
|
+
flowId: ic.flowId,
|
|
1997
|
+
seq: ic.nextSeq(),
|
|
1998
|
+
eventType: "artifact-verification-retry",
|
|
1999
|
+
data: {
|
|
2000
|
+
unitType: preUnitSnapshot?.type,
|
|
2001
|
+
unitId: retryInfo?.unitId,
|
|
2002
|
+
attempt: retryInfo?.attempt,
|
|
2003
|
+
},
|
|
2004
|
+
});
|
|
1927
2005
|
// Continue the loop — next iteration will inject the retry context into the prompt.
|
|
1928
2006
|
debugLog("autoLoop", { phase: "artifact-verification-retry", iteration: ic.iteration });
|
|
1929
2007
|
return { action: "continue" };
|
|
@@ -42,16 +42,25 @@ export async function runUnit(
|
|
|
42
42
|
let sessionResult: { cancelled: boolean };
|
|
43
43
|
let sessionTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
44
44
|
const mySessionSwitchGeneration = ++sessionSwitchGeneration;
|
|
45
|
+
// #3731: Cancellation controller for newSession(). When the session-creation
|
|
46
|
+
// timeout fires, we abort this controller so that the still-in-flight
|
|
47
|
+
// newSession() discards itself after await this.abort() completes, preventing
|
|
48
|
+
// it from capturing the (now-root) process.cwd() and rebuilding the tool
|
|
49
|
+
// runtime with the wrong cwd.
|
|
50
|
+
const sessionAbortController = new AbortController();
|
|
45
51
|
_setSessionSwitchInFlight(true);
|
|
46
52
|
try {
|
|
47
|
-
const sessionPromise = s.cmdCtx!.newSession().finally(() => {
|
|
53
|
+
const sessionPromise = s.cmdCtx!.newSession({ abortSignal: sessionAbortController.signal }).finally(() => {
|
|
48
54
|
if (sessionSwitchGeneration === mySessionSwitchGeneration) {
|
|
49
55
|
_setSessionSwitchInFlight(false);
|
|
50
56
|
}
|
|
51
57
|
});
|
|
52
58
|
const timeoutPromise = new Promise<{ cancelled: true }>((resolve) => {
|
|
53
59
|
sessionTimeoutHandle = setTimeout(
|
|
54
|
-
() =>
|
|
60
|
+
() => {
|
|
61
|
+
sessionAbortController.abort();
|
|
62
|
+
resolve({ cancelled: true });
|
|
63
|
+
},
|
|
55
64
|
NEW_SESSION_TIMEOUT_MS,
|
|
56
65
|
);
|
|
57
66
|
});
|
|
@@ -118,6 +127,35 @@ export async function runUnit(
|
|
|
118
127
|
logWarning("engine", "Failed to chdir to basePath before dispatch", { basePath: s.basePath, error: String(e) });
|
|
119
128
|
}
|
|
120
129
|
|
|
130
|
+
// ── Provider request-readiness pre-check (#4555) ──
|
|
131
|
+
// Verify the provider can accept requests before dispatching. If the token
|
|
132
|
+
// has expired since bootstrap, return cancelled immediately so the unit is
|
|
133
|
+
// not wasted on a guaranteed 401.
|
|
134
|
+
{
|
|
135
|
+
const provider = s.currentUnitModel?.provider ?? ctx.model?.provider;
|
|
136
|
+
const registry = (ctx as any).modelRegistry;
|
|
137
|
+
|
|
138
|
+
if (provider && registry != null && typeof registry.isProviderRequestReady === "function") {
|
|
139
|
+
let ready = false;
|
|
140
|
+
try {
|
|
141
|
+
ready = registry.isProviderRequestReady(provider);
|
|
142
|
+
} catch {
|
|
143
|
+
ready = false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!ready) {
|
|
147
|
+
return {
|
|
148
|
+
status: "cancelled",
|
|
149
|
+
errorContext: {
|
|
150
|
+
message: `Provider ${provider} is not request-ready (login/token expired)`,
|
|
151
|
+
category: "provider",
|
|
152
|
+
isTransient: false,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
121
159
|
// ── Send the prompt ──
|
|
122
160
|
debugLog("runUnit", { phase: "send-message", unitType, unitId });
|
|
123
161
|
|