Repository navigation
Conversation
Strip growing <total_tokens> and TaskCreate footers from Claude Code system text before it becomes Responses instructions, and reattach the latest notice as a trailing input message so Muse/Go prefix cache can hit. Paper and measured rates live under docs/claude-instructions-cache-stabilize/. Co-authored-by: Warexpor <warexpor@gmail.com>
|
✅ Deterministic PR hygiene checks passed. |
The Claude Code path still uses metadata.user_id. Desktop has no session id, so the fallback key now fingerprints the cacheable prefix instead of raw systemParts that still contain <total_tokens> / TaskCreate footers. Co-authored-by: Warexpor <warexpor@gmail.com>
Restrict strip to standalone integer <total_tokens> footer lines and the exact Claude Code reminder paragraph so inline docs of those tags or TaskCreate stay in instructions. Co-authored-by: Warexpor <warexpor@gmail.com>
Identify Claude Code <total_tokens> / TaskCreate notices as a trailing suffix outside markdown fences. No match returns the original system text byte-for-byte so fenced examples and whitespace-only prompts stay untouched. Translator regressions cover those no-ops. Co-authored-by: Warexpor <warexpor@gmail.com>
Relocation runs only when metadata.user_id identifies Claude Code. Unmatched fence openers cover through EOF so an unfinished fenced example plus a numeric total_tokens line is not a harness suffix. Desktop keeps the raw systemParts cache-key fallback. Co-authored-by: Warexpor <warexpor@gmail.com>
Claude Code writes <total_tokens>N tokens left</total_tokens>, not a bare integer. Digit-only matching left the harness suffix in instructions and collapsed Muse/Go hit rate to baseline. Match that shape as a trailing unfenced suffix, run peel on every inbound translation (no-op when absent), and hash Desktop prompt_cache_key from the same string used for instructions. Co-authored-by: Warexpor <warexpor@gmail.com>
Keep the real N tokens left matcher and unclosed-fence-to-EOF handling. Relocation is now stabilizePromptCache (default false) on the translator, not metadata.user_id. Ordinary callers keep a matching suffix in instructions; /v1/messages passes true. Desktop prompt_cache_key hashes raw systemParts outside opt-in and the instructions string when opted in. Co-authored-by: Warexpor <warexpor@gmail.com>
|
@Ingwannu addressed on Kept the real
Verification: |
|
@Ingwannu addressed on Kept the real
Verification: inbound-cache-stabilize + inbound — 75 pass; |
|
Duplicate of upstream lidge-jun#4052 — same head branch . Closing to avoid split review. |
…d hardened (lidge-jun#4285) * feat(devin): add experimental Cognition (Devin/Windsurf) adapter Carries the adapter from lidge-jun#4078 onto current dev and places the test in its layout domain (tests/providers/devin-adapter.test.ts) with the layout map and membership fixture updated. Co-authored-by: Sayo <hi@sayo.wtf> * fix(devin): harden the cloud-direct adapter before it ships Four independent reviews of the carried lidge-jun#4078 adapter found one credential-leak blocker, one abort blocker, and a set of routing and lifecycle defects. Credentials: RegisterUser and GetUserJwt copied raw upstream bodies into Error.message, which reaches CLI output, the adapter error event, and /api/logs. A Connect error can quote the request, and the request holds the sign-in token or the api_key; redactSecretString does not match a bare JWT. Every auth and chat error now reports status, an allowlisted Connect code, and a trace id only. The four credential-bearing POSTs stop following redirects, and the api-server host is checked against a Cognition allowlist before it reaches a URL - including on the way into auth.json, so an EU or FedStart tenant host survives a reload instead of being dropped by the Copilot-only validator. Routing: the adapter posted to the static registry baseUrl, so an EU or FedStart account signed in and then sent every RPC to a server it is not provisioned on. The signed-in account's tenant now decides the host. Cancellation: after headers arrived nothing observed the caller's signal, so a client cancel drained until the idle timer fired and then surfaced as truncated_stream, while the adapter emitted neither done nor error and left the bridge to synthesize adapter_eof. The body is cancelled on abort and the turn reports the cancellation. Also: a natural completion no longer reports stopReason "stop", which was costing every clean turn its final_answer phase; sampling options reach the cloud instead of its 128k/0.7 defaults; thinking stays out of replayed assistant content; usage survives an error; gzip frames are bounded on output as well as input; dotted model ids normalize to the catalog spelling; the session cache is bounded; and logout clears the cached user_jwt whose payload carries the api_key. Co-authored-by: Sayo <hi@sayo.wtf> * test(devin): assemble the userinfo URL so the privacy scanner does not read it as an email * feat(devin): pin the client version to the shipped release and accept the real token shape Evidence from a live free-tier account and the shipped Devin Desktop 3.9.19 bundle. Details in devlog/_plan/260911_devin_two_providers/003_live_evidence.md. The sign-in token is not a JWT. A real sign-in returns a 47-character ott$<base64url> one-time value and RegisterUser accepts it, so the JWT-shape gate would have rejected every real login. The paste parser now recognises one opaque credential-shaped word rather than a token format. RegisterUser returned api_server_url https://server.self-serve.windsurf.com for an ordinary free account, which is what the tenant-routing fix in the previous commit exists for: the hardcoded server.codeium.com was wrong for this account before anyone reached an enterprise tenant. The api-server allowlist gains the staging and beta hosts the shipped bundle names, and the client version default moves from 2.0.0 - which predates the Devin rebrand - to the 3.9.19 the desktop client reports, overridable through OPENCODEX_DEVIN_CLIENT_VERSION. Co-authored-by: Sayo <hi@sayo.wtf> * docs(devin): carry the upstream MIT notice on the derived cloud-direct files A similarity check against rsvedant/opencode-windsurf-auth puts wire.ts at 1.000, index.ts at 0.988, chat.ts at 0.912, metadata.ts at 0.863, auth.ts at 0.835 and catalog.ts at 0.753 - same module split, same comments, same field layout. These files are a derivative of that repository's src/cloud-direct/, which is MIT licensed, Copyright (c) 2026 Vedant, and the carry arrived with no notice at all. The full permission notice sits in the module entry point and the other five files carry a short attribution header pointing at it, which is what MIT asks for in a distributed source tree. Co-authored-by: Sayo <hi@sayo.wtf> * docs(devin): record that the cloud chat path is unverified on a free account * fix(devin): explain the opaque cloud denial instead of guessing at it * docs(devin): restore the cloud provider rows and adapter sections on the new dev devin-cli landed on dev as lidge-jun#4288, so this branch now carries only the cloud provider. The provider rows, the login line, and the adapters reference sections for devin are re-added on top of the current docs, and every locale still leads with the measured result that its chat path is unverified. * fix(devin): restore the invalid_argument matcher the rebuild dropped The rebuild re-applied the last commit's documentation by hand and its chat.ts hunk went with it, so the runtime explanation was back to keying only on permission_denied while the measured free account returns invalid_argument - the one trailer it needed to fire for. Also from the rebuild audit: the conformance test no longer names devin-cli in guards the RUN_TURN_ONLY_WIRES set already skips, the two layout maps list the devin test files alphabetically, and structure/adapters/registry.md records why the cloud devin wire is a direct registry entry alongside devin-cli. * feat(devin): fix the request encoding that made every chat turn fail The cloud provider could not complete a single turn on any account. A paid account settled what it was not: all 229 catalogue models came back enabled and GetChatMessage failed exactly as it had on the free tier, so entitlement was never the cause. Importing the working reference's zero-dependency builder and sending its request through our own transport returned HTTP 200 and a real stream, which put the fault in our encoder rather than the wire. Diffing the two encoded messages field by field left one difference: in CompletionConfiguration, #2 is the output cap and #3 is the context window, and we had them swapped. A caller asking for 32 output tokens wrote 32 into the context-window field, and Cognition answered with an opaque invalid_argument. Fields #6 and lidge-jun#11 are not part of the message at all. A temperature of exactly 0 is refused with that same opaque error. Deterministic output is the ordinary case for a coding client, so it is clamped to the smallest accepted value rather than silently replaced with the service default. Three transport facts had to hold together, which is why testing them one at a time looked fruitless: the credential is the session token doubled and dash-joined in an Authorization: Basic header while the body keeps one copy, the request envelope is uncompressed, and Metadata lidge-jun#31 carries a 732-character fingerprint whose length the service checks. The metadata identity is its own seven-field shape rather than the desktop client's telemetry set, the request carries the verified tag set, and the short-lived user_jwt is now opt-in because the chat path does not need it. Verified live on a paid account: six combinations, two hosts by three models, all returning PONG with a finish reason and usage. A regression test pins the tag map so the swap cannot return silently. Co-authored-by: Sayo <hi@sayo.wtf> * fix(devin): stop the opaque-denial message asserting a retracted explanation The error string still told the user entitlement was proved and that the request fields had been ruled out. That was the hypothesis this work retracted: the same sentence came back for every turn until the CompletionConfiguration tag map was corrected, and a temperature of exactly 0 still produces it. It now points at the request first and names the test that pins the accepted field layout, and only then at the account's model access. Also from the pre-merge review: the comments claiming the hosted chat path needs the user_jwt, the stale 128k output-default comment, the promptId that is now optional because lidge-jun#22 is omitted on a first turn, and an English docs line that claimed tool calls were verified when the live evidence is chat and usage across three models. --------- Co-authored-by: Sayo <hi@sayo.wtf>
…flag tool errors, system prompt in #2 (carry lidge-jun#6092) (lidge-jun#6178) * fix(devin): system prompt in #2, tool-error flag, Gemini schemas, history overflow - Send the leading system text as GetChatMessage #2 instead of folding it into the first user prompt. Live on swe-1-6 with a ~6.7k-token system prompt the turn-2 cache ratio is unchanged (6688/6715 vs 7072/7097) and the prompt is smaller; swe-1-6, swe-2-medium, gemini and claude all obey #2. - Mark failed tool results with ChatMessagePrompt lidge-jun#9 tool_result_is_error instead of an in-band "ERROR:" prefix. - Rewrite JSON-Schema type arrays to anyOf in tool parameters for Gemini uids, which Cognition refuses with invalid_argument on every turn. - Surface a pre-output invalid_argument on a history near or past the model's input window as context_length_exceeded so Codex compacts; small requests with the same code stay a plain 400. - Correct the lidge-jun#15 (CortexTrajectoryReference), lidge-jun#17 prompt_id and lidge-jun#22 execution_id comments. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 29688bf) * fix(devin): measured overflow boundary, per-type Gemini branches, tool-error marker - Overflow: binary-searched live on swe-1-6 (200k window) the refusal sits at ~200-203k real tokens for prose and JSON alike, and a 32000 output cap does not move it. Characters per token ran 1.33-5.53 across samples, so the chars/4 estimate is replaced by a word-piece count (1.00-1.54x real) at 95% of the window: every sample at the window is caught, none at 60% is. - Gemini schemas: `{type:[T,"null"], ...}` becomes anyOf branches that carry the type-specific keywords (items, properties, ...); an existing anyOf is folded in rather than nested under allOf. Draft-7 `dependencies` is walked as a schema map with name lists left as data. - Tool errors keep the in-band ERROR: marker beside lidge-jun#9: with a neutral result flagged as an error only gemini reported a failure; swe-1-6, gpt-6-sol-low and gpt-5-6-luna-low read it as success. - A request with only system text keeps it as a user prompt instead of sending no prompts. - Docs: describe the context_length_exceeded reclassification beside the allowDevinInvalidArgument recovery option. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit d84edae) * fix(devin): keep outer constraints when folding a Gemini anyOf Folding each existing anyOf branch into the outer keywords let a branch override a contradicting outer keyword, so `{maxLength: 5, anyOf: [{maxLength: 50}]}` loosened to 50. On any such disagreement, keep both constraints under allOf instead; live, gemini-3-8-flash-medium accepts allOf. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit db28e2d) * docs(devin): match the collapseSystemIntoUser example to the #2 system prompt The example still showed the leading system messages folded into the first user turn, which now travel in request #2. Show a mid-conversation system run instead, with the real blank-line join, and note that a system-only request passes through whole. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit b2d9ded) * docs(devin): show the blank-line join in the collapse example Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit 3232330) * fix(devin): keep both constraints when a Gemini type union and anyOf are disjoint When no existing anyOf branch shared a type with the type array, the fallback kept the anyOf and dropped the type union, so the branches admitted types the node never allowed. Keep both under allOf, the same form the conflict path uses; live, gemini-3-8-flash-medium accepts it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> (cherry picked from commit b87b332) * fix(devin): classify overflow from the selected catalog window Co-authored-by: Sayo <hi@sayo.wtf> * fix(devin): preserve null constraints in Gemini schema rewrite Co-authored-by: Sayo <hi@sayo.wtf> * docs(devin): record malformed-schema overflow ambiguity Co-authored-by: Sayo <hi@sayo.wtf> * fix(devin): preserve outer schema constraints on nullable types Co-authored-by: Sayo <hi@sayo.wtf> * fix(devin): estimate overflow from transmitted tool descriptions Co-authored-by: Sayo <hi@sayo.wtf> * docs(structure): keep the effective Devin family default in the adapter row Co-authored-by: Sayo <hi@sayo.wtf> --------- Co-authored-by: Sayo <hi@sayo.wtf> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
instructions. Growing<total_tokens>N tokens left</total_tokens>/ TaskCreate footers churn that prefix and collapse Muse/Go prompt-cache hit rates.stabilizePromptCache?: booleandefaults to false. Ordinary Anthropic callers with that exact unfenced suffix keepinstructionsandinputunchanged. The Claude Code/v1/messagesinbound path passes true. This is not inferred frommetadata.user_id.TRAILING_TOTAL_REfor<total_tokens>\d+\s+tokens left</total_tokens>, exact TaskCreate paragraph, trailing unfenced only, unclosed fence through EOF). No match returns the original string byte-for-byte.prompt_cache_keyfallback hashes rawsystemPartsoutside opt-in (previous key). When opted in, that fallback hashes the same string asbody.instructionsafter stabilize.SOCKS5 / Go session-header work is intentionally out of scope. Related Anthropic thinking-replay issue lidge-jun#3719 stays open (link only).
Upstream review PR: lidge-jun#4052
Verification
bun test tests/claude-integration/claude-inbound-cache-stabilize.test.ts tests/claude-integration/claude-inbound.test.ts— 75 passbun run typecheck— passChecklist