clauderipple 0.8.9 → 0.9.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 (75) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.es.md +0 -4
  3. package/README.ja.md +0 -4
  4. package/README.ko.md +5 -1
  5. package/README.md +5 -1
  6. package/README.pt-BR.md +0 -4
  7. package/README.zh-CN.md +0 -4
  8. package/dist/app/dist/main.js +23 -3
  9. package/dist/app/package.json +1 -1
  10. package/dist/cli/src/codex.js +3 -1
  11. package/dist/cli/src/index.js +1 -1
  12. package/dist/cli/src/settings.js +5 -2
  13. package/dist/cli/src/tray.js +41 -5
  14. package/dist/mod/.claude-plugin/plugin.json +9 -1
  15. package/dist/mod/hooks/cache.ts +60 -0
  16. package/dist/mod/hooks/compact.ts +40 -0
  17. package/dist/mod/hooks/handoff.ts +43 -0
  18. package/dist/mod/hooks/i18n.ts +2716 -0
  19. package/dist/mod/hooks/judge.ts +226 -0
  20. package/dist/mod/hooks/ladder.js +179 -0
  21. package/dist/mod/hooks/ladder.ts +174 -0
  22. package/dist/mod/hooks/modes.ts +93 -0
  23. package/dist/mod/hooks/register.tsx +1458 -256
  24. package/dist/mod/hooks/session.ts +62 -0
  25. package/dist/mod/hooks/settings.js +342 -0
  26. package/dist/mod/hooks/settings.ts +321 -0
  27. package/dist/mod/hooks/stats.ts +59 -0
  28. package/dist/mod/hooks/ui/agents.tsx +138 -0
  29. package/dist/mod/hooks/ui/alerts.tsx +370 -0
  30. package/dist/mod/hooks/ui/art.ts +628 -0
  31. package/dist/mod/hooks/ui/band.tsx +836 -0
  32. package/dist/mod/hooks/ui/kit.tsx +258 -0
  33. package/dist/mod/hooks/ui/log.tsx +109 -0
  34. package/dist/mod/hooks/ui/parts.tsx +217 -0
  35. package/dist/mod/hooks/ui/ripple.ts +614 -0
  36. package/dist/mod/hooks/ui/settings.tsx +376 -0
  37. package/dist/mod/hooks/ui/setup.tsx +148 -0
  38. package/dist/mod/hooks/ui/theme.ts +217 -0
  39. package/dist/mod/hooks/ui/view.ts +371 -0
  40. package/dist/mod/hooks/worker.ts +1 -1
  41. package/dist/mod/hooks/workers.ts +217 -0
  42. package/dist/mod/tests/register.test.tsx +1730 -0
  43. package/dist/mod/tests/ui.test.tsx +853 -0
  44. package/dist/mod/tsconfig.json +2 -1
  45. package/dist/mod/types/index.d.ts +320 -1
  46. package/dist/router/src/admin.js +195 -53
  47. package/dist/router/src/agents.js +24 -5
  48. package/dist/router/src/bootstrap.js +4 -2
  49. package/dist/router/src/compat.js +10 -2
  50. package/dist/router/src/config.js +3 -0
  51. package/dist/router/src/health.js +33 -0
  52. package/dist/router/src/index.js +1 -0
  53. package/dist/router/src/ingress/models.js +4 -1
  54. package/dist/router/src/mod-settings.js +119 -0
  55. package/dist/router/src/model-policy.js +6 -0
  56. package/dist/router/src/model-scores.js +159 -0
  57. package/dist/router/src/picker.js +7 -0
  58. package/dist/router/src/providers/anthropic-account-pool.js +10 -4
  59. package/dist/router/src/providers/anthropic-accounts.js +26 -1
  60. package/dist/router/src/providers/chatgpt/catalog.js +45 -14
  61. package/dist/router/src/providers/chatgpt/index.js +9 -3
  62. package/dist/router/src/providers/chatgpt/translate.js +9 -5
  63. package/dist/router/src/providers/google/index.js +6 -2
  64. package/dist/router/src/providers/openai/index.js +4 -1
  65. package/dist/router/src/providers/openai/translate.js +1 -1
  66. package/dist/router/src/proxy.js +14 -3
  67. package/dist/router/src/version.js +1 -1
  68. package/dist/ui/app.js +932 -209
  69. package/dist/ui/favicon-32.png +0 -0
  70. package/dist/ui/favicon.svg +12 -0
  71. package/dist/ui/i18n.js +923 -237
  72. package/dist/ui/index.html +159 -87
  73. package/dist/ui/style.css +789 -408
  74. package/docs/ARCHITECTURE.md +134 -2
  75. package/package.json +7 -3
@@ -1,3 +1,4 @@
1
1
  {
2
- "extends": "./.claude-plugin/types/tsconfig.json"
2
+ "extends": "./.claude-plugin/types/tsconfig.json",
3
+ "compilerOptions": { "allowImportingTsExtensions": true }
3
4
  }
@@ -1,3 +1,8 @@
1
+ // The mod's shared shapes. The design and the reasons are in docs/specs/2026-10-10-ripple-mod.md.
2
+
3
+ // Engine contracts must be self-contained; i18n re-exports this language type.
4
+ export type Lang = 'ko' | 'en' | 'zh' | 'ja' | 'es' | 'pt'
5
+
1
6
  export type StatusLine = string | null
2
7
 
3
8
  export type StatusRequest = {
@@ -18,15 +23,329 @@ export type StatusRequest = {
18
23
  mine?: boolean
19
24
  }
20
25
 
26
+ export type Effort = 'low' | 'medium' | 'high' | 'xhigh' | 'max'
27
+
28
+ /** API usage may omit counts; the engine's ModelUsage does not expose cache TTL evidence. */
29
+ export type Usage = {
30
+ input_tokens?: number
31
+ output_tokens?: number
32
+ cache_read_input_tokens?: number
33
+ cache_creation_input_tokens?: number
34
+ cache_creation?: {
35
+ ephemeral_1h_input_tokens?: number
36
+ ephemeral_5m_input_tokens?: number
37
+ }
38
+ }
39
+
40
+ /** The bounded excerpts shared by the effort judges; attachments carry counts, not contents. */
41
+ export type JudgeInput = {
42
+ messages: readonly { role: 'user' | 'assistant'; text: string }[]
43
+ next: string
44
+ attachments?: readonly { type: string }[]
45
+ current?: { model: string; effort: Effort } | null
46
+ contextPercent?: number
47
+ midTurn?: boolean
48
+ }
49
+
50
+ /** Who judges: Haiku on the person's Claude login, TypeSafe's Jev, or a model through the router. */
51
+ export type JudgeKind = 'haiku' | 'jev' | 'model'
52
+ /**
53
+ * One verdict as the log pane lists it beside the router's requests: a Jev call never reaches the
54
+ * router, so without this nothing showed who judged, how long it took or that it fell back.
55
+ */
56
+ export type JudgeRun = {
57
+ at: number
58
+ /** The effort of a prompt, or the worker for a spawn. */
59
+ for: 'prompt' | 'worker'
60
+ /** The judge the settings ask for, and the one that answered (Haiku when it fell back). */
61
+ asked: JudgeKind
62
+ by: JudgeKind
63
+ ms: number
64
+ effort?: Effort
65
+ sure?: number
66
+ /** Why the asked judge gave no verdict. */
67
+ error?: string
68
+ }
69
+
70
+ /** Domain is the kind of work; level is its required capability, independently. */
71
+ export type Domain = 'design' | 'frontend' | 'backend' | 'infra' | 'data' | 'test' | 'docs' | 'research' | 'general'
72
+ export type Level = 'light' | 'standard' | 'deep'
73
+ /** Whether a task decides what to build (plan) or carries out what was decided (do). */
74
+ export type Kind = 'plan' | 'do'
75
+ /**
76
+ * The worker ladder's rungs (docs/specs/2026-10-11-worker-ladder.md): the judge's level picks
77
+ * light, standard or hard regardless of kind; only an explicit max tier selects plan.
78
+ */
79
+ export type Rung = 'light' | 'standard' | 'hard' | 'plan'
80
+ /** One rung: what runs there and how hard it thinks. */
81
+ export type Step = { target: TierTarget; effort: Effort }
82
+ /** Historical stats include design counts from before the two-axis migration. */
83
+ export type Tier = Level | 'design'
84
+ /** How a pin stored before 2026-10-11 moved its efforts; read only to migrate it (`ladderFromPin`). */
85
+ export type Calibration = { effortOffset?: -2 | -1 | 0 | 1 | 2; floor?: Effort; ceiling?: Effort }
86
+ /**
87
+ * The four workers' rungs, each model + effort. Loading (`normalizeSettings`) reads pins from
88
+ * before 2026-10-11 and Auto (experimental, removed 2026-10-12) into it, and drops field rules.
89
+ */
90
+ export type Rungs = Partial<Record<Rung, Step[]>>
91
+ export type Rule = { mode: 'ladder'; rungs: Rungs }
92
+ /** How a provider is paid for: what the Workers card tags each model with, and what presets read. */
93
+ export type Billing = 'free' | 'payg' | 'subscription'
94
+ /** A model a rung can run, as GET /api/mod-options describes it. */
95
+ export type LadderChoice = {
96
+ target: TierTarget
97
+ model: string
98
+ provider: string
99
+ billing: Billing
100
+ /** The model's own effort ladder, in order; empty when unknown. */
101
+ efforts: Effort[]
102
+ /** Its cheapest measured AA cost per task, when measured. */
103
+ cost?: number
104
+ }
105
+ /** The fill rule's wishes, by name; only `claudeGpt` is used now (the router fills empty rungs with it). */
106
+ export type PresetId = 'claude' | 'claudeGpt' | 'gpt' | 'cheap'
107
+ /** The fill as computed from the ticked models: the rungs, and the ones it had to fill otherwise. */
108
+ export type Preset = { rungs: Record<Rung, Step>; substituted: Rung[] }
109
+ export type ProviderAvailability = { usable: boolean; reason?: string; exhaustedUntil?: number }
110
+ export type LadderOptions = { until: number; choices: LadderChoice[]; providers?: Record<string, ProviderAvailability> }
111
+ export type RouterModel = { value: string; label: string; provider: string; name?: string }
112
+ export type WorkerCandidate = { target: TierTarget; model: string; provider: string }
113
+ /** A model's AA measurements (GET /api/mod-options `scores`): the fill's cost order reads them. */
114
+ export type ModelScore = { model: string; provider: string; variants: { effort: Effort | null; intelligence: number | null; costPerTask: number | null }[] }
115
+
116
+ /** An agent to spawn: a dispatchable `subagentType`, and a model only for Claude's own agents. */
117
+ export type TierTarget = { agent: string; model?: string }
118
+
119
+ /**
120
+ * An operating mode the user made (docs/specs/2026-10-12-worker-tiers.md "Operating modes").
121
+ * Each rung is an ordered worker list, independent of the base ladder, sharing its ticked models.
122
+ * `until` is an epoch-ms time, the named provider's next known quota reset ('chatgpt' or another
123
+ * ChatGPT provider, or 'anthropic' for Claude), or no automatic end.
124
+ */
125
+ export type Mode = {
126
+ id: string
127
+ name: string
128
+ rungs: Rungs
129
+ delegation: 'normal' | 'eager'
130
+ until: 'none' | number | { reset: string }
131
+ }
132
+
133
+ export type Settings = {
134
+ effortAuto: boolean
135
+ judge: JudgeKind
136
+ /** The router model id the `model` judge asks. */
137
+ judgeModel: string
138
+ bias: -2 | -1 | 0 | 1 | 2
139
+ floor: Effort
140
+ ceiling: Effort
141
+ /** off: never; auto: spawns of ripple:auto; all: also general-purpose spawns with no model. */
142
+ agentAuto: 'off' | 'auto' | 'all'
143
+ jevSource: 'typesafe' | 'zen'
144
+ /** Only the general ladder: the four workers replaced the per-field rules (2026-10-12). */
145
+ rules: { general: Rule }
146
+ /**
147
+ * The models the user ticked (docs/specs/2026-10-12-worker-tiers.md): worker agent names and
148
+ * `general-purpose:<model>` for Claude's own. Empty means every model. Rungs run, and the fill
149
+ * picks, only among these.
150
+ */
151
+ allow: string[]
152
+ /** The user's operating modes; none is built in. */
153
+ modes: Mode[]
154
+ /** The mode that is on, by id; absent when none is. */
155
+ activeMode?: string
156
+ /**
157
+ * When the active mode was turned on (epoch ms): a mode that runs until the quota resets ends at
158
+ * the first reset after this.
159
+ */
160
+ activeSince?: number
161
+ providers: Record<string, boolean>
162
+ /**
163
+ * What ran when a pinned tier's agent was unavailable, before the ladder (2026-10-11). Kept so a
164
+ * stored choice survives; the ladder moves one rung up instead.
165
+ */
166
+ fallback: TierTarget
167
+ keepWarm: boolean
168
+ keepWarmHours: 1 | 2 | 4 | 8
169
+ keepWarmMinTokens: number
170
+ compactWith: 'haiku' | 'session'
171
+ handoffSkill: string
172
+ handoffAfter: 'continue' | 'confirm' | 'copy'
173
+ handoffButton: 'advised' | 'always'
174
+ /** Context percent at which the context reads heavy (and the swamp band shows, when on). */
175
+ swampAt: number
176
+ /** The heavy-context band; off unless switched on. */
177
+ swampAlert: boolean
178
+ layout: 'dashboard' | 'minimal'
179
+ appearance: 'auto' | 'dark' | 'light'
180
+ language: 'auto' | Lang
181
+ /** Band parts switched off: 'timer' | 'reason' | 'route'. */
182
+ hide: string[]
183
+ /** Band chips switched on, off by default: 'context' | 'limits'. */
184
+ extras: string[]
185
+ setupDone: boolean
186
+ }
187
+
188
+ /** The admin API always returns a complete normalized settings object. */
189
+ export type ModSettingsSnapshot = {
190
+ settings: Settings
191
+ revision: number
192
+ stored: boolean
193
+ /** The stored settings still held field overrides or Auto, which the four workers replaced; true until the next save. */
194
+ legacy?: boolean
195
+ }
196
+ /** Arrays and rule mode changes are atomic; other edits are rebased by field path. */
197
+ export type SettingsChange = { path: string[]; value?: unknown; remove?: boolean }
198
+ export type SettingsSync = { schemaVersion?: number; revision: number | null; pending: SettingsChange[]; migrationChecked: boolean; migration: Settings | null }
199
+
200
+ /** A judge's answer for the main chat's next prompt. */
201
+ export type Verdict = { effort: Effort; sure?: number; why: string }
202
+
203
+ /** The effort the main chat runs at now, and who set it. */
204
+ export type Pick = Verdict & { by: JudgeKind | 'manual' | 'followup'; at: number }
205
+
206
+ /** A judge's answer for a delegated task; a judge that names no kind means do. */
207
+ export type WorkerVerdict = { domain: Domain; level: Level; kind?: Kind; effort: Effort; sure?: number; why: string }
208
+
209
+ /** What a spawn of ripple:auto (or a taken-over spawn) was turned into. */
210
+ export type WorkerPick = WorkerVerdict & {
211
+ /** The ladder rung that ran it (absent for explicit choices). */
212
+ rung?: Rung
213
+ /** One-based position in the selected rung; skipped entries explain ordered fallback. */
214
+ position?: number
215
+ skipped?: { rung: Rung; position: number; model: string; reason: string }[]
216
+ target: TierTarget
217
+ by: JudgeKind | 'marker' | 'default'
218
+ ms: number
219
+ /** Set when the tier's own agent could not run and the fallback did. */
220
+ fallbackReason?: string
221
+ }
222
+
223
+ export type AgentState = 'running' | 'waiting' | 'done' | 'failed'
224
+
225
+ export type AgentRec = {
226
+ id: string
227
+ description: string
228
+ /** The agent type that ran. */
229
+ type: string
230
+ model?: string
231
+ pick?: WorkerPick
232
+ state: AgentState
233
+ /** The tool it is in now, in a few words. */
234
+ now?: string
235
+ toolSince?: number
236
+ startedAt: number
237
+ endedAt?: number
238
+ /** Weighted tokens (spec §5 stats weights). */
239
+ cost: number
240
+ }
241
+
242
+ /** The main chat's prompt cache as far as its responses show it. */
243
+ export type CacheMemo = {
244
+ ttlMs: number
245
+ lastReplyAt: number
246
+ expiresAt: number
247
+ }
248
+
249
+ /** Keep-warm bookkeeping for this chat. */
250
+ export type WarmState = {
251
+ pings: number
252
+ /** Weighted tokens the pings cost. */
253
+ cost: number
254
+ /** Set when keep-warm stopped by itself, with why. */
255
+ stopped?: string
256
+ lastPingAt?: number
257
+ }
258
+
259
+ /** The hottest plan window at or above the alert line. */
260
+ export type Hot = { kind: string; percent: number; resetsAt?: string }
261
+
262
+ export type Stats = {
263
+ prompts: number
264
+ requests: number
265
+ input: number
266
+ write: number
267
+ read: number
268
+ out: number
269
+ byEffort: Partial<Record<Effort, { prompts: number; cost: number }>>
270
+ judge: { haiku: number; jev: number; model: number; ms: number; tokens: number }
271
+ workers: Partial<Record<Tier, number>>
272
+ warm: { pings: number; cost: number }
273
+ }
274
+
275
+ export type HandoffKind = 'quick' | 'full'
276
+ /** `ready`: written, waiting on Confirm first's card; `opened`: a new session was opened with it. */
277
+ export type HandoffStage = 'writing' | 'clearing' | 'done' | 'ready' | 'opened' | 'copied' | 'failed'
278
+ export type SetupStep = 1 | 2 | 3 | 4
279
+ export type ResultStage = 'handingOff' | 'handoffReady' | 'handoffDone' | 'compacting' | 'compacted' | 'failed'
280
+ export type JevState = {
281
+ source: 'env' | 'file' | 'pasted' | 'zen' | null
282
+ test: { state: 'idle' | 'testing' | 'ok' | 'fail'; message?: string }
283
+ }
284
+
21
285
  declare module 'claude-code' {
22
286
  interface PluginState {
23
- 'clauderipple-status': {
287
+ ripple: {
24
288
  line: StatusLine
25
289
  last: StatusRequest | null
26
290
  requests: StatusRequest[]
27
291
  logOpen: boolean
28
292
  onlySession: boolean
29
293
  bandHidden: boolean
294
+ settings: Settings | null
295
+ settingsSync: SettingsSync
296
+ settingsBase: Settings | null
297
+ lang: Lang
298
+ pick: Pick | null
299
+ deciding: boolean
300
+ paused: boolean
301
+ agents: AgentRec[]
302
+ cache: CacheMemo | null
303
+ warm: WarmState
304
+ context: { percent: number; tokens: number; window: number } | null
305
+ hot: Hot | null
306
+ hotHidden: number | null
307
+ saveUntil: number | null
308
+ swampHidden: number | null
309
+ coldHidden: boolean
310
+ judgeDown: string | null
311
+ advice: string | null
312
+ handoff: { stage: HandoffStage; kind: HandoffKind; text?: string } | null
313
+ panel: 'settings' | 'setup' | 'compact' | 'handoff' | null
314
+ busy: boolean
315
+ decidingSince: number | null
316
+ heldPick: Pick | null
317
+ appEffort: Effort | number | null
318
+ modelIs: string | null
319
+ lastRequest: { model: string; appEffort?: string | number; sentEffort?: string | number } | null
320
+ lastSpawn: WorkerPick | null
321
+ lastRealReplyAt: number | null
322
+ stats: Stats
323
+ promptCount: number
324
+ adviceQueue: string | null
325
+ handoffQueued: boolean
326
+ handoffWaiting: boolean
327
+ handoffChoiceKind: HandoffKind
328
+ handoffChoiceAfter: 'continue' | 'confirm' | 'copy'
329
+ result: { stage: ResultStage; detail?: string } | null
330
+ settingsDraft: Settings | null
331
+ setupStep: SetupStep
332
+ compactNote: string
333
+ effortMenuOpen: boolean
334
+ agentsOpen: boolean
335
+ agentsShowDone: boolean
336
+ jev: JevState
337
+ routerModels: RouterModel[]
338
+ /** The router's rung choices and presets (GET /api/mod-options), for the quick settings. */
339
+ ladderOptions: LadderOptions | null
340
+ /** The active mode the eager note was last sent for (`modes.ts modeKey`); cleared on compaction. */
341
+ modeNoted: string | null
342
+ judgeDownHidden: string | null
343
+ /** Judge failure reasons already said this session. */
344
+ judgeSeen: string[]
345
+ judgeRuns: JudgeRun[]
346
+ judgeDownNetwork: boolean
347
+ limit: Hot | null
348
+ demo: 'cold' | 'swamp' | 'hot' | 'down' | null
30
349
  }
31
350
  }
32
351
  }