gsd-pi 2.71.0-dev.e17e0ce → 2.72.0-dev.de4c4b3
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/mcp-server.js +37 -14
- 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 +103 -6
- package/dist/resources/extensions/gsd/auto/phases.js +4 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
- package/dist/resources/extensions/gsd/auto-start.js +24 -4
- package/dist/resources/extensions/gsd/auto.js +4 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +2 -5
- package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
- package/dist/resources/extensions/gsd/error-classifier.js +4 -1
- 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/milestone-validation-gates.js +11 -12
- package/dist/resources/extensions/gsd/notification-overlay.js +26 -12
- package/dist/resources/extensions/gsd/notification-store.js +5 -4
- 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/shortcut-defs.js +7 -1
- 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 +8 -8
- 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 +8 -8
- 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-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/retry-handler.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
- package/packages/pi-coding-agent/dist/core/retry-handler.test.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/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/package.json +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
- package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
- package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
- package/pkg/package.json +1 -1
- 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 +109 -3
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +133 -2
- package/src/resources/extensions/gsd/auto/phases.ts +4 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
- package/src/resources/extensions/gsd/auto-start.ts +31 -4
- package/src/resources/extensions/gsd/auto.ts +4 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +2 -5
- package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
- package/src/resources/extensions/gsd/error-classifier.ts +4 -1
- 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/milestone-validation-gates.ts +11 -13
- package/src/resources/extensions/gsd/notification-overlay.ts +27 -11
- package/src/resources/extensions/gsd/notification-store.ts +5 -4
- 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/shortcut-defs.ts +8 -1
- package/src/resources/extensions/gsd/state.ts +13 -2
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +14 -0
- 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/format-shortcut.test.ts +16 -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/prompt-system-gate-coverage.test.ts +208 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +3 -2
- 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/{cYPZv_bAhZk2ms-Pz6vsY → f-Gremw0nLxxFUySaHRPw}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → f-Gremw0nLxxFUySaHRPw}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test for the #unconfigured-models fix: findInitialModel() must
|
|
3
|
+
* skip the saved default when its provider has no working auth, rather than
|
|
4
|
+
* returning an unusable model that every selector surface would display as
|
|
5
|
+
* "current".
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { findInitialModel } from "./model-resolver.js";
|
|
11
|
+
|
|
12
|
+
function fakeRegistry(options: {
|
|
13
|
+
models: Array<{ provider: string; id: string }>;
|
|
14
|
+
readyProviders: Set<string>;
|
|
15
|
+
}) {
|
|
16
|
+
const fullModels = options.models.map((m) => ({
|
|
17
|
+
...m,
|
|
18
|
+
name: m.id,
|
|
19
|
+
api: "anthropic-messages",
|
|
20
|
+
baseUrl: "",
|
|
21
|
+
reasoning: false,
|
|
22
|
+
input: ["text"],
|
|
23
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
24
|
+
contextWindow: 128_000,
|
|
25
|
+
maxTokens: 4096,
|
|
26
|
+
}));
|
|
27
|
+
const available = fullModels.filter((m) => options.readyProviders.has(m.provider));
|
|
28
|
+
return {
|
|
29
|
+
find(provider: string, id: string) {
|
|
30
|
+
return fullModels.find((m) => m.provider === provider && m.id === id);
|
|
31
|
+
},
|
|
32
|
+
getAvailable() {
|
|
33
|
+
return available;
|
|
34
|
+
},
|
|
35
|
+
isProviderRequestReady(provider: string) {
|
|
36
|
+
return options.readyProviders.has(provider);
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
test("findInitialModel skips saved default when provider has no auth", async () => {
|
|
42
|
+
// User saved xai/grok-4 as default, but XAI_API_KEY is unset so xai is
|
|
43
|
+
// in the registry but not ready. Previously findInitialModel() step 3
|
|
44
|
+
// returned xai anyway — now it must fall through to step 4 and pick
|
|
45
|
+
// an available model.
|
|
46
|
+
const registry = fakeRegistry({
|
|
47
|
+
models: [
|
|
48
|
+
{ provider: "xai", id: "grok-4-fast-non-reasoning" },
|
|
49
|
+
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
50
|
+
],
|
|
51
|
+
readyProviders: new Set(["anthropic"]),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await findInitialModel({
|
|
55
|
+
scopedModels: [],
|
|
56
|
+
isContinuing: false,
|
|
57
|
+
defaultProvider: "xai",
|
|
58
|
+
defaultModelId: "grok-4-fast-non-reasoning",
|
|
59
|
+
modelRegistry: registry as any,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
assert.ok(result.model, "a model must be returned");
|
|
63
|
+
assert.equal(result.model!.provider, "anthropic", "unauth'd saved default must be skipped");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("findInitialModel keeps saved default when provider has auth", async () => {
|
|
67
|
+
const registry = fakeRegistry({
|
|
68
|
+
models: [
|
|
69
|
+
{ provider: "anthropic", id: "claude-opus-4-6" },
|
|
70
|
+
{ provider: "openai", id: "gpt-5.4" },
|
|
71
|
+
],
|
|
72
|
+
readyProviders: new Set(["anthropic", "openai"]),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const result = await findInitialModel({
|
|
76
|
+
scopedModels: [],
|
|
77
|
+
isContinuing: false,
|
|
78
|
+
defaultProvider: "openai",
|
|
79
|
+
defaultModelId: "gpt-5.4",
|
|
80
|
+
modelRegistry: registry as any,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
assert.equal(result.model?.provider, "openai");
|
|
84
|
+
assert.equal(result.model?.id, "gpt-5.4");
|
|
85
|
+
});
|
|
@@ -171,6 +171,25 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|
|
171
171
|
const retryStart = emittedEvents.find((e) => e.type === "auto_retry_start");
|
|
172
172
|
assert.ok(retryStart, "Regular 429 should enter backoff retry");
|
|
173
173
|
});
|
|
174
|
+
|
|
175
|
+
it("classifies OpenRouter credit affordability errors as quota_exhausted", async () => {
|
|
176
|
+
const { deps, emittedEvents } = createMockDeps({
|
|
177
|
+
model: createMockModel("openrouter", "openai/gpt-5-pro"),
|
|
178
|
+
markUsageLimitReachedResult: false,
|
|
179
|
+
fallbackResult: null,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const handler = new RetryHandler(deps);
|
|
183
|
+
const msg = errorMessage(
|
|
184
|
+
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const result = await handler.handleRetryableError(msg);
|
|
188
|
+
|
|
189
|
+
assert.equal(result, true, "affordability error should trigger credit-aware retry");
|
|
190
|
+
const retryStart = emittedEvents.find((e) => e.type === "auto_retry_start");
|
|
191
|
+
assert.ok(retryStart, "Expected immediate retry after reducing max tokens");
|
|
192
|
+
});
|
|
174
193
|
});
|
|
175
194
|
|
|
176
195
|
describe("long-context model downgrade", () => {
|
|
@@ -271,6 +290,61 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|
|
271
290
|
});
|
|
272
291
|
});
|
|
273
292
|
|
|
293
|
+
describe("credit-aware maxTokens retry", () => {
|
|
294
|
+
it("reduces maxTokens on same model when provider reports affordable cap", async () => {
|
|
295
|
+
const expensiveModel = createMockModel("openrouter", "openai/gpt-5-pro");
|
|
296
|
+
expensiveModel.maxTokens = 128000;
|
|
297
|
+
|
|
298
|
+
const { deps, emittedEvents, onModelChangeFn } = createMockDeps({
|
|
299
|
+
model: expensiveModel,
|
|
300
|
+
markUsageLimitReachedResult: false,
|
|
301
|
+
fallbackResult: null,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const handler = new RetryHandler(deps);
|
|
305
|
+
const msg = errorMessage(
|
|
306
|
+
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const result = await handler.handleRetryableError(msg);
|
|
310
|
+
assert.equal(result, true, "should retry after reducing maxTokens");
|
|
311
|
+
|
|
312
|
+
const setModelCalls = (deps.agent.setModel as any).mock.calls;
|
|
313
|
+
assert.equal(setModelCalls.length, 1, "should apply one model downgrade");
|
|
314
|
+
const downgraded = setModelCalls[0].arguments[0] as Model<Api>;
|
|
315
|
+
assert.equal(downgraded.provider, "openrouter");
|
|
316
|
+
assert.equal(downgraded.id, "openai/gpt-5-pro");
|
|
317
|
+
assert.equal(downgraded.maxTokens, 297, "expected affordability cap with safety buffer");
|
|
318
|
+
|
|
319
|
+
assert.equal(onModelChangeFn.mock.calls.length, 1, "should notify about model update");
|
|
320
|
+
const switchEvent = emittedEvents.find((e) => e.type === "fallback_provider_switch");
|
|
321
|
+
assert.ok(switchEvent, "should emit model-adjustment event");
|
|
322
|
+
assert.ok(
|
|
323
|
+
String(switchEvent?.reason || "").includes("credit-aware retry"),
|
|
324
|
+
"switch reason should mention credit-aware retry",
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("does not mark credentials in cooldown for affordability quota errors", async () => {
|
|
329
|
+
const expensiveModel = createMockModel("openrouter", "openai/gpt-5-pro");
|
|
330
|
+
expensiveModel.maxTokens = 128000;
|
|
331
|
+
|
|
332
|
+
const { deps, markUsageLimitReached } = createMockDeps({
|
|
333
|
+
model: expensiveModel,
|
|
334
|
+
markUsageLimitReachedResult: false,
|
|
335
|
+
fallbackResult: null,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const handler = new RetryHandler(deps);
|
|
339
|
+
const msg = errorMessage(
|
|
340
|
+
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
await handler.handleRetryableError(msg);
|
|
344
|
+
assert.equal(markUsageLimitReached.mock.calls.length, 0, "quota error should skip credential cooldown");
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
274
348
|
describe("isRetryableError", () => {
|
|
275
349
|
it("considers long-context entitlement error as retryable", () => {
|
|
276
350
|
const { deps } = createMockDeps();
|
|
@@ -291,6 +365,15 @@ describe("RetryHandler — long-context entitlement 429 (#2803)", () => {
|
|
|
291
365
|
);
|
|
292
366
|
assert.equal(handler.isRetryableError(msg), false);
|
|
293
367
|
});
|
|
368
|
+
|
|
369
|
+
it("considers OpenRouter affordability credit errors as retryable", () => {
|
|
370
|
+
const { deps } = createMockDeps();
|
|
371
|
+
const handler = new RetryHandler(deps);
|
|
372
|
+
const msg = errorMessage(
|
|
373
|
+
"402 This request requires more credits, or fewer max_tokens. You requested up to 32000 tokens, but can only afford 329.",
|
|
374
|
+
);
|
|
375
|
+
assert.equal(handler.isRetryableError(msg), true);
|
|
376
|
+
});
|
|
294
377
|
});
|
|
295
378
|
|
|
296
379
|
describe("third-party block claude-code fallback (#3772)", () => {
|
|
@@ -116,7 +116,7 @@ export class RetryHandler {
|
|
|
116
116
|
// generated error from getApiKey() when credentials are in a backoff window.
|
|
117
117
|
// Re-entering the retry handler for that message creates a cascade of empty
|
|
118
118
|
// error entries in the session file, breaking resume (#3429).
|
|
119
|
-
return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|network.?(?:is\s+)?unavailable|credentials.*expired|extra usage is required|(?:out of|no) extra usage|third.party.*draw from extra|third.party.*not.*available/i.test(
|
|
119
|
+
return /overloaded|rate.?limit|too many requests|402|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|network.?(?:is\s+)?unavailable|credentials.*expired|requires more credits|can only afford|insufficient credits|not enough credits|extra usage is required|(?:out of|no) extra usage|third.party.*draw from extra|third.party.*not.*available/i.test(
|
|
120
120
|
err,
|
|
121
121
|
);
|
|
122
122
|
}
|
|
@@ -158,6 +158,14 @@ export class RetryHandler {
|
|
|
158
158
|
const isRateLimit = errorType === "rate_limit";
|
|
159
159
|
const isQuotaError = errorType === "quota_exhausted";
|
|
160
160
|
|
|
161
|
+
// Credit-aware retry (OpenRouter-style 402 affordability errors):
|
|
162
|
+
// when provider reports "can only afford N", lower maxTokens and retry
|
|
163
|
+
// on the same model before rotating credentials/providers.
|
|
164
|
+
if (isQuotaError) {
|
|
165
|
+
const adjusted = this._tryAffordableMaxTokensRetry(message, retryGeneration);
|
|
166
|
+
if (adjusted) return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
161
169
|
// Credential rotation — only for transient rate limits (#3430).
|
|
162
170
|
// Quota errors ("Extra usage is required") are account-level billing
|
|
163
171
|
// gates; rotating to another credential on the same account won't help
|
|
@@ -409,12 +417,63 @@ export class RetryHandler {
|
|
|
409
417
|
// Long-context entitlement errors are billing gates, not transient rate limits.
|
|
410
418
|
// Must be checked before the generic 429/rate_limit regex.
|
|
411
419
|
if (/extra usage is required|long context required/i.test(err)) return "quota_exhausted";
|
|
420
|
+
if (/requires more credits|can only afford|insufficient credits|not enough credits|credit balance/i.test(err))
|
|
421
|
+
return "quota_exhausted";
|
|
412
422
|
if (/quota|billing|exceeded.*limit|usage.*limit/i.test(err)) return "quota_exhausted";
|
|
413
423
|
if (/rate.?limit|too many requests|429/i.test(err)) return "rate_limit";
|
|
414
424
|
if (/500|502|503|504|server.?error|internal.?error|service.?unavailable/i.test(err)) return "server_error";
|
|
415
425
|
return "unknown";
|
|
416
426
|
}
|
|
417
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Attempt a same-model retry by reducing maxTokens when provider reports
|
|
430
|
+
* an affordability cap (e.g., "can only afford 329").
|
|
431
|
+
*/
|
|
432
|
+
private _tryAffordableMaxTokensRetry(message: AssistantMessage, retryGeneration: number): boolean {
|
|
433
|
+
const currentModel = this._deps.getModel();
|
|
434
|
+
if (!currentModel || !message.errorMessage) return false;
|
|
435
|
+
|
|
436
|
+
// Example: "can only afford 329"
|
|
437
|
+
const match = message.errorMessage.match(/can only afford\s+([\d,]+)/i);
|
|
438
|
+
if (!match?.[1]) return false;
|
|
439
|
+
|
|
440
|
+
const affordable = Number.parseInt(match[1].replace(/,/g, ""), 10);
|
|
441
|
+
if (!Number.isFinite(affordable) || affordable <= 0) return false;
|
|
442
|
+
|
|
443
|
+
// Leave a small buffer so slight input variance doesn't immediately re-fail.
|
|
444
|
+
const safetyBuffer = Math.min(64, Math.max(16, Math.floor(affordable * 0.1)));
|
|
445
|
+
const targetMaxTokens = Math.max(64, affordable - safetyBuffer);
|
|
446
|
+
const downgradedMaxTokens = Math.min(currentModel.maxTokens, targetMaxTokens);
|
|
447
|
+
if (downgradedMaxTokens >= currentModel.maxTokens) return false;
|
|
448
|
+
|
|
449
|
+
const downgradedModel = {
|
|
450
|
+
...currentModel,
|
|
451
|
+
maxTokens: downgradedMaxTokens,
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
this._deps.agent.setModel(downgradedModel);
|
|
455
|
+
this._deps.onModelChange(downgradedModel);
|
|
456
|
+
this._removeLastAssistantError();
|
|
457
|
+
|
|
458
|
+
this._deps.emit({
|
|
459
|
+
type: "fallback_provider_switch",
|
|
460
|
+
from: `${currentModel.provider}/${currentModel.id} (maxTokens=${currentModel.maxTokens})`,
|
|
461
|
+
to: `${downgradedModel.provider}/${downgradedModel.id} (maxTokens=${downgradedModel.maxTokens})`,
|
|
462
|
+
reason: `credit-aware retry: provider affordable cap ${affordable} tokens`,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
this._deps.emit({
|
|
466
|
+
type: "auto_retry_start",
|
|
467
|
+
attempt: this._retryAttempt + 1,
|
|
468
|
+
maxAttempts: this._deps.settingsManager.getRetrySettings().maxRetries,
|
|
469
|
+
delayMs: 0,
|
|
470
|
+
errorMessage: `${message.errorMessage} (reducing max tokens)`,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
this._scheduleContinue(retryGeneration);
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
|
|
418
477
|
/**
|
|
419
478
|
* Attempt to downgrade a long-context model (e.g. claude-opus-4-6[1m]) to its
|
|
420
479
|
* base model (claude-opus-4-6) when the account lacks the long-context billing
|
|
@@ -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;
|
|
@@ -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();
|
package/pkg/package.json
CHANGED
|
@@ -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.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-ops
|
|
3
|
+
description: Conflict resolution, rebase strategy, PR preparation, and changelog generation
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are a git operations specialist. You handle merge conflicts, plan rebase strategies, prepare pull requests, and generate changelogs. You understand git internals well enough to choose the right strategy for each situation.
|
|
8
|
+
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
### Conflict Resolution
|
|
12
|
+
- Analyze conflict markers and understand both sides' intent
|
|
13
|
+
- Choose the correct resolution based on code context, not just recency
|
|
14
|
+
- Verify resolved code compiles and tests pass
|
|
15
|
+
|
|
16
|
+
### Rebase Strategy
|
|
17
|
+
- Assess whether rebase or merge is appropriate for the situation
|
|
18
|
+
- Plan interactive rebase sequences (squash, reorder, edit)
|
|
19
|
+
- Handle complex rebase conflicts with minimal manual intervention
|
|
20
|
+
|
|
21
|
+
### PR Preparation
|
|
22
|
+
- Write clear PR titles and descriptions from commit history
|
|
23
|
+
- Organize commits into logical, reviewable units
|
|
24
|
+
- Ensure CI checks will pass before pushing
|
|
25
|
+
|
|
26
|
+
### Changelog Generation
|
|
27
|
+
- Extract user-facing changes from commit messages and code diffs
|
|
28
|
+
- Categorize changes (features, fixes, breaking changes)
|
|
29
|
+
- Write changelog entries for the target audience (users, not developers)
|
|
30
|
+
|
|
31
|
+
## Process
|
|
32
|
+
|
|
33
|
+
1. Assess the git state — branches, commits, conflicts, divergence
|
|
34
|
+
2. Determine the goal — clean history, resolved conflicts, PR ready
|
|
35
|
+
3. Plan the steps — in order, with rollback points
|
|
36
|
+
4. Execute carefully — verify after each step
|
|
37
|
+
5. Confirm the result — clean history, passing tests
|
|
38
|
+
|
|
39
|
+
## Output Format
|
|
40
|
+
|
|
41
|
+
## Git State
|
|
42
|
+
|
|
43
|
+
Current branch, commits, conflicts, or divergence summary.
|
|
44
|
+
|
|
45
|
+
## Strategy
|
|
46
|
+
|
|
47
|
+
What to do and why this approach.
|
|
48
|
+
|
|
49
|
+
## Steps
|
|
50
|
+
|
|
51
|
+
1. Command or action — with expected outcome
|
|
52
|
+
2. Command or action — with verification
|
|
53
|
+
|
|
54
|
+
## Result
|
|
55
|
+
|
|
56
|
+
Final state after operations complete.
|