gsd-pi 2.70.0 → 2.70.1-dev.366a872
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/loader.js +4 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +270 -23
- package/dist/resources/extensions/get-secrets-from-user.js +17 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +33 -19
- package/dist/resources/extensions/gsd/auto-prompts.js +7 -3
- package/dist/resources/extensions/gsd/auto-start.js +28 -12
- package/dist/resources/extensions/gsd/auto.js +12 -8
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +22 -8
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +16 -12
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +12 -0
- package/dist/resources/extensions/gsd/doctor-format.js +2 -0
- package/dist/resources/extensions/gsd/file-lock.js +60 -0
- package/dist/resources/extensions/gsd/guided-flow.js +33 -20
- package/dist/resources/extensions/gsd/init-wizard.js +3 -11
- package/dist/resources/extensions/gsd/pre-execution-checks.js +5 -3
- package/dist/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/dist/resources/extensions/gsd/state.js +234 -332
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +34 -0
- package/dist/resources/extensions/gsd/validate-directory.js +30 -12
- package/dist/resources/extensions/gsd/workflow-events.js +25 -13
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +56 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +12 -1
- package/dist/resources/extensions/slash-commands/audit.js +2 -1
- package/dist/resources/extensions/subagent/isolation.js +4 -2
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +30 -27
- package/dist/update-cmd.js +3 -11
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
- package/dist/web/standalone/.next/build-manifest.json +4 -4
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/63.js +3 -3
- package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-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/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.dd3dc8bbd3025fa5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-6e4d7e9a4f57bed4.js → webpack-b868033a5834586d.js} +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.js +4 -0
- package/package.json +11 -11
- package/packages/mcp-server/dist/env-writer.d.ts +39 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -0
- package/packages/mcp-server/dist/env-writer.js +158 -0
- package/packages/mcp-server/dist/env-writer.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +11 -2
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +102 -2
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +2 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +35 -3
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/env-writer.test.ts +280 -0
- package/packages/mcp-server/src/env-writer.ts +183 -0
- package/packages/mcp-server/src/import-candidates.test.ts +48 -0
- package/packages/mcp-server/src/secure-env-collect.test.ts +265 -0
- package/packages/mcp-server/src/server.ts +137 -3
- package/packages/mcp-server/src/workflow-tools.ts +34 -1
- package/packages/pi-agent-core/dist/agent.d.ts +8 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +3 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/src/agent.test.ts +82 -0
- package/packages/pi-agent-core/src/agent.ts +12 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +348 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -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/lsp/config.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +38 -15
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +19 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +50 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +158 -23
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- 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 +58 -2
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +3 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +418 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +43 -17
- package/packages/pi-coding-agent/src/core/sdk.ts +8 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +58 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +2 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +189 -29
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +66 -2
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -5
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
- package/packages/pi-coding-agent/src/modes/rpc/rpc-types.ts +1 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js +9 -0
- package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +66 -0
- package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -0
- package/packages/pi-tui/dist/components/input.d.ts +2 -0
- package/packages/pi-tui/dist/components/input.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/input.js +7 -4
- package/packages/pi-tui/dist/components/input.js.map +1 -1
- package/packages/pi-tui/dist/components/markdown.d.ts +3 -0
- package/packages/pi-tui/dist/components/markdown.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/markdown.js +17 -1
- package/packages/pi-tui/dist/components/markdown.js.map +1 -1
- package/packages/pi-tui/src/components/__tests__/input.test.ts +11 -0
- package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +75 -0
- package/packages/pi-tui/src/components/input.ts +7 -4
- package/packages/pi-tui/src/components/markdown.ts +22 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +384 -24
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +317 -0
- package/src/resources/extensions/get-secrets-from-user.ts +24 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +39 -25
- package/src/resources/extensions/gsd/auto-prompts.ts +7 -3
- package/src/resources/extensions/gsd/auto-start.ts +37 -14
- package/src/resources/extensions/gsd/auto.ts +12 -8
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +22 -7
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +19 -14
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +14 -0
- package/src/resources/extensions/gsd/doctor-format.ts +1 -0
- package/src/resources/extensions/gsd/doctor-types.ts +1 -0
- package/src/resources/extensions/gsd/file-lock.ts +59 -0
- package/src/resources/extensions/gsd/guided-flow.ts +36 -17
- package/src/resources/extensions/gsd/init-wizard.ts +3 -13
- package/src/resources/extensions/gsd/pre-execution-checks.ts +6 -3
- package/src/resources/extensions/gsd/prompts/discuss.md +31 -13
- package/src/resources/extensions/gsd/state.ts +274 -344
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +436 -0
- package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +8 -7
- package/src/resources/extensions/gsd/tests/secure-env-collect.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/validate-directory.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +87 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +180 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +22 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +60 -25
- package/src/resources/extensions/gsd/validate-directory.ts +33 -11
- package/src/resources/extensions/gsd/workflow-events.ts +34 -25
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +76 -0
- package/src/resources/extensions/gsd/workflow-mcp.ts +16 -1
- package/src/resources/extensions/slash-commands/audit.ts +2 -1
- package/src/resources/extensions/subagent/isolation.ts +4 -3
- package/dist/web/standalone/.next/static/chunks/2826.821e01b07d92e948.js +0 -9
- package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{Nl6lg7zP5dNgNBV1107v1 → eMly3KqfZihJJAHQGmxvO}/_ssgManifest.js +0 -0
package/dist/loader.js
CHANGED
|
@@ -93,6 +93,10 @@ if (!existsSync(appRoot)) {
|
|
|
93
93
|
}
|
|
94
94
|
// GSD_CODING_AGENT_DIR — tells pi's getAgentDir() to return ~/.gsd/agent/ instead of ~/.gsd/agent/
|
|
95
95
|
process.env.GSD_CODING_AGENT_DIR = agentDir;
|
|
96
|
+
// GSD_PKG_ROOT — absolute path to gsd-pi package root. Used by deployed extensions
|
|
97
|
+
// (e.g. auto.ts resume path) to import modules like resource-loader.js that live
|
|
98
|
+
// in the package tree, not in the deployed ~/.gsd/agent/ tree.
|
|
99
|
+
process.env.GSD_PKG_ROOT = gsdRoot;
|
|
96
100
|
// RTK environment — make ~/.gsd/agent/bin visible to all child-process paths,
|
|
97
101
|
// not just the bash tool, and force-disable RTK telemetry for GSD-managed use.
|
|
98
102
|
applyRtkProcessEnv(process.env);
|
|
@@ -10,6 +10,9 @@ import { EventStream } from "@gsd/pi-ai";
|
|
|
10
10
|
import { execSync } from "node:child_process";
|
|
11
11
|
import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
|
|
12
12
|
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
|
|
13
|
+
import { showInterviewRound } from "../shared/tui.js";
|
|
14
|
+
const OTHER_OPTION_LABEL = "None of the above";
|
|
15
|
+
const SENSITIVE_FIELD_PATTERN = /(password|passphrase|secret|token|api[_\s-]*key|private[_\s-]*key|credential)/i;
|
|
13
16
|
// ---------------------------------------------------------------------------
|
|
14
17
|
// Stream factory
|
|
15
18
|
// ---------------------------------------------------------------------------
|
|
@@ -124,6 +127,233 @@ export function makeStreamExhaustedErrorMessage(model, lastTextContent) {
|
|
|
124
127
|
}
|
|
125
128
|
return message;
|
|
126
129
|
}
|
|
130
|
+
function readElicitationChoices(options) {
|
|
131
|
+
if (!Array.isArray(options))
|
|
132
|
+
return [];
|
|
133
|
+
return options
|
|
134
|
+
.map((option) => (typeof option?.const === "string" ? option.const : typeof option?.title === "string" ? option.title : ""))
|
|
135
|
+
.filter((option) => option.length > 0);
|
|
136
|
+
}
|
|
137
|
+
export function parseAskUserQuestionsElicitation(request) {
|
|
138
|
+
if (request.mode && request.mode !== "form")
|
|
139
|
+
return null;
|
|
140
|
+
const properties = request.requestedSchema?.properties;
|
|
141
|
+
if (!properties || typeof properties !== "object")
|
|
142
|
+
return null;
|
|
143
|
+
const questions = [];
|
|
144
|
+
for (const [fieldId, rawField] of Object.entries(properties)) {
|
|
145
|
+
if (fieldId.endsWith("__note"))
|
|
146
|
+
continue;
|
|
147
|
+
if (!rawField || typeof rawField !== "object")
|
|
148
|
+
return null;
|
|
149
|
+
const header = typeof rawField.title === "string" && rawField.title.length > 0 ? rawField.title : fieldId;
|
|
150
|
+
const question = typeof rawField.description === "string" ? rawField.description : "";
|
|
151
|
+
if (rawField.type === "array") {
|
|
152
|
+
const options = readElicitationChoices(rawField.items?.anyOf).map((label) => ({ label, description: "" }));
|
|
153
|
+
if (options.length === 0)
|
|
154
|
+
return null;
|
|
155
|
+
questions.push({
|
|
156
|
+
id: fieldId,
|
|
157
|
+
header,
|
|
158
|
+
question,
|
|
159
|
+
options,
|
|
160
|
+
allowMultiple: true,
|
|
161
|
+
});
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (rawField.type === "string") {
|
|
165
|
+
const noteFieldId = Object.prototype.hasOwnProperty.call(properties, `${fieldId}__note`)
|
|
166
|
+
? `${fieldId}__note`
|
|
167
|
+
: undefined;
|
|
168
|
+
const options = readElicitationChoices(rawField.oneOf)
|
|
169
|
+
.filter((label) => label !== OTHER_OPTION_LABEL)
|
|
170
|
+
.map((label) => ({ label, description: "" }));
|
|
171
|
+
if (options.length === 0)
|
|
172
|
+
return null;
|
|
173
|
+
questions.push({
|
|
174
|
+
id: fieldId,
|
|
175
|
+
header,
|
|
176
|
+
question,
|
|
177
|
+
options,
|
|
178
|
+
noteFieldId,
|
|
179
|
+
});
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
return questions.length > 0 ? questions : null;
|
|
185
|
+
}
|
|
186
|
+
function isSecureElicitationField(requestMessage, fieldId, field) {
|
|
187
|
+
if (field.format === "password")
|
|
188
|
+
return true;
|
|
189
|
+
if (field.writeOnly === true)
|
|
190
|
+
return true;
|
|
191
|
+
const rawField = field;
|
|
192
|
+
if (rawField.sensitive === true || rawField["x-sensitive"] === true)
|
|
193
|
+
return true;
|
|
194
|
+
const haystack = [
|
|
195
|
+
requestMessage,
|
|
196
|
+
fieldId.replace(/[_-]+/g, " "),
|
|
197
|
+
typeof field.title === "string" ? field.title : "",
|
|
198
|
+
typeof field.description === "string" ? field.description : "",
|
|
199
|
+
]
|
|
200
|
+
.join(" ")
|
|
201
|
+
.toLowerCase();
|
|
202
|
+
return SENSITIVE_FIELD_PATTERN.test(haystack);
|
|
203
|
+
}
|
|
204
|
+
export function parseTextInputElicitation(request) {
|
|
205
|
+
if (request.mode && request.mode !== "form")
|
|
206
|
+
return null;
|
|
207
|
+
const schema = request.requestedSchema;
|
|
208
|
+
const fieldsSource = schema?.properties && typeof schema.properties === "object"
|
|
209
|
+
? schema.properties
|
|
210
|
+
: schema?.keys && typeof schema.keys === "object"
|
|
211
|
+
? schema.keys
|
|
212
|
+
: undefined;
|
|
213
|
+
if (!fieldsSource)
|
|
214
|
+
return null;
|
|
215
|
+
const requiredSet = new Set(Array.isArray(request.requestedSchema?.required)
|
|
216
|
+
? request.requestedSchema.required.filter((value) => typeof value === "string")
|
|
217
|
+
: []);
|
|
218
|
+
const fields = [];
|
|
219
|
+
for (const [fieldId, field] of Object.entries(fieldsSource)) {
|
|
220
|
+
if (!field || typeof field !== "object")
|
|
221
|
+
continue;
|
|
222
|
+
if (field.type !== "string")
|
|
223
|
+
continue;
|
|
224
|
+
if (Array.isArray(field.oneOf) && field.oneOf.length > 0)
|
|
225
|
+
continue;
|
|
226
|
+
fields.push({
|
|
227
|
+
id: fieldId,
|
|
228
|
+
title: typeof field.title === "string" && field.title.length > 0 ? field.title : fieldId,
|
|
229
|
+
description: typeof field.description === "string" ? field.description : "",
|
|
230
|
+
required: requiredSet.has(fieldId),
|
|
231
|
+
secure: isSecureElicitationField(request.message, fieldId, field),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return fields.length > 0 ? fields : null;
|
|
235
|
+
}
|
|
236
|
+
export function roundResultToElicitationContent(questions, result) {
|
|
237
|
+
const content = {};
|
|
238
|
+
for (const question of questions) {
|
|
239
|
+
const answer = result.answers[question.id];
|
|
240
|
+
if (!answer)
|
|
241
|
+
continue;
|
|
242
|
+
if (question.allowMultiple) {
|
|
243
|
+
const selected = Array.isArray(answer.selected) ? answer.selected : [answer.selected];
|
|
244
|
+
content[question.id] = selected;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
const selected = Array.isArray(answer.selected) ? answer.selected[0] ?? "" : answer.selected;
|
|
248
|
+
content[question.id] = selected;
|
|
249
|
+
if (question.noteFieldId && selected === OTHER_OPTION_LABEL && answer.notes.trim().length > 0) {
|
|
250
|
+
content[question.noteFieldId] = answer.notes.trim();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return content;
|
|
254
|
+
}
|
|
255
|
+
function buildElicitationPromptTitle(request, question) {
|
|
256
|
+
const parts = [
|
|
257
|
+
request.serverName ? `[${request.serverName}]` : "",
|
|
258
|
+
question.header,
|
|
259
|
+
question.question,
|
|
260
|
+
].filter((part) => part && part.trim().length > 0);
|
|
261
|
+
return parts.join("\n\n");
|
|
262
|
+
}
|
|
263
|
+
async function promptElicitationWithDialogs(request, questions, ui, signal) {
|
|
264
|
+
const content = {};
|
|
265
|
+
for (const question of questions) {
|
|
266
|
+
const title = buildElicitationPromptTitle(request, question);
|
|
267
|
+
if (question.allowMultiple) {
|
|
268
|
+
const selected = await ui.select(title, question.options.map((option) => option.label), {
|
|
269
|
+
allowMultiple: true,
|
|
270
|
+
signal,
|
|
271
|
+
});
|
|
272
|
+
if (Array.isArray(selected)) {
|
|
273
|
+
if (selected.length === 0)
|
|
274
|
+
return { action: "cancel" };
|
|
275
|
+
content[question.id] = selected;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (typeof selected === "string" && selected.length > 0) {
|
|
279
|
+
content[question.id] = [selected];
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
return { action: "cancel" };
|
|
283
|
+
}
|
|
284
|
+
const selected = await ui.select(title, [...question.options.map((option) => option.label), OTHER_OPTION_LABEL], { signal });
|
|
285
|
+
if (typeof selected !== "string" || selected.length === 0) {
|
|
286
|
+
return { action: "cancel" };
|
|
287
|
+
}
|
|
288
|
+
content[question.id] = selected;
|
|
289
|
+
if (question.noteFieldId && selected === OTHER_OPTION_LABEL) {
|
|
290
|
+
const note = await ui.input(`${question.header} note`, "Explain your answer", { signal });
|
|
291
|
+
if (note === undefined)
|
|
292
|
+
return { action: "cancel" };
|
|
293
|
+
if (note.trim().length > 0) {
|
|
294
|
+
content[question.noteFieldId] = note.trim();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return { action: "accept", content };
|
|
299
|
+
}
|
|
300
|
+
function buildTextInputPromptTitle(request, field) {
|
|
301
|
+
const parts = [
|
|
302
|
+
request.serverName ? `[${request.serverName}]` : "",
|
|
303
|
+
field.title,
|
|
304
|
+
field.description,
|
|
305
|
+
].filter((part) => typeof part === "string" && part.trim().length > 0);
|
|
306
|
+
return parts.join("\n\n");
|
|
307
|
+
}
|
|
308
|
+
function buildTextInputPlaceholder(field) {
|
|
309
|
+
const desc = field.description.trim();
|
|
310
|
+
if (!desc)
|
|
311
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
312
|
+
const formatLine = desc
|
|
313
|
+
.split(/\r?\n/)
|
|
314
|
+
.map((line) => line.trim())
|
|
315
|
+
.find((line) => /^format:/i.test(line));
|
|
316
|
+
if (!formatLine)
|
|
317
|
+
return field.required ? "Required" : "Leave empty to skip";
|
|
318
|
+
const hint = formatLine.replace(/^format:\s*/i, "").trim();
|
|
319
|
+
return hint.length > 0 ? hint : field.required ? "Required" : "Leave empty to skip";
|
|
320
|
+
}
|
|
321
|
+
async function promptTextInputElicitation(request, fields, ui, signal) {
|
|
322
|
+
const content = {};
|
|
323
|
+
for (const field of fields) {
|
|
324
|
+
const value = await ui.input(buildTextInputPromptTitle(request, field), buildTextInputPlaceholder(field), { signal, ...(field.secure ? { secure: true } : {}) });
|
|
325
|
+
if (value === undefined) {
|
|
326
|
+
return { action: "cancel" };
|
|
327
|
+
}
|
|
328
|
+
content[field.id] = value;
|
|
329
|
+
}
|
|
330
|
+
return { action: "accept", content };
|
|
331
|
+
}
|
|
332
|
+
export function createClaudeCodeElicitationHandler(ui) {
|
|
333
|
+
if (!ui)
|
|
334
|
+
return undefined;
|
|
335
|
+
return async (request, { signal }) => {
|
|
336
|
+
if (request.mode === "url") {
|
|
337
|
+
return { action: "decline" };
|
|
338
|
+
}
|
|
339
|
+
const questions = parseAskUserQuestionsElicitation(request);
|
|
340
|
+
if (questions) {
|
|
341
|
+
const interviewResult = await showInterviewRound(questions, { signal }, { ui }).catch(() => undefined);
|
|
342
|
+
if (interviewResult && Object.keys(interviewResult.answers).length > 0) {
|
|
343
|
+
return {
|
|
344
|
+
action: "accept",
|
|
345
|
+
content: roundResultToElicitationContent(questions, interviewResult),
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return promptElicitationWithDialogs(request, questions, ui, signal);
|
|
349
|
+
}
|
|
350
|
+
const textFields = parseTextInputElicitation(request);
|
|
351
|
+
if (textFields) {
|
|
352
|
+
return promptTextInputElicitation(request, textFields, ui, signal);
|
|
353
|
+
}
|
|
354
|
+
return { action: "decline" };
|
|
355
|
+
};
|
|
356
|
+
}
|
|
127
357
|
// ---------------------------------------------------------------------------
|
|
128
358
|
// SDK options builder
|
|
129
359
|
// ---------------------------------------------------------------------------
|
|
@@ -133,8 +363,9 @@ export function makeStreamExhaustedErrorMessage(model, lastTextContent) {
|
|
|
133
363
|
* Extracted for testability — callers can verify session persistence,
|
|
134
364
|
* beta flags, and other configuration without mocking the full SDK.
|
|
135
365
|
*/
|
|
136
|
-
export function buildSdkOptions(modelId, prompt) {
|
|
366
|
+
export function buildSdkOptions(modelId, prompt, extraOptions = {}) {
|
|
137
367
|
const mcpServers = buildWorkflowMcpServers();
|
|
368
|
+
const disallowedTools = ["AskUserQuestion"];
|
|
138
369
|
return {
|
|
139
370
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
140
371
|
model: modelId,
|
|
@@ -145,8 +376,10 @@ export function buildSdkOptions(modelId, prompt) {
|
|
|
145
376
|
allowDangerouslySkipPermissions: true,
|
|
146
377
|
settingSources: ["project"],
|
|
147
378
|
systemPrompt: { type: "preset", preset: "claude_code" },
|
|
379
|
+
disallowedTools,
|
|
148
380
|
...(mcpServers ? { mcpServers } : {}),
|
|
149
381
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
382
|
+
...extraOptions,
|
|
150
383
|
};
|
|
151
384
|
}
|
|
152
385
|
function normalizeToolResultContent(content) {
|
|
@@ -227,9 +460,9 @@ export function extractToolResultsFromSdkUserMessage(message) {
|
|
|
227
460
|
}
|
|
228
461
|
return extracted;
|
|
229
462
|
}
|
|
230
|
-
function
|
|
231
|
-
for (const block of
|
|
232
|
-
if (block.type !== "toolCall")
|
|
463
|
+
function attachExternalResultsToToolBlocks(toolBlocks, toolResultsById) {
|
|
464
|
+
for (const block of toolBlocks) {
|
|
465
|
+
if (block.type !== "toolCall" && block.type !== "serverToolUse")
|
|
233
466
|
continue;
|
|
234
467
|
const externalResult = toolResultsById.get(block.id);
|
|
235
468
|
if (!externalResult)
|
|
@@ -258,8 +491,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
258
491
|
/** Track the last text content seen across all assistant turns for the final message. */
|
|
259
492
|
let lastTextContent = "";
|
|
260
493
|
let lastThinkingContent = "";
|
|
261
|
-
/** Collect tool
|
|
262
|
-
const
|
|
494
|
+
/** Collect tool blocks from intermediate SDK turns for tool execution rendering. */
|
|
495
|
+
const intermediateToolBlocks = [];
|
|
263
496
|
/** Preserve real external tool results from Claude Code's synthetic user messages. */
|
|
264
497
|
const toolResultsById = new Map();
|
|
265
498
|
try {
|
|
@@ -272,7 +505,11 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
272
505
|
options.signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
273
506
|
}
|
|
274
507
|
const prompt = buildPromptFromContext(context);
|
|
275
|
-
const sdkOpts = buildSdkOptions(modelId, prompt
|
|
508
|
+
const sdkOpts = buildSdkOptions(modelId, prompt, typeof options?.extensionUIContext === "object"
|
|
509
|
+
? {
|
|
510
|
+
onElicitation: createClaudeCodeElicitationHandler(options?.extensionUIContext),
|
|
511
|
+
}
|
|
512
|
+
: {});
|
|
276
513
|
const queryResult = sdk.query({
|
|
277
514
|
prompt,
|
|
278
515
|
options: {
|
|
@@ -343,9 +580,9 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
343
580
|
else if (block.type === "thinking" && block.thinking) {
|
|
344
581
|
lastThinkingContent = block.thinking;
|
|
345
582
|
}
|
|
346
|
-
else if (block.type === "toolCall") {
|
|
347
|
-
// Collect tool
|
|
348
|
-
|
|
583
|
+
else if (block.type === "toolCall" || block.type === "serverToolUse") {
|
|
584
|
+
// Collect tool blocks for externalToolExecution rendering
|
|
585
|
+
intermediateToolBlocks.push(block);
|
|
349
586
|
}
|
|
350
587
|
}
|
|
351
588
|
}
|
|
@@ -354,25 +591,35 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
354
591
|
for (const { toolUseId, result } of extractToolResultsFromSdkUserMessage(msg)) {
|
|
355
592
|
toolResultsById.set(toolUseId, result);
|
|
356
593
|
}
|
|
357
|
-
|
|
594
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
358
595
|
// Push a synthetic toolcall_end for each tool call from this turn
|
|
359
596
|
// so the TUI can render tool results in real-time during the SDK
|
|
360
597
|
// session instead of waiting until the entire session completes.
|
|
361
598
|
if (builder) {
|
|
362
599
|
for (const block of builder.message.content) {
|
|
363
|
-
if (block.type !== "toolCall")
|
|
364
|
-
continue;
|
|
365
600
|
const extResult = block.externalResult;
|
|
366
601
|
if (!extResult)
|
|
367
602
|
continue;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
603
|
+
const contentIndex = builder.message.content.indexOf(block);
|
|
604
|
+
if (contentIndex < 0)
|
|
605
|
+
continue;
|
|
606
|
+
// Push synthetic completion events with result attached so the
|
|
607
|
+
// chat-controller can update pending ToolExecutionComponents.
|
|
608
|
+
if (block.type === "toolCall") {
|
|
609
|
+
stream.push({
|
|
610
|
+
type: "toolcall_end",
|
|
611
|
+
contentIndex,
|
|
612
|
+
toolCall: block,
|
|
613
|
+
partial: builder.message,
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
else if (block.type === "serverToolUse") {
|
|
617
|
+
stream.push({
|
|
618
|
+
type: "server_tool_use",
|
|
619
|
+
contentIndex,
|
|
620
|
+
partial: builder.message,
|
|
621
|
+
});
|
|
622
|
+
}
|
|
376
623
|
}
|
|
377
624
|
}
|
|
378
625
|
builder = null;
|
|
@@ -386,8 +633,8 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
386
633
|
// events for proper TUI rendering, followed by the text response.
|
|
387
634
|
const finalContent = [];
|
|
388
635
|
// Add tool calls from intermediate turns first (renders above text)
|
|
389
|
-
|
|
390
|
-
finalContent.push(...
|
|
636
|
+
attachExternalResultsToToolBlocks(intermediateToolBlocks, toolResultsById);
|
|
637
|
+
finalContent.push(...intermediateToolBlocks);
|
|
391
638
|
// Add text/thinking from the last turn
|
|
392
639
|
if (builder && builder.message.content.length > 0) {
|
|
393
640
|
for (const block of builder.message.content) {
|
|
@@ -93,7 +93,7 @@ export function detectDestination(basePath) {
|
|
|
93
93
|
async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guidance) {
|
|
94
94
|
if (!ctx.hasUI)
|
|
95
95
|
return null;
|
|
96
|
-
|
|
96
|
+
const customResult = await ctx.ui.custom((tui, theme, _kb, done) => {
|
|
97
97
|
let value = "";
|
|
98
98
|
let cachedLines;
|
|
99
99
|
const editorTheme = {
|
|
@@ -178,6 +178,22 @@ async function collectOneSecret(ctx, pageIndex, totalPages, keyName, hint, guida
|
|
|
178
178
|
handleInput,
|
|
179
179
|
};
|
|
180
180
|
});
|
|
181
|
+
// RPC/web surfaces may not implement ctx.ui.custom(). Fall back to a
|
|
182
|
+
// standard input prompt so users can still provide the secret.
|
|
183
|
+
if (customResult !== undefined) {
|
|
184
|
+
return customResult;
|
|
185
|
+
}
|
|
186
|
+
if (typeof ctx.ui?.input !== "function") {
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
const inputTitle = `Secure value for ${keyName} (${pageIndex + 1}/${totalPages})`;
|
|
190
|
+
const inputPlaceholder = hint || "Enter secret value";
|
|
191
|
+
const inputResult = await ctx.ui.input(inputTitle, inputPlaceholder, { secure: true });
|
|
192
|
+
if (typeof inputResult !== "string") {
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
const trimmed = inputResult.trim();
|
|
196
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
181
197
|
}
|
|
182
198
|
/**
|
|
183
199
|
* Exported wrapper around collectOneSecret for testing.
|
|
@@ -8,10 +8,17 @@ import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
|
|
|
8
8
|
import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet } from "./model-router.js";
|
|
9
9
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
10
10
|
import { unitPhaseLabel } from "./auto-dashboard.js";
|
|
11
|
-
export function resolvePreferredModelConfig(unitType, autoModeStartModel
|
|
11
|
+
export function resolvePreferredModelConfig(unitType, autoModeStartModel,
|
|
12
|
+
/** When false, only return explicit per-phase model configs — do not
|
|
13
|
+
* synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
|
|
14
|
+
isAutoMode = true) {
|
|
12
15
|
const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
13
16
|
if (explicitConfig)
|
|
14
17
|
return explicitConfig;
|
|
18
|
+
// In interactive mode, don't synthesize a routing-based model config.
|
|
19
|
+
// The user's session model (/model) should be used as-is (#3962).
|
|
20
|
+
if (!isAutoMode)
|
|
21
|
+
return undefined;
|
|
15
22
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
16
23
|
if (!routingConfig.enabled || !routingConfig.tier_models)
|
|
17
24
|
return undefined;
|
|
@@ -34,14 +41,23 @@ export function resolvePreferredModelConfig(unitType, autoModeStartModel) {
|
|
|
34
41
|
*
|
|
35
42
|
* Returns routing metadata for metrics tracking.
|
|
36
43
|
*/
|
|
37
|
-
export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel, retryContext
|
|
38
|
-
|
|
44
|
+
export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel, retryContext,
|
|
45
|
+
/** When false (interactive/guided-flow), skip dynamic routing and use the session model.
|
|
46
|
+
* Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
|
|
47
|
+
isAutoMode = true) {
|
|
48
|
+
const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
|
|
39
49
|
let routing = null;
|
|
40
50
|
let appliedModel = null;
|
|
41
51
|
if (modelConfig) {
|
|
42
52
|
const availableModels = ctx.modelRegistry.getAvailable();
|
|
43
53
|
// ─── Dynamic Model Routing ─────────────────────────────────────────
|
|
54
|
+
// Dynamic routing (complexity-based downgrading) only applies in auto-mode.
|
|
55
|
+
// Interactive/guided-flow dispatches use the user's session model directly,
|
|
56
|
+
// respecting their /model selection without silent downgrades (#3962).
|
|
44
57
|
const routingConfig = resolveDynamicRoutingConfig();
|
|
58
|
+
if (!isAutoMode) {
|
|
59
|
+
routingConfig.enabled = false;
|
|
60
|
+
}
|
|
45
61
|
let effectiveModelConfig = modelConfig;
|
|
46
62
|
let routingTierLabel = "";
|
|
47
63
|
// Disable routing for flat-rate providers like GitHub Copilot (#3453).
|
|
@@ -85,9 +101,8 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
|
|
|
85
101
|
const escalated = escalateTier(retryContext.previousTier);
|
|
86
102
|
if (escalated) {
|
|
87
103
|
classification = { ...classification, tier: escalated, reason: "escalated after failure" };
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
104
|
+
// Always notify on tier escalation — model changes should be visible (#3962)
|
|
105
|
+
ctx.ui.notify(`Tier escalation: ${retryContext.previousTier} → ${escalated} (retry after failure)`, "info");
|
|
91
106
|
}
|
|
92
107
|
}
|
|
93
108
|
// Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
|
|
@@ -139,19 +154,18 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
|
|
|
139
154
|
primary: routingResult.modelId,
|
|
140
155
|
fallbacks: routingResult.fallbacks,
|
|
141
156
|
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
157
|
+
// Always notify on model downgrade — users should see when their
|
|
158
|
+
// model selection is overridden, not just in verbose mode (#3962).
|
|
159
|
+
if (routingResult.selectionMethod === "capability-scored" && routingResult.capabilityScores) {
|
|
160
|
+
const tierLbl = tierLabel(classification.tier);
|
|
161
|
+
const scores = Object.entries(routingResult.capabilityScores)
|
|
162
|
+
.sort(([, a], [, b]) => b - a)
|
|
163
|
+
.map(([id, score]) => `${id}: ${score.toFixed(1)}`)
|
|
164
|
+
.join(", ");
|
|
165
|
+
ctx.ui.notify(`Dynamic routing [${tierLbl}]: ${routingResult.modelId} (capability-scored) — ${scores}`, "info");
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
ctx.ui.notify(`Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`, "info");
|
|
155
169
|
}
|
|
156
170
|
}
|
|
157
171
|
routingTierLabel = ` [${tierLabel(classification.tier)}]`;
|
|
@@ -876,7 +876,7 @@ export async function buildDiscussMilestonePrompt(mid, midTitle, base) {
|
|
|
876
876
|
milestoneId: mid,
|
|
877
877
|
milestoneTitle: midTitle,
|
|
878
878
|
inlinedTemplates: discussTemplates,
|
|
879
|
-
structuredQuestionsAvailable: "
|
|
879
|
+
structuredQuestionsAvailable: "false",
|
|
880
880
|
commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
|
|
881
881
|
fastPathInstruction: "",
|
|
882
882
|
});
|
|
@@ -1319,7 +1319,9 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1319
1319
|
try {
|
|
1320
1320
|
const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
|
|
1321
1321
|
if (isDbAvailable()) {
|
|
1322
|
-
sliceIds = getMilestoneSlices(mid)
|
|
1322
|
+
sliceIds = getMilestoneSlices(mid)
|
|
1323
|
+
.filter(s => s.status !== "skipped")
|
|
1324
|
+
.map(s => s.id);
|
|
1323
1325
|
}
|
|
1324
1326
|
}
|
|
1325
1327
|
catch (err) {
|
|
@@ -1415,7 +1417,9 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
|
|
|
1415
1417
|
try {
|
|
1416
1418
|
const { isDbAvailable, getMilestoneSlices } = await import("./gsd-db.js");
|
|
1417
1419
|
if (isDbAvailable()) {
|
|
1418
|
-
valSliceIds = getMilestoneSlices(mid)
|
|
1420
|
+
valSliceIds = getMilestoneSlices(mid)
|
|
1421
|
+
.filter(s => s.status !== "skipped")
|
|
1422
|
+
.map(s => s.id);
|
|
1419
1423
|
}
|
|
1420
1424
|
}
|
|
1421
1425
|
catch (err) {
|
|
@@ -38,7 +38,7 @@ import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync, } fro
|
|
|
38
38
|
import { join } from "node:path";
|
|
39
39
|
import { sep as pathSep } from "node:path";
|
|
40
40
|
import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
|
|
41
|
-
import { resolveDefaultSessionModel } from "./preferences-models.js";
|
|
41
|
+
import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
|
|
42
42
|
/**
|
|
43
43
|
* Bootstrap a fresh auto-mode session. Handles everything from git init
|
|
44
44
|
* through secrets collection, returning when ready for the first
|
|
@@ -248,17 +248,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
248
248
|
logWarning("engine", `mkdir failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const result = ensureProjectWorkflowMcpConfig(base);
|
|
255
|
-
if (result.status !== "unchanged") {
|
|
256
|
-
ctx.ui.notify(`Claude Code MCP prepared at ${result.configPath}`, "info");
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
catch (err) {
|
|
260
|
-
ctx.ui.notify(`Claude Code MCP prep failed: ${err instanceof Error ? err.message : String(err)}`, "warning");
|
|
261
|
-
}
|
|
251
|
+
{
|
|
252
|
+
const { prepareWorkflowMcpForProject } = await import("./workflow-mcp-auto-prep.js");
|
|
253
|
+
prepareWorkflowMcpForProject(ctx, base);
|
|
262
254
|
}
|
|
263
255
|
// Initialize GitServiceImpl
|
|
264
256
|
s.gitService = new GitServiceImpl(s.basePath, loadEffectiveGSDPreferences()?.preferences?.git ?? {});
|
|
@@ -609,6 +601,30 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
609
601
|
? `Will loop through ${pendingCount} milestones.`
|
|
610
602
|
: "Will loop until milestone complete.";
|
|
611
603
|
ctx.ui.notify(`${modeLabel} started. ${scopeMsg}`, "info");
|
|
604
|
+
// Show dynamic routing status so users know upfront if models will be
|
|
605
|
+
// downgraded for simple tasks (#3962).
|
|
606
|
+
// Use the same effective logic as selectAndApplyModel: check flat-rate
|
|
607
|
+
// provider suppression and resolve the actual ceiling model.
|
|
608
|
+
const routingConfig = resolveDynamicRoutingConfig();
|
|
609
|
+
const startModelLabel = s.autoModeStartModel
|
|
610
|
+
? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
|
|
611
|
+
: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
|
|
612
|
+
// Flat-rate providers (e.g. GitHub Copilot, claude-code) suppress routing
|
|
613
|
+
// at dispatch time (#3453) — reflect that in the banner.
|
|
614
|
+
const { isFlatRateProvider } = await import("./auto-model-selection.js");
|
|
615
|
+
const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
|
|
616
|
+
const effectivelyEnabled = routingConfig.enabled
|
|
617
|
+
&& !(effectiveProvider && isFlatRateProvider(effectiveProvider));
|
|
618
|
+
// The actual ceiling may come from tier_models.heavy, not the start model.
|
|
619
|
+
const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
|
|
620
|
+
? routingConfig.tier_models.heavy
|
|
621
|
+
: startModelLabel;
|
|
622
|
+
if (effectivelyEnabled) {
|
|
623
|
+
ctx.ui.notify(`Dynamic routing: enabled — simple tasks may use cheaper models (ceiling: ${effectiveCeiling})`, "info");
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
ctx.ui.notify(`Dynamic routing: disabled — all tasks will use ${startModelLabel}`, "info");
|
|
627
|
+
}
|
|
612
628
|
updateSessionLock(lockBase(), "starting", s.currentMilestoneId ?? "unknown");
|
|
613
629
|
writeLock(lockBase(), "starting", s.currentMilestoneId ?? "unknown");
|
|
614
630
|
// Secrets collection gate
|
|
@@ -37,9 +37,9 @@ import { getRtkSessionSavings } from "../shared/rtk-session-stats.js";
|
|
|
37
37
|
import { initMetrics, resetMetrics, getLedger, getProjectTotals, formatCost, formatTokenCount, } from "./metrics.js";
|
|
38
38
|
import { logWarning } from "./workflow-logger.js";
|
|
39
39
|
import { homedir } from "node:os";
|
|
40
|
-
import { join
|
|
40
|
+
import { join } from "node:path";
|
|
41
|
+
import { pathToFileURL } from "node:url";
|
|
41
42
|
import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
42
|
-
import { createRequire } from "node:module";
|
|
43
43
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
44
44
|
import { autoCommitCurrentBranch, captureIntegrationBranch, detectWorktreeName, getCurrentBranch, getMainBranch, setActiveMilestoneId, } from "./worktree.js";
|
|
45
45
|
import { GitServiceImpl } from "./git-service.js";
|
|
@@ -1021,13 +1021,17 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1021
1021
|
restoreHookState(s.basePath);
|
|
1022
1022
|
// Re-sync managed resources on resume so long-lived auto sessions pick up
|
|
1023
1023
|
// bundled extension updates before resume-time verification/state logic runs.
|
|
1024
|
+
// GSD_PKG_ROOT is set by loader.ts and points to the gsd-pi package root.
|
|
1025
|
+
// The relative import ("../../../resource-loader.js") only works from the source
|
|
1026
|
+
// tree; deployed extensions live at ~/.gsd/agent/extensions/gsd/ where the
|
|
1027
|
+
// relative path resolves to ~/.gsd/agent/resource-loader.js which doesn't exist.
|
|
1028
|
+
// Using GSD_PKG_ROOT constructs a correct absolute path in both contexts (#3949).
|
|
1024
1029
|
const agentDir = process.env.GSD_CODING_AGENT_DIR || join(process.env.GSD_HOME || homedir(), ".gsd", "agent");
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
const
|
|
1030
|
-
const { initResources } = await import(join(pkgRoot, "dist", "resource-loader.js"));
|
|
1030
|
+
const pkgRoot = process.env.GSD_PKG_ROOT;
|
|
1031
|
+
const resourceLoaderPath = pkgRoot
|
|
1032
|
+
? pathToFileURL(join(pkgRoot, "dist", "resource-loader.js")).href
|
|
1033
|
+
: new URL("../../../resource-loader.js", import.meta.url).href;
|
|
1034
|
+
const { initResources } = await import(resourceLoaderPath);
|
|
1031
1035
|
initResources(agentDir);
|
|
1032
1036
|
// Open the project DB before rebuild/derive so resume uses DB-backed
|
|
1033
1037
|
// state instead of falling back to stale markdown parsing (#2940).
|
|
@@ -39,6 +39,8 @@ export function registerHooks(pi) {
|
|
|
39
39
|
resetToolCallLoopGuard();
|
|
40
40
|
resetAskUserQuestionsCache();
|
|
41
41
|
await syncServiceTierStatus(ctx);
|
|
42
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
43
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
42
44
|
// Apply show_token_cost preference (#1515)
|
|
43
45
|
try {
|
|
44
46
|
const { loadEffectiveGSDPreferences } = await import("../preferences.js");
|
|
@@ -78,6 +80,8 @@ export function registerHooks(pi) {
|
|
|
78
80
|
resetAskUserQuestionsCache();
|
|
79
81
|
clearDiscussionFlowState();
|
|
80
82
|
await syncServiceTierStatus(ctx);
|
|
83
|
+
const { prepareWorkflowMcpForProject } = await import("../workflow-mcp-auto-prep.js");
|
|
84
|
+
prepareWorkflowMcpForProject(ctx, process.cwd());
|
|
81
85
|
loadToolApiKeys();
|
|
82
86
|
});
|
|
83
87
|
pi.on("before_agent_start", async (event, ctx) => {
|