@pellux/goodvibes-tui 1.0.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/CHANGELOG.md +130 -127
  2. package/README.md +32 -14
  3. package/docs/foundation-artifacts/operator-contract.json +1 -1
  4. package/package.json +2 -2
  5. package/src/cli/ensure-goodvibes-gitignore.ts +32 -0
  6. package/src/cli/entrypoint.ts +2 -0
  7. package/src/core/alert-gating.ts +67 -0
  8. package/src/core/approval-alert.ts +72 -0
  9. package/src/core/budget-breach-notifier.ts +106 -0
  10. package/src/core/conversation-rendering.ts +19 -0
  11. package/src/core/conversation.ts +58 -0
  12. package/src/core/focus-tracker.ts +41 -0
  13. package/src/core/long-task-notifier.ts +33 -6
  14. package/src/core/stream-event-wiring.ts +104 -2
  15. package/src/core/stream-stall-watchdog.ts +41 -17
  16. package/src/core/system-message-router.ts +37 -51
  17. package/src/core/turn-cancellation.ts +20 -0
  18. package/src/core/turn-event-wiring.ts +64 -3
  19. package/src/export/cost-utils.ts +126 -8
  20. package/src/input/command-registry.ts +54 -1
  21. package/src/input/commands/checkpoint-runtime.ts +280 -0
  22. package/src/input/commands/codebase-runtime.ts +192 -0
  23. package/src/input/commands/diff-runtime.ts +61 -30
  24. package/src/input/commands/eval.ts +1 -1
  25. package/src/input/commands/health-runtime.ts +1 -1
  26. package/src/input/commands/image-runtime.ts +112 -0
  27. package/src/input/commands/intelligence-runtime.ts +11 -1
  28. package/src/input/commands/local-auth-runtime.ts +4 -1
  29. package/src/input/commands/local-runtime.ts +9 -3
  30. package/src/input/commands/marketplace-runtime.ts +2 -2
  31. package/src/input/commands/memory.ts +6 -1
  32. package/src/input/commands/operator-panel-runtime.ts +40 -24
  33. package/src/input/commands/planning-runtime.ts +26 -3
  34. package/src/input/commands/platform-sandbox-runtime.ts +1 -6
  35. package/src/input/commands/plugin-runtime.ts +2 -2
  36. package/src/input/commands/policy-dispatch.ts +21 -0
  37. package/src/input/commands/provider-accounts-runtime.ts +1 -1
  38. package/src/input/commands/qrcode-runtime.ts +3 -3
  39. package/src/input/commands/recall-review.ts +35 -0
  40. package/src/input/commands/remote-runtime.ts +3 -3
  41. package/src/input/commands/runtime-services.ts +54 -13
  42. package/src/input/commands/services-runtime.ts +1 -1
  43. package/src/input/commands/session-content.ts +12 -0
  44. package/src/input/commands/session-workflow.ts +19 -1
  45. package/src/input/commands/settings-sync-runtime.ts +1 -1
  46. package/src/input/commands/share-runtime.ts +9 -2
  47. package/src/input/commands/shell-core.ts +15 -7
  48. package/src/input/commands/skills-runtime.ts +2 -8
  49. package/src/input/commands/subscription-runtime.ts +2 -2
  50. package/src/input/commands/test-runtime.ts +277 -0
  51. package/src/input/commands/websearch-runtime.ts +101 -0
  52. package/src/input/commands/work-plan-runtime.ts +36 -10
  53. package/src/input/commands/workstream-runtime.ts +338 -0
  54. package/src/input/commands.ts +12 -0
  55. package/src/input/config-modal-types.ts +161 -0
  56. package/src/input/config-modal.ts +377 -0
  57. package/src/input/feed-context-factory.ts +7 -8
  58. package/src/input/handler-command-route.ts +27 -17
  59. package/src/input/handler-content-actions.ts +22 -8
  60. package/src/input/handler-feed-routes.ts +107 -7
  61. package/src/input/handler-feed.ts +49 -13
  62. package/src/input/handler-interactions.ts +3 -3
  63. package/src/input/handler-modal-routes.ts +65 -0
  64. package/src/input/handler-modal-stack.ts +3 -5
  65. package/src/input/handler-modal-token-routes.ts +16 -49
  66. package/src/input/handler-picker-routes.ts +11 -94
  67. package/src/input/handler-shortcuts.ts +24 -14
  68. package/src/input/handler-types.ts +3 -6
  69. package/src/input/handler-ui-state.ts +10 -22
  70. package/src/input/handler.ts +10 -28
  71. package/src/input/keybindings.ts +22 -8
  72. package/src/input/model-picker-types.ts +24 -6
  73. package/src/input/model-picker.ts +58 -0
  74. package/src/input/panel-integration-actions.ts +9 -170
  75. package/src/input/settings-modal-data.ts +95 -0
  76. package/src/input/settings-modal-mutations.ts +6 -4
  77. package/src/main.ts +41 -44
  78. package/src/panels/agent-inspector-shared.ts +35 -11
  79. package/src/panels/base-panel.ts +22 -1
  80. package/src/panels/builtin/agent.ts +21 -107
  81. package/src/panels/builtin/development.ts +16 -62
  82. package/src/panels/builtin/knowledge.ts +8 -32
  83. package/src/panels/builtin/operations.ts +84 -428
  84. package/src/panels/builtin/session.ts +21 -112
  85. package/src/panels/builtin/shared.ts +9 -43
  86. package/src/panels/builtin-modals.ts +218 -0
  87. package/src/panels/builtin-panels.ts +5 -0
  88. package/src/panels/cost-tracker-panel.ts +63 -14
  89. package/src/panels/diff-panel.ts +123 -60
  90. package/src/panels/eval-registry.ts +60 -0
  91. package/src/panels/fleet-panel.ts +800 -0
  92. package/src/panels/fleet-read-model.ts +477 -0
  93. package/src/panels/fleet-steer.ts +92 -0
  94. package/src/panels/fleet-stop.ts +125 -0
  95. package/src/panels/fleet-tabs.ts +230 -0
  96. package/src/panels/fleet-transcript.ts +356 -0
  97. package/src/panels/git-panel.ts +9 -9
  98. package/src/panels/index.ts +7 -31
  99. package/src/panels/local-auth-panel.ts +10 -0
  100. package/src/panels/modals/hooks-modal.ts +187 -0
  101. package/src/panels/modals/keybindings-modal.ts +151 -0
  102. package/src/panels/modals/knowledge-modal.ts +158 -0
  103. package/src/panels/modals/local-auth-modal.ts +132 -0
  104. package/src/panels/modals/marketplace-modal.ts +218 -0
  105. package/src/panels/modals/memory-modal.ts +180 -0
  106. package/src/panels/modals/modal-surface-helpers.ts +54 -0
  107. package/src/panels/modals/modal-theme.ts +36 -0
  108. package/src/panels/modals/pairing-modal.ts +144 -0
  109. package/src/panels/modals/planning-modal.ts +282 -0
  110. package/src/panels/modals/plugins-modal.ts +174 -0
  111. package/src/panels/modals/policy-modal.ts +256 -0
  112. package/src/panels/modals/provider-health-modal.ts +136 -0
  113. package/src/panels/modals/remote-modal.ts +115 -0
  114. package/src/panels/modals/sandbox-modal.ts +150 -0
  115. package/src/panels/modals/security-modal.ts +217 -0
  116. package/src/panels/modals/services-modal.ts +179 -0
  117. package/src/panels/modals/settings-sync-modal.ts +142 -0
  118. package/src/panels/modals/skills-modal.ts +189 -0
  119. package/src/panels/modals/subscription-modal.ts +172 -0
  120. package/src/panels/modals/work-plan-modal.ts +149 -0
  121. package/src/panels/panel-confirm-overlay.ts +72 -0
  122. package/src/panels/panel-manager.ts +108 -5
  123. package/src/panels/project-planning-answer-actions.ts +4 -2
  124. package/src/panels/scrollable-list-panel.ts +9 -0
  125. package/src/panels/skills-panel.ts +7 -51
  126. package/src/panels/token-budget-panel.ts +5 -3
  127. package/src/panels/types.ts +26 -0
  128. package/src/permissions/hunk-selection.ts +179 -0
  129. package/src/permissions/prompt.ts +60 -4
  130. package/src/renderer/compaction-history-modal.ts +19 -3
  131. package/src/renderer/compaction-preview.ts +13 -2
  132. package/src/renderer/compaction-quality.ts +100 -0
  133. package/src/renderer/config-modal.ts +81 -0
  134. package/src/renderer/conversation-overlays.ts +10 -17
  135. package/src/renderer/fleet-tab-strip.ts +56 -0
  136. package/src/renderer/footer-tips.ts +10 -2
  137. package/src/renderer/git-status.ts +40 -0
  138. package/src/renderer/help-overlay.ts +2 -2
  139. package/src/renderer/model-workspace.ts +44 -1
  140. package/src/renderer/panel-workspace-bar.ts +8 -2
  141. package/src/renderer/shell-surface.ts +8 -1
  142. package/src/renderer/turn-injection.ts +114 -0
  143. package/src/renderer/ui-factory.ts +91 -7
  144. package/src/runtime/bootstrap-command-context.ts +24 -1
  145. package/src/runtime/bootstrap-command-parts.ts +36 -7
  146. package/src/runtime/bootstrap-core.ts +19 -4
  147. package/src/runtime/bootstrap-hook-bridge.ts +5 -0
  148. package/src/runtime/bootstrap-shell.ts +41 -17
  149. package/src/runtime/bootstrap.ts +11 -17
  150. package/src/runtime/code-index-services.ts +112 -0
  151. package/src/runtime/orchestrator-core-services.ts +49 -0
  152. package/src/runtime/process-lifecycle.ts +3 -1
  153. package/src/runtime/services.ts +91 -43
  154. package/src/runtime/ui-services.ts +8 -0
  155. package/src/runtime/workstream-services.ts +195 -0
  156. package/src/shell/blocking-input.ts +22 -1
  157. package/src/shell/ui-openers.ts +129 -17
  158. package/src/utils/format-duration.ts +8 -18
  159. package/src/utils/splash-lines.ts +1 -1
  160. package/src/version.ts +1 -1
  161. package/src/panels/agent-inspector-panel.ts +0 -786
  162. package/src/panels/approval-panel.ts +0 -252
  163. package/src/panels/automation-control-panel.ts +0 -479
  164. package/src/panels/cockpit-panel.ts +0 -481
  165. package/src/panels/cockpit-read-model.ts +0 -233
  166. package/src/panels/communication-panel.ts +0 -256
  167. package/src/panels/control-plane-panel.ts +0 -470
  168. package/src/panels/debug-panel.ts +0 -609
  169. package/src/panels/docs-panel.ts +0 -375
  170. package/src/panels/eval-panel.ts +0 -627
  171. package/src/panels/file-explorer-panel.ts +0 -664
  172. package/src/panels/file-preview-panel.ts +0 -517
  173. package/src/panels/hooks-panel.ts +0 -401
  174. package/src/panels/incident-review-panel.ts +0 -406
  175. package/src/panels/intelligence-panel.ts +0 -383
  176. package/src/panels/knowledge-graph-panel.ts +0 -506
  177. package/src/panels/marketplace-panel.ts +0 -399
  178. package/src/panels/memory-panel.ts +0 -558
  179. package/src/panels/ops-control-panel.ts +0 -329
  180. package/src/panels/ops-strategy-panel.ts +0 -321
  181. package/src/panels/orchestration-panel.ts +0 -465
  182. package/src/panels/panel-list-panel.ts +0 -557
  183. package/src/panels/plan-dashboard-panel.ts +0 -707
  184. package/src/panels/policy-panel.ts +0 -517
  185. package/src/panels/project-planning-panel.ts +0 -721
  186. package/src/panels/provider-health-panel.ts +0 -678
  187. package/src/panels/provider-health-tracker.ts +0 -306
  188. package/src/panels/provider-health-views.ts +0 -560
  189. package/src/panels/qr-panel.ts +0 -280
  190. package/src/panels/remote-panel.ts +0 -534
  191. package/src/panels/routes-panel.ts +0 -241
  192. package/src/panels/sandbox-panel.ts +0 -456
  193. package/src/panels/security-panel.ts +0 -447
  194. package/src/panels/services-panel.ts +0 -329
  195. package/src/panels/session-browser-panel.ts +0 -487
  196. package/src/panels/settings-sync-panel.ts +0 -398
  197. package/src/panels/subscription-panel.ts +0 -342
  198. package/src/panels/symbol-outline-panel.ts +0 -619
  199. package/src/panels/system-messages-panel.ts +0 -364
  200. package/src/panels/tasks-panel.ts +0 -608
  201. package/src/panels/thinking-panel.ts +0 -333
  202. package/src/panels/tool-inspector-panel.ts +0 -537
  203. package/src/panels/work-plan-panel.ts +0 -530
  204. package/src/panels/worktree-panel.ts +0 -360
  205. package/src/panels/wrfc-panel.ts +0 -790
  206. package/src/renderer/agent-detail-modal.ts +0 -465
  207. package/src/renderer/live-tail-modal.ts +0 -156
  208. package/src/renderer/process-modal.ts +0 -656
  209. package/src/renderer/qr-renderer.ts +0 -120
@@ -1,306 +0,0 @@
1
- // ProviderStatus is the shared SDK type — imported from the runtime barrel
2
- // to eliminate the duplicate local definition that diverged from the SDK shape.
3
- import type {
4
- ProviderHealthDomainState,
5
- ProviderHealthRecord,
6
- ProviderStatus,
7
- } from '@/runtime/index.ts';
8
- import { calcSessionCost } from '../export/cost-utils.ts';
9
-
10
- export type { ProviderStatus };
11
-
12
- /** Token/cost usage delta carried by one LLM_RESPONSE_RECEIVED event. */
13
- export interface ProviderUsageDelta {
14
- readonly model?: string;
15
- readonly inputTokens?: number;
16
- readonly outputTokens?: number;
17
- readonly cacheReadTokens?: number;
18
- readonly cacheWriteTokens?: number;
19
- }
20
-
21
- export interface ProviderHealth {
22
- name: string;
23
- status: ProviderStatus;
24
- lastLatencyMs?: number;
25
- lastErrorMessage?: string;
26
- lastSuccessAt?: number;
27
- lastErrorAt?: number;
28
- rateLimitExpiresAt: number;
29
- /** Most recent model id seen for this provider (from LLM responses). */
30
- lastModelId?: string;
31
- /** Session request counter (successes + errors). */
32
- requests: number;
33
- /** Session error counter. */
34
- errors: number;
35
- /** Session token counters (absorbed from the retired provider-stats panel). */
36
- inputTokens: number;
37
- outputTokens: number;
38
- cacheReadTokens: number;
39
- cacheWriteTokens: number;
40
- totalTokens: number;
41
- /** Session USD cost accumulated per call via calcSessionCost. */
42
- totalCostUsd: number;
43
- /** Ring buffer of recent request latencies in ms (most-recent last). */
44
- latencies: number[];
45
- }
46
-
47
- /** Per-provider identity/config metadata used to build SDK domain state. */
48
- export interface ProviderHealthMeta {
49
- readonly providerId: string;
50
- readonly displayName?: string;
51
- readonly isActive: boolean;
52
- readonly isConfigured: boolean;
53
- }
54
-
55
- const LATENCY_RING_SIZE = 20;
56
-
57
- function avg(values: readonly number[]): number {
58
- if (values.length === 0) return 0;
59
- return values.reduce((sum, value) => sum + value, 0) / values.length;
60
- }
61
-
62
- /**
63
- * Tracks provider request posture from shell-facing turn and provider events.
64
- * The panel owns event subscriptions and feeds those events into this tracker.
65
- *
66
- * WO-112: the tracker also accumulates the session metrics the retired
67
- * provider-stats panel used to duplicate (latency ring, request/error/token
68
- * counters, per-call cost) and can project its records into the SDK
69
- * ProviderHealthDomainState shape so the orphaned ProviderHealthDataProvider
70
- * (60-point timelines, success/error rates, cache metrics) is the single
71
- * derivation engine for the provider console.
72
- */
73
- export class ProviderHealthTracker {
74
- private records = new Map<string, ProviderHealth>();
75
- private streamStartMs: number | null = null;
76
- private turnStartMs: number | null = null;
77
- private revision = 0;
78
-
79
- private static readonly DEFAULT_COOLDOWN_MS = 60_000;
80
-
81
- onTurnStart(): void {
82
- this.turnStartMs = Date.now();
83
- }
84
-
85
- onStreamStart(): void {
86
- this.streamStartMs = Date.now();
87
- }
88
-
89
- onLlmResponse(providerName: string, usage?: ProviderUsageDelta): void {
90
- const now = Date.now();
91
- const latencyMs =
92
- this.streamStartMs !== null
93
- ? now - this.streamStartMs
94
- : this.turnStartMs !== null
95
- ? now - this.turnStartMs
96
- : undefined;
97
- this.streamStartMs = null;
98
-
99
- this.recordSuccess(providerName, latencyMs, usage);
100
- }
101
-
102
- /**
103
- * Record a turn error against a concrete provider. The panel resolves the
104
- * active provider (in-flight request, model domain, config, last response)
105
- * before calling this — the tracker never invents an 'unknown' row.
106
- */
107
- onTurnError(error: string, providerName: string): void {
108
- this.streamStartMs = null;
109
- this.turnStartMs = null;
110
- const isRateLimit = this.isRateLimitMessage(error);
111
-
112
- this.recordError(providerName, error, isRateLimit);
113
- }
114
-
115
- onProvidersChanged(providerIds: readonly string[]): void {
116
- try {
117
- for (const providerId of providerIds) {
118
- if (!this.records.has(providerId)) {
119
- this.ensureRecord(providerId);
120
- }
121
- }
122
- } catch {
123
- // Ignore provider catalog churn while the shell is refreshing.
124
- }
125
- }
126
-
127
- getAll(): ProviderHealth[] {
128
- return [...this.records.values()];
129
- }
130
-
131
- get(name: string): ProviderHealth | undefined {
132
- return this.records.get(name);
133
- }
134
-
135
- /**
136
- * Project tracked records into the SDK ProviderHealthDomainState shape so
137
- * ProviderHealthDataProvider can derive timelines, rates, and sort order.
138
- * Providers present in `meta` but never seen by the tracker are emitted as
139
- * zero-stat 'unknown' records; tracked providers missing from `meta` are
140
- * kept so attribution/table rows never silently disappear.
141
- */
142
- buildHealthDomainState(meta: readonly ProviderHealthMeta[]): ProviderHealthDomainState {
143
- const providers = new Map<string, ProviderHealthRecord>();
144
- const metaById = new Map(meta.map((entry) => [entry.providerId, entry] as const));
145
- const ids = new Set<string>([...metaById.keys(), ...this.records.keys()]);
146
-
147
- let degradedCount = 0;
148
- let unavailableCount = 0;
149
- let healthySeen = 0;
150
-
151
- for (const id of ids) {
152
- const record = this.records.get(id);
153
- const entry = metaById.get(id);
154
- const latencies = record?.latencies ?? [];
155
- const requests = record?.requests ?? 0;
156
- const errors = record?.errors ?? 0;
157
- const status: ProviderStatus = record?.status ?? 'unknown';
158
-
159
- if (status === 'degraded' || status === 'rate_limited' || status === 'auth_error') degradedCount++;
160
- if (status === 'unavailable') unavailableCount++;
161
- if (status === 'healthy') healthySeen++;
162
-
163
- const cacheRead = record?.cacheReadTokens ?? 0;
164
- const cacheWrite = record?.cacheWriteTokens ?? 0;
165
- const promptTokens = record?.inputTokens ?? 0;
166
-
167
- providers.set(id, {
168
- providerId: id,
169
- displayName: entry?.displayName ?? id,
170
- status,
171
- isActive: entry?.isActive ?? false,
172
- isConfigured: entry?.isConfigured ?? true,
173
- stats: {
174
- totalCalls: requests,
175
- successCalls: Math.max(0, requests - errors),
176
- errorCalls: errors,
177
- avgLatencyMs: Math.round(avg(latencies)),
178
- minLatencyMs: latencies.length > 0 ? Math.min(...latencies) : 0,
179
- maxLatencyMs: latencies.length > 0 ? Math.max(...latencies) : 0,
180
- lastSuccessAt: record?.lastSuccessAt,
181
- lastErrorAt: record?.lastErrorAt,
182
- lastErrorMessage: record?.lastErrorMessage,
183
- },
184
- ...(cacheRead + cacheWrite > 0
185
- ? {
186
- cacheMetrics: {
187
- cacheReadTokens: cacheRead,
188
- cacheWriteTokens: cacheWrite,
189
- hitRate: promptTokens + cacheRead > 0 ? cacheRead / (promptTokens + cacheRead) : 0,
190
- },
191
- }
192
- : {}),
193
- lastCheckedAt: Date.now(),
194
- ...(record && record.rateLimitExpiresAt > Date.now()
195
- ? { rateLimitResetAt: record.rateLimitExpiresAt }
196
- : {}),
197
- });
198
- }
199
-
200
- const compositeStatus = unavailableCount > 0
201
- ? 'critical'
202
- : degradedCount > 0
203
- ? 'degraded'
204
- : healthySeen > 0 && healthySeen === ids.size
205
- ? 'healthy'
206
- : ids.size === 0
207
- ? 'unknown'
208
- : healthySeen > 0
209
- ? 'healthy'
210
- : 'unknown';
211
-
212
- this.revision += 1;
213
- return {
214
- revision: this.revision,
215
- lastUpdatedAt: Date.now(),
216
- source: 'provider-health-panel',
217
- providers,
218
- compositeStatus,
219
- degradedCount,
220
- unavailableCount,
221
- warnings: [],
222
- };
223
- }
224
-
225
- private ensureRecord(name: string): ProviderHealth {
226
- let record = this.records.get(name);
227
- if (!record) {
228
- record = {
229
- name,
230
- status: 'unknown',
231
- rateLimitExpiresAt: 0,
232
- requests: 0,
233
- errors: 0,
234
- inputTokens: 0,
235
- outputTokens: 0,
236
- cacheReadTokens: 0,
237
- cacheWriteTokens: 0,
238
- totalTokens: 0,
239
- totalCostUsd: 0,
240
- latencies: [],
241
- };
242
- this.records.set(name, record);
243
- }
244
- return record;
245
- }
246
-
247
- private applyUsage(record: ProviderHealth, usage: ProviderUsageDelta | undefined): void {
248
- if (!usage) return;
249
- const input = usage.inputTokens ?? 0;
250
- const output = usage.outputTokens ?? 0;
251
- const cacheRead = usage.cacheReadTokens ?? 0;
252
- const cacheWrite = usage.cacheWriteTokens ?? 0;
253
- record.inputTokens += input;
254
- record.outputTokens += output;
255
- record.cacheReadTokens += cacheRead;
256
- record.cacheWriteTokens += cacheWrite;
257
- record.totalTokens += input + output + cacheRead + cacheWrite;
258
- if (usage.model) record.lastModelId = usage.model;
259
- const model = usage.model ?? record.lastModelId;
260
- if (model) {
261
- record.totalCostUsd += calcSessionCost(input, output, cacheRead, cacheWrite, model);
262
- }
263
- }
264
-
265
- private recordSuccess(name: string, latencyMs: number | undefined, usage?: ProviderUsageDelta): void {
266
- const record = this.ensureRecord(name);
267
- record.status = 'healthy';
268
- record.lastSuccessAt = Date.now();
269
- record.lastErrorMessage = undefined;
270
- record.requests += 1;
271
- this.applyUsage(record, usage);
272
- if (latencyMs !== undefined) {
273
- record.lastLatencyMs = latencyMs;
274
- if (latencyMs > 0) {
275
- record.latencies.push(latencyMs);
276
- if (record.latencies.length > LATENCY_RING_SIZE) record.latencies.shift();
277
- }
278
- }
279
- if (record.rateLimitExpiresAt > 0 && record.rateLimitExpiresAt <= Date.now()) {
280
- record.rateLimitExpiresAt = 0;
281
- }
282
- }
283
-
284
- private recordError(name: string, message: string, isRateLimit: boolean): void {
285
- const record = this.ensureRecord(name);
286
- record.lastErrorAt = Date.now();
287
- record.lastErrorMessage = message.slice(0, 120);
288
- record.requests += 1;
289
- record.errors += 1;
290
- if (isRateLimit) {
291
- record.status = 'rate_limited';
292
- record.rateLimitExpiresAt = Date.now() + ProviderHealthTracker.DEFAULT_COOLDOWN_MS;
293
- return;
294
- }
295
- record.status = 'degraded';
296
- }
297
-
298
- private isRateLimitMessage(message: string): boolean {
299
- const lower = message.toLowerCase();
300
- return (
301
- lower.includes('429')
302
- || lower.includes('402')
303
- || /rate.limit|too many requests|quota exceeded|throttl|depleted|credits/.test(lower)
304
- );
305
- }
306
- }