clauderipple 0.9.4 → 0.10.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.
- package/CHANGELOG.md +80 -0
- package/README.md +31 -0
- package/dist/app/package.json +1 -1
- package/dist/cli/src/hooks/agent-title.js +7 -4
- package/dist/cli/src/index.js +43 -0
- package/dist/cli/src/remote.js +263 -0
- package/dist/cli/src/schtasks.js +48 -40
- package/dist/cli/src/settings.js +2 -1
- package/dist/mod/hooks/i18n.ts +252 -6
- package/dist/mod/hooks/judge.ts +35 -17
- package/dist/mod/hooks/modes.ts +56 -1
- package/dist/mod/hooks/register.tsx +238 -32
- package/dist/mod/hooks/session.ts +98 -1
- package/dist/mod/hooks/settings.js +7 -3
- package/dist/mod/hooks/settings.ts +9 -4
- package/dist/mod/hooks/stats.ts +25 -1
- package/dist/mod/hooks/ui/agents.tsx +858 -79
- package/dist/mod/hooks/ui/alerts.tsx +20 -17
- package/dist/mod/hooks/ui/art.ts +33 -7
- package/dist/mod/hooks/ui/band.tsx +262 -81
- package/dist/mod/hooks/ui/flat.ts +265 -0
- package/dist/mod/hooks/ui/kit.tsx +70 -55
- package/dist/mod/hooks/ui/look.ts +129 -0
- package/dist/mod/hooks/ui/settings.tsx +71 -46
- package/dist/mod/hooks/ui/setup.tsx +15 -13
- package/dist/mod/hooks/ui/theme.ts +3 -0
- package/dist/mod/hooks/ui/tools.ts +124 -0
- package/dist/mod/hooks/ui/view.ts +116 -3
- package/dist/mod/hooks/workers.ts +33 -6
- package/dist/mod/tests/register.test.tsx +155 -11
- package/dist/mod/tests/ui.test.tsx +375 -27
- package/dist/mod/types/index.d.ts +68 -2
- package/dist/remote/package.json +28 -0
- package/dist/remote/src/away.js +85 -0
- package/dist/remote/src/bridge-client.js +295 -0
- package/dist/remote/src/composer-menu.js +11 -0
- package/dist/remote/src/config.js +102 -0
- package/dist/remote/src/desktop.js +1018 -0
- package/dist/remote/src/e2ee.js +126 -0
- package/dist/remote/src/e2ee.ts +126 -0
- package/dist/remote/src/eventlog.js +298 -0
- package/dist/remote/src/expo-push.js +127 -0
- package/dist/remote/src/folders.js +79 -0
- package/dist/remote/src/fsutil.js +39 -0
- package/dist/remote/src/handoff.js +38 -0
- package/dist/remote/src/host-service.js +168 -0
- package/dist/remote/src/images.js +215 -0
- package/dist/remote/src/local-control.js +138 -0
- package/dist/remote/src/main.js +264 -0
- package/dist/remote/src/permissions.js +124 -0
- package/dist/remote/src/power.js +76 -0
- package/dist/remote/src/processes.js +24 -0
- package/dist/remote/src/protocol.js +32 -0
- package/dist/remote/src/protocol.ts +284 -0
- package/dist/remote/src/push-content.js +32 -0
- package/dist/remote/src/push.js +134 -0
- package/dist/remote/src/receipts.js +111 -0
- package/dist/remote/src/relay.js +438 -0
- package/dist/remote/src/router.js +208 -0
- package/dist/remote/src/sdk-launcher.js +18 -0
- package/dist/remote/src/server.js +706 -0
- package/dist/remote/src/service.js +157 -0
- package/dist/remote/src/sessions.js +1800 -0
- package/dist/remote/src/sidebar-storage.js +211 -0
- package/dist/remote/src/sidebar.js +173 -0
- package/dist/remote/src/tool-details.js +205 -0
- package/dist/remote/src/tool-details.ts +153 -0
- package/dist/remote/src/worktree.js +99 -0
- package/dist/remote/web/app.ts +746 -0
- package/dist/remote/web/conn.ts +193 -0
- package/dist/remote/web/demo.ts +134 -0
- package/dist/remote/web/dom.ts +124 -0
- package/dist/remote/web/esc.ts +4 -0
- package/dist/remote/web/i18n.ts +374 -0
- package/dist/remote/web/icon.svg +1 -0
- package/dist/remote/web/index.html +24 -0
- package/dist/remote/web/manifest.webmanifest +10 -0
- package/dist/remote/web/mascot.ts +60 -0
- package/dist/remote/web/md.ts +78 -0
- package/dist/remote/web/notify.ts +55 -0
- package/dist/remote/web/pushstate.ts +27 -0
- package/dist/remote/web/secure.ts +109 -0
- package/dist/remote/web/sidebar.ts +106 -0
- package/dist/remote/web/style.css +311 -0
- package/dist/remote/web/sw.ts +48 -0
- package/dist/remote/web/timeline.ts +224 -0
- package/dist/remote/web/views.ts +323 -0
- package/dist/router/src/admin.js +227 -4
- package/dist/router/src/config.js +30 -0
- package/dist/router/src/index.js +39 -0
- package/dist/router/src/ingress/translate.js +6 -2
- package/dist/router/src/phone.js +82 -0
- package/dist/router/src/presets.js +3 -0
- package/dist/router/src/providers/anthropic-usage.js +33 -1
- package/dist/router/src/providers/chatgpt/index.js +41 -11
- package/dist/router/src/providers/chatgpt/translate.js +147 -7
- package/dist/router/src/providers/google/index.js +26 -0
- package/dist/router/src/providers/google/prompt-refusal.js +29 -0
- package/dist/router/src/providers/google/usage.js +257 -0
- package/dist/router/src/providers/grok/index.js +18 -0
- package/dist/router/src/providers/grok/usage.js +77 -0
- package/dist/router/src/providers/openai/index.js +29 -5
- package/dist/router/src/providers/openai/translate.js +260 -27
- package/dist/router/src/providers/opencode-usage.js +116 -0
- package/dist/router/src/proxy.js +339 -258
- package/dist/router/src/reasoning.js +157 -0
- package/dist/router/src/remote-host.js +320 -0
- package/dist/router/src/remote-tunnel.js +320 -0
- package/dist/router/src/requestlog.js +48 -39
- package/dist/router/src/thinking.js +20 -9
- package/dist/router/src/usage-history.js +227 -0
- package/dist/router/src/version.js +1 -1
- package/dist/ui/app.js +436 -59
- package/dist/ui/i18n.js +184 -0
- package/dist/ui/index.html +24 -5
- package/dist/ui/style.css +39 -0
- package/docs/ARCHITECTURE.md +164 -12
- package/package.json +7 -1
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -46,6 +46,41 @@ Why this path is stable:
|
|
|
46
46
|
- The CA is never installed in the keychain. `NODE_EXTRA_CA_CERTS` is a Node-only
|
|
47
47
|
trust addition, no admin password, no effect on other programs.
|
|
48
48
|
|
|
49
|
+
### 1a. Remote (SSH) sessions (2026-10-20, issue #67)
|
|
50
|
+
|
|
51
|
+
A Code-tab session on a remote host runs Claude Code **on that host**, started by
|
|
52
|
+
Desktop's remote server with `--setting-sources user,project,local`, so the
|
|
53
|
+
host's own `~/.claude/settings.json` applies and its 127.0.0.1 is itself. Desktop
|
|
54
|
+
opens that connection with its bundled `ssh2` library, uses only `forwardOut`
|
|
55
|
+
(local → remote), and never reads `RemoteForward` from `~/.ssh/config` (verified
|
|
56
|
+
for #67). Nothing Desktop does can carry the host's traffic back to us, so the
|
|
57
|
+
router holds its own reverse tunnel: `ssh -N -o ExitOnForwardFailure=yes -R
|
|
58
|
+
127.0.0.1:<remote port>:127.0.0.1:<proxy port> <host>` per configured host
|
|
59
|
+
(`remote.hosts`, `packages/router/src/remote-tunnel.ts`), with the user's own ssh
|
|
60
|
+
and keys (`BatchMode=yes`: never a prompt). `clauderipple remote add` writes the
|
|
61
|
+
same two env keys as §1 into the host's settings.json (same foreign-proxy rule,
|
|
62
|
+
one backup), the public CA to `~/.clauderipple-remote/ca.pem`, and the generated
|
|
63
|
+
agent files, all by POSIX `sh` scripts on ssh's stdin — the host needs no Node.
|
|
64
|
+
The host name is validated and only ever passed as its own argv element.
|
|
65
|
+
|
|
66
|
+
- Up is when ssh says `remote forward success for: listen …` (it needs `-v`;
|
|
67
|
+
every other debug line is dropped before the log). A dropped tunnel restarts
|
|
68
|
+
with 1s→60s backoff; each comeback re-copies the CA and agent files, and the
|
|
69
|
+
config callback re-copies them when the router regenerates the agents.
|
|
70
|
+
- While the tunnel is down, the host's Claude Code gets *connection refused* —
|
|
71
|
+
a visible error, never a silent route to Anthropic around the router. The
|
|
72
|
+
remote session only exists while this machine's Desktop is connected anyway.
|
|
73
|
+
- Tunnels are killed at process exit, not when a drain starts: a remote
|
|
74
|
+
session's in-flight model call travels through one (§5, drain). Their pids are
|
|
75
|
+
kept in `<home>/remote-tunnels.json` and a new router kills any that still run
|
|
76
|
+
with our command line — an orphan holding the remote port would make every new
|
|
77
|
+
tunnel fail `ExitOnForwardFailure` for as long as it lives.
|
|
78
|
+
- `clauderipple remote remove` stops the tunnel, removes only the env values
|
|
79
|
+
that are still ours, the agent files listed in the host's
|
|
80
|
+
`~/.clauderipple-remote/agents.json`, and that directory.
|
|
81
|
+
- Not yet on remote hosts: the status mod, the `cli.models` slot env, and image
|
|
82
|
+
generation (generated agents name the admin port, which is not tunnelled).
|
|
83
|
+
|
|
49
84
|
## 2. What the app does with the official third-party setting (why competitors lose)
|
|
50
85
|
|
|
51
86
|
### Optional Auto mode classifier target
|
|
@@ -731,8 +766,13 @@ must preserve **Data: Artificial Analysis (artificialanalysis.ai)** attribution.
|
|
|
731
766
|
- `WebFetch` needs none of this: the CLI fetches the URL itself (its own transport, cache and
|
|
732
767
|
preflight; there is no `web_fetch` server tool in the binary) and has the session's model read
|
|
733
768
|
the text. It works on any provider.
|
|
734
|
-
- Cache-safety decisions:
|
|
735
|
-
|
|
769
|
+
- Cache-safety decisions: a translated provider's own reasoning that comes back with the
|
|
770
|
+
request is sent back **byte-identically, in its original position** — that is what the
|
|
771
|
+
vendor's native clients do, and it is not a `cache_control` breakpoint; the forbidden
|
|
772
|
+
thing is *altering or re-signing* reasoning, which is what cost 12–20% cache (2026-09-20).
|
|
773
|
+
A thinking block of ours that names no vendor model, or another model than this request's,
|
|
774
|
+
is dropped (§5, "Muse workers"); foreign thinking of any other kind is dropped from
|
|
775
|
+
replayed history; identity line and `instructionsAppend` are constant
|
|
736
776
|
text; `prompt_cache_key` = sha256(metadata.user_id + first user message),
|
|
737
777
|
and sha256(system prompt) for a request that carries no `metadata.user_id`
|
|
738
778
|
and one lone user turn — see the conversation-key note in §4.
|
|
@@ -767,11 +807,29 @@ text is added. Source: `requestlog.ts`, `proxy.ts`, provider adapter outcomes.
|
|
|
767
807
|
- `upstreamRequestId`: the final forwarded native Anthropic response's `request-id` header (passthrough or account pool).
|
|
768
808
|
- `messageId`: the Anthropic message id delivered by a translated adapter or forwarded compatible response; not a generated id from a non-streaming error.
|
|
769
809
|
- `upstreamCalled`: `false` only for a known pre-model-API exit; omitted when a search backend was attempted or the call state is unknown.
|
|
770
|
-
- `refusal`: `thread-continue`, `drain`, `unroutable`, `no-account`, `unknown-provider
|
|
810
|
+
- `refusal`: `thread-continue`, `drain`, `unroutable`, `no-account`, `unknown-provider`, `web-search-unsupported` or `prompt-incompatible`, from the actual exit branch. `prompt-incompatible` follows an upstream request; it does not set `upstreamCalled: false`.
|
|
771
811
|
- `purpose`: `auto-mode-classifier` from the security-monitor request shape regardless of routing settings, or `web-search` for router-served searches.
|
|
772
812
|
- `requestedTier`: the service tier actually sent upstream, including ChatGPT's `speed: "fast"` → `priority`; omitted when no tier was sent.
|
|
773
813
|
- `servedTier`: the response-reported tier (Responses/Chat `service_tier` or Anthropic `usage.service_tier`); never inferred from the request.
|
|
774
814
|
|
|
815
|
+
**Hourly usage history (`usage-hourly.jsonl`, 2026-10-20).** `requests.jsonl` is
|
|
816
|
+
bounded (2,000 records in memory, 20 MB plus one `.1` backup), so the GUI could only
|
|
817
|
+
summarise about an hour. `RequestLog` now also owns a `UsageHistory` that appends one
|
|
818
|
+
JSON line per `(hour, provider, model)` partial bucket —
|
|
819
|
+
`{"h": floor(at/3600000), "p", "m", "count","ok","failed","input","cached","cacheWrite","output","ms"}`
|
|
820
|
+
— to `usage-hourly.jsonl` beside `requests.jsonl`. Lines are **additive**: reading
|
|
821
|
+
sums every line with the same `(h,p,m)`, so the file stays append-only. Only the
|
|
822
|
+
**open hour** (the newest record's hour) lives in memory; when the hour advances its
|
|
823
|
+
buckets are flushed, and a late record whose hour is below the open hour is written
|
|
824
|
+
at once as its own line. On startup the highest hour in the file is the watermark and
|
|
825
|
+
request records above it are replayed from `requests.jsonl` (and `.1` when its mtime
|
|
826
|
+
hour is above the watermark — not the first record of `requests.jsonl`, which a long
|
|
827
|
+
request straddling the rotation makes older than records left in `.1`), so the open
|
|
828
|
+
hour survives a restart without an hour being counted twice. An fs error is swallowed: history must never alter the response path.
|
|
829
|
+
`GET /api/requests/summary?from=&to=` reports `{total, providers, models, earliest,
|
|
830
|
+
from, to}` at hour granularity — buckets whose hour start is in `[from, to)`. Source:
|
|
831
|
+
`usage-history.ts`.
|
|
832
|
+
|
|
775
833
|
- **Server-side threads ("tether").** The first request of a session carries
|
|
776
834
|
`thread: {type:"create"}` and the full history; later turns carry
|
|
777
835
|
`thread: {type:"continue", previous_message_id: <our last assistant msg id>}` and
|
|
@@ -811,6 +869,9 @@ text is added. Source: `requestlog.ts`, `proxy.ts`, provider adapter outcomes.
|
|
|
811
869
|
is parked, the turn goes to the first fallback that has one, chosen **before anything is sent**,
|
|
812
870
|
since failing over mid-turn would splice two answers together. With nothing usable anywhere the
|
|
813
871
|
primary is kept, so the provider refuses rather than the router inventing a refusal.
|
|
872
|
+
The narrowly classified Antigravity prompt refusal (§4d-ii) additionally permits one same-turn
|
|
873
|
+
route fallback after the upstream rejects the request but **before any response reaches the
|
|
874
|
+
client**; it does not mark a usable account as exhausted.
|
|
814
875
|
- Four things an independent review found, each of which had made it past the unit tests because
|
|
815
876
|
each lived in the wiring rather than the state machine: the pool never heard about the ChatGPT
|
|
816
877
|
or openai-compatible adapters, so those providers looked healthy forever and a slot pointing at
|
|
@@ -1061,9 +1122,11 @@ what we do not have yet — so that adding a provider does not start with readin
|
|
|
1061
1122
|
config still written as three providers is folded on the way in and its `direct` and
|
|
1062
1123
|
`routes` references follow; nothing is rewritten on disk, so it stays readable by an
|
|
1063
1124
|
older router until the GUI saves it.
|
|
1064
|
-
- Translation deliberately strips `
|
|
1125
|
+
- Translation deliberately strips `context_management`, `thread`,
|
|
1065
1126
|
`diagnostics`, and `container`; `thread:continue` is refused exactly as in §4a so
|
|
1066
|
-
the CLI resends a full stateless history.
|
|
1127
|
+
the CLI resends a full stateless history. Thinking is stripped by default too —
|
|
1128
|
+
except the reasoning this adapter itself handed out, which returns as the vendor's
|
|
1129
|
+
own item (see the reasoning-replay note below). The per-turn
|
|
1067
1130
|
`x-anthropic-billing-header` system block is also stripped because its changing
|
|
1068
1131
|
value breaks every vendor's stable-prefix cache opportunity. Orphan tool results
|
|
1069
1132
|
are sent as user text, not a tool result with no antecedent call.
|
|
@@ -1085,6 +1148,50 @@ what we do not have yet — so that adding a provider does not start with readin
|
|
|
1085
1148
|
before (`catalog.ts`, `capabilities.ts`). Chat wire
|
|
1086
1149
|
forwards Anthropic `stop_sequences` as `stop`; Responses wire deliberately omits
|
|
1087
1150
|
them because its common stateless schema has no corresponding universal field.
|
|
1151
|
+
- **Reasoning replay (both wires, 2026-10-14).** A reasoning model on either wire
|
|
1152
|
+
expects its own previous reasoning back on the next turn of a tool loop, and does
|
|
1153
|
+
badly without it. Measured on OpenCode Go's Muse at `xhigh`, same task, 3 runs
|
|
1154
|
+
each: with no replay 9.5k–24.5k reasoning tokens per run (2,197 per step past 80k
|
|
1155
|
+
context) and one run exhausted the context with no answer; with replay 3.2k–4.3k
|
|
1156
|
+
per run (725 per step past 80k) and all three answered, 94% cached.
|
|
1157
|
+
Native clients (OpenCode, the Codex CLI) do this by carrying the vendor's item in
|
|
1158
|
+
their own transcript and putting it back byte-for-byte where it was.
|
|
1159
|
+
Claude Code's transcript is the same medium, so the carrier is the **signature of a
|
|
1160
|
+
thinking block**: `reasoning.ts` encodes `clauderipple.v1:` + base64url JSON
|
|
1161
|
+
naming the wire, provider and model and the thing to restore — the vendor's whole
|
|
1162
|
+
Responses item for the Responses wire, or just the field name
|
|
1163
|
+
(`reasoning_content` / `reasoning`) for Chat. On the Responses wire the item is
|
|
1164
|
+
`{type:"reasoning", id, summary, encrypted_content}` and the request asks for it
|
|
1165
|
+
with `include:["reasoning.encrypted_content"]` (with `store:false` and
|
|
1166
|
+
`reasoning.summary:"auto"`, both of which the chatgpt adapter already sent); the
|
|
1167
|
+
block's text is the summary, or empty when the item had none. `include` is sent on
|
|
1168
|
+
every turn to the ChatGPT backend (as Codex sends it) and to a preset marked
|
|
1169
|
+
`sealedReasoning` (OpenCode Go, as OpenCode's own client sends it): a `store:false`
|
|
1170
|
+
backend that is not asked hands back nothing, so asking only once a conversation
|
|
1171
|
+
already carried reasoning would never start one. Any other Responses vendor is asked
|
|
1172
|
+
only once its own reasoning is already being carried, so an unmeasured vendor keeps
|
|
1173
|
+
its old request. Measured 2026-10-14 against OpenCode Go: the sealed item came back
|
|
1174
|
+
with `include` and without it (1426 bytes either way), replaying it in place
|
|
1175
|
+
answered 200, and the vendor tolerated both a tampered blob and a dropped item. On Chat the
|
|
1176
|
+
block's own text *is* the reasoning and it is reassembled into the field it arrived
|
|
1177
|
+
under on that assistant message — DeepSeek's docs make that mandatory whenever
|
|
1178
|
+
`tools` is present (`https://api-docs.deepseek.com/guides/thinking_mode/`), and it
|
|
1179
|
+
is harmless where a vendor ignores it, so this is not gated by an allowlist.
|
|
1180
|
+
Restore happens only when the envelope names this request's provider and model
|
|
1181
|
+
(model scope validation): another model's or account's `encrypted_content` is a
|
|
1182
|
+
400, and the envelope says whose it is. This is not exposed as a setting, and it
|
|
1183
|
+
does not weaken the cache lesson above — the vendor's bytes are re-sent unchanged,
|
|
1184
|
+
never re-signed or edited. `opencodex` (github.com/lidge-jun/opencodex,
|
|
1185
|
+
`devlog/_fin/260711_claude_inbound/003_evidence.md:93-118`) uses the same
|
|
1186
|
+
envelope-in-the-transcript approach and measured that Claude Code replays thinking
|
|
1187
|
+
blocks with their signature and does not validate it client-side; no code was
|
|
1188
|
+
copied from it. A 4xx that names reasoning/encrypted/item is answered by asking
|
|
1189
|
+
**once more without any replayed reasoning**, so an account switch that invalidates
|
|
1190
|
+
`encrypted_content` costs a little cache and not the turn. `router.log` and
|
|
1191
|
+
`requests.jsonl` carry `replayed=<n>` (items put back) and `reasoning=<n>` (items
|
|
1192
|
+
handed to the client). Gemini and the other translators strip these blocks; a
|
|
1193
|
+
conversation switched to a Claude model does too (`thinking.ts`), since Anthropic
|
|
1194
|
+
would reject a foreign signature.
|
|
1088
1195
|
- We do not assert a provider-wide cache hit rate: the adapter records
|
|
1089
1196
|
`prompt_tokens_details.cached_tokens` / `input_tokens_details.cached_tokens` when
|
|
1090
1197
|
a vendor returns it, and otherwise logs zero cache read. The byte-stable
|
|
@@ -1106,9 +1213,10 @@ adapter lives in `packages/router/src/providers/google/` and separates translati
|
|
|
1106
1213
|
(`thoughtSignatures.ts`) so Stage 2 (Antigravity Code Assist) is a new transport only.
|
|
1107
1214
|
|
|
1108
1215
|
Both modes are implemented: `api-key` (AI Studio) below, and `auth: "antigravity"` (Cloud
|
|
1109
|
-
Code Assist) in its own subsection.
|
|
1110
|
-
|
|
1111
|
-
|
|
1216
|
+
Code Assist) in its own subsection. At the original implementation on 2026-10-01, no live
|
|
1217
|
+
key or Antigravity subscription was available in the implementation environment, so those
|
|
1218
|
+
initial facts were doc- and source-derived, **not measured**. Later dated observations,
|
|
1219
|
+
including the 2026-10-16 Antigravity measurement in §4d-ii, are identified separately:
|
|
1112
1220
|
|
|
1113
1221
|
- Stream endpoint `POST {base}/v1beta/models/{model}:streamGenerateContent?alt=sse`,
|
|
1114
1222
|
the key on header `x-goog-api-key`, base default `https://generativelanguage.googleapis.com`
|
|
@@ -1174,10 +1282,10 @@ Cloud Code Assist (CCA) backend, with several accounts rotating. Same translatio
|
|
|
1174
1282
|
Studio; only `transport.ts` differs. Accounts live in `<home>/google-accounts.json` (0600,
|
|
1175
1283
|
cross-process lock, atomic write, one account per email+project, a re-login replaces its own
|
|
1176
1284
|
entry), the sign-in in `login.ts`, the account pool and refresh in `accounts.ts`, and the wire
|
|
1177
|
-
facts and model-id resolution in `antigravity.ts`. **
|
|
1178
|
-
reference implementation opencodex (GitHub
|
|
1179
|
-
behavioral spec — no code copied; project rule)
|
|
1180
|
-
|
|
1285
|
+
facts and model-id resolution in `antigravity.ts`. **Except for the dated live observation
|
|
1286
|
+
below, these wire facts come from the reference implementation opencodex** (GitHub
|
|
1287
|
+
`lidge-jun/opencodex`, read 2026-10-01 as a behavioral spec — no code copied; project rule).
|
|
1288
|
+
They were not live-tested during the original implementation. Files are under `src/`:
|
|
1181
1289
|
|
|
1182
1290
|
- OAuth is Google's standard installed-app flow with PKCE S256. Client id
|
|
1183
1291
|
`1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com`, secret
|
|
@@ -1240,6 +1348,47 @@ subscription exists in this environment. Files are under `src/`:
|
|
|
1240
1348
|
and is not retried. Refresh is per account, single-flight per token generation, five minutes
|
|
1241
1349
|
ahead; only a structured `invalid_grant` marks an account for sign-in. Only the access token
|
|
1242
1350
|
and the account email/project id are ever in memory; no token reaches a log or an API response.
|
|
1351
|
+
- **SDK prompt refusal (measured 2026-10-16, Windows, Claude Code 2.1.289).** The Opus request
|
|
1352
|
+
used the CCA wire id `claude-opus-5-5-high` unchanged, with
|
|
1353
|
+
`generationConfig.thinkingConfig.thinkingLevel: "low"` (not an id from the static fallback
|
|
1354
|
+
table). The Gemini comparison used picker id `gemini-3.8-flash`, effort `low`, resolved by
|
|
1355
|
+
`resolveAntigravityWireModel` to wire id `gemini-3.8-flash-low`, without a separate
|
|
1356
|
+
`thinkingConfig`. The saved comparison records the Opus id and both generation configs;
|
|
1357
|
+
Gemini's wire id is reconstructed from the reproduction script's inputs and the resolver,
|
|
1358
|
+
not a captured outbound envelope. Both requests used `maxOutputTokens: 128000`.
|
|
1359
|
+
Otherwise identical requests returned HTTP 429 with the standalone system paragraph
|
|
1360
|
+
`You are a Claude agent, built on Anthropic's Claude Agent SDK.` and HTTP 200 without it.
|
|
1361
|
+
The paragraph alone reproduced the refusal; reducing output tokens did not. OAuth, project
|
|
1362
|
+
discovery and short compatible requests succeeded on the same account. This establishes a
|
|
1363
|
+
request-specific refusal in that measurement, not Google's internal policy or every account's
|
|
1364
|
+
quota. Related independent reports: [OpenCodex #2899](https://github.com/lidge-jun/opencodex/issues/2899),
|
|
1365
|
+
[Magpie #666](https://github.com/yetone/magpie/issues/666).
|
|
1366
|
+
The SDK paragraph alone does not distinguish refusal from exhaustion: SDK clients send it on
|
|
1367
|
+
every request, and Google can use the same generic 429 for real limits. `prompt-refusal.ts`
|
|
1368
|
+
checks the outbound paragraph, exact generic `RESOURCE_EXHAUSTED` JSON, and absence of a
|
|
1369
|
+
retry header or nonempty error details. The adapter additionally requires a **fresh successful
|
|
1370
|
+
usage snapshot for the attempted account and project**, with quota remaining in both the 5h
|
|
1371
|
+
and weekly windows of the requested model's known quota group (Gemini or Claude/GPT).
|
|
1372
|
+
Evidence expires after five minutes or a bucket's reset time, and a failed refresh invalidates
|
|
1373
|
+
it even when the dashboard retains old values. Missing, malformed, ambiguous, stale or exhausted
|
|
1374
|
+
evidence follows ordinary 429 cooldown/account rotation. This is a cache-only check; it does
|
|
1375
|
+
not probe quota per request. Group-level quota is supporting evidence, not proof about every
|
|
1376
|
+
model or concurrent request; unknown groups/models are not classified.
|
|
1377
|
+
With that evidence the adapter leaves the prompt intact and returns a distinct **unwritten**
|
|
1378
|
+
`prompt-incompatible` outcome, without cooling the account or trying the prompt on its peers.
|
|
1379
|
+
Before any response bytes, the proxy tries the same request once on the first usable configured
|
|
1380
|
+
route fallback other than the attempted provider/model pair. Each attempt starts from the
|
|
1381
|
+
original request and applies the target's own model, effort and translation. Another model on
|
|
1382
|
+
the same provider is eligible; no fallback chain or unconfigured provider is tried. An absent
|
|
1383
|
+
usable fallback, or a second prompt refusal, produces the explanatory `400 invalid_request_error`.
|
|
1384
|
+
Other fallback errors keep their normal handling. A successful fallback's request record names
|
|
1385
|
+
the actual responder and retains the initial upstream 429/refusal in its note, without marking
|
|
1386
|
+
the successful turn as refused. A terminal refusal records `refusal: prompt-incompatible`.
|
|
1387
|
+
Another compatible request can still use the primary account immediately. The message makes
|
|
1388
|
+
no claim that changing accounts cannot help: only one account was measured.
|
|
1389
|
+
This is error handling, not a workaround that makes Google accept the SDK prompt. HTTP-200
|
|
1390
|
+
error frames were not part of this live reproduction and retain the existing stream handling;
|
|
1391
|
+
no fallback can replace an answer once response bytes have been sent.
|
|
1243
1392
|
- Session id and prompt cache. The real client puts a stable per-conversation id at
|
|
1244
1393
|
`request.sessionId` so provider-side replay can associate a signature with the call that made
|
|
1245
1394
|
it; instability loses it (`google-antigravity-wire.ts:62-88`). We derive it from the router's
|
|
@@ -1386,7 +1535,10 @@ plan unless marked otherwise.
|
|
|
1386
1535
|
| 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. |
|
|
1387
1536
|
| In picker mode, claude.ai requests failed as `502 … ECONNRESET` in under a second — 259 times in four days (2026-10-03..06), mostly `current_user_access`, `desktop/features` and `event_logging`. The kept-alive upstream socket had been closed by claude.ai as the next request was written on it, and Node does not resend | A GET/HEAD/OPTIONS whose reused socket resets before any response is sent again (twice at most), logged `STALE`. A POST is never resent: a reset does not prove the upstream did not act on it. Not yet shown to be the cause of the Windows "chat history loads forever" report (DC, 2026-10-06). |
|
|
1388
1537
|
| An API-key provider with no declared effort ladder — DeepSeek, Kimi, GLM, MiniMax, Qwen, Groq, Mistral, a key and a URL — stripped every effort the user picked (`effort high→removed`) and the Claude app showed no effort menu for its models (DC reports, 2026-10-06). Declaring nothing was meant as "do not guess", but DeepSeek's API, for one, validates `output_config.effort` and maps it onto low/high/max | When nothing declares a ladder (no config ladder, no preset ladder, OpenAI-compatible `reasoning` not `effort` — the form writes `none` by default), a measurement sends a level no vendor defines first: accepted means the field is ignored (no effort, `[]`); refused means it is validated, and the levels the refusal names (else every known one) are sent one by one and the accepted ones become the model's ladder. A 429/5xx concludes nothing. The router runs this a minute after start for unsettled models, so existing providers need no re-save. The DeepSeek preset now declares low/high/max from its docs. |
|
|
1538
|
+
| Muse workers spent minutes per tool call, repeated the same reasoning every step, and one ended with no answer at all (2026-10-14). A reasoning model on a translated wire was getting its own previous reasoning dropped, so each step reasoned the whole problem again from scratch: measured at `xhigh` on OpenCode Go, 9.5k–24.5k reasoning tokens per run without replay against 3.2k–4.3k with it (3 runs each; 2,197 against 725 per step past 80k context; one no-replay run exhausted the context) | The vendor's reasoning is carried in the client's transcript and put back byte-identically at its original position: the Responses item whole, under `include:["reasoning.encrypted_content"]`, and on Chat its text as `reasoning_content`. `reasoning.ts` states the envelope; a block whose envelope names another provider or model is dropped, and a 4xx naming the reasoning is retried once without it. |
|
|
1389
1539
|
| ChatGPT accounts that had bought Codex credits were rested by the router the moment a window read 100%, though OpenAI serves them from credits past the window | `exhaustedForMs` ignores a full window while `credits.has_credits` or `unlimited` holds (and `overage_limit_reached` does not); an actual 429 still rests the account until the window resets. |
|
|
1540
|
+
| After ~120 host↔relay reconnects no phone could connect for minutes — web and native sat at "connecting" while the host's control socket was open and answering pings, and a host restart did not help (the simulator's own log: every attempt 19:10–19:17 timed out waiting for the relay's `open`, then the next one worked with no change on the phone; 2026-10-15). Measured on the deployed relay with throwaway server ids: a replaced host control socket whose other end never answers the close stays in `getWebSockets("host")`, in acceptance order, for 8+ minutes, and the room sent every phone's `connect` to `sockets("host")[0]` — that dead socket — through any number of reconnects and a restart | The room routes a phone only to the **newest** host socket (each is tagged `at:<accepted ms>`), and only while it pinged within `HOST_SILENT_MS` (the runtime's auto-response timestamp); a silent newest socket is closed `4408` and the phone hears `4404 host offline` at once. The host walks the phone's path itself after every hello and every 5 min (a client socket to its own room must come back `open` within 10 s) and reconnects when it does not; a ping timer that ran late (sleep, blocked event loop) is not taken for a dead socket. Close codes, reasons and ISO timestamps are in `host.log`. In-process tests reproduce the sequence over a network that keeps dead sockets listed (`relay-reconnect.test.ts`). |
|
|
1541
|
+
| An expired Claude login on the Mac wrote `[desktop state] …로그인이 만료되었습니다…` every few seconds per Claude app session and read the keychain (a synchronous child process) for every request, while phones learned of it only by a failed send (2026-10-15) | A login problem is one host state: logged when it starts and ends, sent to phones as `HostInfo.desktopProblem` (a banner), and the keychain is reread at most every 30 s while expired or after a 401. Per-session Remote Control errors log once per change. The host never refreshes the login itself. |
|
|
1390
1542
|
|
|
1391
1543
|
## 6. Blocked paths (measured, do not retry)
|
|
1392
1544
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clauderipple",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Use GPT (any ChatGPT plan with Codex), 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": [
|
|
@@ -77,6 +77,12 @@
|
|
|
77
77
|
"url": "https://github.com/PBJ-2/clauderipple/issues"
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.283",
|
|
81
|
+
"qrcode-generator": "2.0.4",
|
|
82
|
+
"tweetnacl": "1.0.3",
|
|
83
|
+
"web-push": "3.6.7",
|
|
84
|
+
"ws": "8.21.3",
|
|
85
|
+
"zod": "4.1.12",
|
|
80
86
|
"proper-lockfile": "^4.1.2",
|
|
81
87
|
"undici": "7.24.4"
|
|
82
88
|
}
|