gsd-pi 2.76.0 → 2.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -25
- 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/agents/researcher.md +1 -1
- package/dist/resources/extensions/claude-code-cli/readiness.js +31 -8
- 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 +104 -11
- 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 +53 -16
- 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-verification.js +33 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +51 -53
- package/dist/resources/extensions/gsd/auto.js +70 -28
- package/dist/resources/extensions/gsd/blocked-models.js +68 -0
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +93 -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/memory-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +12 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +52 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +84 -23
- 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-extract-learnings.js +54 -89
- 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/db-writer.js +88 -16
- package/dist/resources/extensions/gsd/doctor-git-checks.js +23 -29
- package/dist/resources/extensions/gsd/doctor-providers.js +51 -5
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +1 -0
- package/dist/resources/extensions/gsd/error-classifier.js +31 -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 +168 -23
- 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/hook-emitter.js +108 -0
- 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/memory-backfill.js +126 -0
- package/dist/resources/extensions/gsd/memory-store.js +19 -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/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/dist/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- 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/execute-task.md +3 -2
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -0
- 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/state.js +43 -4
- 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/memory-tools.js +26 -1
- 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/workflow-templates/spike.md +6 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/extensions/search-the-web/command-search-provider.js +5 -4
- package/dist/resources/extensions/search-the-web/native-search.js +45 -13
- package/dist/resources/skills/api-design/SKILL.md +190 -0
- package/dist/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/dist/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/dist/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/dist/resources/skills/design-an-interface/SKILL.md +102 -0
- package/dist/resources/skills/forensics/SKILL.md +153 -0
- package/dist/resources/skills/grill-me/SKILL.md +93 -0
- package/dist/resources/skills/handoff/SKILL.md +121 -0
- package/dist/resources/skills/observability/SKILL.md +174 -0
- package/dist/resources/skills/security-review/SKILL.md +181 -0
- package/dist/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/dist/resources/skills/tdd/SKILL.md +112 -0
- package/dist/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/dist/resources/skills/write-docs/SKILL.md +82 -0
- package/dist/resources/skills/write-milestone-brief/SKILL.md +135 -0
- 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 +10 -10
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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 +10 -10
- 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 +1 -1
- 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/server.js +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- 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 +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +163 -25
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -3
- 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 +215 -43
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +12 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/types.d.ts +30 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +14 -0
- package/packages/pi-agent-core/src/types.ts +34 -0
- package/packages/pi-agent-core/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-bearer-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +13 -0
- package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -0
- 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 +13 -4
- 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/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js +12 -2
- package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +164 -14
- package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js +15 -3
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js +16 -3
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.js.map +1 -1
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +67 -0
- package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +289 -0
- package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +1 -0
- package/packages/pi-ai/package.json +1 -1
- 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-bearer-auth.test.ts +26 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +15 -4
- 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/src/utils/oauth/github-copilot.test.ts +200 -23
- package/packages/pi-ai/src/utils/oauth/github-copilot.ts +12 -2
- package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-antigravity.ts +15 -5
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +84 -0
- package/packages/pi-ai/src/utils/oauth/google-gemini-cli.ts +16 -5
- package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +363 -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 +32 -2
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +4 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +35 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +233 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +205 -2
- 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/hooks-runner.d.ts +53 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js +337 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +234 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/index.js +1 -0
- package/packages/pi-coding-agent/dist/core/index.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-auth-header.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js +40 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-header.test.js.map +1 -0
- 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/core/settings-manager.d.ts +55 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.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/package.json +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 +38 -2
- package/packages/pi-coding-agent/src/core/extensions/index.ts +16 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +5 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +351 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +258 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +269 -0
- package/packages/pi-coding-agent/src/core/hooks-runner.ts +460 -0
- package/packages/pi-coding-agent/src/core/index.ts +10 -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-auth-header.test.ts +44 -0
- 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/core/settings-manager.ts +57 -0
- package/packages/pi-coding-agent/src/index.ts +16 -0
- 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/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +1 -0
- package/src/resources/agents/researcher.md +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +32 -8
- 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 +131 -10
- 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 +71 -15
- 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-verification.ts +33 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +62 -63
- package/src/resources/extensions/gsd/auto.ts +73 -28
- package/src/resources/extensions/gsd/blocked-models.ts +98 -0
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +120 -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/memory-tools.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +15 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +54 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +89 -26
- 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-extract-learnings.ts +55 -90
- 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/db-writer.ts +88 -17
- package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -27
- package/src/resources/extensions/gsd/doctor-providers.ts +59 -6
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +2 -0
- package/src/resources/extensions/gsd/error-classifier.ts +36 -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 +186 -23
- 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/hook-emitter.ts +188 -0
- 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/memory-backfill.ts +140 -0
- package/src/resources/extensions/gsd/memory-store.ts +26 -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/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
- package/src/resources/extensions/gsd/prompts/debug-diagnose.md +2 -0
- 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/execute-task.md +3 -2
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -0
- 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/state.ts +45 -4
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +188 -2
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +95 -1
- 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/blocked-models.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/bundled-skill-triggers.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +68 -66
- 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/derive-state-db.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +8 -4
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +148 -3
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +306 -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/flat-rate-routing-guard.test.ts +40 -9
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +62 -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/doctor-git-symlink-cwd.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +78 -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/interactive-routing-bypass.test.ts +1 -1
- 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 +37 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/load-memory-block.test.ts +36 -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 +103 -4
- 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/validate-milestone.test.ts +9 -3
- 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/memory-tools.ts +31 -1
- 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 +4 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +3 -0
- package/src/resources/extensions/gsd/workflow-templates/spike.md +6 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/extensions/search-the-web/command-search-provider.ts +5 -4
- package/src/resources/extensions/search-the-web/native-search.ts +48 -12
- package/src/resources/skills/api-design/SKILL.md +190 -0
- package/src/resources/skills/create-mcp-server/SKILL.md +121 -0
- package/src/resources/skills/decompose-into-slices/SKILL.md +139 -0
- package/src/resources/skills/dependency-upgrade/SKILL.md +158 -0
- package/src/resources/skills/design-an-interface/SKILL.md +102 -0
- package/src/resources/skills/forensics/SKILL.md +153 -0
- package/src/resources/skills/grill-me/SKILL.md +93 -0
- package/src/resources/skills/handoff/SKILL.md +121 -0
- package/src/resources/skills/observability/SKILL.md +174 -0
- package/src/resources/skills/security-review/SKILL.md +181 -0
- package/src/resources/skills/spike-wrap-up/SKILL.md +138 -0
- package/src/resources/skills/tdd/SKILL.md +112 -0
- package/src/resources/skills/verify-before-complete/SKILL.md +98 -0
- package/src/resources/skills/write-docs/SKILL.md +82 -0
- package/src/resources/skills/write-milestone-brief/SKILL.md +135 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ssX7BLv3Dw9Fb4CtrCGeR → pV-mPo7rYGb5JBC09C8GG}/_ssgManifest.js +0 -0
|
@@ -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";
|
|
@@ -613,7 +613,7 @@ async function handleAllSlicesDone(
|
|
|
613
613
|
const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
|
|
614
614
|
const verdict = validationContent ? extractVerdict(validationContent) : undefined;
|
|
615
615
|
|
|
616
|
-
if (!validationTerminal
|
|
616
|
+
if (!validationTerminal) {
|
|
617
617
|
return {
|
|
618
618
|
activeMilestone, activeSlice: null, activeTask: null,
|
|
619
619
|
phase: 'validating-milestone',
|
|
@@ -624,6 +624,24 @@ async function handleAllSlicesDone(
|
|
|
624
624
|
};
|
|
625
625
|
}
|
|
626
626
|
|
|
627
|
+
// All roadmap slices are done (enforced by caller) and verdict is
|
|
628
|
+
// needs-remediation — remediation cannot progress without new slices.
|
|
629
|
+
// Return blocked instead of re-dispatching validate-milestone (#4506).
|
|
630
|
+
if (verdict === 'needs-remediation') {
|
|
631
|
+
return {
|
|
632
|
+
activeMilestone, activeSlice: null, activeTask: null,
|
|
633
|
+
phase: 'blocked',
|
|
634
|
+
recentDecisions: [],
|
|
635
|
+
blockers: [
|
|
636
|
+
`Milestone ${activeMilestone.id} validation verdict is needs-remediation but all slices are complete. ` +
|
|
637
|
+
`Add remediation slices via gsd_reassess_roadmap or override the verdict manually.`,
|
|
638
|
+
],
|
|
639
|
+
nextAction: `Resolve ${activeMilestone.id} remediation before proceeding.`,
|
|
640
|
+
registry, requirements,
|
|
641
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress },
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
|
|
627
645
|
return {
|
|
628
646
|
activeMilestone, activeSlice: null, activeTask: null,
|
|
629
647
|
phase: 'completing-milestone',
|
|
@@ -1450,9 +1468,11 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
1450
1468
|
total: activeRoadmap.slices.length,
|
|
1451
1469
|
};
|
|
1452
1470
|
|
|
1453
|
-
// Force re-validation when
|
|
1454
|
-
// may have completed since the stale validation was
|
|
1455
|
-
|
|
1471
|
+
// Force re-validation when VALIDATION.md is absent or non-terminal —
|
|
1472
|
+
// remediation slices may have completed since the stale validation was
|
|
1473
|
+
// written (#3596). But needs-remediation with all slices done is a dead
|
|
1474
|
+
// end — return blocked to avoid an infinite dispatch loop (#4506).
|
|
1475
|
+
if (!validationTerminal) {
|
|
1456
1476
|
return {
|
|
1457
1477
|
activeMilestone,
|
|
1458
1478
|
activeSlice: null,
|
|
@@ -1470,6 +1490,27 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
1470
1490
|
};
|
|
1471
1491
|
}
|
|
1472
1492
|
|
|
1493
|
+
if (verdict === 'needs-remediation') {
|
|
1494
|
+
return {
|
|
1495
|
+
activeMilestone,
|
|
1496
|
+
activeSlice: null,
|
|
1497
|
+
activeTask: null,
|
|
1498
|
+
phase: 'blocked',
|
|
1499
|
+
recentDecisions: [],
|
|
1500
|
+
blockers: [
|
|
1501
|
+
`Milestone ${activeMilestone.id} validation verdict is needs-remediation but all slices are complete. ` +
|
|
1502
|
+
`Add remediation slices via gsd_reassess_roadmap or override the verdict manually.`,
|
|
1503
|
+
],
|
|
1504
|
+
nextAction: `Resolve ${activeMilestone.id} remediation before proceeding.`,
|
|
1505
|
+
registry,
|
|
1506
|
+
requirements,
|
|
1507
|
+
progress: {
|
|
1508
|
+
milestones: milestoneProgress,
|
|
1509
|
+
slices: sliceProgress,
|
|
1510
|
+
},
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1473
1514
|
return {
|
|
1474
1515
|
activeMilestone,
|
|
1475
1516
|
activeSlice: null,
|
|
@@ -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)}`,
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
|
|
8
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
|
|
10
|
-
import { resolvePreferredModelConfig, resolveModelId } from "../auto-model-selection.js";
|
|
10
|
+
import { resolvePreferredModelConfig, resolveModelId, selectAndApplyModel } from "../auto-model-selection.js";
|
|
11
11
|
|
|
12
12
|
function makeTempDir(prefix: string): string {
|
|
13
13
|
return mkdtempSync(join(tmpdir(), prefix));
|
|
@@ -46,6 +46,7 @@ test("resolvePreferredModelConfig synthesizes heavy routing ceiling when models
|
|
|
46
46
|
assert.deepEqual(config, {
|
|
47
47
|
primary: "claude-opus-4-6",
|
|
48
48
|
fallbacks: [],
|
|
49
|
+
source: "synthesized",
|
|
49
50
|
});
|
|
50
51
|
} finally {
|
|
51
52
|
process.chdir(originalCwd);
|
|
@@ -88,6 +89,7 @@ test("resolvePreferredModelConfig falls back to auto start model when heavy tier
|
|
|
88
89
|
assert.deepEqual(config, {
|
|
89
90
|
primary: "openai/gpt-5.4",
|
|
90
91
|
fallbacks: [],
|
|
92
|
+
source: "synthesized",
|
|
91
93
|
});
|
|
92
94
|
} finally {
|
|
93
95
|
process.chdir(originalCwd);
|
|
@@ -131,6 +133,7 @@ test("resolvePreferredModelConfig keeps explicit phase models as the ceiling", (
|
|
|
131
133
|
assert.deepEqual(config, {
|
|
132
134
|
primary: "claude-sonnet-4-6",
|
|
133
135
|
fallbacks: [],
|
|
136
|
+
source: "explicit",
|
|
134
137
|
});
|
|
135
138
|
} finally {
|
|
136
139
|
process.chdir(originalCwd);
|
|
@@ -141,6 +144,85 @@ test("resolvePreferredModelConfig keeps explicit phase models as the ceiling", (
|
|
|
141
144
|
}
|
|
142
145
|
});
|
|
143
146
|
|
|
147
|
+
test("selectAndApplyModel honors explicit phase models without downgrading (#3617)", async () => {
|
|
148
|
+
const originalCwd = process.cwd();
|
|
149
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
150
|
+
const tempProject = makeTempDir("gsd-routing-project-");
|
|
151
|
+
const tempGsdHome = makeTempDir("gsd-routing-home-");
|
|
152
|
+
const setModelCalls: string[] = [];
|
|
153
|
+
let beforeModelSelectCalled = false;
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
157
|
+
writeFileSync(
|
|
158
|
+
join(tempProject, ".gsd", "PREFERENCES.md"),
|
|
159
|
+
[
|
|
160
|
+
"---",
|
|
161
|
+
"models:",
|
|
162
|
+
" planning: claude-opus-4-6",
|
|
163
|
+
"dynamic_routing:",
|
|
164
|
+
" enabled: true",
|
|
165
|
+
" tier_models:",
|
|
166
|
+
" light: gpt-4o-mini",
|
|
167
|
+
" standard: claude-sonnet-4-6",
|
|
168
|
+
" heavy: claude-opus-4-6",
|
|
169
|
+
"---",
|
|
170
|
+
].join("\n"),
|
|
171
|
+
"utf-8",
|
|
172
|
+
);
|
|
173
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
174
|
+
process.chdir(tempProject);
|
|
175
|
+
|
|
176
|
+
const availableModels = [
|
|
177
|
+
{ id: "claude-opus-4-6", provider: "anthropic", api: "anthropic-messages" },
|
|
178
|
+
{ id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" },
|
|
179
|
+
{ id: "gpt-4o-mini", provider: "openai", api: "responses" },
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const result = await selectAndApplyModel(
|
|
183
|
+
{
|
|
184
|
+
modelRegistry: { getAvailable: () => availableModels },
|
|
185
|
+
sessionManager: { getSessionId: () => "test-session" },
|
|
186
|
+
ui: { notify: () => {} },
|
|
187
|
+
model: { provider: "anthropic", id: "claude-opus-4-6", api: "anthropic-messages" },
|
|
188
|
+
} as any,
|
|
189
|
+
{
|
|
190
|
+
setModel: async (model: { provider: string; id: string }) => {
|
|
191
|
+
setModelCalls.push(`${model.provider}/${model.id}`);
|
|
192
|
+
return true;
|
|
193
|
+
},
|
|
194
|
+
emitBeforeModelSelect: async () => {
|
|
195
|
+
beforeModelSelectCalled = true;
|
|
196
|
+
return undefined;
|
|
197
|
+
},
|
|
198
|
+
getActiveTools: () => [],
|
|
199
|
+
emitAdjustToolSet: async () => undefined,
|
|
200
|
+
setActiveTools: () => {},
|
|
201
|
+
} as any,
|
|
202
|
+
"plan-slice",
|
|
203
|
+
"slice-1",
|
|
204
|
+
tempProject,
|
|
205
|
+
undefined,
|
|
206
|
+
false,
|
|
207
|
+
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
208
|
+
undefined,
|
|
209
|
+
true,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
assert.equal(beforeModelSelectCalled, false, "explicit phase models should skip dynamic routing hooks");
|
|
213
|
+
assert.deepEqual(setModelCalls, ["anthropic/claude-opus-4-6"]);
|
|
214
|
+
assert.equal(result.routing, null, "explicit phase models should not record a routing downgrade");
|
|
215
|
+
assert.equal(result.appliedModel?.provider, "anthropic");
|
|
216
|
+
assert.equal(result.appliedModel?.id, "claude-opus-4-6");
|
|
217
|
+
} finally {
|
|
218
|
+
process.chdir(originalCwd);
|
|
219
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
220
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
221
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
222
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
|
|
144
226
|
// ─── resolveModelId tests ─────────────────────────────────────────────────
|
|
145
227
|
|
|
146
228
|
test("resolveModelId: bare ID resolves to claude-code when session is claude-code (#3772)", () => {
|
|
@@ -259,6 +341,18 @@ test("dynamic routing passes provider-qualified model keys to the router", () =>
|
|
|
259
341
|
);
|
|
260
342
|
});
|
|
261
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
|
+
|
|
262
356
|
test("resolveModelId: anthropic wins over claude-code when session provider is not claude-code", () => {
|
|
263
357
|
const availableModels = [
|
|
264
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 {
|