gsd-pi 2.71.0-dev.06b86c6 → 2.71.0-dev.4c35d99
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -1
- package/dist/cli.js +17 -0
- package/dist/headless-events.d.ts +2 -0
- package/dist/headless-events.js +7 -0
- package/dist/headless.js +16 -3
- package/dist/mcp-server.js +37 -14
- package/dist/resource-loader.js +6 -3
- package/dist/resources/agents/debugger.md +58 -0
- package/dist/resources/agents/doc-writer.md +43 -0
- package/dist/resources/agents/git-ops.md +56 -0
- package/dist/resources/agents/javascript-pro.md +46 -271
- package/dist/resources/agents/planner.md +55 -0
- package/dist/resources/agents/refactorer.md +47 -0
- package/dist/resources/agents/reviewer.md +48 -0
- package/dist/resources/agents/security.md +59 -0
- package/dist/resources/agents/tester.md +50 -0
- package/dist/resources/agents/typescript-pro.md +41 -235
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +113 -10
- package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
- package/dist/resources/extensions/gsd/auto/loop.js +32 -1
- package/dist/resources/extensions/gsd/auto/phases.js +5 -1
- package/dist/resources/extensions/gsd/auto/session.js +11 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
- package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-start.js +33 -6
- package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +56 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
- package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- package/dist/resources/extensions/gsd/forensics.js +19 -6
- package/dist/resources/extensions/gsd/gate-registry.js +208 -0
- package/dist/resources/extensions/gsd/gsd-db.js +41 -0
- package/dist/resources/extensions/gsd/guided-flow.js +5 -10
- package/dist/resources/extensions/gsd/metrics.js +1 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
- package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
- package/dist/resources/extensions/gsd/notification-store.js +35 -4
- package/dist/resources/extensions/gsd/notification-widget.js +5 -13
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
- package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/dist/resources/extensions/gsd/session-model-override.js +25 -0
- package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
- package/dist/resources/extensions/gsd/state.js +9 -2
- package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
- package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +4 -1
- package/dist/resources/extensions/ollama/index.js +13 -5
- package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
- package/dist/resources/extensions/subagent/agents.js +8 -0
- package/dist/resources/extensions/subagent/index.js +17 -0
- package/dist/startup-model-validation.d.ts +0 -1
- package/dist/startup-model-validation.js +6 -2
- 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 +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/dist/server.d.ts +12 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +90 -42
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/server.ts +110 -38
- package/packages/mcp-server/src/workflow-tools.ts +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +7 -4
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
- package/packages/pi-ai/src/providers/anthropic.ts +8 -4
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
- package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
- package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +43 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +7 -2
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -3
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
- package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
- package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +47 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -2
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -3
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
- package/src/resources/agents/debugger.md +58 -0
- package/src/resources/agents/doc-writer.md +43 -0
- package/src/resources/agents/git-ops.md +56 -0
- package/src/resources/agents/javascript-pro.md +46 -271
- package/src/resources/agents/planner.md +55 -0
- package/src/resources/agents/refactorer.md +47 -0
- package/src/resources/agents/reviewer.md +48 -0
- package/src/resources/agents/security.md +59 -0
- package/src/resources/agents/tester.md +50 -0
- package/src/resources/agents/typescript-pro.md +41 -235
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +122 -8
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +189 -6
- package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
- package/src/resources/extensions/gsd/auto/loop.ts +45 -1
- package/src/resources/extensions/gsd/auto/phases.ts +6 -0
- package/src/resources/extensions/gsd/auto/session.ts +11 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
- package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-start.ts +40 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +72 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
- package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- package/src/resources/extensions/gsd/forensics.ts +23 -7
- package/src/resources/extensions/gsd/gate-registry.ts +251 -0
- package/src/resources/extensions/gsd/gsd-db.ts +51 -0
- package/src/resources/extensions/gsd/guided-flow.ts +5 -10
- package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
- package/src/resources/extensions/gsd/metrics.ts +12 -1
- package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
- package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
- package/src/resources/extensions/gsd/notification-store.ts +35 -4
- package/src/resources/extensions/gsd/notification-widget.ts +5 -14
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
- package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
- package/src/resources/extensions/gsd/session-model-override.ts +36 -0
- package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
- package/src/resources/extensions/gsd/state.ts +13 -2
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
- package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/notification-widget.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
- package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +4 -1
- package/src/resources/extensions/gsd/types.ts +26 -0
- package/src/resources/extensions/ollama/index.ts +13 -3
- package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
- package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
- package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
- package/src/resources/extensions/subagent/agents.ts +10 -0
- package/src/resources/extensions/subagent/index.ts +18 -0
- package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
- /package/dist/web/standalone/.next/static/{dYVdRaunb2ZSEA8fjkT-V → OI4n_CKC-lM8IQbvGJ_tK}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{dYVdRaunb2ZSEA8fjkT-V → OI4n_CKC-lM8IQbvGJ_tK}/_ssgManifest.js +0 -0
|
@@ -504,27 +504,31 @@ export async function findInitialModel(options: {
|
|
|
504
504
|
|
|
505
505
|
// 3. Try saved default from settings
|
|
506
506
|
if (defaultProvider && defaultModelId) {
|
|
507
|
-
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
507
|
+
// Guard against stale settings defaults: only use the saved provider/model
|
|
508
|
+
// if the provider is actually request-ready (auth/OAuth/CLI ready).
|
|
509
|
+
if (modelRegistry.isProviderRequestReady(defaultProvider)) {
|
|
510
|
+
const found = modelRegistry.find(defaultProvider, defaultModelId);
|
|
511
|
+
if (found) {
|
|
512
|
+
// Check if the provider's recommended default is a higher-capability variant
|
|
513
|
+
// of the saved model (e.g. saved "claude-opus-4-6" vs recommended "claude-opus-4-6-extended").
|
|
514
|
+
// If so, prefer the recommended variant to avoid using a smaller context window (#1125).
|
|
515
|
+
const recommendedId = defaultModelPerProvider[defaultProvider as KnownProvider];
|
|
516
|
+
if (recommendedId && recommendedId !== defaultModelId && recommendedId.startsWith(defaultModelId)) {
|
|
517
|
+
const recommended = modelRegistry.find(defaultProvider, recommendedId);
|
|
518
|
+
if (recommended) {
|
|
519
|
+
model = recommended;
|
|
520
|
+
if (defaultThinkingLevel) {
|
|
521
|
+
thinkingLevel = defaultThinkingLevel;
|
|
522
|
+
}
|
|
523
|
+
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
519
524
|
}
|
|
520
|
-
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
521
525
|
}
|
|
526
|
+
model = found;
|
|
527
|
+
if (defaultThinkingLevel) {
|
|
528
|
+
thinkingLevel = defaultThinkingLevel;
|
|
529
|
+
}
|
|
530
|
+
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
522
531
|
}
|
|
523
|
-
model = found;
|
|
524
|
-
if (defaultThinkingLevel) {
|
|
525
|
-
thinkingLevel = defaultThinkingLevel;
|
|
526
|
-
}
|
|
527
|
-
return { model, thinkingLevel, fallbackMessage: undefined };
|
|
528
532
|
}
|
|
529
533
|
}
|
|
530
534
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// pi-coding-agent / CredentialCooldownError unit tests
|
|
2
|
+
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
+
|
|
4
|
+
import { describe, it } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { CredentialCooldownError } from "./sdk.js";
|
|
7
|
+
|
|
8
|
+
// ─── CredentialCooldownError ──────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
describe("CredentialCooldownError", () => {
|
|
11
|
+
it("is an instance of Error", () => {
|
|
12
|
+
const err = new CredentialCooldownError("anthropic");
|
|
13
|
+
assert.ok(err instanceof Error);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("has name set to CredentialCooldownError", () => {
|
|
17
|
+
const err = new CredentialCooldownError("anthropic");
|
|
18
|
+
assert.equal(err.name, "CredentialCooldownError");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("has code set to AUTH_COOLDOWN", () => {
|
|
22
|
+
const err = new CredentialCooldownError("anthropic");
|
|
23
|
+
assert.equal(err.code, "AUTH_COOLDOWN");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("message includes the provider name", () => {
|
|
27
|
+
const err = new CredentialCooldownError("openai");
|
|
28
|
+
assert.ok(
|
|
29
|
+
err.message.includes("openai"),
|
|
30
|
+
`Expected message to include provider "openai", got: ${err.message}`,
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("message mentions cooldown window", () => {
|
|
35
|
+
const err = new CredentialCooldownError("anthropic");
|
|
36
|
+
assert.ok(
|
|
37
|
+
/cooldown window/i.test(err.message),
|
|
38
|
+
`Expected message to mention "cooldown window", got: ${err.message}`,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("retryAfterMs is undefined when not provided", () => {
|
|
43
|
+
const err = new CredentialCooldownError("anthropic");
|
|
44
|
+
assert.equal(err.retryAfterMs, undefined);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("retryAfterMs holds the provided value when specified", () => {
|
|
48
|
+
const err = new CredentialCooldownError("anthropic", 30_000);
|
|
49
|
+
assert.equal(err.retryAfterMs, 30_000);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("retryAfterMs is 0 when explicitly passed as 0", () => {
|
|
53
|
+
const err = new CredentialCooldownError("anthropic", 0);
|
|
54
|
+
assert.equal(err.retryAfterMs, 0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("code property is readonly and always AUTH_COOLDOWN regardless of provider", () => {
|
|
58
|
+
for (const provider of ["anthropic", "openai", "google", "openrouter"]) {
|
|
59
|
+
const err = new CredentialCooldownError(provider);
|
|
60
|
+
assert.equal(err.code, "AUTH_COOLDOWN", `code should be AUTH_COOLDOWN for provider "${provider}"`);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("different providers produce different messages", () => {
|
|
65
|
+
const err1 = new CredentialCooldownError("anthropic");
|
|
66
|
+
const err2 = new CredentialCooldownError("openai");
|
|
67
|
+
assert.notEqual(err1.message, err2.message);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("can be caught as an Error in a try/catch", () => {
|
|
71
|
+
let caught: unknown;
|
|
72
|
+
try {
|
|
73
|
+
throw new CredentialCooldownError("anthropic", 5_000);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
caught = e;
|
|
76
|
+
}
|
|
77
|
+
assert.ok(caught instanceof Error);
|
|
78
|
+
assert.ok(caught instanceof CredentialCooldownError);
|
|
79
|
+
assert.equal((caught as CredentialCooldownError).retryAfterMs, 5_000);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("code property is detectable via plain object check (cross-process pattern)", () => {
|
|
83
|
+
const err = new CredentialCooldownError("anthropic", 15_000);
|
|
84
|
+
// Simulate cross-process serialization: only plain properties survive JSON round-trip
|
|
85
|
+
const plain = { code: err.code, retryAfterMs: err.retryAfterMs, message: err.message };
|
|
86
|
+
assert.equal(plain.code, "AUTH_COOLDOWN");
|
|
87
|
+
assert.equal(plain.retryAfterMs, 15_000);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structured error thrown when all credentials for a provider are in a
|
|
5
|
+
* backoff window. Carries typed metadata so callers (e.g. the auto-loop)
|
|
6
|
+
* can make informed retry decisions instead of string-matching the message.
|
|
7
|
+
*/
|
|
8
|
+
export class CredentialCooldownError extends Error {
|
|
9
|
+
readonly code = "AUTH_COOLDOWN" as const;
|
|
10
|
+
/** Milliseconds until the earliest credential becomes available, or undefined if unknown. */
|
|
11
|
+
readonly retryAfterMs: number | undefined;
|
|
12
|
+
|
|
13
|
+
constructor(provider: string, retryAfterMs?: number) {
|
|
14
|
+
super(
|
|
15
|
+
`All credentials for "${provider}" are in a cooldown window. ` +
|
|
16
|
+
`Please wait a moment and try again, or switch to a different provider.`,
|
|
17
|
+
);
|
|
18
|
+
this.name = "CredentialCooldownError";
|
|
19
|
+
this.retryAfterMs = retryAfterMs;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
2
22
|
import { Agent, type AgentMessage, type ThinkingLevel } from "@gsd/pi-agent-core";
|
|
3
23
|
import type { Message, Model } from "@gsd/pi-ai";
|
|
4
24
|
import { getAgentDir, getDocsPath } from "../config.js";
|
|
@@ -363,8 +383,12 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
363
383
|
|
|
364
384
|
// Retry key resolution with backoff to handle transient network failures
|
|
365
385
|
// (e.g., OAuth token refresh failing due to brief connectivity loss).
|
|
386
|
+
// When credentials are in a cooldown window (e.g., after a 429), wait
|
|
387
|
+
// for the backoff to expire instead of using fixed delays that are
|
|
388
|
+
// shorter than the cooldown duration.
|
|
366
389
|
const maxAttempts = 3;
|
|
367
390
|
const baseDelayMs = 2000;
|
|
391
|
+
const maxCooldownWaitMs = 60_000; // Don't wait longer than 60s (skip quota-exhausted 30min backoffs)
|
|
368
392
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
369
393
|
const key = await modelRegistry.getApiKeyForProvider(resolvedProvider);
|
|
370
394
|
if (key) return key;
|
|
@@ -379,7 +403,21 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
379
403
|
const isOAuth = model && modelRegistry.isUsingOAuth(model);
|
|
380
404
|
if (!hasAuth && !isOAuth) break;
|
|
381
405
|
|
|
382
|
-
//
|
|
406
|
+
// If credentials are in a cooldown window, wait for the earliest
|
|
407
|
+
// one to expire rather than using a fixed delay that's too short.
|
|
408
|
+
const backoffExpiry = modelRegistry.authStorage.getEarliestBackoffExpiry(resolvedProvider);
|
|
409
|
+
if (backoffExpiry !== undefined) {
|
|
410
|
+
const waitMs = backoffExpiry - Date.now() + 500; // 500ms buffer
|
|
411
|
+
if (waitMs > 0 && waitMs <= maxCooldownWaitMs) {
|
|
412
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
413
|
+
continue; // Retry immediately after cooldown clears
|
|
414
|
+
}
|
|
415
|
+
if (waitMs > maxCooldownWaitMs) {
|
|
416
|
+
break; // Quota-exhausted or very long backoff — don't block
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Standard exponential backoff for non-cooldown transient failures
|
|
383
421
|
await new Promise(resolve => setTimeout(resolve, baseDelayMs * attempt));
|
|
384
422
|
}
|
|
385
423
|
|
|
@@ -390,10 +428,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
390
428
|
// the retry handler and creating cascading error entries (#3429).
|
|
391
429
|
const hasAuth = modelRegistry.authStorage.hasAuth(resolvedProvider);
|
|
392
430
|
if (hasAuth) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
);
|
|
431
|
+
const expiry = modelRegistry.authStorage.getEarliestBackoffExpiry(resolvedProvider);
|
|
432
|
+
const retryAfterMs = expiry !== undefined ? Math.max(0, expiry - Date.now()) : undefined;
|
|
433
|
+
throw new CredentialCooldownError(resolvedProvider, retryAfterMs);
|
|
397
434
|
}
|
|
398
435
|
const model = agent.state.model;
|
|
399
436
|
const isOAuth = model && modelRegistry.isUsingOAuth(model);
|
|
@@ -401,10 +438,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
401
438
|
// If credentials exist but are all in a backoff window (quota / rate-limit),
|
|
402
439
|
// surface a specific message instead of the misleading "Authentication failed".
|
|
403
440
|
if (modelRegistry.authStorage.areAllCredentialsBackedOff(resolvedProvider)) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
);
|
|
441
|
+
const expiry = modelRegistry.authStorage.getEarliestBackoffExpiry(resolvedProvider);
|
|
442
|
+
const retryAfterMs = expiry !== undefined ? Math.max(0, expiry - Date.now()) : undefined;
|
|
443
|
+
throw new CredentialCooldownError(resolvedProvider, retryAfterMs);
|
|
408
444
|
}
|
|
409
445
|
throw new Error(
|
|
410
446
|
`Authentication failed for "${resolvedProvider}". ` +
|
package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { buildAuthUrlPresentation } from "../login-dialog.js";
|
|
4
|
+
|
|
5
|
+
describe("LoginDialogComponent", () => {
|
|
6
|
+
test("shows the full OAuth URL when the hyperlink label is truncated", () => {
|
|
7
|
+
const presentation = buildAuthUrlPresentation(
|
|
8
|
+
"https://auth.example.com/device?code=ABCD-1234&callback=oauth&state=needs-full-visibility",
|
|
9
|
+
52,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
assert.notEqual(
|
|
13
|
+
presentation.displayUrl,
|
|
14
|
+
"https://auth.example.com/device?code=ABCD-1234&callback=oauth&state=needs-full-visibility",
|
|
15
|
+
"narrow terminals should still truncate the hyperlink label",
|
|
16
|
+
);
|
|
17
|
+
assert.ok(presentation.fullUrlLines.length > 1, "truncated URLs should expose wrapped full-url lines");
|
|
18
|
+
assert.match(presentation.fullUrlLines[0] ?? "", /https:\/\/auth\.example\.com\/device\?code=ABCD-1234&/);
|
|
19
|
+
assert.match(
|
|
20
|
+
presentation.fullUrlLines[presentation.fullUrlLines.length - 1] ?? "",
|
|
21
|
+
/state=needs-full-visibility/,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -7,6 +7,27 @@ import { theme } from "../theme/theme.js";
|
|
|
7
7
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
8
8
|
import { keyHint } from "./keybinding-hints.js";
|
|
9
9
|
|
|
10
|
+
function wrapPlainText(text: string, width: number): string[] {
|
|
11
|
+
const lines: string[] = [];
|
|
12
|
+
const safeWidth = Math.max(1, width);
|
|
13
|
+
for (let idx = 0; idx < text.length; idx += safeWidth) {
|
|
14
|
+
lines.push(text.slice(idx, idx + safeWidth));
|
|
15
|
+
}
|
|
16
|
+
return lines.length > 0 ? lines : [""];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function buildAuthUrlPresentation(url: string, terminalColumns: number): {
|
|
20
|
+
displayUrl: string;
|
|
21
|
+
fullUrlLines: string[];
|
|
22
|
+
} {
|
|
23
|
+
const maxUrlWidth = Math.max(20, terminalColumns - 4);
|
|
24
|
+
const displayUrl = truncateToWidth(url, maxUrlWidth);
|
|
25
|
+
return {
|
|
26
|
+
displayUrl,
|
|
27
|
+
fullUrlLines: displayUrl === url ? [] : wrapPlainText(url, maxUrlWidth),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
/**
|
|
11
32
|
* Login dialog component - replaces editor during OAuth login flow.
|
|
12
33
|
*
|
|
@@ -124,14 +145,21 @@ export class LoginDialogComponent extends Container implements Focusable {
|
|
|
124
145
|
|
|
125
146
|
// Truncate the visible URL text so it never wraps (which would break
|
|
126
147
|
// the OSC 8 hyperlink). The full URL is still the link target.
|
|
127
|
-
const
|
|
128
|
-
const displayUrl = truncateToWidth(url, maxUrlWidth);
|
|
148
|
+
const { displayUrl, fullUrlLines } = buildAuthUrlPresentation(url, this.tui.terminal.columns);
|
|
129
149
|
const urlLink = `\x1b]8;;${url}\x07${theme.fg("accent", displayUrl)}\x1b]8;;\x07`;
|
|
130
150
|
this.contentContainer.addChild(new Text(urlLink, 1, 0));
|
|
131
151
|
|
|
132
152
|
const clickHint = process.platform === "darwin" ? "Cmd+click to open" : "Ctrl+click to open";
|
|
133
153
|
this.contentContainer.addChild(new Text(theme.fg("dim", clickHint), 1, 0));
|
|
134
154
|
|
|
155
|
+
if (fullUrlLines.length > 0) {
|
|
156
|
+
this.contentContainer.addChild(new Spacer(1));
|
|
157
|
+
this.contentContainer.addChild(new Text(theme.fg("dim", "Full URL:"), 1, 0));
|
|
158
|
+
for (const line of fullUrlLines) {
|
|
159
|
+
this.contentContainer.addChild(new Text(theme.fg("dim", line), 1, 0));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
135
163
|
if (instructions) {
|
|
136
164
|
this.contentContainer.addChild(new Spacer(1));
|
|
137
165
|
this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
|
|
@@ -120,7 +120,12 @@ export class ModelSelectorComponent extends Container implements Focusable {
|
|
|
120
120
|
this.settingsManager = settingsManager;
|
|
121
121
|
this.modelRegistry = modelRegistry;
|
|
122
122
|
this.scopedModels = scopedModels;
|
|
123
|
-
|
|
123
|
+
// Only land in "scoped" view when at least one scoped model has working
|
|
124
|
+
// auth — otherwise the user would see an empty picker (#unconfigured-models).
|
|
125
|
+
const hasReadyScopedModel = scopedModels.some((scoped) =>
|
|
126
|
+
modelRegistry.isProviderRequestReady(scoped.model.provider),
|
|
127
|
+
);
|
|
128
|
+
this.scope = hasReadyScopedModel ? "scoped" : "all";
|
|
124
129
|
this.onSelectCallback = onSelect;
|
|
125
130
|
this.onCancelCallback = onCancel;
|
|
126
131
|
|
|
@@ -215,12 +220,16 @@ export class ModelSelectorComponent extends Container implements Focusable {
|
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
this.allModels = this.sortModelsWithinProvider(models);
|
|
223
|
+
// Scoped models must also be filtered by provider readiness so users
|
|
224
|
+
// can't pick a scoped model whose provider has no API key / OAuth.
|
|
218
225
|
this.scopedModelItems = this.sortModelsWithinProvider(
|
|
219
|
-
this.scopedModels
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
226
|
+
this.scopedModels
|
|
227
|
+
.filter((scoped) => this.modelRegistry.isProviderRequestReady(scoped.model.provider))
|
|
228
|
+
.map((scoped) => ({
|
|
229
|
+
provider: scoped.model.provider,
|
|
230
|
+
id: scoped.model.id,
|
|
231
|
+
model: scoped.model,
|
|
232
|
+
})),
|
|
224
233
|
);
|
|
225
234
|
this.activeModels = this.scope === "scoped" ? this.scopedModelItems : this.allModels;
|
|
226
235
|
this.filteredModels = this.activeModels;
|
|
@@ -325,6 +325,29 @@ export class ToolExecutionComponent extends Container {
|
|
|
325
325
|
this.maybeConvertImagesForKitty();
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Finalize a pending tool call as failed/interrupted while preserving any streamed partial output.
|
|
330
|
+
*/
|
|
331
|
+
completeWithError(message?: string): void {
|
|
332
|
+
this.isPartial = false;
|
|
333
|
+
if (this.result) {
|
|
334
|
+
let content = this.result.content;
|
|
335
|
+
if (message) {
|
|
336
|
+
const alreadyHasMessage = content.some((block) => block.type === "text" && block.text === message);
|
|
337
|
+
if (!alreadyHasMessage) {
|
|
338
|
+
content = [...content, { type: "text", text: message }];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
this.result = { ...this.result, content, isError: true };
|
|
342
|
+
} else {
|
|
343
|
+
this.result = {
|
|
344
|
+
content: message ? [{ type: "text", text: message }] : [],
|
|
345
|
+
isError: true,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
this.updateDisplay();
|
|
349
|
+
}
|
|
350
|
+
|
|
328
351
|
/**
|
|
329
352
|
* Convert non-PNG images to PNG for Kitty graphics protocol.
|
|
330
353
|
* Kitty requires PNG format (f=100), so JPEG/GIF/WebP won't display.
|
|
@@ -652,6 +675,12 @@ export class ToolExecutionComponent extends Container {
|
|
|
652
675
|
text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`;
|
|
653
676
|
|
|
654
677
|
if (this.result) {
|
|
678
|
+
if (this.result.isError) {
|
|
679
|
+
const errorText = this.getTextOutput().trim() || "read failed";
|
|
680
|
+
text += `\n\n${theme.fg("error", errorText)}`;
|
|
681
|
+
return text;
|
|
682
|
+
}
|
|
683
|
+
|
|
655
684
|
const rawOutput = this.getTextOutput();
|
|
656
685
|
// Strip hashline prefixes (e.g. "1#BQ:content") for TUI display
|
|
657
686
|
const output = rawOutput.replace(/^(\s*)\d+#[ZPMQVRWSNKTXJBYH]{2}:/gm, "$1");
|
|
@@ -804,6 +833,12 @@ export class ToolExecutionComponent extends Container {
|
|
|
804
833
|
}
|
|
805
834
|
|
|
806
835
|
if (this.result) {
|
|
836
|
+
if (this.result.isError) {
|
|
837
|
+
const errorText = this.getTextOutput().trim() || "ls failed";
|
|
838
|
+
text += `\n\n${theme.fg("error", errorText)}`;
|
|
839
|
+
return text;
|
|
840
|
+
}
|
|
841
|
+
|
|
807
842
|
const output = this.getTextOutput().trim();
|
|
808
843
|
if (output) {
|
|
809
844
|
const lines = output.split("\n");
|
|
@@ -846,6 +881,12 @@ export class ToolExecutionComponent extends Container {
|
|
|
846
881
|
}
|
|
847
882
|
|
|
848
883
|
if (this.result) {
|
|
884
|
+
if (this.result.isError) {
|
|
885
|
+
const errorText = this.getTextOutput().trim() || "find failed";
|
|
886
|
+
text += `\n\n${theme.fg("error", errorText)}`;
|
|
887
|
+
return text;
|
|
888
|
+
}
|
|
889
|
+
|
|
849
890
|
const output = this.getTextOutput().trim();
|
|
850
891
|
if (output) {
|
|
851
892
|
const lines = output.split("\n");
|
|
@@ -892,6 +933,12 @@ export class ToolExecutionComponent extends Container {
|
|
|
892
933
|
}
|
|
893
934
|
|
|
894
935
|
if (this.result) {
|
|
936
|
+
if (this.result.isError) {
|
|
937
|
+
const errorText = this.getTextOutput().trim() || "grep failed";
|
|
938
|
+
text += `\n\n${theme.fg("error", errorText)}`;
|
|
939
|
+
return text;
|
|
940
|
+
}
|
|
941
|
+
|
|
895
942
|
const output = this.getTextOutput().trim();
|
|
896
943
|
if (output) {
|
|
897
944
|
const lines = output.split("\n");
|
|
@@ -369,8 +369,13 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
369
369
|
if (!errorMessage) {
|
|
370
370
|
errorMessage = host.streamingMessage.errorMessage || "Error";
|
|
371
371
|
}
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
const pendingComponents = Array.from(host.pendingTools.values());
|
|
373
|
+
if (pendingComponents.length > 0) {
|
|
374
|
+
const [first, ...rest] = pendingComponents;
|
|
375
|
+
first.completeWithError(errorMessage);
|
|
376
|
+
for (const component of rest) {
|
|
377
|
+
component.completeWithError();
|
|
378
|
+
}
|
|
374
379
|
}
|
|
375
380
|
host.pendingTools.clear();
|
|
376
381
|
} else {
|
|
@@ -52,7 +52,12 @@ export async function findExactModelMatch(host: any, searchTerm: string): Promis
|
|
|
52
52
|
|
|
53
53
|
export async function getModelCandidates(host: any): Promise<Model<any>[]> {
|
|
54
54
|
if (host.session.scopedModels.length > 0) {
|
|
55
|
-
|
|
55
|
+
// Filter scoped models by provider auth readiness so callers like
|
|
56
|
+
// findExactModelMatch can't resolve a scoped-but-unconfigured model.
|
|
57
|
+
const registry = host.session.modelRegistry;
|
|
58
|
+
return host.session.scopedModels
|
|
59
|
+
.filter((scoped: any) => registry.isProviderRequestReady(scoped.model.provider))
|
|
60
|
+
.map((scoped: any) => scoped.model);
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
host.session.modelRegistry.refresh();
|
|
@@ -1785,7 +1785,7 @@ export class InteractiveMode {
|
|
|
1785
1785
|
} else if (type === "warning") {
|
|
1786
1786
|
this.showWarning(message);
|
|
1787
1787
|
} else {
|
|
1788
|
-
this.showStatus(message);
|
|
1788
|
+
this.showStatus(message, { append: true });
|
|
1789
1789
|
}
|
|
1790
1790
|
}
|
|
1791
1791
|
|
|
@@ -2052,12 +2052,13 @@ export class InteractiveMode {
|
|
|
2052
2052
|
* If multiple status messages are emitted back-to-back (without anything else being added to the chat),
|
|
2053
2053
|
* we update the previous status line instead of appending new ones to avoid log spam.
|
|
2054
2054
|
*/
|
|
2055
|
-
private showStatus(message: string): void {
|
|
2055
|
+
private showStatus(message: string, options?: { append?: boolean }): void {
|
|
2056
|
+
const append = options?.append ?? false;
|
|
2056
2057
|
const children = this.chatContainer.children;
|
|
2057
2058
|
const last = children.length > 0 ? children[children.length - 1] : undefined;
|
|
2058
2059
|
const secondLast = children.length > 1 ? children[children.length - 2] : undefined;
|
|
2059
2060
|
|
|
2060
|
-
if (last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
|
|
2061
|
+
if (!append && last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
|
|
2061
2062
|
this.lastStatusText.setText(theme.fg("dim", message));
|
|
2062
2063
|
this.ui.requestRender();
|
|
2063
2064
|
return;
|
|
@@ -499,12 +499,14 @@ function handleHotkeysCommand(ctx: SlashCommandContext): void {
|
|
|
499
499
|
const suspend = getAppKeyDisplay(ctx.keybindings, "suspend");
|
|
500
500
|
const cycleThinkingLevel = getAppKeyDisplay(ctx.keybindings, "cycleThinkingLevel");
|
|
501
501
|
const cycleModelForward = getAppKeyDisplay(ctx.keybindings, "cycleModelForward");
|
|
502
|
+
const cycleModelBackward = getAppKeyDisplay(ctx.keybindings, "cycleModelBackward");
|
|
502
503
|
const selectModel = getAppKeyDisplay(ctx.keybindings, "selectModel");
|
|
503
504
|
const expandTools = getAppKeyDisplay(ctx.keybindings, "expandTools");
|
|
504
505
|
const toggleThinking = getAppKeyDisplay(ctx.keybindings, "toggleThinking");
|
|
505
506
|
const externalEditor = getAppKeyDisplay(ctx.keybindings, "externalEditor");
|
|
506
507
|
const followUp = getAppKeyDisplay(ctx.keybindings, "followUp");
|
|
507
508
|
const dequeue = getAppKeyDisplay(ctx.keybindings, "dequeue");
|
|
509
|
+
const pasteImage = getAppKeyDisplay(ctx.keybindings, "pasteImage");
|
|
508
510
|
|
|
509
511
|
let hotkeys = `
|
|
510
512
|
**Navigation**
|
|
@@ -540,14 +542,14 @@ function handleHotkeysCommand(ctx: SlashCommandContext): void {
|
|
|
540
542
|
| \`${exit}\` | Exit (when editor is empty) |
|
|
541
543
|
| \`${suspend}\` | Suspend to background |
|
|
542
544
|
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
543
|
-
| \`${cycleModelForward}\` | Cycle models |
|
|
545
|
+
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
544
546
|
| \`${selectModel}\` | Open model selector |
|
|
545
547
|
| \`${expandTools}\` | Toggle tool output expansion |
|
|
546
548
|
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
547
549
|
| \`${externalEditor}\` | Edit message in external editor |
|
|
548
550
|
| \`${followUp}\` | Queue follow-up message |
|
|
549
551
|
| \`${dequeue}\` | Restore queued messages |
|
|
550
|
-
| \`
|
|
552
|
+
| \`${pasteImage}\` | Paste image from clipboard |
|
|
551
553
|
| \`/\` | Slash commands |
|
|
552
554
|
| \`!\` | Run bash command |
|
|
553
555
|
| \`!!\` | Run bash command (excluded from context) |
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: debugger
|
|
3
|
+
description: Hypothesis-driven bug investigation with root cause analysis
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a debugger. Investigate bugs using a systematic, hypothesis-driven approach. Your goal is to find the root cause, not just suppress symptoms.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. **Reproduce**: Understand the symptoms — what happens vs. what should happen
|
|
12
|
+
2. **Hypothesize**: List 2-3 most likely causes based on symptoms
|
|
13
|
+
3. **Investigate**: For each hypothesis, gather evidence (read code, check logs, trace execution)
|
|
14
|
+
4. **Narrow**: Eliminate hypotheses that don't match the evidence
|
|
15
|
+
5. **Root cause**: Identify the actual cause with file:line references
|
|
16
|
+
6. **Fix**: Propose the minimal change that addresses the root cause
|
|
17
|
+
|
|
18
|
+
## Investigation Tools
|
|
19
|
+
|
|
20
|
+
- Read source files at specific line ranges
|
|
21
|
+
- Grep for error messages, function names, variable usage
|
|
22
|
+
- Check git blame for recent changes to suspect areas
|
|
23
|
+
- Read test files to understand expected behavior
|
|
24
|
+
- Run tests to reproduce failures
|
|
25
|
+
|
|
26
|
+
## Output Format
|
|
27
|
+
|
|
28
|
+
## Symptoms
|
|
29
|
+
|
|
30
|
+
What's happening vs. what's expected.
|
|
31
|
+
|
|
32
|
+
## Hypotheses
|
|
33
|
+
|
|
34
|
+
1. **[hypothesis]** — why this could be the cause
|
|
35
|
+
2. **[hypothesis]** — why this could be the cause
|
|
36
|
+
|
|
37
|
+
## Investigation
|
|
38
|
+
|
|
39
|
+
### Hypothesis 1: [name]
|
|
40
|
+
|
|
41
|
+
Evidence gathered, files read, what was found.
|
|
42
|
+
**Verdict:** Confirmed / Eliminated — reason.
|
|
43
|
+
|
|
44
|
+
### Hypothesis 2: [name]
|
|
45
|
+
|
|
46
|
+
(same structure)
|
|
47
|
+
|
|
48
|
+
## Root Cause
|
|
49
|
+
|
|
50
|
+
**File:** `path/to/file.ts:42`
|
|
51
|
+
**Cause:** Clear explanation of the bug.
|
|
52
|
+
**Why it wasn't caught:** Missing test, edge case, etc.
|
|
53
|
+
|
|
54
|
+
## Recommended Fix
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// minimal fix with explanation
|
|
58
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: doc-writer
|
|
3
|
+
description: Documentation generation from code — API docs, inline comments, READMEs
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a documentation specialist. You read code and produce clear, accurate documentation. You write for the reader, not the author — explain what they need to know to use or maintain the code.
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
1. Read the code thoroughly — understand what it does, not just how
|
|
12
|
+
2. Identify the audience — users (API docs), maintainers (inline docs), or newcomers (guides)
|
|
13
|
+
3. Write documentation that answers the reader's actual questions
|
|
14
|
+
4. Verify accuracy — every code reference must match the current implementation
|
|
15
|
+
|
|
16
|
+
## Documentation Types
|
|
17
|
+
|
|
18
|
+
- **API docs**: Function signatures, parameters, return values, examples, error cases
|
|
19
|
+
- **Inline comments**: Explain *why*, not *what* — the code shows what, comments explain intent
|
|
20
|
+
- **Module docs**: What this module does, its public API, and how it fits in the architecture
|
|
21
|
+
- **Guides**: Step-by-step instructions for common tasks with working examples
|
|
22
|
+
|
|
23
|
+
## Quality Rules
|
|
24
|
+
|
|
25
|
+
- Every claim must be verifiable against the current code
|
|
26
|
+
- Examples must be working code, not pseudocode
|
|
27
|
+
- Don't document the obvious — focus on non-obvious behavior, gotchas, and edge cases
|
|
28
|
+
- Keep it concise — more docs isn't better docs
|
|
29
|
+
- Use the project's existing documentation style and format
|
|
30
|
+
|
|
31
|
+
## Output Format
|
|
32
|
+
|
|
33
|
+
## Documentation Plan
|
|
34
|
+
|
|
35
|
+
What to document and for whom.
|
|
36
|
+
|
|
37
|
+
## Documentation
|
|
38
|
+
|
|
39
|
+
(The actual documentation content, formatted appropriately for its type)
|
|
40
|
+
|
|
41
|
+
## Accuracy Check
|
|
42
|
+
|
|
43
|
+
Files referenced and verified against current implementation.
|