gsd-pi 2.45.0 → 2.46.0-dev.cc9d310
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/help-text.js +1 -1
- package/dist/loader.js +34 -0
- package/dist/resources/extensions/gsd/auto/phases.js +27 -42
- package/dist/resources/extensions/gsd/auto/run-unit.js +6 -3
- package/dist/resources/extensions/gsd/auto/session.js +0 -11
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +5 -4
- package/dist/resources/extensions/gsd/auto.js +12 -57
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +15 -12
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
- package/dist/resources/extensions/gsd/commands/context.js +0 -4
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
- package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
- package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
- package/dist/resources/extensions/gsd/db-writer.js +9 -9
- package/dist/resources/extensions/gsd/doctor-checks.js +167 -2
- package/dist/resources/extensions/gsd/doctor.js +5 -3
- package/dist/resources/extensions/gsd/gsd-db.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +1 -2
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
- package/dist/resources/extensions/gsd/preferences-types.js +2 -2
- package/dist/resources/extensions/gsd/preferences.js +8 -4
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
- package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/session-lock.js +1 -3
- package/dist/resources/extensions/gsd/state.js +7 -0
- package/dist/resources/extensions/gsd/sync-lock.js +89 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +61 -11
- package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
- package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
- package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
- package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
- package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
- package/dist/resources/extensions/gsd/workflow-events.js +102 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +193 -0
- package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
- package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
- package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
- package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +4 -3
- package/dist/resources/extensions/gsd/worktree-resolver.js +37 -0
- package/dist/resources/extensions/gsd/write-intercept.js +84 -0
- package/dist/resources/extensions/voice/index.js +11 -16
- package/dist/resources/extensions/voice/linux-ready.js +67 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
- package/dist/web/standalone/.next/build-manifest.json +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/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 +5 -5
- 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 +17 -17
- package/dist/web/standalone/.next/server/chunks/229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/471.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-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-6654a8cca61a3d1c.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 +2 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- 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/lifecycle-hooks.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +10 -5
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js +185 -0
- package/packages/pi-coding-agent/dist/core/lifecycle-hooks.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +239 -10
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +20 -2
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-commands.test.js +206 -195
- package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.test.ts +227 -0
- package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +11 -5
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +297 -11
- package/packages/pi-coding-agent/src/core/model-registry.ts +30 -3
- package/packages/pi-coding-agent/src/core/package-commands.test.ts +227 -205
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
- package/src/resources/extensions/gsd/auto/phases.ts +24 -44
- package/src/resources/extensions/gsd/auto/run-unit.ts +6 -3
- package/src/resources/extensions/gsd/auto/session.ts +0 -18
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
- package/src/resources/extensions/gsd/auto-start.ts +1 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +8 -5
- package/src/resources/extensions/gsd/auto.ts +7 -83
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -12
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
- package/src/resources/extensions/gsd/commands/context.ts +0 -5
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
- package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
- package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
- package/src/resources/extensions/gsd/db-writer.ts +9 -17
- package/src/resources/extensions/gsd/doctor-checks.ts +180 -2
- package/src/resources/extensions/gsd/doctor-types.ts +7 -1
- package/src/resources/extensions/gsd/doctor.ts +6 -3
- package/src/resources/extensions/gsd/gsd-db.ts +16 -3
- package/src/resources/extensions/gsd/guided-flow.ts +1 -2
- package/src/resources/extensions/gsd/journal.ts +6 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
- package/src/resources/extensions/gsd/preferences-types.ts +2 -2
- package/src/resources/extensions/gsd/preferences.ts +7 -3
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +21 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
- package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
- package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/prompts/queue.md +2 -2
- package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/session-lock.ts +0 -4
- package/src/resources/extensions/gsd/state.ts +8 -0
- package/src/resources/extensions/gsd/sync-lock.ts +94 -0
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +42 -3
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +220 -0
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +74 -11
- package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
- package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
- package/src/resources/extensions/gsd/types.ts +8 -0
- package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
- package/src/resources/extensions/gsd/workflow-events.ts +154 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +243 -0
- package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
- package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
- package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
- package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +4 -9
- package/src/resources/extensions/gsd/worktree-resolver.ts +37 -0
- package/src/resources/extensions/gsd/write-intercept.ts +90 -0
- package/src/resources/extensions/voice/index.ts +11 -21
- package/src/resources/extensions/voice/linux-ready.ts +87 -0
- package/src/resources/extensions/voice/tests/linux-ready.test.ts +124 -0
- package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.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/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{wUzEX1U3CmFcMry2SUDJn → ZIDqryyYDroh_8AnaAOSG}/_ssgManifest.js +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import { describe, it } from "node:test";
|
|
3
|
-
import type { Api, Model } from "@gsd/pi-ai";
|
|
3
|
+
import type { Api, Model, SimpleStreamOptions, Context, AssistantMessageEventStream } from "@gsd/pi-ai";
|
|
4
|
+
import { getApiProvider } from "@gsd/pi-ai";
|
|
4
5
|
import type { AuthStorage } from "./auth-storage.js";
|
|
5
6
|
import { ModelRegistry } from "./model-registry.js";
|
|
6
7
|
|
|
@@ -17,11 +18,11 @@ function createRegistry(hasAuthFn?: (provider: string) => boolean): ModelRegistr
|
|
|
17
18
|
return new ModelRegistry(authStorage, undefined);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
function createProviderModel(id: string): NonNullable<Parameters<ModelRegistry["registerProvider"]>[1]["models"]>[number] {
|
|
21
|
+
function createProviderModel(id: string, api?: string): NonNullable<Parameters<ModelRegistry["registerProvider"]>[1]["models"]>[number] {
|
|
21
22
|
return {
|
|
22
23
|
id,
|
|
23
24
|
name: id,
|
|
24
|
-
api: "openai-completions",
|
|
25
|
+
api: (api ?? "openai-completions") as Api,
|
|
25
26
|
reasoning: false,
|
|
26
27
|
input: ["text"],
|
|
27
28
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
@@ -34,34 +35,89 @@ function findModel(registry: ModelRegistry, provider: string, id: string): Model
|
|
|
34
35
|
return registry.getAvailable().find((m) => m.provider === provider && m.id === id);
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
function makeModel(provider: string, id: string, api: string): Model<Api> {
|
|
39
|
+
return {
|
|
40
|
+
id,
|
|
41
|
+
name: id,
|
|
42
|
+
api: api as Api,
|
|
43
|
+
provider,
|
|
44
|
+
baseUrl: `${provider}:`,
|
|
45
|
+
reasoning: false,
|
|
46
|
+
input: ["text"],
|
|
47
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
48
|
+
contextWindow: 128000,
|
|
49
|
+
maxTokens: 16384,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function makeContext(): Context {
|
|
54
|
+
return {
|
|
55
|
+
systemPrompt: "test",
|
|
56
|
+
messages: [{ role: "user", content: "hello", timestamp: Date.now() }],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** No-op streamSimple for tests that need one to pass validation but don't inspect it. */
|
|
61
|
+
const noopStreamSimple = (_model: Model<Api>, _context: Context, _options?: SimpleStreamOptions) => {
|
|
62
|
+
return {
|
|
63
|
+
[Symbol.asyncIterator]() { return { next: async () => ({ value: undefined, done: true as const }) }; },
|
|
64
|
+
result: () => Promise.resolve({ role: "assistant" as const, content: [], api: "test" as Api, provider: "test", model: "test", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop" as const, timestamp: Date.now() }),
|
|
65
|
+
push: () => {},
|
|
66
|
+
end: () => {},
|
|
67
|
+
} as unknown as AssistantMessageEventStream;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/** Create a spy streamSimple that captures the options it receives and returns a stub stream. */
|
|
71
|
+
function createStreamSpy(): {
|
|
72
|
+
streamSimple: (model: Model<Api>, context: Context, options?: SimpleStreamOptions) => AssistantMessageEventStream;
|
|
73
|
+
getCapturedOptions: () => SimpleStreamOptions | undefined;
|
|
74
|
+
} {
|
|
75
|
+
let capturedOptions: SimpleStreamOptions | undefined;
|
|
76
|
+
const streamSimple = (_model: Model<Api>, _context: Context, options?: SimpleStreamOptions) => {
|
|
77
|
+
capturedOptions = options;
|
|
78
|
+
// Return a minimal stub that satisfies AssistantMessageEventStream
|
|
79
|
+
return {
|
|
80
|
+
[Symbol.asyncIterator]() { return { next: async () => ({ value: undefined, done: true as const }) }; },
|
|
81
|
+
result: () => Promise.resolve({ role: "assistant" as const, content: [], api: "test" as Api, provider: "test", model: "test", usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } }, stopReason: "stop" as const, timestamp: Date.now() }),
|
|
82
|
+
push: () => {},
|
|
83
|
+
end: () => {},
|
|
84
|
+
} as unknown as AssistantMessageEventStream;
|
|
85
|
+
};
|
|
86
|
+
return { streamSimple, getCapturedOptions: () => capturedOptions };
|
|
87
|
+
}
|
|
88
|
+
|
|
37
89
|
// ─── Registration ─────────────────────────────────────────────────────────────
|
|
38
90
|
|
|
39
91
|
describe("ModelRegistry authMode — registration", () => {
|
|
40
|
-
it("registers externalCli provider without apiKey/oauth", () => {
|
|
92
|
+
it("registers externalCli provider with streamSimple and without apiKey/oauth", () => {
|
|
41
93
|
const registry = createRegistry();
|
|
94
|
+
const spy = createStreamSpy();
|
|
42
95
|
assert.doesNotThrow(() => {
|
|
43
96
|
registry.registerProvider("cli-provider", {
|
|
44
97
|
authMode: "externalCli",
|
|
45
98
|
baseUrl: "https://cli.local",
|
|
46
99
|
api: "openai-completions",
|
|
100
|
+
streamSimple: spy.streamSimple,
|
|
47
101
|
models: [createProviderModel("cli-model")],
|
|
48
102
|
});
|
|
49
103
|
});
|
|
50
104
|
});
|
|
51
105
|
|
|
52
|
-
it("registers none provider without apiKey/oauth", () => {
|
|
106
|
+
it("registers none provider with streamSimple and without apiKey/oauth", () => {
|
|
53
107
|
const registry = createRegistry();
|
|
108
|
+
const spy = createStreamSpy();
|
|
54
109
|
assert.doesNotThrow(() => {
|
|
55
110
|
registry.registerProvider("none-provider", {
|
|
56
111
|
authMode: "none",
|
|
57
112
|
baseUrl: "http://localhost:11434",
|
|
58
113
|
api: "openai-completions",
|
|
114
|
+
streamSimple: spy.streamSimple,
|
|
59
115
|
models: [createProviderModel("local-model")],
|
|
60
116
|
});
|
|
61
117
|
});
|
|
62
118
|
});
|
|
63
119
|
|
|
64
|
-
it("rejects apiKey provider without apiKey or oauth", () => {
|
|
120
|
+
it("rejects apiKey provider without apiKey or oauth — message mentions authMode", () => {
|
|
65
121
|
const registry = createRegistry();
|
|
66
122
|
assert.throws(() => {
|
|
67
123
|
registry.registerProvider("apikey-provider", {
|
|
@@ -70,6 +126,10 @@ describe("ModelRegistry authMode — registration", () => {
|
|
|
70
126
|
api: "openai-completions",
|
|
71
127
|
models: [createProviderModel("model")],
|
|
72
128
|
});
|
|
129
|
+
}, (err: Error) => {
|
|
130
|
+
assert.ok(err.message.includes("authMode"), "error message must mention authMode");
|
|
131
|
+
assert.ok(err.message.includes("externalCli"), "error message must suggest externalCli");
|
|
132
|
+
return true;
|
|
73
133
|
});
|
|
74
134
|
});
|
|
75
135
|
|
|
@@ -81,6 +141,79 @@ describe("ModelRegistry authMode — registration", () => {
|
|
|
81
141
|
api: "openai-completions",
|
|
82
142
|
models: [createProviderModel("model")],
|
|
83
143
|
});
|
|
144
|
+
}, (err: Error) => {
|
|
145
|
+
assert.ok(err.message.includes("authMode"), "error message must mention authMode");
|
|
146
|
+
return true;
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("rejects externalCli provider without streamSimple", () => {
|
|
151
|
+
const registry = createRegistry();
|
|
152
|
+
assert.throws(() => {
|
|
153
|
+
registry.registerProvider("cli-no-stream", {
|
|
154
|
+
authMode: "externalCli",
|
|
155
|
+
baseUrl: "https://cli.local",
|
|
156
|
+
api: "openai-completions",
|
|
157
|
+
models: [createProviderModel("model")],
|
|
158
|
+
});
|
|
159
|
+
}, (err: Error) => {
|
|
160
|
+
assert.ok(err.message.includes("streamSimple"), "error message must mention streamSimple");
|
|
161
|
+
assert.ok(err.message.includes("externalCli"), "error message must mention authMode");
|
|
162
|
+
return true;
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("rejects none provider without streamSimple", () => {
|
|
167
|
+
const registry = createRegistry();
|
|
168
|
+
assert.throws(() => {
|
|
169
|
+
registry.registerProvider("none-no-stream", {
|
|
170
|
+
authMode: "none",
|
|
171
|
+
baseUrl: "http://localhost:11434",
|
|
172
|
+
api: "openai-completions",
|
|
173
|
+
models: [createProviderModel("model")],
|
|
174
|
+
});
|
|
175
|
+
}, (err: Error) => {
|
|
176
|
+
assert.ok(err.message.includes("streamSimple"), "error message must mention streamSimple");
|
|
177
|
+
assert.ok(err.message.includes("none"), "error message must mention authMode");
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("rejects externalCli provider that also sets apiKey", () => {
|
|
183
|
+
const registry = createRegistry();
|
|
184
|
+
const spy = createStreamSpy();
|
|
185
|
+
assert.throws(() => {
|
|
186
|
+
registry.registerProvider("cli-with-key", {
|
|
187
|
+
authMode: "externalCli",
|
|
188
|
+
baseUrl: "https://cli.local",
|
|
189
|
+
api: "openai-completions",
|
|
190
|
+
apiKey: "SHOULD_NOT_EXIST",
|
|
191
|
+
streamSimple: spy.streamSimple,
|
|
192
|
+
models: [createProviderModel("model")],
|
|
193
|
+
});
|
|
194
|
+
}, (err: Error) => {
|
|
195
|
+
assert.ok(err.message.includes("apiKey"), "error message must mention apiKey");
|
|
196
|
+
assert.ok(err.message.includes("externalCli"), "error message must mention authMode");
|
|
197
|
+
return true;
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("rejects none provider that also sets apiKey", () => {
|
|
202
|
+
const registry = createRegistry();
|
|
203
|
+
const spy = createStreamSpy();
|
|
204
|
+
assert.throws(() => {
|
|
205
|
+
registry.registerProvider("none-with-key", {
|
|
206
|
+
authMode: "none",
|
|
207
|
+
baseUrl: "http://localhost:11434",
|
|
208
|
+
api: "openai-completions",
|
|
209
|
+
apiKey: "SHOULD_NOT_EXIST",
|
|
210
|
+
streamSimple: spy.streamSimple,
|
|
211
|
+
models: [createProviderModel("model")],
|
|
212
|
+
});
|
|
213
|
+
}, (err: Error) => {
|
|
214
|
+
assert.ok(err.message.includes("apiKey"), "error message must mention apiKey");
|
|
215
|
+
assert.ok(err.message.includes("none"), "error message must mention authMode");
|
|
216
|
+
return true;
|
|
84
217
|
});
|
|
85
218
|
});
|
|
86
219
|
});
|
|
@@ -99,6 +232,7 @@ describe("ModelRegistry authMode — getProviderAuthMode", () => {
|
|
|
99
232
|
authMode: "externalCli",
|
|
100
233
|
baseUrl: "https://cli.local",
|
|
101
234
|
api: "openai-completions",
|
|
235
|
+
streamSimple: noopStreamSimple,
|
|
102
236
|
models: [createProviderModel("m")],
|
|
103
237
|
});
|
|
104
238
|
assert.equal(registry.getProviderAuthMode("cli"), "externalCli");
|
|
@@ -110,6 +244,7 @@ describe("ModelRegistry authMode — getProviderAuthMode", () => {
|
|
|
110
244
|
authMode: "none",
|
|
111
245
|
baseUrl: "http://localhost:11434",
|
|
112
246
|
api: "openai-completions",
|
|
247
|
+
streamSimple: noopStreamSimple,
|
|
113
248
|
models: [createProviderModel("m")],
|
|
114
249
|
});
|
|
115
250
|
assert.equal(registry.getProviderAuthMode("local"), "none");
|
|
@@ -125,6 +260,7 @@ describe("ModelRegistry authMode — isProviderRequestReady", () => {
|
|
|
125
260
|
authMode: "externalCli",
|
|
126
261
|
baseUrl: "https://cli.local",
|
|
127
262
|
api: "openai-completions",
|
|
263
|
+
streamSimple: noopStreamSimple,
|
|
128
264
|
models: [createProviderModel("m")],
|
|
129
265
|
});
|
|
130
266
|
assert.equal(registry.isProviderRequestReady("cli"), true);
|
|
@@ -136,6 +272,7 @@ describe("ModelRegistry authMode — isProviderRequestReady", () => {
|
|
|
136
272
|
authMode: "none",
|
|
137
273
|
baseUrl: "http://localhost:11434",
|
|
138
274
|
api: "openai-completions",
|
|
275
|
+
streamSimple: noopStreamSimple,
|
|
139
276
|
models: [createProviderModel("m")],
|
|
140
277
|
});
|
|
141
278
|
assert.equal(registry.isProviderRequestReady("local"), true);
|
|
@@ -161,6 +298,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
|
|
|
161
298
|
authMode: "externalCli",
|
|
162
299
|
baseUrl: "https://cli.local",
|
|
163
300
|
api: "openai-completions",
|
|
301
|
+
streamSimple: noopStreamSimple,
|
|
164
302
|
isReady: () => false,
|
|
165
303
|
models: [createProviderModel("m")],
|
|
166
304
|
});
|
|
@@ -185,6 +323,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
|
|
|
185
323
|
authMode: "externalCli",
|
|
186
324
|
baseUrl: "https://cli.local",
|
|
187
325
|
api: "openai-completions",
|
|
326
|
+
streamSimple: noopStreamSimple,
|
|
188
327
|
isReady: () => true,
|
|
189
328
|
models: [createProviderModel("m")],
|
|
190
329
|
});
|
|
@@ -197,6 +336,7 @@ describe("ModelRegistry authMode — isReady callback", () => {
|
|
|
197
336
|
authMode: "externalCli",
|
|
198
337
|
baseUrl: "https://cli.local",
|
|
199
338
|
api: "openai-completions",
|
|
339
|
+
streamSimple: noopStreamSimple,
|
|
200
340
|
models: [createProviderModel("m")],
|
|
201
341
|
});
|
|
202
342
|
// externalCli without isReady → true (default)
|
|
@@ -213,6 +353,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
|
|
|
213
353
|
authMode: "externalCli",
|
|
214
354
|
baseUrl: "https://cli.local",
|
|
215
355
|
api: "openai-completions",
|
|
356
|
+
streamSimple: noopStreamSimple,
|
|
216
357
|
models: [createProviderModel("cli-model")],
|
|
217
358
|
});
|
|
218
359
|
assert.ok(findModel(registry, "cli", "cli-model"));
|
|
@@ -224,6 +365,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
|
|
|
224
365
|
authMode: "none",
|
|
225
366
|
baseUrl: "http://localhost:11434",
|
|
226
367
|
api: "openai-completions",
|
|
368
|
+
streamSimple: noopStreamSimple,
|
|
227
369
|
models: [createProviderModel("local-model")],
|
|
228
370
|
});
|
|
229
371
|
assert.ok(findModel(registry, "local", "local-model"));
|
|
@@ -235,6 +377,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
|
|
|
235
377
|
authMode: "externalCli",
|
|
236
378
|
baseUrl: "https://cli.local",
|
|
237
379
|
api: "openai-completions",
|
|
380
|
+
streamSimple: noopStreamSimple,
|
|
238
381
|
isReady: () => false,
|
|
239
382
|
models: [createProviderModel("m")],
|
|
240
383
|
});
|
|
@@ -243,10 +386,7 @@ describe("ModelRegistry authMode — getAvailable", () => {
|
|
|
243
386
|
|
|
244
387
|
it("excludes apiKey models without stored auth", () => {
|
|
245
388
|
const registry = createRegistry(() => false);
|
|
246
|
-
// Built-in providers have no registeredProviders entry, so authMode defaults to apiKey
|
|
247
|
-
// getAvailable filters by isProviderRequestReady → hasAuth → false
|
|
248
389
|
const available = registry.getAvailable();
|
|
249
|
-
// No models should be available since hasAuth returns false for everything
|
|
250
390
|
assert.equal(available.length, 0);
|
|
251
391
|
});
|
|
252
392
|
});
|
|
@@ -260,6 +400,7 @@ describe("ModelRegistry authMode — getApiKey", () => {
|
|
|
260
400
|
authMode: "externalCli",
|
|
261
401
|
baseUrl: "https://cli.local",
|
|
262
402
|
api: "openai-completions",
|
|
403
|
+
streamSimple: noopStreamSimple,
|
|
263
404
|
models: [createProviderModel("m")],
|
|
264
405
|
});
|
|
265
406
|
const model = registry.getAll().find((m) => m.provider === "cli")!;
|
|
@@ -272,6 +413,7 @@ describe("ModelRegistry authMode — getApiKey", () => {
|
|
|
272
413
|
authMode: "none",
|
|
273
414
|
baseUrl: "http://localhost:11434",
|
|
274
415
|
api: "openai-completions",
|
|
416
|
+
streamSimple: noopStreamSimple,
|
|
275
417
|
models: [createProviderModel("m")],
|
|
276
418
|
});
|
|
277
419
|
const model = registry.getAll().find((m) => m.provider === "local")!;
|
|
@@ -280,9 +422,153 @@ describe("ModelRegistry authMode — getApiKey", () => {
|
|
|
280
422
|
|
|
281
423
|
it("delegates to authStorage for apiKey provider", async () => {
|
|
282
424
|
const registry = createRegistry();
|
|
283
|
-
// authStorage.getApiKey returns undefined (no key configured)
|
|
284
|
-
// For apiKey providers this is an expected "no key" response, not early exit
|
|
285
425
|
const key = await registry.getApiKeyForProvider("anthropic");
|
|
286
426
|
assert.equal(key, undefined);
|
|
287
427
|
});
|
|
288
428
|
});
|
|
429
|
+
|
|
430
|
+
// ─── streamSimple apiKey stripping ────────────────────────────────────────────
|
|
431
|
+
|
|
432
|
+
describe("ModelRegistry authMode — streamSimple apiKey boundary", () => {
|
|
433
|
+
it("strips apiKey from options for externalCli provider", () => {
|
|
434
|
+
const registry = createRegistry();
|
|
435
|
+
const spy = createStreamSpy();
|
|
436
|
+
const apiType = `ext-cli-strip-${Date.now()}`;
|
|
437
|
+
|
|
438
|
+
registry.registerProvider("cli-strip", {
|
|
439
|
+
authMode: "externalCli",
|
|
440
|
+
baseUrl: "https://cli.local",
|
|
441
|
+
api: apiType as Api,
|
|
442
|
+
streamSimple: spy.streamSimple,
|
|
443
|
+
models: [createProviderModel("m", apiType)],
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const provider = getApiProvider(apiType as Api);
|
|
447
|
+
assert.ok(provider, "provider must be registered in api registry");
|
|
448
|
+
|
|
449
|
+
provider.streamSimple(
|
|
450
|
+
makeModel("cli-strip", "m", apiType),
|
|
451
|
+
makeContext(),
|
|
452
|
+
{ apiKey: "should-be-stripped", maxTokens: 1024 } as SimpleStreamOptions,
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
const captured = spy.getCapturedOptions();
|
|
456
|
+
assert.ok(captured, "streamSimple must have been called");
|
|
457
|
+
assert.equal("apiKey" in captured, false, "apiKey must not exist in options for externalCli provider");
|
|
458
|
+
assert.equal(captured.maxTokens, 1024, "other options must pass through");
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it("strips apiKey from options for none provider", () => {
|
|
462
|
+
const registry = createRegistry();
|
|
463
|
+
const spy = createStreamSpy();
|
|
464
|
+
const apiType = `none-strip-${Date.now()}`;
|
|
465
|
+
|
|
466
|
+
registry.registerProvider("none-strip", {
|
|
467
|
+
authMode: "none",
|
|
468
|
+
baseUrl: "http://localhost:11434",
|
|
469
|
+
api: apiType as Api,
|
|
470
|
+
streamSimple: spy.streamSimple,
|
|
471
|
+
models: [createProviderModel("m", apiType)],
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
const provider = getApiProvider(apiType as Api);
|
|
475
|
+
assert.ok(provider, "provider must be registered in api registry");
|
|
476
|
+
|
|
477
|
+
provider.streamSimple(
|
|
478
|
+
makeModel("none-strip", "m", apiType),
|
|
479
|
+
makeContext(),
|
|
480
|
+
{ apiKey: "should-be-stripped", maxTokens: 2048 } as SimpleStreamOptions,
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
const captured = spy.getCapturedOptions();
|
|
484
|
+
assert.ok(captured, "streamSimple must have been called");
|
|
485
|
+
assert.equal("apiKey" in captured, false, "apiKey must not exist in options for none provider");
|
|
486
|
+
assert.equal(captured.maxTokens, 2048, "other options must pass through");
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it("preserves apiKey in options for apiKey provider", () => {
|
|
490
|
+
const registry = createRegistry();
|
|
491
|
+
const spy = createStreamSpy();
|
|
492
|
+
const apiType = `apikey-preserve-${Date.now()}`;
|
|
493
|
+
|
|
494
|
+
registry.registerProvider("apikey-preserve", {
|
|
495
|
+
apiKey: "MY_KEY",
|
|
496
|
+
baseUrl: "https://api.local",
|
|
497
|
+
api: apiType as Api,
|
|
498
|
+
streamSimple: spy.streamSimple,
|
|
499
|
+
models: [createProviderModel("m", apiType)],
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
const provider = getApiProvider(apiType as Api);
|
|
503
|
+
assert.ok(provider, "provider must be registered in api registry");
|
|
504
|
+
|
|
505
|
+
provider.streamSimple(
|
|
506
|
+
makeModel("apikey-preserve", "m", apiType),
|
|
507
|
+
makeContext(),
|
|
508
|
+
{ apiKey: "sk-real-key", maxTokens: 4096 } as SimpleStreamOptions,
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
const captured = spy.getCapturedOptions();
|
|
512
|
+
assert.ok(captured, "streamSimple must have been called");
|
|
513
|
+
assert.equal(captured.apiKey, "sk-real-key", "apiKey must be preserved for apiKey provider");
|
|
514
|
+
assert.equal(captured.maxTokens, 4096, "other options must pass through");
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
it("handles undefined options for externalCli provider", () => {
|
|
518
|
+
const registry = createRegistry();
|
|
519
|
+
const spy = createStreamSpy();
|
|
520
|
+
const apiType = `ext-cli-undef-${Date.now()}`;
|
|
521
|
+
|
|
522
|
+
registry.registerProvider("cli-undef", {
|
|
523
|
+
authMode: "externalCli",
|
|
524
|
+
baseUrl: "https://cli.local",
|
|
525
|
+
api: apiType as Api,
|
|
526
|
+
streamSimple: spy.streamSimple,
|
|
527
|
+
models: [createProviderModel("m", apiType)],
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const provider = getApiProvider(apiType as Api);
|
|
531
|
+
assert.ok(provider, "provider must be registered in api registry");
|
|
532
|
+
|
|
533
|
+
provider.streamSimple(
|
|
534
|
+
makeModel("cli-undef", "m", apiType),
|
|
535
|
+
makeContext(),
|
|
536
|
+
undefined,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
const captured = spy.getCapturedOptions();
|
|
540
|
+
assert.ok(captured !== undefined, "streamSimple must have been called");
|
|
541
|
+
assert.equal("apiKey" in captured, false, "apiKey must not exist even when options is undefined");
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("strips apiKey but preserves signal and other fields for externalCli", () => {
|
|
545
|
+
const registry = createRegistry();
|
|
546
|
+
const spy = createStreamSpy();
|
|
547
|
+
const apiType = `ext-cli-fields-${Date.now()}`;
|
|
548
|
+
const abortController = new AbortController();
|
|
549
|
+
|
|
550
|
+
registry.registerProvider("cli-fields", {
|
|
551
|
+
authMode: "externalCli",
|
|
552
|
+
baseUrl: "https://cli.local",
|
|
553
|
+
api: apiType as Api,
|
|
554
|
+
streamSimple: spy.streamSimple,
|
|
555
|
+
models: [createProviderModel("m", apiType)],
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
const provider = getApiProvider(apiType as Api);
|
|
559
|
+
assert.ok(provider, "provider must be registered in api registry");
|
|
560
|
+
|
|
561
|
+
provider.streamSimple(
|
|
562
|
+
makeModel("cli-fields", "m", apiType),
|
|
563
|
+
makeContext(),
|
|
564
|
+
{ apiKey: "strip-me", maxTokens: 8192, signal: abortController.signal, reasoning: "high" } as SimpleStreamOptions,
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
const captured = spy.getCapturedOptions();
|
|
568
|
+
assert.ok(captured, "streamSimple must have been called");
|
|
569
|
+
assert.equal("apiKey" in captured, false, "apiKey must be stripped");
|
|
570
|
+
assert.equal(captured.maxTokens, 8192, "maxTokens must pass through");
|
|
571
|
+
assert.equal(captured.signal, abortController.signal, "signal must pass through");
|
|
572
|
+
assert.equal((captured as Record<string, unknown>).reasoning, "high", "reasoning must pass through");
|
|
573
|
+
});
|
|
574
|
+
});
|
|
@@ -623,7 +623,18 @@ export class ModelRegistry {
|
|
|
623
623
|
if (!config.api) {
|
|
624
624
|
throw new Error(`Provider ${providerName}: "api" is required when registering streamSimple.`);
|
|
625
625
|
}
|
|
626
|
-
const
|
|
626
|
+
const rawStreamSimple = config.streamSimple;
|
|
627
|
+
const authMode = config.authMode ?? "apiKey";
|
|
628
|
+
|
|
629
|
+
// Keyless providers never see apiKey in options — enforced at registration,
|
|
630
|
+
// not by convention. Prevents undefined from reaching any handler.
|
|
631
|
+
const streamSimple = (authMode === "externalCli" || authMode === "none")
|
|
632
|
+
? ((model: Model<Api>, context: Context, options?: SimpleStreamOptions) => {
|
|
633
|
+
const { apiKey: _, ...opts } = options ?? {};
|
|
634
|
+
return rawStreamSimple(model, context, opts as SimpleStreamOptions);
|
|
635
|
+
})
|
|
636
|
+
: rawStreamSimple;
|
|
637
|
+
|
|
627
638
|
registerApiProvider(
|
|
628
639
|
{
|
|
629
640
|
api: config.api,
|
|
@@ -649,7 +660,22 @@ export class ModelRegistry {
|
|
|
649
660
|
}
|
|
650
661
|
const authMode = config.authMode ?? (config.oauth ? "oauth" : config.apiKey ? "apiKey" : "apiKey");
|
|
651
662
|
if (authMode === "apiKey" && !config.apiKey && !config.oauth) {
|
|
652
|
-
throw new Error(
|
|
663
|
+
throw new Error(
|
|
664
|
+
`Provider ${providerName}: "apiKey" or "oauth" is required when authMode is "apiKey" (the default). ` +
|
|
665
|
+
`Set authMode to "externalCli" or "none" for keyless providers.`,
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
if ((authMode === "externalCli" || authMode === "none") && !config.streamSimple) {
|
|
669
|
+
throw new Error(
|
|
670
|
+
`Provider ${providerName}: "streamSimple" is required when authMode is "${authMode}". ` +
|
|
671
|
+
`Keyless providers must supply their own stream handler.`,
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
if ((authMode === "externalCli" || authMode === "none") && config.apiKey) {
|
|
675
|
+
throw new Error(
|
|
676
|
+
`Provider ${providerName}: "apiKey" cannot be set when authMode is "${authMode}". ` +
|
|
677
|
+
`Keyless providers should not provide API key credentials.`,
|
|
678
|
+
);
|
|
653
679
|
}
|
|
654
680
|
|
|
655
681
|
// Parse and add new models
|
|
@@ -834,7 +860,8 @@ export class ModelRegistry {
|
|
|
834
860
|
*/
|
|
835
861
|
export interface ProviderConfigInput {
|
|
836
862
|
authMode?: ProviderAuthMode;
|
|
837
|
-
/** Optional readiness check. Called by isProviderRequestReady() before default auth checks.
|
|
863
|
+
/** Optional readiness check. Called by isProviderRequestReady() before default auth checks.
|
|
864
|
+
* Trusted at the same level as extension code — extensions already have arbitrary code execution. */
|
|
838
865
|
isReady?: () => boolean;
|
|
839
866
|
baseUrl?: string;
|
|
840
867
|
apiKey?: string;
|