clauderipple 0.8.10 → 0.9.1
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/CHANGELOG.md +60 -0
- package/README.es.md +21 -15
- package/README.ja.md +13 -9
- package/README.ko.md +23 -9
- package/README.md +26 -11
- package/README.pt-BR.md +21 -15
- package/README.zh-CN.md +13 -9
- package/dist/app/package.json +1 -1
- package/dist/cli/src/codex.js +3 -1
- package/dist/cli/src/settings.js +5 -2
- package/dist/mod/.claude-plugin/plugin.json +9 -1
- package/dist/mod/hooks/cache.ts +60 -0
- package/dist/mod/hooks/compact.ts +40 -0
- package/dist/mod/hooks/handoff.ts +43 -0
- package/dist/mod/hooks/i18n.ts +2716 -0
- package/dist/mod/hooks/judge.ts +242 -0
- package/dist/mod/hooks/ladder.js +179 -0
- package/dist/mod/hooks/ladder.ts +174 -0
- package/dist/mod/hooks/modes.ts +93 -0
- package/dist/mod/hooks/register.tsx +1458 -256
- package/dist/mod/hooks/session.ts +62 -0
- package/dist/mod/hooks/settings.js +342 -0
- package/dist/mod/hooks/settings.ts +321 -0
- package/dist/mod/hooks/stats.ts +59 -0
- package/dist/mod/hooks/ui/agents.tsx +138 -0
- package/dist/mod/hooks/ui/alerts.tsx +373 -0
- package/dist/mod/hooks/ui/art.ts +628 -0
- package/dist/mod/hooks/ui/band.tsx +836 -0
- package/dist/mod/hooks/ui/kit.tsx +258 -0
- package/dist/mod/hooks/ui/log.tsx +109 -0
- package/dist/mod/hooks/ui/parts.tsx +217 -0
- package/dist/mod/hooks/ui/ripple.ts +614 -0
- package/dist/mod/hooks/ui/settings.tsx +376 -0
- package/dist/mod/hooks/ui/setup.tsx +148 -0
- package/dist/mod/hooks/ui/theme.ts +217 -0
- package/dist/mod/hooks/ui/view.ts +371 -0
- package/dist/mod/hooks/worker.ts +1 -1
- package/dist/mod/hooks/workers.ts +217 -0
- package/dist/mod/tests/register.test.tsx +1730 -0
- package/dist/mod/tests/ui.test.tsx +853 -0
- package/dist/mod/tsconfig.json +2 -1
- package/dist/mod/types/index.d.ts +320 -1
- package/dist/router/src/admin.js +170 -52
- package/dist/router/src/agents.js +24 -5
- package/dist/router/src/bootstrap.js +4 -2
- package/dist/router/src/config.js +3 -0
- package/dist/router/src/health.js +33 -0
- package/dist/router/src/index.js +1 -0
- package/dist/router/src/ingress/models.js +4 -1
- package/dist/router/src/mod-settings.js +120 -0
- package/dist/router/src/model-policy.js +6 -0
- package/dist/router/src/model-scores.js +159 -0
- package/dist/router/src/picker.js +7 -0
- package/dist/router/src/providers/chatgpt/index.js +23 -5
- package/dist/router/src/providers/chatgpt/translate.js +4 -1
- package/dist/router/src/providers/google/index.js +8 -3
- package/dist/router/src/providers/openai/index.js +6 -1
- package/dist/router/src/providers/openai/translate.js +6 -1
- package/dist/router/src/proxy.js +62 -24
- package/dist/router/src/requestlog.js +78 -20
- package/dist/router/src/version.js +1 -1
- package/dist/ui/app.js +908 -209
- package/dist/ui/favicon-32.png +0 -0
- package/dist/ui/favicon.svg +12 -0
- package/dist/ui/i18n.js +919 -233
- package/dist/ui/index.html +159 -87
- package/dist/ui/style.css +789 -410
- package/docs/ARCHITECTURE.md +141 -0
- package/package.json +7 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Operating modes in the session (docs/specs/2026-10-12-worker-tiers.md "Operating modes"): which
|
|
2
|
+
// mode is on, when it ends by itself, and the eager-delegation note. Pure; register.tsx wires them.
|
|
3
|
+
|
|
4
|
+
import type { Hot, Mode, Settings } from '../types'
|
|
5
|
+
|
|
6
|
+
/** The mode that is on, if the settings name one that exists. */
|
|
7
|
+
export function activeModeOf(s: Pick<Settings, 'modes' | 'activeMode'>): Mode | undefined {
|
|
8
|
+
return s.activeMode ? s.modes.find(m => m.id === s.activeMode) : undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** One turning-on of a mode: the same mode turned off and on again is a new one. */
|
|
12
|
+
export function modeKey(s: Pick<Settings, 'activeMode' | 'activeSince'>): string | null {
|
|
13
|
+
return s.activeMode ? `${s.activeMode}@${s.activeSince ?? 0}` : null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** What the main chat reads beside the prompt while an eager mode is on. */
|
|
17
|
+
export function modeNote(name: string): string {
|
|
18
|
+
return `ClaudeRipple mode ${name}: delegate everything except judgment and final review to ripple:auto.`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The eager note for this prompt: sent on the first prompt after the mode turned on, and again on
|
|
23
|
+
* the first after a compaction (which clears `noted`), never on the turns between.
|
|
24
|
+
*/
|
|
25
|
+
export function eagerNote(s: Pick<Settings, 'modes' | 'activeMode' | 'activeSince'>, noted: string | null): { note?: string; noted: string | null } {
|
|
26
|
+
const mode = activeModeOf(s)
|
|
27
|
+
const key = modeKey(s)
|
|
28
|
+
if (!mode || mode.delegation !== 'eager') return { noted }
|
|
29
|
+
return noted === key ? { noted } : { note: modeNote(mode.name), noted: key }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Modes replace the base worker set, without changing fallback, allow-list or provider policy. */
|
|
33
|
+
export function routingSettings(s: Settings): Settings {
|
|
34
|
+
const mode = activeModeOf(s)
|
|
35
|
+
return mode ? { ...s, rules: { general: { mode: 'ladder', rungs: mode.rungs } } } : s
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The provider whose quota reset ends the mode: 'anthropic' is Claude, any other a ChatGPT provider. */
|
|
39
|
+
export function resetProvider(mode: Pick<Mode, 'until'>): string | undefined {
|
|
40
|
+
return typeof mode.until === 'object' ? mode.until.reset : undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Same binding limit as the band: highest percent used, input order breaking ties. */
|
|
44
|
+
export function claudeWindow(limit: Hot | null): QuotaWindow[] {
|
|
45
|
+
const at = limit?.resetsAt ? Date.parse(limit.resetsAt) : NaN
|
|
46
|
+
return Number.isFinite(at) ? [{ at }] : []
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One account's quota window: when it resets next, and how long it is when known. */
|
|
50
|
+
export type QuotaWindow = { at: number; windowMs?: number }
|
|
51
|
+
|
|
52
|
+
function record(value: unknown): Record<string, unknown> {
|
|
53
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Each signed-in, unpaused ChatGPT account's known windows, from GET
|
|
58
|
+
* /api/chatgpt-accounts: `quota.rate_limits.{primary,secondary}` with `reset_at` (epoch seconds,
|
|
59
|
+
* or ms) or `reset_after_seconds` from the snapshot's `at`, and `window_minutes`
|
|
60
|
+
* (packages/router/src/providers/chatgpt/index.ts rateLimitsFromHeaders/rateLimitsFromUsage).
|
|
61
|
+
* An account whose reset is unknown is left out.
|
|
62
|
+
*/
|
|
63
|
+
export function quotaWindows(body: unknown): QuotaWindow[] {
|
|
64
|
+
const accounts = record(body).accounts
|
|
65
|
+
if (!Array.isArray(accounts)) return []
|
|
66
|
+
return accounts.flatMap(value => {
|
|
67
|
+
const account = record(value)
|
|
68
|
+
if (account.state === 'paused' || account.state === 'needs-login') return []
|
|
69
|
+
const quota = record(account.quota)
|
|
70
|
+
const limits = record(quota.rate_limits)
|
|
71
|
+
return ['primary', 'secondary'].flatMap(key => {
|
|
72
|
+
const window = record(limits[key])
|
|
73
|
+
const resetAt = typeof window.reset_at === 'number' ? (window.reset_at > 1e12 ? window.reset_at : window.reset_at * 1000)
|
|
74
|
+
: typeof window.reset_after_seconds === 'number' && typeof quota.at === 'number' ? quota.at + window.reset_after_seconds * 1000 : undefined
|
|
75
|
+
if (resetAt === undefined || !Number.isFinite(resetAt)) return []
|
|
76
|
+
const minutes = window.window_minutes
|
|
77
|
+
return [{ at: resetAt, ...(typeof minutes === 'number' && minutes > 0 ? { windowMs: minutes * 60_000 } : {}) }]
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* End at the first known reset after activation of the mode's chosen provider (`windows` are only
|
|
84
|
+
* that provider's), not after every account resets. ChatGPT's fixed window length also identifies a
|
|
85
|
+
* reset when a fresh snapshot has rolled forward. Claude windows are not extrapolated: the
|
|
86
|
+
* five-hour window can start lazily after idle.
|
|
87
|
+
*/
|
|
88
|
+
export function modeOver(mode: Pick<Mode, 'until'>, since: number | undefined, now: number, windows: readonly QuotaWindow[] = []): boolean {
|
|
89
|
+
if (typeof mode.until === 'number') return now >= mode.until
|
|
90
|
+
if (!resetProvider(mode) || since === undefined || !windows.length) return false
|
|
91
|
+
const passed = (at: number) => at > since && at <= now
|
|
92
|
+
return windows.some(w => passed(w.at) || (w.windowMs !== undefined && passed(w.at - w.windowMs)))
|
|
93
|
+
}
|