instar 1.3.64 → 1.3.65

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.
Files changed (30) hide show
  1. package/dist/commands/reflect.d.ts.map +1 -1
  2. package/dist/commands/reflect.js +2 -1
  3. package/dist/commands/reflect.js.map +1 -1
  4. package/dist/commands/server.d.ts.map +1 -1
  5. package/dist/commands/server.js +16 -4
  6. package/dist/commands/server.js.map +1 -1
  7. package/dist/core/CircuitBreakingIntelligenceProvider.d.ts +40 -0
  8. package/dist/core/CircuitBreakingIntelligenceProvider.d.ts.map +1 -0
  9. package/dist/core/CircuitBreakingIntelligenceProvider.js +72 -0
  10. package/dist/core/CircuitBreakingIntelligenceProvider.js.map +1 -0
  11. package/dist/core/ClaudeCliIntelligenceProvider.d.ts.map +1 -1
  12. package/dist/core/ClaudeCliIntelligenceProvider.js +5 -2
  13. package/dist/core/ClaudeCliIntelligenceProvider.js.map +1 -1
  14. package/dist/core/CodexCliIntelligenceProvider.d.ts.map +1 -1
  15. package/dist/core/CodexCliIntelligenceProvider.js +3 -1
  16. package/dist/core/CodexCliIntelligenceProvider.js.map +1 -1
  17. package/dist/core/LlmCircuitBreaker.d.ts +137 -0
  18. package/dist/core/LlmCircuitBreaker.d.ts.map +1 -0
  19. package/dist/core/LlmCircuitBreaker.js +236 -0
  20. package/dist/core/LlmCircuitBreaker.js.map +1 -0
  21. package/dist/core/intelligenceProviderFactory.d.ts.map +1 -1
  22. package/dist/core/intelligenceProviderFactory.js +8 -3
  23. package/dist/core/intelligenceProviderFactory.js.map +1 -1
  24. package/dist/core/types.d.ts +22 -0
  25. package/dist/core/types.d.ts.map +1 -1
  26. package/dist/core/types.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/data/builtin-manifest.json +2 -2
  29. package/upgrades/1.3.65.md +67 -0
  30. package/upgrades/side-effects/llm-ratelimit-circuit-breaker.md +75 -0
@@ -0,0 +1,40 @@
1
+ /**
2
+ * CircuitBreakingIntelligenceProvider — wraps any IntelligenceProvider with the
3
+ * account-global LlmCircuitBreaker.
4
+ *
5
+ * This is the structural chokepoint that makes the breaker universal: every
6
+ * IntelligenceProvider built by the factory (and at the few direct construction
7
+ * sites) is wrapped once, so EVERY consumer — current and future — is protected
8
+ * without having to remember to consult the breaker (Structure > Willpower).
9
+ *
10
+ * Behaviour:
11
+ * - Before delegating, ask the breaker. If it's open, throw
12
+ * LlmCircuitOpenError WITHOUT spawning the underlying provider — this is
13
+ * what actually stops the bleeding: no `claude -p` subprocess, no tokens.
14
+ * - On success, tell the breaker the provider responded (closes it).
15
+ * - On a rate-limit error, trip the breaker and re-throw a typed
16
+ * RateLimitError so callers/telemetry can distinguish it.
17
+ * - On any other error, close the breaker (the limit isn't the cause) and
18
+ * re-throw the original error unchanged so existing fallback paths behave
19
+ * exactly as before.
20
+ *
21
+ * Callers that already swallow provider errors (e.g. PromptGate's silent
22
+ * fallback) will swallow LlmCircuitOpenError too — which is precisely the
23
+ * desired behaviour: skip the LLM step while the breaker is open and fall back
24
+ * to heuristics, at zero subprocess cost.
25
+ */
26
+ import type { IntelligenceProvider, IntelligenceOptions } from './types.js';
27
+ import { LlmCircuitBreaker } from './LlmCircuitBreaker.js';
28
+ export declare class CircuitBreakingIntelligenceProvider implements IntelligenceProvider {
29
+ private readonly inner;
30
+ private readonly breaker;
31
+ constructor(inner: IntelligenceProvider, breaker?: LlmCircuitBreaker);
32
+ evaluate(prompt: string, options?: IntelligenceOptions): Promise<string>;
33
+ }
34
+ /**
35
+ * Wrap a provider with the shared circuit breaker. No-ops on null (so callers
36
+ * can pass a possibly-null factory result through unchanged) and is idempotent
37
+ * (never double-wraps).
38
+ */
39
+ export declare function wrapIntelligenceWithCircuitBreaker(provider: IntelligenceProvider | null | undefined): IntelligenceProvider | null;
40
+ //# sourceMappingURL=CircuitBreakingIntelligenceProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CircuitBreakingIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/CircuitBreakingIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EACL,iBAAiB,EAKlB,MAAM,wBAAwB,CAAC;AAEhC,qBAAa,mCAAoC,YAAW,oBAAoB;IAE5E,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBADP,KAAK,EAAE,oBAAoB,EAC3B,OAAO,GAAE,iBAA0C;IAGhE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CAwB/E;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,GAChD,oBAAoB,GAAG,IAAI,CAI7B"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * CircuitBreakingIntelligenceProvider — wraps any IntelligenceProvider with the
3
+ * account-global LlmCircuitBreaker.
4
+ *
5
+ * This is the structural chokepoint that makes the breaker universal: every
6
+ * IntelligenceProvider built by the factory (and at the few direct construction
7
+ * sites) is wrapped once, so EVERY consumer — current and future — is protected
8
+ * without having to remember to consult the breaker (Structure > Willpower).
9
+ *
10
+ * Behaviour:
11
+ * - Before delegating, ask the breaker. If it's open, throw
12
+ * LlmCircuitOpenError WITHOUT spawning the underlying provider — this is
13
+ * what actually stops the bleeding: no `claude -p` subprocess, no tokens.
14
+ * - On success, tell the breaker the provider responded (closes it).
15
+ * - On a rate-limit error, trip the breaker and re-throw a typed
16
+ * RateLimitError so callers/telemetry can distinguish it.
17
+ * - On any other error, close the breaker (the limit isn't the cause) and
18
+ * re-throw the original error unchanged so existing fallback paths behave
19
+ * exactly as before.
20
+ *
21
+ * Callers that already swallow provider errors (e.g. PromptGate's silent
22
+ * fallback) will swallow LlmCircuitOpenError too — which is precisely the
23
+ * desired behaviour: skip the LLM step while the breaker is open and fall back
24
+ * to heuristics, at zero subprocess cost.
25
+ */
26
+ import { LlmCircuitOpenError, RateLimitError, getLlmCircuitBreaker, isRateLimitError, } from './LlmCircuitBreaker.js';
27
+ export class CircuitBreakingIntelligenceProvider {
28
+ inner;
29
+ breaker;
30
+ constructor(inner, breaker = getLlmCircuitBreaker()) {
31
+ this.inner = inner;
32
+ this.breaker = breaker;
33
+ }
34
+ async evaluate(prompt, options) {
35
+ const gate = this.breaker.acquire();
36
+ if (!gate.allow) {
37
+ throw new LlmCircuitOpenError(gate.retryAfterMs);
38
+ }
39
+ try {
40
+ const result = await this.inner.evaluate(prompt, options);
41
+ this.breaker.onResolved();
42
+ return result;
43
+ }
44
+ catch (err) {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ if (err instanceof RateLimitError || isRateLimitError(message)) {
47
+ this.breaker.onRateLimited(message);
48
+ if (err instanceof RateLimitError)
49
+ throw err;
50
+ throw new RateLimitError(message, err);
51
+ }
52
+ // Non-rate-limit failure (timeout, parse error, network blip, …): not the
53
+ // breaker's concern. Close it so we don't keep all LLM features down on an
54
+ // unrelated error, and surface the original error to the caller's fallback.
55
+ this.breaker.onResolved();
56
+ throw err;
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Wrap a provider with the shared circuit breaker. No-ops on null (so callers
62
+ * can pass a possibly-null factory result through unchanged) and is idempotent
63
+ * (never double-wraps).
64
+ */
65
+ export function wrapIntelligenceWithCircuitBreaker(provider) {
66
+ if (!provider)
67
+ return null;
68
+ if (provider instanceof CircuitBreakingIntelligenceProvider)
69
+ return provider;
70
+ return new CircuitBreakingIntelligenceProvider(provider);
71
+ }
72
+ //# sourceMappingURL=CircuitBreakingIntelligenceProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CircuitBreakingIntelligenceProvider.js","sourceRoot":"","sources":["../../src/core/CircuitBreakingIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAEL,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,OAAO,mCAAmC;IAE3B;IACA;IAFnB,YACmB,KAA2B,EAC3B,UAA6B,oBAAoB,EAAE;QADnD,UAAK,GAAL,KAAK,CAAsB;QAC3B,YAAO,GAAP,OAAO,CAA4C;IACnE,CAAC;IAEJ,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAA6B;QAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,mBAAmB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC1D,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAC1B,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,IAAI,GAAG,YAAY,cAAc,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC/D,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;gBACpC,IAAI,GAAG,YAAY,cAAc;oBAAE,MAAM,GAAG,CAAC;gBAC7C,MAAM,IAAI,cAAc,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YACzC,CAAC;YACD,0EAA0E;YAC1E,2EAA2E;YAC3E,4EAA4E;YAC5E,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,kCAAkC,CAChD,QAAiD;IAEjD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,QAAQ,YAAY,mCAAmC;QAAE,OAAO,QAAQ,CAAC;IAC7E,OAAO,IAAI,mCAAmC,CAAC,QAAQ,CAAC,CAAC;AAC3D,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeCliIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/ClaudeCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAO5E,qBAAa,6BAA8B,YAAW,oBAAoB;IACxE,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAYxB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CA4C/E"}
1
+ {"version":3,"file":"ClaudeCliIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/ClaudeCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAO5E,qBAAa,6BAA8B,YAAW,oBAAoB;IACxE,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,EAAE,MAAM;IAYxB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CA+C/E"}
@@ -55,8 +55,11 @@ export class ClaudeCliIntelligenceProvider {
55
55
  env: childEnv,
56
56
  }, (error, stdout, stderr) => {
57
57
  if (error) {
58
- // Timeout or other error — return empty so caller can fall back
59
- reject(new Error(`Claude CLI error: ${error.message}${stderr ? ` — ${stderr.slice(0, 200)}` : ''}`));
58
+ // Timeout or other error — reject so caller can fall back. Include a
59
+ // generous stderr slice so the circuit breaker's rate-limit
60
+ // classifier (isRateLimitError) can see usage/limit language that
61
+ // often appears past the first 200 chars.
62
+ reject(new Error(`Claude CLI error: ${error.message}${stderr ? ` — ${stderr.slice(0, 600)}` : ''}`));
60
63
  return;
61
64
  }
62
65
  resolve(stdout.trim());
@@ -1 +1 @@
1
- {"version":3,"file":"ClaudeCliIntelligenceProvider.js","sourceRoot":"","sources":["../../src/core/ClaudeCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,OAAO,6BAA6B;IAChC,UAAU,CAAS;IAE3B,YAAY,UAAkB;QAC5B,wEAAwE;QACxE,mEAAmE;QACnE,oEAAoE;QACpE,qEAAqE;QACrE,mEAAmE;QACnE,wEAAwE;QACxE,yDAAyD;QACzD,mBAAmB,CAAC,+BAA+B,CAAC,CAAC;QACrD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAA6B;QAC1D,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,IAAI,aAAa,CAAC,CAAC;QAE9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG;gBACX,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,KAAK;gBAChB,aAAa,EAAE,GAAG;gBAClB,iBAAiB,EAAE,MAAM;gBACzB,8DAA8D;gBAC9D,4DAA4D;gBAC5D,mBAAmB,EAAE,MAAM;aAC5B,CAAC;YAEF,uEAAuE;YACvE,4EAA4E;YAC5E,0EAA0E;YAC1E,yEAAyE;YACzE,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YACpC,OAAO,QAAQ,CAAC,UAAU,CAAC;YAC3B,OAAO,QAAQ,CAAC,iBAAiB,CAAC;YAElC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE;gBAC5C,2EAA2E;gBAC3E,2EAA2E;gBAC3E,qEAAqE;gBACrE,8DAA8D;gBAC9D,OAAO,EAAE,OAAO,EAAE,SAAS,IAAI,kBAAkB;gBACjD,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM;gBAC9B,GAAG,EAAE,QAAQ;aACd,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBAC3B,IAAI,KAAK,EAAE,CAAC;oBACV,gEAAgE;oBAChE,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;oBACrG,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;YAEH,qEAAqE;YACrE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"ClaudeCliIntelligenceProvider.js","sourceRoot":"","sources":["../../src/core/ClaudeCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,OAAO,6BAA6B;IAChC,UAAU,CAAS;IAE3B,YAAY,UAAkB;QAC5B,wEAAwE;QACxE,mEAAmE;QACnE,oEAAoE;QACpE,qEAAqE;QACrE,mEAAmE;QACnE,wEAAwE;QACxE,yDAAyD;QACzD,mBAAmB,CAAC,+BAA+B,CAAC,CAAC;QACrD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAA6B;QAC1D,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,KAAK,IAAI,aAAa,CAAC,CAAC;QAE9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG;gBACX,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,KAAK;gBAChB,aAAa,EAAE,GAAG;gBAClB,iBAAiB,EAAE,MAAM;gBACzB,8DAA8D;gBAC9D,4DAA4D;gBAC5D,mBAAmB,EAAE,MAAM;aAC5B,CAAC;YAEF,uEAAuE;YACvE,4EAA4E;YAC5E,0EAA0E;YAC1E,yEAAyE;YACzE,MAAM,QAAQ,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YACpC,OAAO,QAAQ,CAAC,UAAU,CAAC;YAC3B,OAAO,QAAQ,CAAC,iBAAiB,CAAC;YAElC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE;gBAC5C,2EAA2E;gBAC3E,2EAA2E;gBAC3E,qEAAqE;gBACrE,8DAA8D;gBAC9D,OAAO,EAAE,OAAO,EAAE,SAAS,IAAI,kBAAkB;gBACjD,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,MAAM;gBAC9B,GAAG,EAAE,QAAQ;aACd,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBAC3B,IAAI,KAAK,EAAE,CAAC;oBACV,qEAAqE;oBACrE,4DAA4D;oBAC5D,kEAAkE;oBAClE,0CAA0C;oBAC1C,MAAM,CAAC,IAAI,KAAK,CAAC,qBAAqB,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;oBACrG,OAAO;gBACT,CAAC;gBAED,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,CAAC,CAAC,CAAC;YAEH,qEAAqE;YACrE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"CodexCliIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/CodexCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA6C5E,MAAM,WAAW,mCAAmC;IAClD,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;IACrE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,4BAA6B,YAAW,oBAAoB;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyD;gBAEzE,OAAO,EAAE,mCAAmC;IASlD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CA+E/E"}
1
+ {"version":3,"file":"CodexCliIntelligenceProvider.d.ts","sourceRoot":"","sources":["../../src/core/CodexCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA6C5E,MAAM,WAAW,mCAAmC;IAClD,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;IACrE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qBAAa,4BAA6B,YAAW,oBAAoB;IACvE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyD;gBAEzE,OAAO,EAAE,mCAAmC;IASlD,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC;CAiF/E"}
@@ -119,8 +119,10 @@ export class CodexCliIntelligenceProvider {
119
119
  env: childEnv,
120
120
  }, (error, stdout, stderr) => {
121
121
  if (error) {
122
+ // Generous stderr slice so the circuit breaker's rate-limit
123
+ // classifier can see usage/limit language past the first 200 chars.
122
124
  reject(new Error(`Codex CLI error: ${error.message}` +
123
- (stderr ? ` — ${stderr.slice(0, 200)}` : '')));
125
+ (stderr ? ` — ${stderr.slice(0, 600)}` : '')));
124
126
  return;
125
127
  }
126
128
  resolve(stdout.trim());
@@ -1 +1 @@
1
- {"version":3,"file":"CodexCliIntelligenceProvider.js","sourceRoot":"","sources":["../../src/core/CodexCliIntelligenceProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,4DAA4D,CAAC;AAEhG,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,+BAA+B,GAAG,6BAA6B,CAAC;AAEtE,IAAI,gBAAgB,GAAkB,IAAI,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,SAAS,6BAA6B;IACpC,IAAI,gBAAgB,IAAI,UAAU,CAAC,gBAAgB,CAAC;QAAE,OAAO,gBAAgB,CAAC;IAC9E,gBAAgB,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,+BAA+B,CAAC,CAAC,CAAC;IAChF,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAqBD,MAAM,OAAO,4BAA4B;IACtB,SAAS,CAAS;IAClB,WAAW,CAAyD;IAErF,YAAY,OAA4C;QACtD,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,WAAW,CAAC;QACtD,uEAAuE;QACvE,yEAAyE;QACzE,0EAA0E;QAC1E,qEAAqE;IACvE,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAA6B;QAC1D,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAElD,MAAM,UAAU,GAAG,6BAA6B,EAAE,CAAC;QAEnD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG;gBACX,MAAM;gBACN,SAAS,EAAE,KAAK;gBAChB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,sEAAsE;gBACtE,oEAAoE;gBACpE,yDAAyD;gBACzD,mEAAmE;gBACnE,qEAAqE;gBACrE,uDAAuD;gBACvD,MAAM,EAAE,UAAU;gBAClB,oEAAoE;gBACpE,+DAA+D;gBAC/D,IAAI,EAAE,yBAAyB;gBAC/B,iEAAiE;gBACjE,+DAA+D;gBAC/D,iEAAiE;gBACjE,sCAAsC;gBACtC,kFAAkF;gBAClF,6DAA6D;gBAC7D,iEAAiE;gBACjE,4BAA4B;gBAC5B,uBAAuB;gBACvB,6DAA6D;gBAC7D,gEAAgE;gBAChE,gEAAgE;gBAChE,YAAY;gBACZ,MAAM;aACP,CAAC;YAEF,mEAAmE;YACnE,uEAAuE;YACvE,mEAAmE;YACnE,oEAAoE;YACpE,mEAAmE;YACnE,qEAAqE;YACrE,2DAA2D;YAC3D,EAAE;YACF,kEAAkE;YAClE,iEAAiE;YACjE,kEAAkE;YAClE,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;YAEtC,MAAM,KAAK,GAAG,QAAQ,CACpB,IAAI,CAAC,SAAS,EACd,IAAI,EACJ;gBACE,2EAA2E;gBAC3E,2EAA2E;gBAC3E,OAAO,EAAE,OAAO,EAAE,SAAS,IAAI,kBAAkB;gBACjD,SAAS,EAAE,IAAI,GAAG,IAAI;gBACtB,GAAG,EAAE,QAAQ;aACd,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;gBACxB,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CACJ,IAAI,KAAK,CACP,oBAAoB,KAAK,CAAC,OAAO,EAAE;wBACjC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC/C,CACF,CAAC;oBACF,OAAO;gBACT,CAAC;gBACD,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACzB,CAAC,CACF,CAAC;YAEF,iEAAiE;YACjE,kEAAkE;YAClE,kEAAkE;YAClE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACrB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
1
+ {"version":3,"file":"CodexCliIntelligenceProvider.js","sourceRoot":"","sources":["../../src/core/CodexCliIntelligenceProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * LlmCircuitBreaker — account-global reactive circuit breaker for LLM calls.
3
+ *
4
+ * Motivating incident (2026-05-28): a wild agent burned $452/$455 in usage
5
+ * credits because PromptGate's per-tick Haiku detection loop kept calling
6
+ * `claude -p` while the account was over its weekly spend limit. The loop's
7
+ * error handler swallowed the rate-limit error with no backoff, so every
8
+ * monitor tick spawned another doomed (or, with auto-reload, freshly-billed)
9
+ * subprocess. There is NO legitimate reason to keep calling the LLM once the
10
+ * provider has told us we are over our limit — every further call is either
11
+ * wasted (rejected) or actively harmful (auto-reload refuels the burn).
12
+ *
13
+ * This breaker reacts to the provider's OWN rate-limit signal in milliseconds,
14
+ * which is distinct from (and complementary to) the volume-based
15
+ * token-burn-detection system (BurnDetector + LlmRateGate), which reacts to
16
+ * statistical token-share over a ~30-minute window. Where the burn system
17
+ * answers "this path is spending too much," the breaker answers "the provider
18
+ * just said we're rate-limited — stop."
19
+ *
20
+ * Scope: ACCOUNT-GLOBAL. A usage/rate limit applies to the whole subscription,
21
+ * not one session or one component, so a single shared breaker pauses every
22
+ * LLM-backed feature (PromptGate, PresenceProxy, PromiseBeacon, sentinels,
23
+ * reviewers) at once. It is wired structurally at the IntelligenceProvider
24
+ * construction chokepoint via CircuitBreakingIntelligenceProvider, so no
25
+ * consumer has to remember to consult it (Structure > Willpower).
26
+ *
27
+ * State: in-memory, process-local. A restart resets it — which is correct: the
28
+ * first call after restart probes the provider, and if still limited the
29
+ * breaker re-trips immediately.
30
+ */
31
+ /** Thrown by a provider (or re-thrown by the decorator) when the underlying LLM call was rejected for a usage/rate limit. */
32
+ export declare class RateLimitError extends Error {
33
+ readonly providerError?: unknown | undefined;
34
+ readonly isRateLimit: true;
35
+ constructor(message: string, providerError?: unknown | undefined);
36
+ }
37
+ /** Thrown by the decorator when the breaker is open — the call was refused WITHOUT spawning the underlying provider. */
38
+ export declare class LlmCircuitOpenError extends Error {
39
+ readonly retryAfterMs: number;
40
+ readonly circuitOpen: true;
41
+ constructor(retryAfterMs: number);
42
+ }
43
+ /**
44
+ * Classify whether an error message indicates a provider usage/rate/spend
45
+ * limit (as opposed to a timeout, a parse error, a network blip, etc.).
46
+ *
47
+ * Pure + exported so it is unit-testable against representative CLI error
48
+ * strings from both `claude -p` and `codex exec`. Deliberately conservative:
49
+ * a false positive trips the breaker and degrades all LLM-backed features to
50
+ * heuristic-only for one open window, so we match only language that is
51
+ * unambiguously about hitting a usage/spend/quota ceiling — never generic
52
+ * "error"/"failed"/timeout text.
53
+ */
54
+ export declare function isRateLimitError(message: string | null | undefined): boolean;
55
+ type CircuitState = 'closed' | 'open' | 'half-open';
56
+ export interface LlmCircuitBreakerOptions {
57
+ /** How long to stay fully open before allowing a single probe. Default 15 min. */
58
+ openMs?: number;
59
+ /** When false, the breaker is a passthrough — acquire() always allows, records are no-ops. Default true. */
60
+ enabled?: boolean;
61
+ /** Injectable clock for tests. */
62
+ now?: () => number;
63
+ /** Injectable logger (defaults to console.warn → server log). */
64
+ log?: (line: string) => void;
65
+ }
66
+ export interface AcquireDecision {
67
+ /** Whether the caller may invoke the underlying provider. */
68
+ allow: boolean;
69
+ /** True when this acquisition is the single half-open probe. */
70
+ probe: boolean;
71
+ /** When blocked, how long until the next probe window (ms). */
72
+ retryAfterMs: number;
73
+ }
74
+ export interface LlmCircuitBreakerStatus {
75
+ state: CircuitState;
76
+ enabled: boolean;
77
+ openUntil: number | null;
78
+ retryAfterMs: number;
79
+ tripCount: number;
80
+ lastReason: string | null;
81
+ lastTrippedAt: number | null;
82
+ }
83
+ export declare class LlmCircuitBreaker {
84
+ private state;
85
+ private openUntil;
86
+ private firstOpenedAt;
87
+ private probeInFlight;
88
+ private tripCount;
89
+ private lastReason;
90
+ private lastTrippedAt;
91
+ private openMs;
92
+ private enabled;
93
+ private readonly now;
94
+ private readonly log;
95
+ constructor(opts?: LlmCircuitBreakerOptions);
96
+ /** Runtime reconfiguration (called once at server startup from config). */
97
+ configure(opts: {
98
+ openMs?: number;
99
+ enabled?: boolean;
100
+ }): void;
101
+ /**
102
+ * Gate a call. Call BEFORE invoking the underlying provider. When it returns
103
+ * { allow: false }, the caller MUST NOT call the provider — throw
104
+ * LlmCircuitOpenError instead (the whole point is to avoid the spend).
105
+ *
106
+ * Exactly one probe is admitted when transitioning out of the open window;
107
+ * concurrent callers are blocked until that probe resolves.
108
+ */
109
+ acquire(): AcquireDecision;
110
+ /**
111
+ * Record that the underlying call returned (or threw a non-rate-limit
112
+ * error). Either way the rate-limit condition is not currently present, so
113
+ * the breaker closes. A non-rate-limit error is handled by the caller's own
114
+ * fallback — it is not the breaker's concern, and staying open on unrelated
115
+ * errors would needlessly keep all LLM features down.
116
+ */
117
+ onResolved(): void;
118
+ /**
119
+ * Record that the underlying call was rejected for a usage/rate limit. Opens
120
+ * (or re-extends) the breaker for a full window.
121
+ */
122
+ onRateLimited(reason: string): void;
123
+ status(): LlmCircuitBreakerStatus;
124
+ /** Reset to a clean closed state. For tests + an explicit operator override. */
125
+ reset(): void;
126
+ }
127
+ /** Process-wide account-global breaker. All wrapped providers share this instance. */
128
+ export declare function getLlmCircuitBreaker(): LlmCircuitBreaker;
129
+ /** Configure the singleton at startup. Safe to call before first use. */
130
+ export declare function configureLlmCircuitBreaker(opts: {
131
+ openMs?: number;
132
+ enabled?: boolean;
133
+ }): void;
134
+ /** Test-only: drop the singleton so a fresh one is built next access. */
135
+ export declare function __resetLlmCircuitBreakerSingleton(): void;
136
+ export {};
137
+ //# sourceMappingURL=LlmCircuitBreaker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmCircuitBreaker.d.ts","sourceRoot":"","sources":["../../src/core/LlmCircuitBreaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,6HAA6H;AAC7H,qBAAa,cAAe,SAAQ,KAAK;IAEV,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO;IAD7D,QAAQ,CAAC,WAAW,EAAG,IAAI,CAAU;gBACzB,OAAO,EAAE,MAAM,EAAW,aAAa,CAAC,EAAE,OAAO,YAAA;CAI9D;AAED,wHAAwH;AACxH,qBAAa,mBAAoB,SAAQ,KAAK;IAEhC,QAAQ,CAAC,YAAY,EAAE,MAAM;IADzC,QAAQ,CAAC,WAAW,EAAG,IAAI,CAAU;gBAChB,YAAY,EAAE,MAAM;CAM1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAyC5E;AAED,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAEpD,MAAM,WAAW,wBAAwB;IACvC,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4GAA4G;IAC5G,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iEAAiE;IACjE,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe;IAC9B,6DAA6D;IAC7D,KAAK,EAAE,OAAO,CAAC;IACf,gEAAgE;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAID,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,aAAa,CAAuB;IAE5C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAyB;gBAEjC,IAAI,GAAE,wBAA6B;IAO/C,2EAA2E;IAC3E,SAAS,CAAC,IAAI,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAK7D;;;;;;;OAOG;IACH,OAAO,IAAI,eAAe;IA+B1B;;;;;;OAMG;IACH,UAAU,IAAI,IAAI;IAUlB;;;OAGG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAmBnC,MAAM,IAAI,uBAAuB;IAajC,gFAAgF;IAChF,KAAK,IAAI,IAAI;CAMd;AAID,sFAAsF;AACtF,wBAAgB,oBAAoB,IAAI,iBAAiB,CAGxD;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAE7F;AAED,yEAAyE;AACzE,wBAAgB,iCAAiC,IAAI,IAAI,CAExD"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * LlmCircuitBreaker — account-global reactive circuit breaker for LLM calls.
3
+ *
4
+ * Motivating incident (2026-05-28): a wild agent burned $452/$455 in usage
5
+ * credits because PromptGate's per-tick Haiku detection loop kept calling
6
+ * `claude -p` while the account was over its weekly spend limit. The loop's
7
+ * error handler swallowed the rate-limit error with no backoff, so every
8
+ * monitor tick spawned another doomed (or, with auto-reload, freshly-billed)
9
+ * subprocess. There is NO legitimate reason to keep calling the LLM once the
10
+ * provider has told us we are over our limit — every further call is either
11
+ * wasted (rejected) or actively harmful (auto-reload refuels the burn).
12
+ *
13
+ * This breaker reacts to the provider's OWN rate-limit signal in milliseconds,
14
+ * which is distinct from (and complementary to) the volume-based
15
+ * token-burn-detection system (BurnDetector + LlmRateGate), which reacts to
16
+ * statistical token-share over a ~30-minute window. Where the burn system
17
+ * answers "this path is spending too much," the breaker answers "the provider
18
+ * just said we're rate-limited — stop."
19
+ *
20
+ * Scope: ACCOUNT-GLOBAL. A usage/rate limit applies to the whole subscription,
21
+ * not one session or one component, so a single shared breaker pauses every
22
+ * LLM-backed feature (PromptGate, PresenceProxy, PromiseBeacon, sentinels,
23
+ * reviewers) at once. It is wired structurally at the IntelligenceProvider
24
+ * construction chokepoint via CircuitBreakingIntelligenceProvider, so no
25
+ * consumer has to remember to consult it (Structure > Willpower).
26
+ *
27
+ * State: in-memory, process-local. A restart resets it — which is correct: the
28
+ * first call after restart probes the provider, and if still limited the
29
+ * breaker re-trips immediately.
30
+ */
31
+ /** Thrown by a provider (or re-thrown by the decorator) when the underlying LLM call was rejected for a usage/rate limit. */
32
+ export class RateLimitError extends Error {
33
+ providerError;
34
+ isRateLimit = true;
35
+ constructor(message, providerError) {
36
+ super(message);
37
+ this.providerError = providerError;
38
+ this.name = 'RateLimitError';
39
+ }
40
+ }
41
+ /** Thrown by the decorator when the breaker is open — the call was refused WITHOUT spawning the underlying provider. */
42
+ export class LlmCircuitOpenError extends Error {
43
+ retryAfterMs;
44
+ circuitOpen = true;
45
+ constructor(retryAfterMs) {
46
+ super(`LLM circuit breaker open — provider rate-limited; pausing LLM-backed work, retry in ~${Math.ceil(retryAfterMs / 1000)}s`);
47
+ this.retryAfterMs = retryAfterMs;
48
+ this.name = 'LlmCircuitOpenError';
49
+ }
50
+ }
51
+ /**
52
+ * Classify whether an error message indicates a provider usage/rate/spend
53
+ * limit (as opposed to a timeout, a parse error, a network blip, etc.).
54
+ *
55
+ * Pure + exported so it is unit-testable against representative CLI error
56
+ * strings from both `claude -p` and `codex exec`. Deliberately conservative:
57
+ * a false positive trips the breaker and degrades all LLM-backed features to
58
+ * heuristic-only for one open window, so we match only language that is
59
+ * unambiguously about hitting a usage/spend/quota ceiling — never generic
60
+ * "error"/"failed"/timeout text.
61
+ */
62
+ export function isRateLimitError(message) {
63
+ if (!message)
64
+ return false;
65
+ const m = message.toLowerCase();
66
+ // Explicit HTTP status codes for limit / billing rejections.
67
+ if (/\b429\b/.test(m))
68
+ return true; // Too Many Requests
69
+ if (/\b402\b/.test(m))
70
+ return true; // Payment Required
71
+ const phrases = [
72
+ 'rate limit',
73
+ 'rate-limit',
74
+ 'rate_limit',
75
+ 'ratelimit',
76
+ 'too many requests',
77
+ 'usage limit',
78
+ 'usage_limit',
79
+ 'usage-limit',
80
+ 'limit reached',
81
+ 'limit will reset',
82
+ 'limit resets',
83
+ 'reached your limit',
84
+ 'payment required',
85
+ 'out of credit',
86
+ 'credit balance is too low',
87
+ 'insufficient credit',
88
+ 'insufficient quota',
89
+ 'quota exceeded',
90
+ 'exceeded your',
91
+ 'spend limit',
92
+ 'spending limit',
93
+ 'billing',
94
+ ];
95
+ if (phrases.some((p) => m.includes(p)))
96
+ return true;
97
+ // "exceeded ... (limit|quota|usage)" with words in between.
98
+ if (/exceed(?:ed|s)?\b[^.\n]{0,40}(?:limit|quota|usage|credit)/.test(m))
99
+ return true;
100
+ // Bare "quota" is a strong-enough signal on its own (Anthropic/OpenAI both use it).
101
+ if (/\bquota\b/.test(m))
102
+ return true;
103
+ return false;
104
+ }
105
+ const DEFAULT_OPEN_MS = 15 * 60_000; // 15 minutes
106
+ export class LlmCircuitBreaker {
107
+ state = 'closed';
108
+ openUntil = 0;
109
+ firstOpenedAt = 0;
110
+ probeInFlight = false;
111
+ tripCount = 0;
112
+ lastReason = null;
113
+ lastTrippedAt = null;
114
+ openMs;
115
+ enabled;
116
+ now;
117
+ log;
118
+ constructor(opts = {}) {
119
+ this.openMs = opts.openMs && opts.openMs > 0 ? opts.openMs : DEFAULT_OPEN_MS;
120
+ this.enabled = opts.enabled ?? true;
121
+ this.now = opts.now ?? (() => Date.now());
122
+ this.log = opts.log ?? ((line) => console.warn(line));
123
+ }
124
+ /** Runtime reconfiguration (called once at server startup from config). */
125
+ configure(opts) {
126
+ if (typeof opts.openMs === 'number' && opts.openMs > 0)
127
+ this.openMs = opts.openMs;
128
+ if (typeof opts.enabled === 'boolean')
129
+ this.enabled = opts.enabled;
130
+ }
131
+ /**
132
+ * Gate a call. Call BEFORE invoking the underlying provider. When it returns
133
+ * { allow: false }, the caller MUST NOT call the provider — throw
134
+ * LlmCircuitOpenError instead (the whole point is to avoid the spend).
135
+ *
136
+ * Exactly one probe is admitted when transitioning out of the open window;
137
+ * concurrent callers are blocked until that probe resolves.
138
+ */
139
+ acquire() {
140
+ if (!this.enabled)
141
+ return { allow: true, probe: false, retryAfterMs: 0 };
142
+ const now = this.now();
143
+ if (this.state === 'closed') {
144
+ return { allow: true, probe: false, retryAfterMs: 0 };
145
+ }
146
+ if (this.state === 'open') {
147
+ if (now >= this.openUntil) {
148
+ // Window elapsed → admit exactly one probe.
149
+ this.state = 'half-open';
150
+ this.probeInFlight = true;
151
+ this.log(`[llm-circuit] half-open: admitting one probe call after ~${Math.round((now - this.firstOpenedAt) / 1000)}s open`);
152
+ return { allow: true, probe: true, retryAfterMs: 0 };
153
+ }
154
+ return { allow: false, probe: false, retryAfterMs: this.openUntil - now };
155
+ }
156
+ // half-open
157
+ if (this.probeInFlight) {
158
+ // A probe is already out — block everyone else until it resolves.
159
+ return { allow: false, probe: false, retryAfterMs: Math.max(0, this.openUntil - now) };
160
+ }
161
+ // No probe in flight (defensive) — admit one.
162
+ this.probeInFlight = true;
163
+ return { allow: true, probe: true, retryAfterMs: 0 };
164
+ }
165
+ /**
166
+ * Record that the underlying call returned (or threw a non-rate-limit
167
+ * error). Either way the rate-limit condition is not currently present, so
168
+ * the breaker closes. A non-rate-limit error is handled by the caller's own
169
+ * fallback — it is not the breaker's concern, and staying open on unrelated
170
+ * errors would needlessly keep all LLM features down.
171
+ */
172
+ onResolved() {
173
+ if (!this.enabled)
174
+ return;
175
+ if (this.state !== 'closed') {
176
+ this.log(`[llm-circuit] closing: provider responded (was ${this.state})`);
177
+ }
178
+ this.state = 'closed';
179
+ this.probeInFlight = false;
180
+ this.openUntil = 0;
181
+ }
182
+ /**
183
+ * Record that the underlying call was rejected for a usage/rate limit. Opens
184
+ * (or re-extends) the breaker for a full window.
185
+ */
186
+ onRateLimited(reason) {
187
+ if (!this.enabled)
188
+ return;
189
+ const now = this.now();
190
+ if (this.state === 'closed') {
191
+ this.firstOpenedAt = now;
192
+ }
193
+ this.tripCount += 1;
194
+ this.lastReason = reason.slice(0, 200);
195
+ this.lastTrippedAt = now;
196
+ this.state = 'open';
197
+ this.openUntil = now + this.openMs;
198
+ this.probeInFlight = false;
199
+ this.log(`[llm-circuit] OPEN: provider rate-limited — pausing ALL LLM-backed work for ~${Math.round(this.openMs / 1000)}s (trip #${this.tripCount}); reason: ${this.lastReason}`);
200
+ }
201
+ status() {
202
+ const now = this.now();
203
+ return {
204
+ state: this.state,
205
+ enabled: this.enabled,
206
+ openUntil: this.state === 'closed' ? null : this.openUntil,
207
+ retryAfterMs: this.state === 'open' ? Math.max(0, this.openUntil - now) : 0,
208
+ tripCount: this.tripCount,
209
+ lastReason: this.lastReason,
210
+ lastTrippedAt: this.lastTrippedAt,
211
+ };
212
+ }
213
+ /** Reset to a clean closed state. For tests + an explicit operator override. */
214
+ reset() {
215
+ this.state = 'closed';
216
+ this.openUntil = 0;
217
+ this.firstOpenedAt = 0;
218
+ this.probeInFlight = false;
219
+ }
220
+ }
221
+ let _singleton = null;
222
+ /** Process-wide account-global breaker. All wrapped providers share this instance. */
223
+ export function getLlmCircuitBreaker() {
224
+ if (!_singleton)
225
+ _singleton = new LlmCircuitBreaker();
226
+ return _singleton;
227
+ }
228
+ /** Configure the singleton at startup. Safe to call before first use. */
229
+ export function configureLlmCircuitBreaker(opts) {
230
+ getLlmCircuitBreaker().configure(opts);
231
+ }
232
+ /** Test-only: drop the singleton so a fresh one is built next access. */
233
+ export function __resetLlmCircuitBreakerSingleton() {
234
+ _singleton = null;
235
+ }
236
+ //# sourceMappingURL=LlmCircuitBreaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmCircuitBreaker.js","sourceRoot":"","sources":["../../src/core/LlmCircuitBreaker.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"intelligenceProviderFactory.d.ts","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKvD;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,WAAW,CAAC;AAEhE,MAAM,WAAW,gCAAgC;IAC/C;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,oBAAoB,GAAG,IAAI,CAyB7B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,GAAG,IAAI,CAMnG"}
1
+ {"version":3,"file":"intelligenceProviderFactory.d.ts","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAMvD;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,aAAa,GAAG,WAAW,CAAC;AAEhE,MAAM,WAAW,gCAAgC;IAC/C;;;;OAIG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAC;IAClC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,oBAAoB,GAAG,IAAI,CA+B7B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,qBAAqB,GAAG,IAAI,CAMnG"}
@@ -16,6 +16,7 @@
16
16
  import { detectClaudePath, detectCodexPath } from './Config.js';
17
17
  import { ClaudeCliIntelligenceProvider } from './ClaudeCliIntelligenceProvider.js';
18
18
  import { CodexCliIntelligenceProvider } from './CodexCliIntelligenceProvider.js';
19
+ import { wrapIntelligenceWithCircuitBreaker } from './CircuitBreakingIntelligenceProvider.js';
19
20
  /**
20
21
  * Build an IntelligenceProvider for the given framework. Returns null
21
22
  * if the required binary can't be located. The caller decides what
@@ -30,21 +31,25 @@ import { CodexCliIntelligenceProvider } from './CodexCliIntelligenceProvider.js'
30
31
  */
31
32
  export function buildIntelligenceProvider(options = {}) {
32
33
  const framework = options.framework ?? 'claude-code';
34
+ // Every provider the factory hands out is wrapped with the account-global
35
+ // LLM circuit breaker (CircuitBreakingIntelligenceProvider). A closed breaker
36
+ // is a transparent passthrough, so this is always safe; it only acts when the
37
+ // provider reports a usage/rate limit. See LlmCircuitBreaker for the why.
33
38
  switch (framework) {
34
39
  case 'claude-code': {
35
40
  const path = options.binaryPath ?? detectClaudePath();
36
41
  if (!path)
37
42
  return null;
38
- return new ClaudeCliIntelligenceProvider(path);
43
+ return wrapIntelligenceWithCircuitBreaker(new ClaudeCliIntelligenceProvider(path));
39
44
  }
40
45
  case 'codex-cli': {
41
46
  const path = options.binaryPath ?? detectCodexPath();
42
47
  if (!path)
43
48
  return null;
44
- return new CodexCliIntelligenceProvider({
49
+ return wrapIntelligenceWithCircuitBreaker(new CodexCliIntelligenceProvider({
45
50
  codexPath: path,
46
51
  ...(options.workingDirectory ? { workingDirectory: options.workingDirectory } : {}),
47
- });
52
+ }));
48
53
  }
49
54
  default: {
50
55
  // Exhaustiveness check — extending IntelligenceFramework without
@@ -1 +1 @@
1
- {"version":3,"file":"intelligenceProviderFactory.js","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AA0BjF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAA4C,EAAE;IAE9C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC;IAErD,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO,IAAI,6BAA6B,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,eAAe,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO,IAAI,4BAA4B,CAAC;gBACtC,SAAS,EAAE,IAAI;gBACf,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpF,CAAC,CAAC;QACL,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,iEAAiE;YACjE,sCAAsC;YACtC,MAAM,WAAW,GAAU,SAAS,CAAC;YACrC,KAAK,WAAW,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,MAAM,GAAG,GAAG,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,aAAa,CAAC;IACpE,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,WAAW,CAAC;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"intelligenceProviderFactory.js","sourceRoot":"","sources":["../../src/core/intelligenceProviderFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oCAAoC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,EAAE,kCAAkC,EAAE,MAAM,0CAA0C,CAAC;AA0B9F;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,yBAAyB,CACvC,UAA4C,EAAE;IAE9C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,aAAa,CAAC;IAErD,0EAA0E;IAC1E,8EAA8E;IAC9E,8EAA8E;IAC9E,0EAA0E;IAC1E,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC;YACtD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO,kCAAkC,CAAC,IAAI,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,IAAI,eAAe,EAAE,CAAC;YACrD,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAC;YACvB,OAAO,kCAAkC,CACvC,IAAI,4BAA4B,CAAC;gBAC/B,SAAS,EAAE,IAAI;gBACf,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpF,CAAC,CACH,CAAC;QACJ,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,iEAAiE;YACjE,sCAAsC;YACtC,MAAM,WAAW,GAAU,SAAS,CAAC;YACrC,KAAK,WAAW,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACnE,MAAM,GAAG,GAAG,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC1D,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,KAAK,aAAa,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,aAAa,CAAC;IACpE,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,OAAO;QAAE,OAAO,WAAW,CAAC;IAC/D,OAAO,IAAI,CAAC;AACd,CAAC"}