gsd-pi 2.60.0-dev.d9052f5 → 2.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/extensions/ask-user-questions.js +7 -4
- package/dist/resources/extensions/gsd/auto/phases.js +15 -7
- package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +58 -9
- package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +36 -20
- package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
- package/dist/resources/extensions/gsd/auto-start.js +9 -5
- package/dist/resources/extensions/gsd/auto-timers.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
- package/dist/resources/extensions/gsd/auto-verification.js +3 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
- package/dist/resources/extensions/gsd/auto.js +39 -17
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
- package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
- package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
- package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
- package/dist/resources/extensions/gsd/complexity-classifier.js +8 -4
- package/dist/resources/extensions/gsd/custom-verification.js +3 -2
- package/dist/resources/extensions/gsd/gsd-db.js +33 -13
- package/dist/resources/extensions/gsd/guided-flow.js +19 -9
- package/dist/resources/extensions/gsd/init-wizard.js +12 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
- package/dist/resources/extensions/gsd/md-importer.js +5 -4
- package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
- package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
- package/dist/resources/extensions/gsd/model-router.js +156 -121
- package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +45 -0
- package/dist/resources/extensions/gsd/preferences.js +15 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/rule-registry.js +7 -6
- package/dist/resources/extensions/gsd/safe-fs.js +6 -8
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
- package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
- package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
- package/dist/resources/extensions/gsd/triage-resolution.js +11 -4
- package/dist/resources/extensions/gsd/workflow-events.js +2 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
- package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
- package/dist/resources/extensions/shared/interview-ui.js +3 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- 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 +2 -2
- 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/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 +14 -14
- package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-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 +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.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 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -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.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +7 -3
- package/src/resources/extensions/gsd/auto/phases.ts +17 -7
- package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
- package/src/resources/extensions/gsd/auto-model-selection.ts +77 -15
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +37 -20
- package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
- package/src/resources/extensions/gsd/auto-start.ts +10 -9
- package/src/resources/extensions/gsd/auto-timers.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
- package/src/resources/extensions/gsd/auto-verification.ts +3 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
- package/src/resources/extensions/gsd/auto.ts +40 -17
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
- package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
- package/src/resources/extensions/gsd/complexity-classifier.ts +9 -4
- package/src/resources/extensions/gsd/custom-verification.ts +3 -2
- package/src/resources/extensions/gsd/gsd-db.ts +12 -14
- package/src/resources/extensions/gsd/guided-flow.ts +9 -8
- package/src/resources/extensions/gsd/init-wizard.ts +12 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
- package/src/resources/extensions/gsd/md-importer.ts +5 -4
- package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/model-router.ts +199 -173
- package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
- package/src/resources/extensions/gsd/preferences-types.ts +13 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +45 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -3
- package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/rule-registry.ts +7 -6
- package/src/resources/extensions/gsd/safe-fs.ts +6 -5
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +403 -3
- package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
- package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
- package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
- package/src/resources/extensions/gsd/triage-resolution.ts +11 -4
- package/src/resources/extensions/gsd/types.ts +1 -0
- package/src/resources/extensions/gsd/workflow-events.ts +2 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
- package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
- package/src/resources/extensions/shared/interview-ui.ts +3 -1
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
- package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_ssgManifest.js +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Maps complexity tiers to models, enforcing downgrade-only semantics.
|
|
3
3
|
// The user's configured model is always the ceiling.
|
|
4
4
|
|
|
5
|
-
import type { ComplexityTier, ClassificationResult } from "./complexity-classifier.js";
|
|
5
|
+
import type { ComplexityTier, ClassificationResult, TaskMetadata } from "./complexity-classifier.js";
|
|
6
6
|
import { tierOrdinal } from "./complexity-classifier.js";
|
|
7
7
|
import type { ResolvedModelConfig } from "./preferences.js";
|
|
8
8
|
|
|
@@ -33,14 +33,27 @@ export interface RoutingDecision {
|
|
|
33
33
|
wasDowngraded: boolean;
|
|
34
34
|
/** Human-readable reason for this decision */
|
|
35
35
|
reason: string;
|
|
36
|
-
/** How the model was selected
|
|
37
|
-
selectionMethod
|
|
38
|
-
/** Capability scores per model (
|
|
36
|
+
/** How the model was selected */
|
|
37
|
+
selectionMethod: "tier-only" | "capability-scored";
|
|
38
|
+
/** Capability scores per eligible model (capability-scored path only) */
|
|
39
39
|
capabilityScores?: Record<string, number>;
|
|
40
|
-
/** Task requirement vector
|
|
40
|
+
/** Task requirement vector used for scoring */
|
|
41
41
|
taskRequirements?: Partial<Record<string, number>>;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// ─── Capability Profiles ─────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/** Seven-dimension capability profile for a model. All values in 0–100 range. */
|
|
47
|
+
export interface ModelCapabilities {
|
|
48
|
+
coding: number;
|
|
49
|
+
debugging: number;
|
|
50
|
+
research: number;
|
|
51
|
+
reasoning: number;
|
|
52
|
+
speed: number;
|
|
53
|
+
longContext: number;
|
|
54
|
+
instruction: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
44
57
|
// ─── Known Model Tiers ───────────────────────────────────────────────────────
|
|
45
58
|
// Maps known model IDs to their capability tier. Used when tier_models is not
|
|
46
59
|
// explicitly configured to pick the best available model for each tier.
|
|
@@ -121,33 +134,27 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
|
|
|
121
134
|
"deepseek-chat": 0.00014,
|
|
122
135
|
};
|
|
123
136
|
|
|
124
|
-
// ─── Capability Profiles
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
export interface ModelCapabilities {
|
|
129
|
-
coding: number;
|
|
130
|
-
debugging: number;
|
|
131
|
-
research: number;
|
|
132
|
-
reasoning: number;
|
|
133
|
-
speed: number;
|
|
134
|
-
longContext: number;
|
|
135
|
-
instruction: number;
|
|
136
|
-
}
|
|
137
|
+
// ─── Capability Profiles Data Table ──────────────────────────────────────────
|
|
138
|
+
// Per-model capability profiles (0–100 scale). Used for capability-aware
|
|
139
|
+
// model selection within an eligible tier set.
|
|
137
140
|
|
|
138
141
|
export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
|
|
139
|
-
"claude-opus-4-6":
|
|
140
|
-
"claude-sonnet-4-6":
|
|
141
|
-
"claude-haiku-4-5":
|
|
142
|
-
"gpt-4o":
|
|
143
|
-
"gpt-4o-mini":
|
|
144
|
-
"gemini-2.5-pro":
|
|
145
|
-
"gemini-2.0-flash":
|
|
146
|
-
"deepseek-chat":
|
|
147
|
-
"o3":
|
|
142
|
+
"claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
|
|
143
|
+
"claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
|
|
144
|
+
"claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
|
|
145
|
+
"gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
|
|
146
|
+
"gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
|
|
147
|
+
"gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
|
|
148
|
+
"gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
|
|
149
|
+
"deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
|
|
150
|
+
"o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
|
|
148
151
|
};
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
// ─── Base Task Requirements Data Table ───────────────────────────────────────
|
|
154
|
+
// Per-unit-type base requirement vectors. Weights indicate how important each
|
|
155
|
+
// capability dimension is for this unit type.
|
|
156
|
+
|
|
157
|
+
export const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
|
|
151
158
|
"execute-task": { coding: 0.9, instruction: 0.7, speed: 0.3 },
|
|
152
159
|
"research-milestone": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
153
160
|
"research-slice": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
|
|
@@ -161,15 +168,36 @@ const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities,
|
|
|
161
168
|
"complete-milestone": { instruction: 0.8, reasoning: 0.5 },
|
|
162
169
|
};
|
|
163
170
|
|
|
171
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
172
|
+
|
|
164
173
|
/**
|
|
165
|
-
*
|
|
174
|
+
* Score a model's suitability for a task given a requirement vector.
|
|
175
|
+
* Returns a weighted average of capability dimensions (0–100).
|
|
176
|
+
* Returns 50 if requirements are empty (neutral score).
|
|
177
|
+
*/
|
|
178
|
+
export function scoreModel(
|
|
179
|
+
model: ModelCapabilities,
|
|
180
|
+
requirements: Partial<Record<keyof ModelCapabilities, number>>,
|
|
181
|
+
): number {
|
|
182
|
+
let weightedSum = 0;
|
|
183
|
+
let weightSum = 0;
|
|
184
|
+
for (const [dim, weight] of Object.entries(requirements)) {
|
|
185
|
+
const capability = model[dim as keyof ModelCapabilities] ?? 50;
|
|
186
|
+
weightedSum += weight * capability;
|
|
187
|
+
weightSum += weight;
|
|
188
|
+
}
|
|
189
|
+
return weightSum > 0 ? weightedSum / weightSum : 50;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Compute dynamic task requirements from unit type and optional task metadata.
|
|
194
|
+
* Returns a requirement vector refined by task-specific signals.
|
|
166
195
|
*/
|
|
167
196
|
export function computeTaskRequirements(
|
|
168
197
|
unitType: string,
|
|
169
|
-
metadata?:
|
|
198
|
+
metadata?: TaskMetadata,
|
|
170
199
|
): Partial<Record<keyof ModelCapabilities, number>> {
|
|
171
|
-
const base =
|
|
172
|
-
|
|
200
|
+
const base = BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 };
|
|
173
201
|
if (unitType === "execute-task" && metadata) {
|
|
174
202
|
if (metadata.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
|
|
175
203
|
return { ...base, instruction: 0.9, coding: 0.3, speed: 0.7 };
|
|
@@ -184,29 +212,101 @@ export function computeTaskRequirements(
|
|
|
184
212
|
return { ...base, coding: 0.9, reasoning: 0.7 };
|
|
185
213
|
}
|
|
186
214
|
}
|
|
187
|
-
|
|
188
215
|
return base;
|
|
189
216
|
}
|
|
190
217
|
|
|
191
218
|
/**
|
|
192
|
-
* Score
|
|
193
|
-
*
|
|
219
|
+
* Score all eligible models against a requirement vector and return them
|
|
220
|
+
* sorted by score descending. Within 2 points: prefer cheaper; equal cost:
|
|
221
|
+
* lexicographic tie-break by model ID.
|
|
194
222
|
*/
|
|
195
|
-
export function
|
|
196
|
-
|
|
223
|
+
export function scoreEligibleModels(
|
|
224
|
+
eligibleModelIds: string[],
|
|
197
225
|
requirements: Partial<Record<keyof ModelCapabilities, number>>,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
226
|
+
capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
|
|
227
|
+
): Array<{ modelId: string; score: number }> {
|
|
228
|
+
const scored = eligibleModelIds.map(modelId => {
|
|
229
|
+
const builtin = MODEL_CAPABILITY_PROFILES[modelId];
|
|
230
|
+
const override = capabilityOverrides?.[modelId];
|
|
231
|
+
const profile: ModelCapabilities = builtin
|
|
232
|
+
? override ? { ...builtin, ...override } : builtin
|
|
233
|
+
: { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
|
|
234
|
+
return { modelId, score: scoreModel(profile, requirements) };
|
|
235
|
+
});
|
|
236
|
+
scored.sort((a, b) => {
|
|
237
|
+
const scoreDiff = b.score - a.score;
|
|
238
|
+
if (Math.abs(scoreDiff) > 2) return scoreDiff;
|
|
239
|
+
const costA = MODEL_COST_PER_1K_INPUT[a.modelId] ?? Infinity;
|
|
240
|
+
const costB = MODEL_COST_PER_1K_INPUT[b.modelId] ?? Infinity;
|
|
241
|
+
if (costA !== costB) return costA - costB;
|
|
242
|
+
return a.modelId.localeCompare(b.modelId);
|
|
243
|
+
});
|
|
244
|
+
return scored;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Return all models eligible for a given tier, sorted cheapest first.
|
|
249
|
+
* If routingConfig.tier_models[tier] is set and available, returns only that
|
|
250
|
+
* model. Otherwise filters availableModelIds by tier from MODEL_CAPABILITY_TIER.
|
|
251
|
+
*/
|
|
252
|
+
export function getEligibleModels(
|
|
253
|
+
tier: ComplexityTier,
|
|
254
|
+
availableModelIds: string[],
|
|
255
|
+
routingConfig: DynamicRoutingConfig,
|
|
256
|
+
): string[] {
|
|
257
|
+
// 1. Check explicit tier_models config
|
|
258
|
+
const explicitModel = routingConfig.tier_models?.[tier];
|
|
259
|
+
if (explicitModel) {
|
|
260
|
+
// Exact match
|
|
261
|
+
if (availableModelIds.includes(explicitModel)) return [explicitModel];
|
|
262
|
+
// Provider-prefix-stripped match
|
|
263
|
+
const match = availableModelIds.find(id => {
|
|
264
|
+
const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
|
|
265
|
+
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
|
|
266
|
+
return bareAvail === bareExplicit;
|
|
267
|
+
});
|
|
268
|
+
if (match) return [match];
|
|
205
269
|
}
|
|
206
|
-
|
|
270
|
+
|
|
271
|
+
// 2. Auto-detect: filter by tier, sort cheapest first
|
|
272
|
+
return availableModelIds
|
|
273
|
+
.filter(id => getModelTier(id) === tier)
|
|
274
|
+
.sort((a, b) => {
|
|
275
|
+
const costA = getModelCost(a);
|
|
276
|
+
const costB = getModelCost(b);
|
|
277
|
+
return costA - costB;
|
|
278
|
+
});
|
|
207
279
|
}
|
|
208
280
|
|
|
209
|
-
|
|
281
|
+
/**
|
|
282
|
+
* Build a fallback chain for a selected model: [selectedModel, ...configuredFallbacks, configuredPrimary]
|
|
283
|
+
* Deduplicates entries while preserving order.
|
|
284
|
+
*/
|
|
285
|
+
function buildFallbackChain(selectedModelId: string, phaseConfig: ResolvedModelConfig): string[] {
|
|
286
|
+
return [
|
|
287
|
+
...phaseConfig.fallbacks.filter(f => f !== selectedModelId),
|
|
288
|
+
phaseConfig.primary,
|
|
289
|
+
].filter(f => f !== selectedModelId);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Load capability overrides from user preferences' modelOverrides section.
|
|
294
|
+
* Returns a map of model ID → partial capability overrides to deep-merge with built-in profiles.
|
|
295
|
+
*
|
|
296
|
+
* Per D-17: partial capability overrides via models.json modelOverrides, deep-merged with defaults.
|
|
297
|
+
*/
|
|
298
|
+
export function loadCapabilityOverrides(
|
|
299
|
+
prefs: { modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }> },
|
|
300
|
+
): Record<string, Partial<ModelCapabilities>> {
|
|
301
|
+
const result: Record<string, Partial<ModelCapabilities>> = {};
|
|
302
|
+
if (!prefs.modelOverrides) return result;
|
|
303
|
+
for (const [modelId, overrideEntry] of Object.entries(prefs.modelOverrides)) {
|
|
304
|
+
if (overrideEntry.capabilities) {
|
|
305
|
+
result[modelId] = overrideEntry.capabilities;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
210
310
|
|
|
211
311
|
/**
|
|
212
312
|
* Resolve the model to use for a given complexity tier.
|
|
@@ -214,10 +314,18 @@ export function scoreModel(
|
|
|
214
314
|
* Downgrade-only: the returned model is always equal to or cheaper than
|
|
215
315
|
* the user's configured primary model. Never upgrades beyond configuration.
|
|
216
316
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
317
|
+
* STEP 1: Filter to eligible models for the requested tier.
|
|
318
|
+
* STEP 2: Capability scoring — ranks eligible models by task-capability match
|
|
319
|
+
* when capability_routing is enabled and multiple eligible models exist.
|
|
320
|
+
* STEP 3: Fallback chain assembly.
|
|
321
|
+
*
|
|
322
|
+
* @param classification The complexity classification result
|
|
323
|
+
* @param phaseConfig The user's configured model for this phase (ceiling)
|
|
324
|
+
* @param routingConfig Dynamic routing configuration
|
|
325
|
+
* @param availableModelIds List of available model IDs (from registry)
|
|
326
|
+
* @param unitType The unit type for capability requirement computation (optional)
|
|
327
|
+
* @param taskMetadata Task metadata for refined requirement vectors (optional)
|
|
328
|
+
* @param capabilityOverrides User-provided capability overrides (deep-merged with built-in profiles, optional)
|
|
221
329
|
*/
|
|
222
330
|
export function resolveModelForComplexity(
|
|
223
331
|
classification: ClassificationResult,
|
|
@@ -225,7 +333,8 @@ export function resolveModelForComplexity(
|
|
|
225
333
|
routingConfig: DynamicRoutingConfig,
|
|
226
334
|
availableModelIds: string[],
|
|
227
335
|
unitType?: string,
|
|
228
|
-
|
|
336
|
+
taskMetadata?: TaskMetadata,
|
|
337
|
+
capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
|
|
229
338
|
): RoutingDecision {
|
|
230
339
|
// If no phase config or routing disabled, pass through
|
|
231
340
|
if (!phaseConfig || !routingConfig.enabled) {
|
|
@@ -235,6 +344,7 @@ export function resolveModelForComplexity(
|
|
|
235
344
|
tier: classification.tier,
|
|
236
345
|
wasDowngraded: false,
|
|
237
346
|
reason: "dynamic routing disabled or no phase config",
|
|
347
|
+
selectionMethod: "tier-only",
|
|
238
348
|
};
|
|
239
349
|
}
|
|
240
350
|
|
|
@@ -254,6 +364,7 @@ export function resolveModelForComplexity(
|
|
|
254
364
|
tier: requestedTier,
|
|
255
365
|
wasDowngraded: false,
|
|
256
366
|
reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`,
|
|
367
|
+
selectionMethod: "tier-only",
|
|
257
368
|
};
|
|
258
369
|
}
|
|
259
370
|
|
|
@@ -265,48 +376,52 @@ export function resolveModelForComplexity(
|
|
|
265
376
|
tier: requestedTier,
|
|
266
377
|
wasDowngraded: false,
|
|
267
378
|
reason: `tier ${requestedTier} >= configured ${configuredTier}`,
|
|
379
|
+
selectionMethod: "tier-only",
|
|
268
380
|
};
|
|
269
381
|
}
|
|
270
382
|
|
|
271
|
-
//
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
let targetModelId: string | null;
|
|
275
|
-
let capabilityScores: Record<string, number> | undefined;
|
|
276
|
-
let taskRequirements: Partial<Record<string, number>> | undefined;
|
|
277
|
-
let selectionMethod: "tier-only" | "capability-scored" = "tier-only";
|
|
278
|
-
|
|
279
|
-
if (useCapabilityScoring) {
|
|
280
|
-
const result = findModelForTierWithCapability(
|
|
281
|
-
requestedTier, routingConfig, availableModelIds,
|
|
282
|
-
routingConfig.cross_provider !== false, unitType, metadata,
|
|
283
|
-
);
|
|
284
|
-
targetModelId = result.modelId;
|
|
285
|
-
capabilityScores = Object.keys(result.scores).length > 0 ? result.scores : undefined;
|
|
286
|
-
taskRequirements = Object.keys(result.requirements).length > 0 ? result.requirements : undefined;
|
|
287
|
-
selectionMethod = capabilityScores ? "capability-scored" : "tier-only";
|
|
288
|
-
} else {
|
|
289
|
-
targetModelId = findModelForTier(
|
|
290
|
-
requestedTier, routingConfig, availableModelIds,
|
|
291
|
-
routingConfig.cross_provider !== false,
|
|
292
|
-
);
|
|
293
|
-
}
|
|
383
|
+
// STEP 1: Get all eligible models for the requested tier
|
|
384
|
+
const eligible = getEligibleModels(requestedTier, availableModelIds, routingConfig);
|
|
294
385
|
|
|
295
|
-
if (
|
|
386
|
+
if (eligible.length === 0) {
|
|
387
|
+
// No suitable model found — use configured primary
|
|
296
388
|
return {
|
|
297
389
|
modelId: configuredPrimary,
|
|
298
390
|
fallbacks: phaseConfig.fallbacks,
|
|
299
391
|
tier: requestedTier,
|
|
300
392
|
wasDowngraded: false,
|
|
301
393
|
reason: `no ${requestedTier}-tier model available`,
|
|
302
|
-
selectionMethod,
|
|
394
|
+
selectionMethod: "tier-only",
|
|
303
395
|
};
|
|
304
396
|
}
|
|
305
397
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
398
|
+
// STEP 2: Capability scoring (when enabled and multiple eligible models exist)
|
|
399
|
+
if (routingConfig.capability_routing !== false && eligible.length > 1 && unitType) {
|
|
400
|
+
const requirements = computeTaskRequirements(unitType, taskMetadata);
|
|
401
|
+
const scored = scoreEligibleModels(eligible, requirements, capabilityOverrides);
|
|
402
|
+
const winner = scored[0];
|
|
403
|
+
if (winner) {
|
|
404
|
+
const capScores: Record<string, number> = {};
|
|
405
|
+
for (const s of scored) capScores[s.modelId] = s.score;
|
|
406
|
+
const fallbacks = buildFallbackChain(winner.modelId, phaseConfig);
|
|
407
|
+
return {
|
|
408
|
+
modelId: winner.modelId,
|
|
409
|
+
fallbacks,
|
|
410
|
+
tier: requestedTier,
|
|
411
|
+
wasDowngraded: true,
|
|
412
|
+
reason: `capability-scored: ${winner.modelId} (${winner.score.toFixed(1)}) for ${unitType}`,
|
|
413
|
+
capabilityScores: capScores,
|
|
414
|
+
taskRequirements: requirements,
|
|
415
|
+
selectionMethod: "capability-scored",
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// STEP 3: Fallback — use first eligible model (cheapest in tier, or single eligible)
|
|
421
|
+
const targetModelId = eligible[0];
|
|
422
|
+
|
|
423
|
+
// Build fallback chain: [downgraded_model, ...configured_fallbacks, configured_primary]
|
|
424
|
+
const fallbacks = buildFallbackChain(targetModelId, phaseConfig);
|
|
310
425
|
|
|
311
426
|
return {
|
|
312
427
|
modelId: targetModelId,
|
|
@@ -314,9 +429,7 @@ export function resolveModelForComplexity(
|
|
|
314
429
|
tier: requestedTier,
|
|
315
430
|
wasDowngraded: true,
|
|
316
431
|
reason: classification.reason,
|
|
317
|
-
selectionMethod,
|
|
318
|
-
capabilityScores,
|
|
319
|
-
taskRequirements,
|
|
432
|
+
selectionMethod: "tier-only",
|
|
320
433
|
};
|
|
321
434
|
}
|
|
322
435
|
|
|
@@ -338,7 +451,7 @@ export function escalateTier(currentTier: ComplexityTier): ComplexityTier | null
|
|
|
338
451
|
export function defaultRoutingConfig(): DynamicRoutingConfig {
|
|
339
452
|
return {
|
|
340
453
|
enabled: true,
|
|
341
|
-
capability_routing:
|
|
454
|
+
capability_routing: true,
|
|
342
455
|
escalate_on_failure: true,
|
|
343
456
|
budget_pressure: true,
|
|
344
457
|
cross_provider: true,
|
|
@@ -360,8 +473,8 @@ function getModelTier(modelId: string): ComplexityTier {
|
|
|
360
473
|
if (bareId.includes(knownId) || knownId.includes(bareId)) return tier;
|
|
361
474
|
}
|
|
362
475
|
|
|
363
|
-
// Unknown models are assumed
|
|
364
|
-
return "
|
|
476
|
+
// Unknown models are assumed standard (per D-15: avoids silently ignoring user config)
|
|
477
|
+
return "standard";
|
|
365
478
|
}
|
|
366
479
|
|
|
367
480
|
/** Check if a model ID has a known capability tier mapping. (#2192) */
|
|
@@ -374,93 +487,6 @@ function isKnownModel(modelId: string): boolean {
|
|
|
374
487
|
return false;
|
|
375
488
|
}
|
|
376
489
|
|
|
377
|
-
function findModelForTier(
|
|
378
|
-
tier: ComplexityTier,
|
|
379
|
-
config: DynamicRoutingConfig,
|
|
380
|
-
availableModelIds: string[],
|
|
381
|
-
crossProvider: boolean,
|
|
382
|
-
): string | null {
|
|
383
|
-
// 1. Check explicit tier_models config
|
|
384
|
-
const explicitModel = config.tier_models?.[tier];
|
|
385
|
-
if (explicitModel && availableModelIds.includes(explicitModel)) {
|
|
386
|
-
return explicitModel;
|
|
387
|
-
}
|
|
388
|
-
// Also check with provider prefix stripped
|
|
389
|
-
if (explicitModel) {
|
|
390
|
-
const match = availableModelIds.find(id => {
|
|
391
|
-
const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
|
|
392
|
-
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
|
|
393
|
-
return bareAvail === bareExplicit;
|
|
394
|
-
});
|
|
395
|
-
if (match) return match;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// 2. Auto-detect: find the cheapest available model in the requested tier
|
|
399
|
-
const candidates = availableModelIds
|
|
400
|
-
.filter(id => {
|
|
401
|
-
const modelTier = getModelTier(id);
|
|
402
|
-
return modelTier === tier;
|
|
403
|
-
})
|
|
404
|
-
.sort((a, b) => {
|
|
405
|
-
if (!crossProvider) return 0;
|
|
406
|
-
const costA = getModelCost(a);
|
|
407
|
-
const costB = getModelCost(b);
|
|
408
|
-
return costA - costB;
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
return candidates[0] ?? null;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function findModelForTierWithCapability(
|
|
415
|
-
tier: ComplexityTier,
|
|
416
|
-
config: DynamicRoutingConfig,
|
|
417
|
-
availableModelIds: string[],
|
|
418
|
-
crossProvider: boolean,
|
|
419
|
-
unitType: string,
|
|
420
|
-
metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
|
|
421
|
-
): { modelId: string | null; scores: Record<string, number>; requirements: Partial<Record<string, number>> } {
|
|
422
|
-
const explicitModel = config.tier_models?.[tier];
|
|
423
|
-
if (explicitModel) {
|
|
424
|
-
const match = availableModelIds.find(id => {
|
|
425
|
-
const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
|
|
426
|
-
const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
|
|
427
|
-
return bareAvail === bareExplicit || id === explicitModel;
|
|
428
|
-
});
|
|
429
|
-
if (match) return { modelId: match, scores: {}, requirements: {} };
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const requirements = computeTaskRequirements(unitType, metadata);
|
|
433
|
-
const candidates = availableModelIds.filter(id => getModelTier(id) === tier);
|
|
434
|
-
if (candidates.length === 0) return { modelId: null, scores: {}, requirements };
|
|
435
|
-
|
|
436
|
-
const scores: Record<string, number> = {};
|
|
437
|
-
for (const id of candidates) {
|
|
438
|
-
const bareId = id.includes("/") ? id.split("/").pop()! : id;
|
|
439
|
-
const profile = getModelProfile(bareId);
|
|
440
|
-
scores[id] = scoreModel(profile, requirements);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
candidates.sort((a, b) => {
|
|
444
|
-
const scoreDiff = scores[b] - scores[a];
|
|
445
|
-
if (Math.abs(scoreDiff) > 2) return scoreDiff;
|
|
446
|
-
if (crossProvider) {
|
|
447
|
-
const costDiff = getModelCost(a) - getModelCost(b);
|
|
448
|
-
if (costDiff !== 0) return costDiff;
|
|
449
|
-
}
|
|
450
|
-
return a.localeCompare(b);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
return { modelId: candidates[0], scores, requirements };
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
function getModelProfile(bareId: string): ModelCapabilities {
|
|
457
|
-
if (MODEL_CAPABILITY_PROFILES[bareId]) return MODEL_CAPABILITY_PROFILES[bareId];
|
|
458
|
-
for (const [knownId, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
|
|
459
|
-
if (bareId.includes(knownId) || knownId.includes(bareId)) return profile;
|
|
460
|
-
}
|
|
461
|
-
return { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
|
|
462
|
-
}
|
|
463
|
-
|
|
464
490
|
function getModelCost(modelId: string): number {
|
|
465
491
|
const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
|
|
466
492
|
|
|
@@ -15,6 +15,7 @@ import { MergeConflictError } from "./git-service.js";
|
|
|
15
15
|
import { removeSessionStatus } from "./session-status-io.js";
|
|
16
16
|
import type { WorkerInfo } from "./parallel-orchestrator.js";
|
|
17
17
|
import { getErrorMessage } from "./error-utils.js";
|
|
18
|
+
import { logWarning } from "./workflow-logger.js";
|
|
18
19
|
|
|
19
20
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
20
21
|
|
|
@@ -47,7 +48,8 @@ export function isMilestoneCompleteInWorktreeDb(basePath: string, mid: string):
|
|
|
47
48
|
{ timeout: 3000, encoding: "utf-8" },
|
|
48
49
|
);
|
|
49
50
|
return (result.stdout || "").trim() === "complete";
|
|
50
|
-
} catch {
|
|
51
|
+
} catch (e) {
|
|
52
|
+
logWarning("parallel", `spawnSync milestone completion check failed for ${mid}: ${(e as Error).message}`);
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
55
|
}
|
|
@@ -65,8 +67,8 @@ function discoverDbCompletedMilestones(basePath: string): Set<string> {
|
|
|
65
67
|
completed.add(entry);
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
|
-
} catch {
|
|
69
|
-
|
|
70
|
+
} catch (e) {
|
|
71
|
+
logWarning("parallel", `readdirSync for completed set failed: ${(e as Error).message}`);
|
|
70
72
|
}
|
|
71
73
|
return completed;
|
|
72
74
|
}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
type ParallelCandidates,
|
|
42
42
|
} from "./parallel-eligibility.js";
|
|
43
43
|
import { getErrorMessage } from "./error-utils.js";
|
|
44
|
+
import { logWarning } from "./workflow-logger.js";
|
|
44
45
|
|
|
45
46
|
// ─── Types ─────────────────────────────────────────────────────────────────
|
|
46
47
|
|
|
@@ -126,7 +127,7 @@ export function persistState(basePath: string): void {
|
|
|
126
127
|
const tmp = dest + TMP_SUFFIX;
|
|
127
128
|
writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
|
|
128
129
|
renameSync(tmp, dest);
|
|
129
|
-
} catch {
|
|
130
|
+
} catch (e) { logWarning("parallel", `persist parallel state failed: ${(e as Error).message}`); }
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
/**
|
|
@@ -136,7 +137,7 @@ function removeStateFile(basePath: string): void {
|
|
|
136
137
|
try {
|
|
137
138
|
const p = stateFilePath(basePath);
|
|
138
139
|
if (existsSync(p)) unlinkSync(p);
|
|
139
|
-
} catch {
|
|
140
|
+
} catch (e) { logWarning("parallel", `clear parallel state file failed: ${(e as Error).message}`); }
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
function isPidAlive(pid: number): boolean {
|
|
@@ -144,7 +145,8 @@ function isPidAlive(pid: number): boolean {
|
|
|
144
145
|
try {
|
|
145
146
|
process.kill(pid, 0);
|
|
146
147
|
return true;
|
|
147
|
-
} catch {
|
|
148
|
+
} catch (e) {
|
|
149
|
+
logWarning("parallel", `pid alive check failed for pid ${pid}: ${(e as Error).message}`);
|
|
148
150
|
return false;
|
|
149
151
|
}
|
|
150
152
|
}
|
|
@@ -176,7 +178,8 @@ export function restoreState(basePath: string): PersistedState | null {
|
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
return persisted;
|
|
179
|
-
} catch {
|
|
181
|
+
} catch (e) {
|
|
182
|
+
logWarning("parallel", `readParallelState JSON parse failed: ${(e as Error).message}`);
|
|
180
183
|
return null;
|
|
181
184
|
}
|
|
182
185
|
}
|
|
@@ -190,8 +193,8 @@ function appendWorkerLog(basePath: string, milestoneId: string, chunk: string):
|
|
|
190
193
|
const dir = join(gsdRoot(basePath), "parallel");
|
|
191
194
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
192
195
|
appendFileSync(workerLogPath(basePath, milestoneId), chunk, "utf-8");
|
|
193
|
-
} catch {
|
|
194
|
-
|
|
196
|
+
} catch (e) {
|
|
197
|
+
logWarning("parallel", `appendFileSync worker log failed for ${milestoneId}: ${(e as Error).message}`);
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
200
|
|
|
@@ -430,9 +433,8 @@ export async function startParallel(
|
|
|
430
433
|
let wtPath: string;
|
|
431
434
|
try {
|
|
432
435
|
wtPath = createMilestoneWorktree(basePath, mid);
|
|
433
|
-
} catch {
|
|
434
|
-
|
|
435
|
-
// is not available. Fall back to a placeholder path.
|
|
436
|
+
} catch (e) {
|
|
437
|
+
logWarning("parallel", `createMilestoneWorktree fallback for ${mid}: ${(e as Error).message}`);
|
|
436
438
|
wtPath = worktreePath(basePath, mid);
|
|
437
439
|
}
|
|
438
440
|
|
|
@@ -564,7 +566,8 @@ export function spawnWorker(
|
|
|
564
566
|
stdio: ["ignore", "pipe", "pipe"],
|
|
565
567
|
detached: false,
|
|
566
568
|
});
|
|
567
|
-
} catch {
|
|
569
|
+
} catch (e) {
|
|
570
|
+
logWarning("parallel", `spawnSync worker failed for ${milestoneId}: ${(e as Error).message}`);
|
|
568
571
|
return false;
|
|
569
572
|
}
|
|
570
573
|
|
|
@@ -694,7 +697,8 @@ function resolveGsdBin(): string | null {
|
|
|
694
697
|
let thisDir: string;
|
|
695
698
|
try {
|
|
696
699
|
thisDir = dirname(fileURLToPath(import.meta.url));
|
|
697
|
-
} catch {
|
|
700
|
+
} catch (e) {
|
|
701
|
+
logWarning("parallel", `dirname(fileURLToPath) failed: ${(e as Error).message}`);
|
|
698
702
|
thisDir = process.cwd();
|
|
699
703
|
}
|
|
700
704
|
const candidates = [
|
|
@@ -722,7 +726,7 @@ function processWorkerLine(basePath: string, milestoneId: string, line: string):
|
|
|
722
726
|
try {
|
|
723
727
|
event = JSON.parse(line);
|
|
724
728
|
} catch {
|
|
725
|
-
return; //
|
|
729
|
+
return; // Non-NDJSON lines (progress text, tool output) are expected — silent drop
|
|
726
730
|
}
|
|
727
731
|
|
|
728
732
|
const type = String(event.type ?? "");
|
|
@@ -817,7 +821,7 @@ export async function stopParallel(
|
|
|
817
821
|
} else if (worker.pid !== process.pid) {
|
|
818
822
|
process.kill(worker.pid, "SIGTERM");
|
|
819
823
|
}
|
|
820
|
-
} catch {
|
|
824
|
+
} catch (e) { logWarning("parallel", `process.kill SIGTERM failed for pid ${worker.pid}: ${(e as Error).message}`); }
|
|
821
825
|
}
|
|
822
826
|
|
|
823
827
|
// Wait for the headless process to cascade SIGTERM to its RPC child.
|
|
@@ -833,7 +837,7 @@ export async function stopParallel(
|
|
|
833
837
|
} else if (worker.pid !== process.pid) {
|
|
834
838
|
process.kill(worker.pid, "SIGKILL");
|
|
835
839
|
}
|
|
836
|
-
} catch {
|
|
840
|
+
} catch (e) { logWarning("parallel", `process.kill SIGKILL failed for pid ${worker.pid}: ${(e as Error).message}`); }
|
|
837
841
|
await waitForWorkerExit(worker, 250);
|
|
838
842
|
}
|
|
839
843
|
|