clauderipple 0.6.0 → 0.8.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 (45) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.ko.md +43 -2
  3. package/README.md +60 -4
  4. package/dist/app/package.json +1 -1
  5. package/dist/cli/src/claude-auth.js +35 -1
  6. package/dist/cli/src/codex.js +23 -0
  7. package/dist/cli/src/hooks/agent-title.js +58 -29
  8. package/dist/cli/src/index.js +89 -1
  9. package/dist/cli/src/runtime.js +3 -0
  10. package/dist/cli/src/settings.js +49 -0
  11. package/dist/mod/.claude-plugin/plugin.json +1 -0
  12. package/dist/mod/hooks/hooks.json +1 -0
  13. package/dist/mod/hooks/register.tsx +309 -0
  14. package/dist/mod/hooks/worker.ts +49 -0
  15. package/dist/mod/tsconfig.json +3 -0
  16. package/dist/mod/types/index.d.ts +30 -0
  17. package/dist/router/src/admin.js +497 -34
  18. package/dist/router/src/agents.js +50 -9
  19. package/dist/router/src/catalog.js +80 -0
  20. package/dist/router/src/compat.js +27 -1
  21. package/dist/router/src/config.js +33 -0
  22. package/dist/router/src/index.js +13 -1
  23. package/dist/router/src/ingress/server.js +3 -2
  24. package/dist/router/src/picker.js +33 -2
  25. package/dist/router/src/presets.js +2 -1
  26. package/dist/router/src/providers/chatgpt/index.js +154 -15
  27. package/dist/router/src/providers/chatgpt/translate.js +34 -6
  28. package/dist/router/src/providers/google/accounts.js +345 -0
  29. package/dist/router/src/providers/google/antigravity.js +392 -0
  30. package/dist/router/src/providers/google/index.js +617 -0
  31. package/dist/router/src/providers/google/login.js +249 -0
  32. package/dist/router/src/providers/google/thoughtSignatures.js +47 -0
  33. package/dist/router/src/providers/google/translate.js +539 -0
  34. package/dist/router/src/providers/google/transport.js +118 -0
  35. package/dist/router/src/providers/openai/translate.js +3 -3
  36. package/dist/router/src/proxy.js +99 -1
  37. package/dist/router/src/requestlog.js +2 -0
  38. package/dist/router/src/routing.js +22 -0
  39. package/dist/router/src/version.js +1 -1
  40. package/dist/ui/app.js +436 -44
  41. package/dist/ui/i18n.js +88 -4
  42. package/dist/ui/index.html +10 -0
  43. package/dist/ui/style.css +4 -0
  44. package/docs/ARCHITECTURE.md +300 -3
  45. package/package.json +1 -1
@@ -48,6 +48,35 @@ Why this path is stable:
48
48
 
49
49
  ## 2. What the app does with the official third-party setting (why competitors lose)
50
50
 
51
+ ### Optional Auto mode classifier target
52
+
53
+ `autoModeClassifier: { provider, model, effort }` selects the inference target for
54
+ Claude Code's tool-free security-monitor system requests. It is opt-in: without
55
+ this setting, native routing remains unchanged. For example:
56
+
57
+ ```json
58
+ "autoModeClassifier": {"provider": "codex", "model": "gpt-6-luna", "effort": "low"}
59
+ ```
60
+
61
+ The named provider must exist and support outbound inference. Detection uses the
62
+ security-monitor system prompt, never user messages or routing markers. The
63
+ original security prompt, context, output format and native decision logic remain
64
+ intact. Configure this field in the JSON config; the settings GUI has no control
65
+ for it yet. Removing the field restores ordinary routing.
66
+
67
+ This request is Auto mode's safety check: it decides whether an action the agent
68
+ wants to take runs without asking. A target that judges worse than the model
69
+ Claude Code chose lets more through, and nothing reports it, since an allowed
70
+ action looks like any other. Choose the target for its judgement, not for its
71
+ quota. Matching keys on the classifier prompt's opening sentence; if a Claude Code
72
+ release rewords it, the requests stop matching and go back to the native
73
+ classifier, which is the safe direction to fail in.
74
+
75
+ Verified with Desktop's Claude Code 2.1.284: both classification stages used the
76
+ configured target; an authorized temporary-file write proceeded and an explicitly
77
+ forbidden deletion was denied. The registered `CLAUDE_CODE_AUTO_MODE_MODEL`
78
+ environment variable did not change the actual native classifier requests.
79
+
51
80
  Verified in `app.asar` (`.vite/build/index.chunk-C0sgyfNn.js`, `-2CuMdv3h.js`):
52
81
 
53
82
  - The gateway setting (`inferenceGatewayBaseUrl`) is a **whole-app deployment
@@ -114,6 +143,14 @@ chat is out of reach for every approach, ours included.
114
143
  router logs `PICKER injected … surfaces: …` on each bootstrap.
115
144
  - Injecting `additional_model_options` into the Claude Code bootstrap response
116
145
  does **not** reach the app picker (measured; the app never reads that field).
146
+ - **The effort menu is per entry.** The Code tab reads each model's own
147
+ `thinking.effort_options` (ids from `low/medium/high/xhigh/max`; an option may
148
+ carry `recommended`, which the app falls back to when the selected level is not
149
+ listed) — read from the app's cached web bundle, 2026-10-02. A cloned Claude
150
+ entry therefore offered Claude's ladder for every routed model; the injected
151
+ entry now keeps only the template options whose id the model's ladder holds,
152
+ recommends high (or the nearest), and drops `thinking` when none remain. The
153
+ real bootstrap's option fields beyond `id`/`recommended` were not captured.
117
154
  - **Windows uses the same mechanism** (measured end to end in a Windows 11 VM,
118
155
  2026-09-14: picker listed the GPT models, a call routed, the model answered).
119
156
  Two things differ and nothing else does:
@@ -128,6 +165,47 @@ chat is out of reach for every approach, ours included.
128
165
  The app's own log line confirms the proxy took effect:
129
166
  `[egress-proxy] pinned to fixed proxy at 127.0.0.1:<port>; OS proxy settings ignored`.
130
167
 
168
+ ### 3a. The status mod (packages/mod)
169
+
170
+ Claude Code mods (function hooks in a plugin, announced 2026-10-01; on by default
171
+ from CLI 2.1.287, and already loading in the desktop app's 2.1.286 on 2026-10-05)
172
+ draw inside the session itself, on the terminal and on the desktop alike.
173
+ `packages/mod` is one: the `AbovePrompt` band shows where this session's last
174
+ request went, and a `Pane` holds the request log.
175
+
176
+ - **How a session's requests are found.** Claude Code sends its session id as
177
+ `X-Claude-Code-Session-Id` on every `/v1/messages` (captured from CLI 2.1.286
178
+ with a local listener, 2026-10-05: the header equalled `--session-id`). The
179
+ router keeps it on the request record and `/api/requests?session=` filters by
180
+ it. A record without the field means a router older than this; the mod says
181
+ so rather than show another session's route.
182
+ - **The display only reads** the admin API (port from `config.json` as
183
+ `adminPort` works it out).
184
+ - **It also sets worker spawns right** (`hooks/worker.ts`, `agent.spawn`), for
185
+ the agents named in `generated-agents.json` only. A `model` argument on the
186
+ Agent call is replaced by the agent file's own id (a Claude alias there ran
187
+ the worker on Claude with its output cut to 64 tokens), with a toast. A
188
+ `[[ripple: name@level]]` marker anywhere in the prompt that names this worker
189
+ becomes the id's `@level` and leaves the prompt; one naming another alias is
190
+ moved to the first line, the only place `routing.ts` reads it. Measured on
191
+ CLI 2.1.286 (2026-10-05): the hook's `model` is what the subagent runs on —
192
+ `model: "sonnet"` plus the marker on the second line reached the router as
193
+ `deepseek-v4.1-flash@low`, effort low. Without the mod, the first-line rule
194
+ and the no-`model` rule stand as before.
195
+ - **Where it can draw.** The desktop app's header icons (terminal, diff,
196
+ browser) are the app's own; the mod API (CLI 2.1.286 types) has no place for
197
+ a plugin's button there. Its sites are the band, the status line, panes,
198
+ transcript rows and slash commands.
199
+ - **A pane's open state is the engine's.** A module reload drops its pane
200
+ without a `ui.close` the module hears, while `$.state` survives the reload: a
201
+ flag kept there stuck the Log button on "Close log" (2026-10-05). The mod asks
202
+ `$.ui.panes()` instead.
203
+ - **Enabling it** is `env.CLAUDE_CODE_PLUGIN_DIRS` in `~/.claude/settings.json`,
204
+ the one place a desktop-started session reads plugin folders from. It is a
205
+ path list other plugins may share, so only the entry whose manifest is named
206
+ `clauderipple-status` is added or removed (`clauderipple mod on|off`, the
207
+ Clients screen switch).
208
+
131
209
  ## 4. Provider adapters
132
210
 
133
211
  - **Declared models route themselves (2026-09-19).** A provider's `models` list —
@@ -162,11 +240,24 @@ chat is out of reach for every approach, ours included.
162
240
  also a subagent the operator can name: for every id exactly one non-ingress
163
241
  provider declares, the router writes `~/.claude/agents/<name>.md` (`agents.ts`;
164
242
  `name` = the id with anything outside `[a-z0-9-]` folded to `-`, frontmatter
165
- `model:` = the exact id, `@medium` when the provider offers that level). It
243
+ `model:` = the exact id, `@high` when the provider offers any effort level —
244
+ `@medium` until 2026-10-02). It
166
245
  records what it wrote in `<home>/generated-agents.json` and touches **only**
167
246
  those files: a hand-written agent of the same name wins and is left alone, an
168
247
  unticked model removes its generated file and nothing else, identical content is
169
- not rewritten. `cli.agentFiles: false` turns generation off. The marker alias
248
+ not rewritten. `cli.agentFiles: false` turns generation off;
249
+ `cli.limitWorkerTools: true` adds a `tools:` line so a worker does not inherit
250
+ every MCP and plugin tool of the session (first request 32,942 → 8,823 tokens,
251
+ 2026-09-29), at the cost of browser and simulator tools. It is opt-in for that
252
+ reason. With a chatgpt provider configured the body also tells a worker how to
253
+ make an image: `POST /api/image` on the admin port, which runs one Codex
254
+ Responses turn with the hosted `image_generation` tool required and answers
255
+ with the image bytes (`clauderipple image` is the same call). Measured
256
+ 2026-09-29 on a subscription: the image is the `image_generation_call` output
257
+ item's base64 `result`, about 30 s; `output_format` and `background` are
258
+ honoured, `size` and `quality` are accepted and ignored (even "999x1" and
259
+ "bogus" returned 200), and the shape follows the prompt ("square composition"
260
+ → 1254x1254). So the API takes `aspect` and says it in words. The marker alias
170
261
  table is the union of `aliases` (explicit, wins) and every agent file's
171
262
  `name → model` — so `[[ripple: <agent>@<effort>]]` resolves for any agent that
172
263
  exists, generated or not, with no second registry to keep in step. Why: on
@@ -336,6 +427,20 @@ chat is out of reach for every approach, ours included.
336
427
  from the start; the ChatGPT and openai-compatible translators do the same, and a `tool_choice`
337
428
  that named a dropped tool is dropped with it. The adapter logs each drop by name, because the
338
429
  failure it replaces is silent.
430
+ - **A dropped server tool's instructions are withdrawn too (issue #47).** The advisor does reach
431
+ adapters: Claude Code 2.1.286 declares `{ type: "advisor_20260301", name: "advisor", model }`
432
+ on every subagent whatever its `tools:` list says, and explains it in an `isMeta` user message
433
+ (`# Advisor Tool`, "call advisor BEFORE substantive work"). Measured 2026-10-04: `dropped server
434
+ tools for gpt-6-astra: advisor` on every worker request, and 26 of 28 routed worker transcripts
435
+ reporting the tool missing. Dropping only the declaration leaves an instruction the model cannot
436
+ follow. Each translated path that drops server tools therefore appends one line after the
437
+ caller's system text naming them and telling the model to disregard instructions to call them
438
+ (`droppedServerToolNote`), the same move the CLI makes when it removes the advisor itself ("The
439
+ advisor tool is no longer available; disregard the earlier advisor instructions."). The CLI's
440
+ wording is not matched or stripped, since it changes between releases. Names are sorted, so one
441
+ set yields one byte sequence and the cached prefix holds. An anthropic-compatible provider with
442
+ `serverTools: true` keeps every server tool and gets no line; whether DeepSeek runs `advisor` is
443
+ unmeasured.
339
444
  - **Where web search actually runs (measured 2026-09-17).** Claude Code does not put `web_search`
340
445
  in the main request. `WebSearch` opens a *separate side request* — system prompt "You are an
341
446
  assistant for performing a web search tool use", one message "Perform a web search for the
@@ -612,6 +717,12 @@ chat is out of reach for every approach, ours included.
612
717
  this turn), the caller's own `Authorization`/`chatgpt-account-id` is used
613
718
  once as sent — pointing Codex here never leaves it worse off. A WebSocket
614
719
  upgrade is answered 426 so Codex falls back to SSE.
720
+ - **Codex app model list.** `codex on` also points `model_catalog_json` at
721
+ `~/.codex/clauderipple-models.json`: Codex's own `models_cache.json` entries
722
+ plus ours. That file *replaces* Codex's list, so it must follow both sides —
723
+ the router rewrites it when its config loads and when Codex's cache changes
724
+ (checked once a minute; the app reads the catalog only at startup). With the
725
+ first alone, gpt-6.1-sol (2026-09-30) reached Codex's cache but not the app.
615
726
  - Configure a native API-key target as
616
727
  `{ "type":"anthropic", "auth":"api-key", "apiKey":"…" }` (or set
617
728
  `ANTHROPIC_API_KEY`). Configure a compatible target as today, for example an
@@ -779,7 +890,16 @@ what we do not have yet — so that adding a provider does not start with readin
779
890
  `effortLevels` overrides provider defaults, including `[]` for “no effort”. When
780
891
  an OpenRouter `/models` entry has `supported_parameters`,
781
892
  `reasoning_effort` maps to `[low, medium, high]`; its absence maps to `[]`.
782
- Vendors that do not report that field keep their configured fallback. Chat wire
893
+ Vendors that do not report that field keep their configured fallback. For the
894
+ OpenCode presets (`opencode-go`, `opencode-zen`) a model's ladder comes from
895
+ models.dev — the catalogue OpenCode's own client reads, whose `reasoning_options`
896
+ states it — and its `provider.npm` puts that wire first; one request then settles
897
+ the wire and catches a plan refusal. Asking every level instead took ~10 sequential
898
+ requests per model (glm-5.3-flash 1m54s, upstream latency 0.5–25s per one-token
899
+ request, 2026-10-02) and was less right: a 200 means "not refused", and
900
+ glm-5.3-flash accepted seven levels where the catalogue lists `low`/`high`/`max`.
901
+ A model the catalogue lacks, or a catalogue that cannot be fetched, is measured as
902
+ before (`catalog.ts`, `capabilities.ts`). Chat wire
783
903
  forwards Anthropic `stop_sequences` as `stop`; Responses wire deliberately omits
784
904
  them because its common stateless schema has no corresponding universal field.
785
905
  - We do not assert a provider-wide cache hit rate: the adapter records
@@ -792,6 +912,167 @@ what we do not have yet — so that adding a provider does not start with readin
792
912
  credit. Preset endpoint and capability claims cite the vendor's official docs in
793
913
  `packages/router/src/presets.ts`.
794
914
 
915
+ ### 4d. `google` providers (implemented 2026-10-01)
916
+
917
+ Gemini is a translated provider, not an ingress-only one: it holds no server-side thread,
918
+ so a `thread:continue` is refused exactly as in §4c and the CLI resends a stateless
919
+ history. It routes by its declared models, its agent files are generated like any other
920
+ provider's, and a `web_search` side request aimed at it is refused, not sent. The
921
+ adapter lives in `packages/router/src/providers/google/` and separates translation
922
+ (`translate.ts`), the wire (`transport.ts`) and the signature store
923
+ (`thoughtSignatures.ts`) so Stage 2 (Antigravity Code Assist) is a new transport only.
924
+
925
+ Both modes are implemented: `api-key` (AI Studio) below, and `auth: "antigravity"` (Cloud
926
+ Code Assist) in its own subsection. No live key or Antigravity subscription exists in this
927
+ environment, so everything below is doc- and source-derived and **not measured** unless it
928
+ says otherwise:
929
+
930
+ - Stream endpoint `POST {base}/v1beta/models/{model}:streamGenerateContent?alt=sse`,
931
+ the key on header `x-goog-api-key`, base default `https://generativelanguage.googleapis.com`
932
+ (https://ai.google.dev/api/generate-content#method:-models.streamgeneratecontent).
933
+ - `systemInstruction` takes `{role:"user"|"model", parts:[…]}`; `contents` is the
934
+ `user`/`model` turn list; a part is `{text}`, `{inlineData:{mimeType,data}}`,
935
+ `{functionCall:{name,args}}` or `{functionResponse:{name,response}}`. The `role` of a
936
+ `functionResponse` part must be `user` (https://ai.google.dev/api/caching#Content).
937
+ - Tools map to `tools[].functionDeclarations[].parametersJsonSchema` — a protobuf
938
+ `Value` carrying a full JSON Schema, mutually exclusive with the legacy `parameters`
939
+ (https://ai.google.dev/api/caching#FunctionDeclaration). We send `parametersJsonSchema`.
940
+ - A function name is at most 64 characters, must start with a letter or underscore and
941
+ may contain letters, digits, underscores, dots, dashes and colons (same
942
+ `FunctionDeclaration.name`); longer ids are mangled and restored as in §4c.
943
+ - `toolConfig.functionCallingConfig.mode` is `AUTO` / `ANY` / `NONE`; `ANY` may also
944
+ carry `allowedFunctionNames` (https://ai.google.dev/api/caching#FunctionCallingConfig).
945
+ Anthropic `tool` maps to `ANY` + `allowedFunctionNames`, `none` to `NONE`.
946
+ - Gemini 3 reasons with `generationConfig.thinkingConfig.thinkingLevel`
947
+ (`minimal|low|medium|high`); Gemini 2.5 with `thinkingConfig.thinkingBudget`
948
+ (0..24576 flash, 128..32768 pro; 0 disables) (https://ai.google.dev/gemini-api/docs/thinking).
949
+ `output_config.effort` is clamped to the model family and to the provider's
950
+ `effortLevels` when set. **(assumption)** Some samples spell the REST field
951
+ `thinking_level` flatly; we emit the documented nested `generationConfig.thinkingConfig`
952
+ shape.
953
+ - `usageMetadata` fields are `promptTokenCount`, `cachedContentTokenCount`,
954
+ `candidatesTokenCount`, `thoughtsTokenCount`
955
+ (https://ai.google.dev/api/generate-content#UsageMetadata). Input =
956
+ `promptTokenCount − cachedContentTokenCount`, cache read = `cachedContentTokenCount`,
957
+ output = `candidatesTokenCount + thoughtsTokenCount`.
958
+ - A `finishReason` of `STOP` closes as `end_turn` (or `tool_use` when function calls
959
+ were seen); `MAX_TOKENS` as `max_tokens`; anything else as `end_turn` with a log. A
960
+ stream that ends with no `finishReason` is the §5 stalled-upstream shape: it becomes a
961
+ retryable `overloaded_error`, never a silent `end_turn`
962
+ (https://ai.google.dev/api/generate-content#FinishReason).
963
+ - A wrong key answers `400` with `status: "API_KEY_INVALID"` (or a `401`/`403` whose
964
+ body names the key); that maps to Anthropic `authentication_error` (401). `429
965
+ RESOURCE_EXHAUSTED` maps to `rate_limit_error`, `5xx` to `overloaded_error`, other
966
+ `4xx` to `invalid_request_error`. A bare `403` is read as a permission/region problem,
967
+ not a bad key.
968
+ - `GET {base}/v1beta/models?pageSize=1000` (key on `x-goog-api-key`) lists
969
+ `{name, displayName, inputTokenLimit, supportedGenerationMethods, thinking}` with a
970
+ `nextPageToken`; only entries whose `supportedGenerationMethods` include
971
+ `generateContent` are kept, `id` is `name` minus `models/`, and
972
+ `contextWindow = inputTokenLimit`
973
+ (https://ai.google.dev/api/models#method:-models.list).
974
+ - **thoughtSignature (Gemini 3).** A `functionCall` part on a reasoning model may carry a
975
+ `thoughtSignature`, and a client replaying that call must return the same signature or
976
+ the API rejects the turn ("Function call is missing a thought_signature in
977
+ functionCall parts"). The store keeps signatures keyed by our minted `tool_use` id in a
978
+ bounded LRU (10,000) and re-attaches them on replay; per-part for parallel calls, where
979
+ Gemini signs only the first call of a step. On a turn with no cached signature at all (a
980
+ router restart, a history another model wrote) the documented sentinel
981
+ `skip_thought_signature_validator` goes on that turn's **first** call, Gemini 3 only. The
982
+ guide calls it a last resort that costs model performance, so it is never added to a turn
983
+ that has a real signature — the unsigned parallel calls of a healthy turn are normal
984
+ (https://cloud.google.com/vertex-ai/generative-ai/docs/thought-signatures, sentinel
985
+ paragraph read 2026-10-01).
986
+
987
+ ### 4d-ii. Google `auth: "antigravity"` (Cloud Code Assist) — implemented 2026-10-01
988
+
989
+ A Google subscription signed in through `clauderipple google-login`, streaming through the
990
+ Cloud Code Assist (CCA) backend, with several accounts rotating. Same translation as AI
991
+ Studio; only `transport.ts` differs. Accounts live in `<home>/google-accounts.json` (0600,
992
+ cross-process lock, atomic write, one account per email+project, a re-login replaces its own
993
+ entry), the sign-in in `login.ts`, the account pool and refresh in `accounts.ts`, and the wire
994
+ facts and model-id resolution in `antigravity.ts`. **Every wire fact below is taken from the
995
+ reference implementation opencodex (GitHub `lidge-jun/opencodex`, read 2026-10-01 as a
996
+ behavioral spec — no code copied; project rule) and is not measured live** — no Antigravity
997
+ subscription exists in this environment. Files are under `src/`:
998
+
999
+ - OAuth is Google's standard installed-app flow with PKCE S256. Client id
1000
+ `1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com`, secret
1001
+ `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` (public client values, overridable by env;
1002
+ `oauth/google-antigravity.ts:17-20`). Authorize
1003
+ `https://accounts.google.com/o/oauth2/v2/auth`, tokens
1004
+ `https://oauth2.googleapis.com/token`, userinfo
1005
+ `https://www.googleapis.com/oauth2/v2/userinfo`; scopes `cloud-platform`, `userinfo.email`,
1006
+ `userinfo.profile`, `cclog`, `experimentsandconfigs`; `access_type=offline`,
1007
+ `prompt=consent select_account`; loopback callback port 51121 preferred, path `/callback`,
1008
+ any free port otherwise (`google-antigravity.ts:21-35,177-196`). The token body carries the
1009
+ client id **and** secret and `code_verifier` (form-encoded).
1010
+ - Project discovery: `POST https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist` with
1011
+ `{metadata:{ideType:"ANTIGRAVITY"}}`; without a project,
1012
+ `POST https://daily-cloudcode-pa.googleapis.com/v1internal:onboardUser` with
1013
+ `{tier_id:"free-tier", metadata:{ide_type, ide_name:"antigravity", ide_version}}`, polled up
1014
+ to 5 times, 2 s apart, until `done:true` (`google-antigravity.ts:97-137`). The project id is
1015
+ read from `cloudaicompanionProject | projectId | project[.id]`. A sign-in that finds no
1016
+ project fails rather than storing a credential that would fail every call.
1017
+ - Request: `POST {base}/v1internal:streamGenerateContent?alt=sse` (non-streaming
1018
+ `:generateContent`), base default `https://daily-cloudcode-pa.googleapis.com`
1019
+ (`adapters/google.ts:1056-1160`; devlog `140.21`). Envelope
1020
+ `{ model, userAgent:"antigravity", requestType:"agent", project, requestId:"agent-<uuid>",
1021
+ request }`, where `request` is the standard Gemini body plus a nested camelCase
1022
+ `sessionId` (`adapters/google.ts:1145-1158`; `google-antigravity-wire.ts:53-77`). Headers:
1023
+ `Authorization: Bearer <token>` and `User-Agent: antigravity/ide/<ver> (os_type=…; arch=…;
1024
+ aidev_client; auth_method=oauth)` — the IDE client family is required, the backend 404s
1025
+ CLI-shaped UAs (`adapters/client-fingerprint.ts:44-65`). Response frames nest the standard
1026
+ Gemini payload under `response` (`adapters/google.ts:1281,1591-1596`).
1027
+ - Model ids. CCA's wire ids carry the tier as a suffix (`gemini-3.8-flash-low`) or a
1028
+ `-tiered` rename (`gemini-3.7-flash-tiered`), while the picker shows a collapsed base id.
1029
+ Resolution, from `providers/antigravity-models.ts`: `gemini-3.8-flash` → its suffix per
1030
+ effort (`…-low/medium/high`, no `thinkingLevel` beside it — the suffix IS the effort);
1031
+ `gemini-3.1-pro` → `gemini-3.1-pro-low` / `gemini-pro-agent` with `thinkingLevel`;
1032
+ `gemini-3.7-flash` → `gemini-3.7-flash-tiered` with `thinkingLevel`; a `claude-*` id takes a
1033
+ `thinkingLevel` only (`resolveAntigravityThinkingLevel` clamps `xhigh/max/ultra`→`high`).
1034
+ CCA's own catalogue is `POST {dailyApi}/v1internal:fetchAvailableModels` with `{project}`,
1035
+ body `{models:{<wireId>:{maxTokens,…}}, agentModelSorts:[{groups:[{modelIds:[…]}]}],
1036
+ tieredModelIds:{flash:[…]}}` (`providers/quota/antigravity.ts:166-168`,
1037
+ `providers/antigravity-models.ts:489-560`); when it cannot be read we fall back to the
1038
+ static collapsed list (`gemini-3.8-flash`, `gemini-3.7-flash`, `gemini-3.1-pro`,
1039
+ `gemini-3.1-flash-image`, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`,
1040
+ `gpt-oss-120b-medium`, context windows from CCA `maxTokens`).
1041
+ - Claude models on CCA force `toolConfig.functionCallingConfig.mode = "VALIDATED"`; a client
1042
+ asking for no tools gets the declarations dropped instead (`adapters/google.ts:1109-1120`).
1043
+ - **thoughtSignature.** The CCA path sends a real signature back on the exact `functionCall`
1044
+ the model signed and never adds the `skip_thought_signature_validator` sentinel (a fabricated
1045
+ signature is not "real"). opencodex filters a stored signature with
1046
+ `isLikelyRealThoughtSignature` (`google-antigravity-wire.ts:30-42`) to keep a foreign id
1047
+ (`toolu_…`, `call_…`) out of the signature field, which Antigravity 400s
1048
+ (`TYPE_BYTES`/Base64 decode failure). We do not need that filter: our store is keyed by the
1049
+ `tool_use` id we minted and holds only a `thoughtSignature` the vendor itself sent, so a
1050
+ foreign id is never placed in the field.
1051
+ - **Accounts and credentials (§4 Credential pools).** A conversation stays on the account that
1052
+ answered while it is healthy (moving it costs the prompt cache); a refusal before any byte
1053
+ reaches the client moves the turn to the next account: 401 (or a 403 that reads as a
1054
+ credential refusal) gets one refresh and a replay, a fresh token refused with a 401 again
1055
+ marks the account for sign-in; 429/402 rest the account to its `retry-after` (or a default);
1056
+ another 403, 5xx or a connect failure rests it briefly; any other 4xx is the request's fault
1057
+ and is not retried. Refresh is per account, single-flight per token generation, five minutes
1058
+ ahead; only a structured `invalid_grant` marks an account for sign-in. Only the access token
1059
+ and the account email/project id are ever in memory; no token reaches a log or an API response.
1060
+ - Session id and prompt cache. The real client puts a stable per-conversation id at
1061
+ `request.sessionId` so provider-side replay can associate a signature with the call that made
1062
+ it; instability loses it (`google-antigravity-wire.ts:62-88`). We derive it from the router's
1063
+ own `conversationKey` (sha256 → BigEndian uint64 masked to 63 bits, left-padded with `-`,
1064
+ CLIProxyAPI's `generateStableSessionID` shape). `requestId` is a fresh `agent-<uuid>` per
1065
+ request, so it does not enter the cacheable body prefix.
1066
+ - **Terms.** Using Antigravity credentials from a non-Google client is against Google's terms
1067
+ (Antigravity terms §6; the Gemini CLI FAQ) and Google blocked a large number of such accounts
1068
+ in February 2026. The CLI prints this once before a sign-in and the sign-in API returns it in
1069
+ a `warning` field for the GUI; the wording states only the fact. ClaudeRipple is not
1070
+ affiliated with Google.
1071
+ - Admin: `POST/GET /api/google-login`, `POST /api/google-login/code`, `POST
1072
+ /api/google-login/cancel`, `POST /api/google-logout`, `GET /api/google-accounts`,
1073
+ `PATCH/DELETE /api/google-accounts/:id`, and `POST /api/providers/probe` with
1074
+ `{type:"google", auth:"antigravity"}`. CLI: `clauderipple google-login` / `google-logout`.
1075
+
795
1076
  ## 5. Failure modes that must not exist in the product (all observed)
796
1077
 
797
1078
  | Observed | Product requirement |
@@ -807,6 +1088,7 @@ what we do not have yet — so that adding a provider does not start with readin
807
1088
  | Tool names over 64 characters were forwarded to the Responses API unchanged, so a single connected MCP server — the product's own use case (§2) — failed every request of that turn (found by reading the source, issue #1, 2026-09-17) | Any name a translated provider sends is mangled into the provider's constraint deterministically and restored on the way back (§4). A translator must validate what it forwards against the wire it forwards to, not only what it builds itself. |
808
1089
  | Unknown-model context window defaulted to 200K, compaction fired at 151K; fixed via `CLAUDE_CODE_MAX_CONTEXT_TOKENS=272000` (applies only to models not in the CLI's built-in table; Claude models unaffected) | Installer sets this env for mapped models; document that it does not affect Claude models. |
809
1090
  | Server tools (`web_search`) were declared to translated providers as ordinary functions: a tool the model can call and nothing can run. Not observed live, because Claude Code currently routes its web search elsewhere (§4) — the failure was one server-side flag away, and its shape is an empty answer with nothing logged (issue #4, 2026-09-17) | A translator declares only what the provider can actually execute, and says in the log what it removed. A capability that silently disappears is worse than one that visibly fails. |
1091
+ | Routed workers were told to call the `advisor` after its declaration was dropped: Claude Code explains the tool in a user message the router left in place, so 26 of 28 worker transcripts reported it missing, every turn (issue #47, 2026-10-04) | Removing a capability includes withdrawing what was said about it. A translator that drops a tool adds one stable line saying so, rather than editing the caller's text. |
810
1092
  | One context window was written to every routed model and every slot, because `cli.autoCompactWindow` is a single number while `auto_compact_windows` and `context_window_by_model` are per-model maps. Routed models do not share a window: set it high and the smaller model overflows before it compacts, set it low and the larger throws away most of its own (issue #2, 2026-09-17) | A window belongs to a model, not to the router. `CliModel.contextWindow` and `Route.contextWindow` win, then whatever the vendor's `/models` reported as `context_length`, then the global value as the fallback it always was. A value the config can only express once must not be injected into a map that is keyed per model. |
811
1093
  | proxenos sends only the 7-day quota window, so the app shows a "weekly limit" banner | Quota reporting must mirror the shape Anthropic returns. |
812
1094
  | After a reboot the router listened 4 minutes after login (26s of it between exec and `listen()`), and for that whole window Claude Desktop was a blank page with `ERR_PROXY_CONNECTION_FAILED` — in picker mode every byte the app sends goes through us, so a router that is merely slow reads as an app that is broken (2026-09-14 09:59 boot → 10:15:59 listening) | The launchd agent is `ProcessType=Interactive`, never `Background` (that key throttles CPU and I/O — launchd.plist(5)). `listen()` comes before certificate minting and any other startup work, so a client waits rather than being refused. Every startup logs its budget (`startup Nms: node …, config …, listen …`). |
@@ -838,6 +1120,7 @@ what we do not have yet — so that adding a provider does not start with readin
838
1120
  | A session switched from gpt-6-astra to claude-opus-5-5 failed every turn with `400 messages.2.content.0.thinking: each thinking block must contain thinking`: the translated adapters hand the CLI reasoning as `thinking` blocks with no signature (and no text when the reasoning item had no summary), the CLI keeps them, and passthrough replayed them to Anthropic (2026-09-28) | Before a request reaches Anthropic (`PASS` and the account-pool provider), assistant `thinking` blocks without a signature are dropped (`thinking.ts`) — Anthropic's own always carry one — and a turn left empty goes with them. Passthrough stays byte-exact when there is nothing to drop. |
839
1121
  | Re-running the installer — the documented update — replaced the files and left the router running the old code: `install` treats a running router as done, and on Windows and Linux an unchanged supervisor definition is not re-registered, so nothing restarted it until the next login (found 2026-09-28 while building `update`; the tray's own restart covered only the packaged app) | `install` asks a router that is already running for its version and restarts one that differs. Measured in the Windows 11 VM: 0.5.1 running, installer run with 0.6.0 → "restarting it on 0.6.0", status 0.6.0. |
840
1122
  | The tray's Electron (~270MB) was fetched into the installed package's own `node_modules`, and `npm install -g` of any other version deleted it with the old files (measured 2026-09-28): every update silently took the tray away, and on Windows a running tray held `electron.exe` inside the folder npm had to replace | Electron lives in `<home>/tray-runtime`, outside anything an update replaces. Both install scripts move one found in the old place there before installing (Windows first stops a tray running from it, and starts it again after setup) — checked on macOS and in the VM with a locked `electron.exe`. |
1123
+ | Adding a provider in the settings window restored three OpenCode effort ladders realigned by hand five hours earlier, so the app's effort menus for DeepSeek and GLM never narrowed (2026-10-02 23:13, found 10-05). `PUT /api/config` takes the whole config, and the screen built it from the copy it loaded at open and never re-read; the router itself also writes `config.json` (a measurement's results), so any of those was undone by the next unrelated save | Every GUI save fetches the router's config first and applies its own change to that. A provider form, which holds per-model fields (wire, ladder, window) from when it opened and has no control for them, takes a saved model's values from the router's copy. The provider and client screens re-read the config whenever they load. |
841
1124
 
842
1125
  ## 6. Blocked paths (measured, do not retry)
843
1126
 
@@ -856,3 +1139,17 @@ CC BY-NC-ND (no forks, no sponsors). CLI-targeting `claude-code-router` has
856
1139
  37,180★, `opencodex` 14,300★ with two README sponsors. Open positions: English
857
1140
  distribution, GPL-3.0 license (chosen 2026-09-13 over MIT: sole author, reversible later, blocks closed commercial repackaging), sponsor slots. Positioning: an **add-on for Claude
858
1141
  subscribers**, not a replacement for people without one.
1142
+
1143
+
1144
+ ### Codex context-overflow recovery
1145
+
1146
+ The subscription backend can reject an oversized request with HTTP 200 followed by
1147
+ `response.failed` / `context_length_exceeded`. The Messages adapter maps this to
1148
+ `invalid_request_error` and prefixes `prompt is too long`, which Desktop CLI
1149
+ 2.1.284 recognizes. Keep the initial message-start event pending until another
1150
+ translated event arrives so an early rejection can retain HTTP 400. Transient
1151
+ overloads and interrupted streams remain retryable.
1152
+
1153
+ Both streaming and non-streaming early-rejection paths are covered by adapter
1154
+ tests. The client still needs to compact its history or otherwise reduce input;
1155
+ this mapping does not increase the backend's context limit.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clauderipple",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Use GPT (ChatGPT Plus/Pro), DeepSeek and 400+ other models in the Claude Desktop Code tab and Claude Code while staying signed in to your Claude subscription — no third-party gateway mode. Several ChatGPT accounts that take over when one runs out, in Codex too.",
5
5
  "license": "GPL-3.0-only",
6
6
  "workspaces": [