gsd-pi 2.73.0 → 2.73.1-dev.06e4302
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/cli-web-branch.d.ts +4 -3
- package/dist/cli-web-branch.js +10 -7
- package/dist/cli.js +99 -253
- package/dist/help-text.js +1 -1
- package/dist/logo.d.ts +1 -1
- package/dist/logo.js +1 -1
- package/dist/onboarding.js +59 -53
- package/dist/resource-loader.js +2 -2
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +68 -4
- package/dist/resources/extensions/gsd/auto/phases.js +15 -9
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -6
- package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
- package/dist/resources/extensions/gsd/auto-post-unit.js +41 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -6
- package/dist/resources/extensions/gsd/auto-start.js +23 -6
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +13 -0
- package/dist/resources/extensions/gsd/auto-verification.js +88 -3
- package/dist/resources/extensions/gsd/auto.js +34 -9
- package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +8 -2
- package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/dist/resources/extensions/gsd/gsd-db.js +36 -2
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
- package/dist/resources/extensions/gsd/notification-widget.js +2 -2
- package/dist/resources/extensions/gsd/preferences-models.js +43 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
- package/dist/resources/extensions/gsd/state.js +61 -14
- package/dist/startup-model-validation.js +8 -5
- package/dist/update-check.d.ts +1 -0
- package/dist/update-check.js +13 -5
- package/dist/update-cmd.js +4 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- 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 +2 -2
- 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 +2 -2
- 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 +4 -4
- 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 +12 -12
- 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-manifest.json +5 -5
- 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/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/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/package.json +1 -2
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/overflow.js +12 -0
- package/packages/pi-ai/dist/utils/overflow.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts +2 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js +50 -0
- package/packages/pi-ai/dist/utils/tests/overflow.test.js.map +1 -0
- package/packages/pi-ai/src/index.ts +4 -0
- package/packages/pi-ai/src/utils/overflow.ts +14 -1
- package/packages/pi-ai/src/utils/tests/overflow.test.ts +58 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +313 -8
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +5 -5
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +45 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -68
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +61 -28
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts +2 -1
- 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 +9 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +52 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- 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 +116 -25
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -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 +11 -3
- 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/auth-storage.test.ts +38 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +355 -8
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +5 -5
- package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +50 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -70
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +74 -32
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +73 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +9 -3
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +136 -30
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +11 -3
- package/packages/pi-tui/dist/__tests__/tui.test.js +60 -1
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +8 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +32 -3
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/tui.test.ts +76 -1
- package/packages/pi-tui/src/tui.ts +31 -3
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +107 -5
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +111 -2
- package/src/resources/extensions/gsd/auto/phases.ts +22 -9
- package/src/resources/extensions/gsd/auto-dispatch.ts +15 -4
- package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
- package/src/resources/extensions/gsd/auto-post-unit.ts +47 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
- package/src/resources/extensions/gsd/auto-start.ts +30 -6
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +17 -0
- package/src/resources/extensions/gsd/auto-verification.ts +98 -3
- package/src/resources/extensions/gsd/auto.ts +36 -14
- package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +8 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +8 -2
- package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
- package/src/resources/extensions/gsd/gsd-db.ts +52 -2
- package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
- package/src/resources/extensions/gsd/notification-widget.ts +2 -2
- package/src/resources/extensions/gsd/preferences-models.ts +41 -0
- package/src/resources/extensions/gsd/preferences-types.ts +12 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
- package/src/resources/extensions/gsd/state.ts +71 -15
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +68 -8
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +179 -0
- 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/{KSZ2dcC3p4z6lOmUpPpzr → RXD20AQgB9BHSQJ07MDdd}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → RXD20AQgB9BHSQJ07MDdd}/_ssgManifest.js +0 -0
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
mergePendingToolCalls,
|
|
11
11
|
resolveClaudePermissionMode,
|
|
12
12
|
buildPromptFromContext,
|
|
13
|
+
buildSdkQueryPrompt,
|
|
13
14
|
buildSdkOptions,
|
|
14
15
|
createClaudeCodeElicitationHandler,
|
|
16
|
+
extractImageBlocksFromContext,
|
|
15
17
|
extractToolResultsFromSdkUserMessage,
|
|
16
18
|
getClaudeLookupCommand,
|
|
17
19
|
parseAskUserQuestionsElicitation,
|
|
@@ -167,6 +169,92 @@ describe("stream-adapter — full context prompt (#2859)", () => {
|
|
|
167
169
|
});
|
|
168
170
|
});
|
|
169
171
|
|
|
172
|
+
describe("stream-adapter — image prompt forwarding (#4183)", () => {
|
|
173
|
+
test("extractImageBlocksFromContext maps user image parts to Anthropic base64 image blocks", () => {
|
|
174
|
+
const context: Context = {
|
|
175
|
+
messages: [
|
|
176
|
+
{
|
|
177
|
+
role: "user",
|
|
178
|
+
content: [
|
|
179
|
+
{ type: "text", text: "look" },
|
|
180
|
+
{
|
|
181
|
+
type: "image",
|
|
182
|
+
data: "data:image/png;base64,abc123",
|
|
183
|
+
mimeType: "image/png",
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
} as Message,
|
|
187
|
+
],
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const imageBlocks = extractImageBlocksFromContext(context);
|
|
191
|
+
assert.deepEqual(imageBlocks, [
|
|
192
|
+
{
|
|
193
|
+
type: "image",
|
|
194
|
+
source: {
|
|
195
|
+
type: "base64",
|
|
196
|
+
media_type: "image/png",
|
|
197
|
+
data: "abc123",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
]);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("buildSdkQueryPrompt returns plain string when no images exist in context", () => {
|
|
204
|
+
const context: Context = {
|
|
205
|
+
messages: [{ role: "user", content: "hello" } as Message],
|
|
206
|
+
};
|
|
207
|
+
const textPrompt = buildPromptFromContext(context);
|
|
208
|
+
|
|
209
|
+
const prompt = buildSdkQueryPrompt(context, textPrompt);
|
|
210
|
+
assert.equal(typeof prompt, "string");
|
|
211
|
+
assert.equal(prompt, textPrompt);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("buildSdkQueryPrompt wraps images and prompt text in an SDK user message iterable", async () => {
|
|
215
|
+
const context: Context = {
|
|
216
|
+
messages: [
|
|
217
|
+
{
|
|
218
|
+
role: "user",
|
|
219
|
+
content: [
|
|
220
|
+
{ type: "image", data: "ZmFrZQ==", mimeType: "image/jpeg" },
|
|
221
|
+
{ type: "text", text: "What is in this image?" },
|
|
222
|
+
],
|
|
223
|
+
} as Message,
|
|
224
|
+
],
|
|
225
|
+
};
|
|
226
|
+
const textPrompt = buildPromptFromContext(context);
|
|
227
|
+
|
|
228
|
+
const prompt = buildSdkQueryPrompt(context, textPrompt);
|
|
229
|
+
assert.notEqual(typeof prompt, "string");
|
|
230
|
+
assert.ok(prompt && typeof (prompt as any)[Symbol.asyncIterator] === "function");
|
|
231
|
+
|
|
232
|
+
const messages: any[] = [];
|
|
233
|
+
for await (const item of prompt as AsyncIterable<any>) {
|
|
234
|
+
messages.push(item);
|
|
235
|
+
}
|
|
236
|
+
assert.equal(messages.length, 1);
|
|
237
|
+
assert.deepEqual(messages[0], {
|
|
238
|
+
type: "user",
|
|
239
|
+
message: {
|
|
240
|
+
role: "user",
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: "image",
|
|
244
|
+
source: {
|
|
245
|
+
type: "base64",
|
|
246
|
+
media_type: "image/jpeg",
|
|
247
|
+
data: "ZmFrZQ==",
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{ type: "text", text: textPrompt },
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
parent_tool_use_id: null,
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
170
258
|
// ---------------------------------------------------------------------------
|
|
171
259
|
// Bug #4102 — transcript fabrication regression tests
|
|
172
260
|
// ---------------------------------------------------------------------------
|
|
@@ -343,6 +431,26 @@ describe("stream-adapter — session persistence (#2859)", () => {
|
|
|
343
431
|
);
|
|
344
432
|
});
|
|
345
433
|
|
|
434
|
+
test("buildSdkOptions maps reasoning to effort for adaptive Claude Code models (#3917)", () => {
|
|
435
|
+
const options = buildSdkOptions("claude-sonnet-4-6", "test", undefined, { reasoning: "high" });
|
|
436
|
+
assert.equal(options.effort, "high");
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test("buildSdkOptions upgrades xhigh reasoning to max for opus 4.6 (#3917)", () => {
|
|
440
|
+
const options = buildSdkOptions("claude-opus-4-6", "test", undefined, { reasoning: "xhigh" });
|
|
441
|
+
assert.equal(options.effort, "max");
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("buildSdkOptions omits effort when reasoning is undefined (#3917)", () => {
|
|
445
|
+
const options = buildSdkOptions("claude-sonnet-4-6", "test");
|
|
446
|
+
assert.equal("effort" in options, false);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test("buildSdkOptions omits effort for non-adaptive Claude models (#3917)", () => {
|
|
450
|
+
const options = buildSdkOptions("claude-sonnet-4-20250514", "test", undefined, { reasoning: "high" });
|
|
451
|
+
assert.equal("effort" in options, false);
|
|
452
|
+
});
|
|
453
|
+
|
|
346
454
|
test("buildSdkOptions includes workflow MCP server config when env is set", () => {
|
|
347
455
|
const prev = {
|
|
348
456
|
GSD_WORKFLOW_MCP_COMMAND: process.env.GSD_WORKFLOW_MCP_COMMAND,
|
|
@@ -774,11 +882,12 @@ describe("stream-adapter — MCP elicitation bridge", () => {
|
|
|
774
882
|
},
|
|
775
883
|
};
|
|
776
884
|
|
|
885
|
+
const secureValue = "ui-collected-value";
|
|
777
886
|
const inputCalls: Array<{ opts?: { secure?: boolean } }> = [];
|
|
778
887
|
const handler = createClaudeCodeElicitationHandler({
|
|
779
888
|
input: async (_title: string, _placeholder?: string, opts?: { secure?: boolean }) => {
|
|
780
889
|
inputCalls.push({ opts });
|
|
781
|
-
return
|
|
890
|
+
return secureValue;
|
|
782
891
|
},
|
|
783
892
|
} as any);
|
|
784
893
|
assert.ok(handler);
|
|
@@ -787,7 +896,7 @@ describe("stream-adapter — MCP elicitation bridge", () => {
|
|
|
787
896
|
assert.deepEqual(result, {
|
|
788
897
|
action: "accept",
|
|
789
898
|
content: {
|
|
790
|
-
TEST_SECURE_FIELD:
|
|
899
|
+
TEST_SECURE_FIELD: secureValue,
|
|
791
900
|
},
|
|
792
901
|
});
|
|
793
902
|
assert.equal(inputCalls.length, 1);
|
|
@@ -481,10 +481,13 @@ export async function runPreDispatch(
|
|
|
481
481
|
);
|
|
482
482
|
} else if (state.phase === "blocked") {
|
|
483
483
|
const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
deps.
|
|
484
|
+
// Pause instead of hard-stop so the session is resumable with `/gsd auto`.
|
|
485
|
+
// Hard-stop here was causing premature termination when slice dependencies
|
|
486
|
+
// were temporarily unresolvable (e.g. after reassessment added new slices).
|
|
487
|
+
await deps.pauseAuto(ctx, pi);
|
|
488
|
+
ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto to resume.`, "warning");
|
|
489
|
+
deps.sendDesktopNotification("GSD", blockerMsg, "warning", "attention", basename(s.originalBasePath || s.basePath));
|
|
490
|
+
deps.logCmuxEvent(prefs, blockerMsg, "warning");
|
|
488
491
|
} else {
|
|
489
492
|
const ids = incomplete.map((m: { id: string }) => m.id).join(", ");
|
|
490
493
|
const diag = `basePath=${s.basePath}, milestones=[${state.registry.map((m: { id: string; status: string }) => `${m.id}:${m.status}`).join(", ")}], phase=${state.phase}`;
|
|
@@ -583,13 +586,23 @@ export async function runPreDispatch(
|
|
|
583
586
|
return { action: "break", reason: "milestone-complete" };
|
|
584
587
|
}
|
|
585
588
|
|
|
586
|
-
// Terminal: blocked
|
|
589
|
+
// Terminal: blocked — pause instead of hard-stop so the session is resumable.
|
|
587
590
|
if (state.phase === "blocked") {
|
|
588
591
|
const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
592
|
+
if (s.currentUnit) {
|
|
593
|
+
await deps.closeoutUnit(
|
|
594
|
+
ctx,
|
|
595
|
+
s.basePath,
|
|
596
|
+
s.currentUnit.type,
|
|
597
|
+
s.currentUnit.id,
|
|
598
|
+
s.currentUnit.startedAt,
|
|
599
|
+
deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id),
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
await deps.pauseAuto(ctx, pi);
|
|
603
|
+
ctx.ui.notify(`${blockerMsg}. Fix and run /gsd auto to resume.`, "warning");
|
|
604
|
+
deps.sendDesktopNotification("GSD", blockerMsg, "warning", "attention", basename(s.originalBasePath || s.basePath));
|
|
605
|
+
deps.logCmuxEvent(prefs, blockerMsg, "warning");
|
|
593
606
|
debugLog("autoLoop", { phase: "exit", reason: "blocked" });
|
|
594
607
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "terminal", data: { reason: "blocked", blockers: state.blockers } });
|
|
595
608
|
return { action: "break", reason: "blocked" };
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
checkNeedsReassessment,
|
|
53
53
|
checkNeedsRunUat,
|
|
54
54
|
} from "./auto-prompts.js";
|
|
55
|
+
import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
|
|
55
56
|
|
|
56
57
|
// ─── Types ────────────────────────────────────────────────────────────────
|
|
57
58
|
|
|
@@ -306,8 +307,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
306
307
|
{
|
|
307
308
|
name: "reassess-roadmap (post-completion)",
|
|
308
309
|
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
309
|
-
if (prefs?.phases?.skip_reassess
|
|
310
|
-
|
|
310
|
+
if (prefs?.phases?.skip_reassess) return null;
|
|
311
|
+
// Default reassess_after_slice to true — reassessment after slice completion
|
|
312
|
+
// is essential for roadmap integrity. Opt-out via explicit `false`.
|
|
313
|
+
const reassessEnabled = prefs?.phases?.reassess_after_slice ?? true;
|
|
314
|
+
if (!reassessEnabled) return null;
|
|
311
315
|
const needsReassess = await checkNeedsReassessment(basePath, mid, state);
|
|
312
316
|
if (!needsReassess) return null;
|
|
313
317
|
return {
|
|
@@ -423,6 +427,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
423
427
|
midTitle,
|
|
424
428
|
researchReadySlices,
|
|
425
429
|
basePath,
|
|
430
|
+
resolveModelWithFallbacksForUnit("subagent")?.primary,
|
|
426
431
|
),
|
|
427
432
|
};
|
|
428
433
|
},
|
|
@@ -510,6 +515,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
510
515
|
sid,
|
|
511
516
|
sTitle,
|
|
512
517
|
basePath,
|
|
518
|
+
resolveModelWithFallbacksForUnit("subagent")?.primary,
|
|
513
519
|
),
|
|
514
520
|
};
|
|
515
521
|
},
|
|
@@ -548,6 +554,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
548
554
|
const sid = state.activeSlice.id;
|
|
549
555
|
const sTitle = state.activeSlice.title;
|
|
550
556
|
const maxParallel = reactiveConfig.max_parallel ?? 2;
|
|
557
|
+
const subagentModel = reactiveConfig.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
|
|
551
558
|
|
|
552
559
|
// Dry-run mode: max_parallel=1 means graph is derived and logged but
|
|
553
560
|
// execution remains sequential
|
|
@@ -618,6 +625,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
618
625
|
sTitle,
|
|
619
626
|
selected,
|
|
620
627
|
basePath,
|
|
628
|
+
subagentModel,
|
|
621
629
|
),
|
|
622
630
|
};
|
|
623
631
|
} catch (err) {
|
|
@@ -872,11 +880,14 @@ export async function resolveDispatch(
|
|
|
872
880
|
}
|
|
873
881
|
}
|
|
874
882
|
|
|
875
|
-
// No rule matched — unhandled phase
|
|
883
|
+
// No rule matched — unhandled phase.
|
|
884
|
+
// Use level "warning" so the loop pauses (resumable) instead of hard-stopping.
|
|
885
|
+
// Hard-stop here was causing premature termination for transient phase gaps
|
|
886
|
+
// (e.g. after reassessment modifies the roadmap and state needs re-derivation).
|
|
876
887
|
return {
|
|
877
888
|
action: "stop",
|
|
878
889
|
reason: `Unhandled phase "${ctx.state.phase}" — run /gsd doctor to diagnose.`,
|
|
879
|
-
level: "
|
|
890
|
+
level: "warning",
|
|
880
891
|
matchedRule: "<no-match>",
|
|
881
892
|
};
|
|
882
893
|
}
|
|
@@ -15,6 +15,7 @@ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabil
|
|
|
15
15
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
16
16
|
import { unitPhaseLabel } from "./auto-dashboard.js";
|
|
17
17
|
import { getSessionModelOverride } from "./session-model-override.js";
|
|
18
|
+
import { logWarning } from "./workflow-logger.js";
|
|
18
19
|
|
|
19
20
|
export interface ModelSelectionResult {
|
|
20
21
|
/** Routing metadata for metrics recording */
|
|
@@ -25,9 +26,7 @@ export interface ModelSelectionResult {
|
|
|
25
26
|
|
|
26
27
|
export function resolvePreferredModelConfig(
|
|
27
28
|
unitType: string,
|
|
28
|
-
autoModeStartModel: { provider: string; id: string } | null,
|
|
29
|
-
/** When false, only return explicit per-phase model configs — do not
|
|
30
|
-
* synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
|
|
29
|
+
autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null,
|
|
31
30
|
isAutoMode = true,
|
|
32
31
|
) {
|
|
33
32
|
const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
@@ -41,7 +40,7 @@ export function resolvePreferredModelConfig(
|
|
|
41
40
|
if (!routingConfig.enabled || !routingConfig.tier_models) return undefined;
|
|
42
41
|
|
|
43
42
|
// Don't synthesize a routing config for flat-rate providers (#3453).
|
|
44
|
-
if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider)) return undefined;
|
|
43
|
+
if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx)) return undefined;
|
|
45
44
|
|
|
46
45
|
const ceilingModel = routingConfig.tier_models.heavy
|
|
47
46
|
?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
|
|
@@ -68,7 +67,7 @@ export async function selectAndApplyModel(
|
|
|
68
67
|
basePath: string,
|
|
69
68
|
prefs: GSDPreferences | undefined,
|
|
70
69
|
verbose: boolean,
|
|
71
|
-
autoModeStartModel: { provider: string; id: string } | null,
|
|
70
|
+
autoModeStartModel: { provider: string; id: string; flatRateCtx?: FlatRateContext } | null,
|
|
72
71
|
retryContext?: { isRetry: boolean; previousTier?: string },
|
|
73
72
|
/** When false (interactive/guided-flow), skip dynamic routing and use the session model.
|
|
74
73
|
* Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
|
|
@@ -79,6 +78,17 @@ export async function selectAndApplyModel(
|
|
|
79
78
|
const effectiveSessionModelOverride = sessionModelOverride === undefined
|
|
80
79
|
? getSessionModelOverride(ctx.sessionManager.getSessionId())
|
|
81
80
|
: (sessionModelOverride ?? undefined);
|
|
81
|
+
// Enrich the start model with a flat-rate context up front so routing
|
|
82
|
+
// synthesis and the dispatch-time guard see the same signals (built-in
|
|
83
|
+
// list + user `flat_rate_providers` preference + externalCli auto-
|
|
84
|
+
// detection). The dispatch-time primary-model check below builds its
|
|
85
|
+
// own per-provider context when it has a resolved primary model.
|
|
86
|
+
if (autoModeStartModel) {
|
|
87
|
+
autoModeStartModel = {
|
|
88
|
+
...autoModeStartModel,
|
|
89
|
+
flatRateCtx: buildFlatRateContext(autoModeStartModel.provider, ctx, prefs),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
82
92
|
const modelConfig = effectiveSessionModelOverride
|
|
83
93
|
? undefined
|
|
84
94
|
: resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
|
|
@@ -107,12 +117,16 @@ export async function selectAndApplyModel(
|
|
|
107
117
|
if (routingConfig.enabled) {
|
|
108
118
|
const primaryModel = resolveModelId(modelConfig.primary, availableModels, ctx.model?.provider);
|
|
109
119
|
if (primaryModel) {
|
|
110
|
-
|
|
120
|
+
const primaryFlatRateCtx = buildFlatRateContext(primaryModel.provider, ctx, prefs);
|
|
121
|
+
if (isFlatRateProvider(primaryModel.provider, primaryFlatRateCtx)) {
|
|
111
122
|
routingConfig.enabled = false;
|
|
112
123
|
}
|
|
113
124
|
} else if (
|
|
114
|
-
(autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
|
|
115
|
-
|| (ctx.model?.provider && isFlatRateProvider(
|
|
125
|
+
(autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx))
|
|
126
|
+
|| (ctx.model?.provider && isFlatRateProvider(
|
|
127
|
+
ctx.model.provider,
|
|
128
|
+
buildFlatRateContext(ctx.model.provider, ctx, prefs),
|
|
129
|
+
))
|
|
116
130
|
) {
|
|
117
131
|
// Primary model unresolvable but provider signals indicate flat-rate —
|
|
118
132
|
// disable routing to prevent quality degradation.
|
|
@@ -416,8 +430,68 @@ export function resolveModelId<T extends { id: string; provider: string }>(
|
|
|
416
430
|
* Uses case-insensitive matching with alias support to prevent fail-open on
|
|
417
431
|
* provider naming variations (e.g. "copilot" vs "github-copilot").
|
|
418
432
|
*/
|
|
419
|
-
const
|
|
433
|
+
const BUILTIN_FLAT_RATE = new Set(["github-copilot", "copilot", "claude-code"]);
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Optional context that lets callers extend flat-rate detection beyond the
|
|
437
|
+
* hard-coded built-in list. Either signal on its own is enough to classify
|
|
438
|
+
* a provider as flat-rate.
|
|
439
|
+
*/
|
|
440
|
+
export interface FlatRateContext {
|
|
441
|
+
/**
|
|
442
|
+
* Auth mode for the specific provider being checked, as returned by
|
|
443
|
+
* `ctx.modelRegistry.getProviderAuthMode(provider)`. Any provider that
|
|
444
|
+
* wraps a local CLI (externalCli) is, by definition, a flat-rate
|
|
445
|
+
* subscription wrapper — every request costs the same regardless of
|
|
446
|
+
* model, so dynamic routing only degrades quality.
|
|
447
|
+
*/
|
|
448
|
+
authMode?: "apiKey" | "oauth" | "externalCli" | "none";
|
|
449
|
+
/**
|
|
450
|
+
* Case-insensitive list of extra provider IDs the user has declared as
|
|
451
|
+
* flat-rate via `preferences.flat_rate_providers`. Used for private
|
|
452
|
+
* subscription-backed proxies and enterprise-gated deployments that the
|
|
453
|
+
* built-in list doesn't know about.
|
|
454
|
+
*/
|
|
455
|
+
userFlatRate?: readonly string[];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export function isFlatRateProvider(provider: string, opts?: FlatRateContext): boolean {
|
|
459
|
+
const p = provider.toLowerCase();
|
|
460
|
+
if (BUILTIN_FLAT_RATE.has(p)) return true;
|
|
461
|
+
if (opts?.userFlatRate?.some(id => id.toLowerCase() === p)) return true;
|
|
462
|
+
if (opts?.authMode === "externalCli") return true;
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
420
465
|
|
|
421
|
-
|
|
422
|
-
|
|
466
|
+
/**
|
|
467
|
+
* Build a FlatRateContext for a given provider from live runtime state.
|
|
468
|
+
* Safe to call when ctx or prefs are undefined — missing pieces are
|
|
469
|
+
* treated as "no signal".
|
|
470
|
+
*/
|
|
471
|
+
export function buildFlatRateContext(
|
|
472
|
+
provider: string,
|
|
473
|
+
ctx?: { modelRegistry?: { getProviderAuthMode?: (p: string) => string } },
|
|
474
|
+
prefs?: { flat_rate_providers?: readonly string[] },
|
|
475
|
+
): FlatRateContext {
|
|
476
|
+
let authMode: FlatRateContext["authMode"];
|
|
477
|
+
const getAuthMode = ctx?.modelRegistry?.getProviderAuthMode;
|
|
478
|
+
if (typeof getAuthMode === "function") {
|
|
479
|
+
try {
|
|
480
|
+
const mode = getAuthMode(provider);
|
|
481
|
+
if (mode === "apiKey" || mode === "oauth" || mode === "externalCli" || mode === "none") {
|
|
482
|
+
authMode = mode;
|
|
483
|
+
}
|
|
484
|
+
} catch (err) {
|
|
485
|
+
// Registry lookup failure must never break flat-rate detection —
|
|
486
|
+
// fall through with authMode undefined and surface the cause.
|
|
487
|
+
logWarning(
|
|
488
|
+
"dispatch",
|
|
489
|
+
`flat-rate auth-mode lookup failed for ${provider}: ${err instanceof Error ? err.message : String(err)}`,
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return {
|
|
494
|
+
authMode,
|
|
495
|
+
userFlatRate: prefs?.flat_rate_providers,
|
|
496
|
+
};
|
|
423
497
|
}
|
|
@@ -104,6 +104,7 @@ import {
|
|
|
104
104
|
updateSliceProgressCache,
|
|
105
105
|
unitVerb,
|
|
106
106
|
hideFooter,
|
|
107
|
+
describeNextUnit,
|
|
107
108
|
} from "./auto-dashboard.js";
|
|
108
109
|
import { existsSync, unlinkSync } from "node:fs";
|
|
109
110
|
import { join } from "node:path";
|
|
@@ -233,6 +234,18 @@ export function detectRogueFileWrites(
|
|
|
233
234
|
return rogues;
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
export const STEP_COMPLETE_FALLBACK_MESSAGE =
|
|
238
|
+
"Step complete. Run /clear, then /gsd to continue (or /gsd auto to run continuously).";
|
|
239
|
+
|
|
240
|
+
export function buildStepCompleteMessage(nextState: import("./types.js").GSDState): string {
|
|
241
|
+
if (nextState.phase === "complete") {
|
|
242
|
+
return "Step complete — milestone finished. Run /gsd status to review, or start the next milestone.";
|
|
243
|
+
}
|
|
244
|
+
const next = describeNextUnit(nextState);
|
|
245
|
+
return `Step complete. Next: ${next.label}\n`
|
|
246
|
+
+ `Run /clear, then /gsd to continue (or /gsd auto to run continuously).`;
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
export interface PreVerificationOpts {
|
|
237
250
|
skipSettleDelay?: boolean;
|
|
238
251
|
skipWorktreeSync?: boolean;
|
|
@@ -619,6 +632,30 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
619
632
|
s.verificationRetryCount.set(retryKey, attempt);
|
|
620
633
|
|
|
621
634
|
if (attempt > MAX_VERIFICATION_RETRIES) {
|
|
635
|
+
// #4175: For complete-milestone, a blocker placeholder is harmful —
|
|
636
|
+
// the stub SUMMARY has no recovery value (milestone is terminal),
|
|
637
|
+
// it does not update DB status (so deriveState never advances),
|
|
638
|
+
// and it fools stopAuto's presence check into merging a milestone
|
|
639
|
+
// that was never legitimately completed. Pause auto-mode with a
|
|
640
|
+
// clear single failure signal and preserve the worktree branch.
|
|
641
|
+
if (s.currentUnit.type === "complete-milestone") {
|
|
642
|
+
debugLog("postUnit", {
|
|
643
|
+
phase: "artifact-verify-pause-complete-milestone",
|
|
644
|
+
unitType: s.currentUnit.type,
|
|
645
|
+
unitId: s.currentUnit.id,
|
|
646
|
+
attempt,
|
|
647
|
+
maxRetries: MAX_VERIFICATION_RETRIES,
|
|
648
|
+
});
|
|
649
|
+
s.verificationRetryCount.delete(retryKey);
|
|
650
|
+
s.pendingVerificationRetry = null;
|
|
651
|
+
ctx.ui.notify(
|
|
652
|
+
`Milestone ${s.currentUnit.id} verification failed after ${MAX_VERIFICATION_RETRIES} retries — worktree branch preserved. Re-run /gsd auto once blockers are resolved.`,
|
|
653
|
+
"error",
|
|
654
|
+
);
|
|
655
|
+
await pauseAuto(ctx, pi);
|
|
656
|
+
return "dispatched";
|
|
657
|
+
}
|
|
658
|
+
|
|
622
659
|
// Retries exhausted — write a blocker placeholder so the pipeline
|
|
623
660
|
// can advance past this stuck unit (#2653).
|
|
624
661
|
debugLog("postUnit", {
|
|
@@ -1025,8 +1062,17 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
|
|
|
1025
1062
|
}
|
|
1026
1063
|
}
|
|
1027
1064
|
|
|
1028
|
-
// Step mode → show wizard instead of dispatch
|
|
1065
|
+
// Step mode → show wizard instead of dispatch.
|
|
1066
|
+
// Without this notify(), /gsd in step mode finishes a unit and silently
|
|
1067
|
+
// exits the loop, leaving the user with no hint to /clear and /gsd again.
|
|
1029
1068
|
if (s.stepMode) {
|
|
1069
|
+
try {
|
|
1070
|
+
const nextState = await deriveState(s.basePath);
|
|
1071
|
+
ctx.ui.notify(buildStepCompleteMessage(nextState), "info");
|
|
1072
|
+
} catch (e) {
|
|
1073
|
+
debugLog("postUnit", { phase: "step-wizard-notify", error: String(e) });
|
|
1074
|
+
ctx.ui.notify(STEP_COMPLETE_FALLBACK_MESSAGE, "info");
|
|
1075
|
+
}
|
|
1030
1076
|
return "step-wizard";
|
|
1031
1077
|
}
|
|
1032
1078
|
|
|
@@ -1926,6 +1926,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1926
1926
|
export async function buildReactiveExecutePrompt(
|
|
1927
1927
|
mid: string, midTitle: string, sid: string, sTitle: string,
|
|
1928
1928
|
readyTaskIds: string[], base: string,
|
|
1929
|
+
subagentModel?: string,
|
|
1929
1930
|
): Promise<string> {
|
|
1930
1931
|
const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
|
|
1931
1932
|
|
|
@@ -1970,10 +1971,11 @@ export async function buildReactiveExecutePrompt(
|
|
|
1970
1971
|
{ carryForwardPaths: depPaths },
|
|
1971
1972
|
);
|
|
1972
1973
|
|
|
1974
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
1973
1975
|
subagentSections.push([
|
|
1974
1976
|
`### ${tid}: ${tTitle}`,
|
|
1975
1977
|
"",
|
|
1976
|
-
|
|
1978
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
|
|
1977
1979
|
"",
|
|
1978
1980
|
"```",
|
|
1979
1981
|
taskPrompt,
|
|
@@ -2049,15 +2051,17 @@ export async function buildParallelResearchSlicesPrompt(
|
|
|
2049
2051
|
midTitle: string,
|
|
2050
2052
|
slices: Array<{ id: string; title: string }>,
|
|
2051
2053
|
basePath: string,
|
|
2054
|
+
subagentModel?: string,
|
|
2052
2055
|
): Promise<string> {
|
|
2053
2056
|
// Build individual research-slice prompts for each slice
|
|
2054
2057
|
const subagentSections: string[] = [];
|
|
2058
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
2055
2059
|
for (const slice of slices) {
|
|
2056
2060
|
const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath);
|
|
2057
2061
|
subagentSections.push([
|
|
2058
2062
|
`### ${slice.id}: ${slice.title}`,
|
|
2059
2063
|
"",
|
|
2060
|
-
|
|
2064
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`gsd-executor\` or the default agent):`,
|
|
2061
2065
|
"",
|
|
2062
2066
|
"```",
|
|
2063
2067
|
slicePrompt,
|
|
@@ -2077,6 +2081,7 @@ export async function buildParallelResearchSlicesPrompt(
|
|
|
2077
2081
|
export async function buildGateEvaluatePrompt(
|
|
2078
2082
|
mid: string, midTitle: string, sid: string, sTitle: string,
|
|
2079
2083
|
base: string,
|
|
2084
|
+
subagentModel?: string,
|
|
2080
2085
|
): Promise<string> {
|
|
2081
2086
|
// Pull only the gates this turn actually owns (Q3/Q4). Filter via the
|
|
2082
2087
|
// registry so that scope:"slice" gates owned by other turns (Q8) can't
|
|
@@ -2128,10 +2133,11 @@ export async function buildGateEvaluatePrompt(
|
|
|
2128
2133
|
"- `findings`: detailed markdown findings (or empty if omitted)",
|
|
2129
2134
|
].join("\n");
|
|
2130
2135
|
|
|
2136
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
2131
2137
|
subagentSections.push([
|
|
2132
2138
|
`### ${def.id}: ${def.question}`,
|
|
2133
2139
|
"",
|
|
2134
|
-
|
|
2140
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
|
|
2135
2141
|
"",
|
|
2136
2142
|
"```",
|
|
2137
2143
|
subPrompt,
|
|
@@ -83,7 +83,11 @@ import { join } from "node:path";
|
|
|
83
83
|
import { sep as pathSep } from "node:path";
|
|
84
84
|
|
|
85
85
|
import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
|
|
86
|
-
import {
|
|
86
|
+
import {
|
|
87
|
+
isCustomProvider,
|
|
88
|
+
resolveDefaultSessionModel,
|
|
89
|
+
resolveDynamicRoutingConfig,
|
|
90
|
+
} from "./preferences-models.js";
|
|
87
91
|
import type { WorktreeResolver } from "./worktree-resolver.js";
|
|
88
92
|
import { getSessionModelOverride } from "./session-model-override.js";
|
|
89
93
|
|
|
@@ -274,8 +278,18 @@ export async function bootstrapAutoSession(
|
|
|
274
278
|
//
|
|
275
279
|
// This preserves #3517 defaults while honoring explicit runtime model
|
|
276
280
|
// selection for subsequent /gsd runs in the same session.
|
|
281
|
+
//
|
|
282
|
+
// Exception (#4122): when the session provider is a custom provider declared
|
|
283
|
+
// in ~/.gsd/agent/models.json (Ollama, vLLM, OpenAI-compatible proxy, etc.),
|
|
284
|
+
// PREFERENCES.md is skipped entirely. PREFERENCES.md cannot reference custom
|
|
285
|
+
// providers, so honoring it would silently reroute auto-mode to a built-in
|
|
286
|
+
// provider the user is not logged into and surface as "Not logged in · Please
|
|
287
|
+
// run /login" before pausing and resetting to claude-code/claude-sonnet-4-6.
|
|
277
288
|
const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
|
|
278
|
-
const
|
|
289
|
+
const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider);
|
|
290
|
+
const preferredModel = sessionProviderIsCustom
|
|
291
|
+
? null
|
|
292
|
+
: resolveDefaultSessionModel(ctx.model?.provider);
|
|
279
293
|
// Validate the preferred model against the live registry + provider auth so
|
|
280
294
|
// an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
|
|
281
295
|
// start-model snapshot. Without this, every subsequent unit would try to
|
|
@@ -792,6 +806,9 @@ export async function bootstrapAutoSession(
|
|
|
792
806
|
|
|
793
807
|
ctx.ui.setStatus("gsd-auto", s.stepMode ? "next" : "auto");
|
|
794
808
|
ctx.ui.setFooter(hideFooter);
|
|
809
|
+
// Hide gsd-health during AUTO — gsd-progress is the single source of truth
|
|
810
|
+
// for last-commit / cost / health signal while auto is running.
|
|
811
|
+
ctx.ui.setWidget("gsd-health", undefined);
|
|
795
812
|
const modeLabel = s.stepMode ? "Step-mode" : "Auto-mode";
|
|
796
813
|
const pendingCount = (state.registry ?? []).filter(
|
|
797
814
|
(m) => m.status !== "complete" && m.status !== "parked",
|
|
@@ -811,12 +828,19 @@ export async function bootstrapAutoSession(
|
|
|
811
828
|
? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
|
|
812
829
|
: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
|
|
813
830
|
|
|
814
|
-
// Flat-rate providers (e.g. GitHub Copilot, claude-code
|
|
815
|
-
//
|
|
816
|
-
|
|
831
|
+
// Flat-rate providers (e.g. GitHub Copilot, claude-code, user-declared
|
|
832
|
+
// subscription proxies, externalCli CLIs) suppress routing at dispatch
|
|
833
|
+
// time (#3453) — reflect that in the banner. Thread the same
|
|
834
|
+
// FlatRateContext used by selectAndApplyModel so user-declared
|
|
835
|
+
// flat-rate providers and externalCli auto-detection are respected.
|
|
836
|
+
const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
|
|
837
|
+
const bannerPrefs = loadEffectiveGSDPreferences()?.preferences;
|
|
817
838
|
const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
|
|
818
839
|
const effectivelyEnabled = routingConfig.enabled
|
|
819
|
-
&& !(effectiveProvider && isFlatRateProvider(
|
|
840
|
+
&& !(effectiveProvider && isFlatRateProvider(
|
|
841
|
+
effectiveProvider,
|
|
842
|
+
buildFlatRateContext(effectiveProvider, ctx, bannerPrefs),
|
|
843
|
+
));
|
|
820
844
|
|
|
821
845
|
// The actual ceiling may come from tier_models.heavy, not the start model.
|
|
822
846
|
const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
|
|
@@ -230,6 +230,23 @@ export async function recoverTimedOutUnit(
|
|
|
230
230
|
return "recovered";
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
// #4175: For complete-milestone, never write a blocker placeholder — a stub
|
|
234
|
+
// SUMMARY has no recovery value (milestone is terminal), it does not update
|
|
235
|
+
// DB status, and downstream merge paths can treat the stub as a legitimate
|
|
236
|
+
// completion signal. Pause instead so the worktree branch is preserved.
|
|
237
|
+
if (unitType === "complete-milestone") {
|
|
238
|
+
writeUnitRuntimeRecord(basePath, unitType, unitId, currentUnitStartedAt, {
|
|
239
|
+
phase: "paused",
|
|
240
|
+
recoveryAttempts: recoveryAttempts + 1,
|
|
241
|
+
lastRecoveryReason: reason,
|
|
242
|
+
});
|
|
243
|
+
ctx.ui.notify(
|
|
244
|
+
`Milestone ${unitId} ${reason}-recovery exhausted ${maxRecoveryAttempts} attempt(s) — worktree branch preserved. Re-run /gsd auto once blockers are resolved.`,
|
|
245
|
+
"error",
|
|
246
|
+
);
|
|
247
|
+
return "paused";
|
|
248
|
+
}
|
|
249
|
+
|
|
233
250
|
// Retries exhausted — write a blocker placeholder and advance the pipeline
|
|
234
251
|
// instead of silently stalling.
|
|
235
252
|
const placeholder = writeBlockerPlaceholder(
|