Skip to content

feat(ai/devin): match Devin CLI wire format from mitmproxy capture - #8534

Open
jroth1111 wants to merge 25 commits into
can1357:mainfrom
jroth1111:feat/devin-cli-wire-format-match
Open

jroth1111 wants to merge 25 commits into
can1357:mainfrom
jroth1111:feat/devin-cli-wire-format-match

Conversation

@jroth1111

@jroth1111 jroth1111 commented Aug 14, 2026 •

Copy link
Copy Markdown

What

Align the Devin adapter with the CLI’s authentication and request format, so streaming and tool calls use the expected wire protocol.

  • Send the Basic token-token authorization header using the credential selected by the existing authentication flow.
  • Apply the CLI-oriented request metadata and completion defaults while preserving caller generation settings and the current tool/history behavior.
  • Forward the optional opaque metadata field f only when explicitly supplied through DEVIN_F_FIELD. Do not manufacture it from an install ID or share a fallback value across installations.

Why

The request-shape fixes need to coexist with the current Devin adapter, rather than replacing its newer authentication, compression, model routing, and streaming safeguards.

The current head retains GetUserJwt, gzip Connect request framing, and supported request controls. It does not remove those paths or claim byte-for-byte parity with every CLI request. This supersedes the old description’s raw-frame, generated-device-field, and live-acceptance claims.

Testing

Verified published head: 9def97f857552b0ef46428392bdd55d30aa90cf8.

  • Injected transport fixtures decode the actual Connect/protobuf request and verify authentication headers, metadata, caller overrides, tool definitions, streamed argument fragments, multiple tool calls, history, and trailer errors.
  • The final focused selection passed 89 tests across 11 files, with 334 assertions, covering the Devin account/usage, frame-cap, history, large-request recovery, login, router, streaming arguments, tool-calling, trailer, and catalog-discovery contracts.
  • bun check passed in packages/ai, packages/catalog, and packages/coding-agent.

The full root bun check and workspace Rust tests were not run on this PR head. No fresh live Devin inference was performed. These fixtures do not prove that the service accepts an opaque f value or that an account is entitled to a particular backend model.


  • bun check passes
  • Tested locally
  • CHANGELOG updated with the required attribution (if user-facing; internal issue fixes use issue links, external contributions add the PR link and contributor credit after creation)

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@jroth1111
jroth1111 force-pushed the feat/devin-cli-wire-format-match branch from f527616 to 5123fd6 Compare August 14, 2026 09:04

@roboomp roboomp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 — provider-wide wire/default changes need an explicit maintainer call, and the current patch is not merge-ready.
Blocking: the fallback attestation is shared across every install, explicit generation controls are discarded, and the focused Devin suite fails 2 tests after the raw-frame change.
Also add the required packages/ai Unreleased changelog entry.
Thanks for the detailed capture and comparison, @jroth1111.

Comment thread packages/ai/src/providers/devin.ts Outdated
Comment thread packages/ai/src/providers/devin.ts Outdated
Comment thread packages/ai/src/providers/devin.ts
Comment thread packages/ai/src/providers/devin.ts Outdated
gwizz and others added 5 commits August 30, 2026 00:32
Align the Devin Connect-RPC adapter's GetChatMessage request with the
actual wire format captured from the Devin CLI (v3000.4.25) and Devin
Desktop via mitmproxy. The previous adapter impersonated Windsurf and
sent fields the CLI never transmits.

Key changes verified against captured traffic:

- Remove GetUserJwt preflight: place devin-session-token$ directly in
  Metadata.apiKey (field 3), matching the CLI which performs no JWT
  exchange.
- Fix metadata identity: ideName="devin-cli", extensionName/ideType=
  "chisel", version="3000.4.25", add os and attestation field 31 (`f`).
  Remove userJwt, sessionId, requestId, triggerId, lsTimestamp.
- Send uncompressed frames (flag 0x00) instead of gzip (flag 0x01),
  matching the CLI. Remove connect-content-encoding and related headers.
- Add authorization header (Basic <token>-<token>) matching the CLI's
  auth pattern. Suppress Bun's default User-Agent. Set Accept-Encoding
  to identity to avoid advertising compression the CLI doesn't send.
- Fix CompletionConfiguration defaults: maxTokens=128000, maxNewlines=
  400, temperature=1.0, topK=40, topP=0.95. All overridable via
  StreamOptions and model.maxTokens. Remove hardcoded stopPatterns
  (only caller-specified ones are sent), firstTemperature,
  fimEotProbThreshold.
- Remove extra request fields absent from CLI traffic: executionId,
  toolChoice, systemPromptCacheOptions, disableParallelToolCalls.
- Use getInstallId() for attestation generation instead of hardcoded ID.
- Update devin-history.test.ts: decode frames based on Connect flag
  byte, remove dead GetUserJwt mock, add test asserting authorization
  header format and User-Agent/Accept-Encoding suppression.
- Add packages/ai CHANGELOG entry under [Unreleased].

Note: streamSimple() fills options.maxTokens from model.maxTokens
(capped at 64000 by Devin model discovery) before reaching the provider,
so the 128000 default only applies when neither the caller nor the
model catalog specify maxTokens. This is a pre-existing model discovery
issue, not introduced by this PR.

Verified via mitmproxy: the patched request's HTTP headers
(authorization, content-type, connect-protocol-version, accept),
top-level protobuf fields (2, 3, 7, 8, 10, 16, 20, 21), metadata
fields (1, 2, 3, 4, 5, 7, 12, 28, 31), and completion config fields
(1, 2, 3, 5, 7, 8) are a structural match to the CLI capture. Response
frame structure is identical (7 frames). All 17 focused Devin tests pass.

The only remaining differences are:
- Field 31 (attestation `f`): machine-specific 366-byte blob, different
  value but same length. Overridable via DEVIN_F_FIELD env var.
- topP encoding: CLI uses float32-promoted double (0x3fee666660000000),
  omp uses native double (0x3fee666666666666) — 1.19e-08 difference.
- maxTokens: omp sends 64000 (from model discovery's DEFAULT_MAX_TOKENS
  cap), CLI sends 128000. Pre-existing issue.
- accept-encoding: omp sends identity, CLI sends nothing (HTTP/2).
- connection: omp sends keep-alive (HTTP/1.1), CLI uses HTTP/2.
- sentry-trace: CLI sends per-request trace IDs (telemetry only).
- System prompt content: omp uses its own prompt (expected).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… parsing

Tests cover:
- Tool definitions are encoded in GetChatMessageRequest.tools (field 10)
- Multiple tool definitions (run_command, edit_file) are encoded correctly
- Tool call deltas from response stream are parsed and arguments merged
- Multiple tool calls in a single response are parsed correctly

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Lock the CLI wire contract (flag 0x00) in the history capture helper so
gzip decode regressions fail loudly.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ence

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the stale gzip log field, retarget tool-calling tests at
devin-proto imports, and assert frameBytes after CLI-matched
uncompressed Connect frames.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jroth1111
jroth1111 force-pushed the feat/devin-cli-wire-format-match branch from 63d0e9f to c43f5dd Compare August 29, 2026 14:50
@will-bogusz

Copy link
Copy Markdown
Contributor

hey my devin provider PR got merged in which refactored provider to be based on devin CLI as opposed to old codeium. looks like you took a look at the desktop app as well, would be curious if there was anything else missing that could go in, but would love to keep the devin support up to date, so let me know if you check this out again to see if any of these still apply so we can get some easy followups for quality of life

gwizz and others added 7 commits August 30, 2026 02:15
Align the Devin Connect-RPC adapter's GetChatMessage request with the
actual wire format captured from the Devin CLI (v3000.4.25) and Devin
Desktop via mitmproxy. The previous adapter impersonated Windsurf and
sent fields the CLI never transmits.

Key changes verified against captured traffic:

- Remove GetUserJwt preflight: place devin-session-token$ directly in
  Metadata.apiKey (field 3), matching the CLI which performs no JWT
  exchange.
- Fix metadata identity: ideName="devin-cli", extensionName/ideType=
  "chisel", version="3000.4.25", add os and attestation field 31 (`f`).
  Remove userJwt, sessionId, requestId, triggerId, lsTimestamp.
- Send uncompressed frames (flag 0x00) instead of gzip (flag 0x01),
  matching the CLI. Remove connect-content-encoding and related headers.
- Add authorization header (Basic <token>-<token>) matching the CLI's
  auth pattern. Suppress Bun's default User-Agent. Set Accept-Encoding
  to identity to avoid advertising compression the CLI doesn't send.
- Fix CompletionConfiguration defaults: maxTokens=128000, maxNewlines=
  400, temperature=1.0, topK=40, topP=0.95. All overridable via
  StreamOptions and model.maxTokens. Remove hardcoded stopPatterns
  (only caller-specified ones are sent), firstTemperature,
  fimEotProbThreshold.
- Remove extra request fields absent from CLI traffic: executionId,
  toolChoice, systemPromptCacheOptions, disableParallelToolCalls.
- Use getInstallId() for attestation generation instead of hardcoded ID.
- Update devin-history.test.ts: decode frames based on Connect flag
  byte, remove dead GetUserJwt mock, add test asserting authorization
  header format and User-Agent/Accept-Encoding suppression.
- Add packages/ai CHANGELOG entry under [Unreleased].

Note: streamSimple() fills options.maxTokens from model.maxTokens
(capped at 64000 by Devin model discovery) before reaching the provider,
so the 128000 default only applies when neither the caller nor the
model catalog specify maxTokens. This is a pre-existing model discovery
issue, not introduced by this PR.

Verified via mitmproxy: the patched request's HTTP headers
(authorization, content-type, connect-protocol-version, accept),
top-level protobuf fields (2, 3, 7, 8, 10, 16, 20, 21), metadata
fields (1, 2, 3, 4, 5, 7, 12, 28, 31), and completion config fields
(1, 2, 3, 5, 7, 8) are a structural match to the CLI capture. Response
frame structure is identical (7 frames). All 17 focused Devin tests pass.

The only remaining differences are:
- Field 31 (attestation `f`): machine-specific 366-byte blob, different
  value but same length. Overridable via DEVIN_F_FIELD env var.
- topP encoding: CLI uses float32-promoted double (0x3fee666660000000),
  omp uses native double (0x3fee666666666666) — 1.19e-08 difference.
- maxTokens: omp sends 64000 (from model discovery's DEFAULT_MAX_TOKENS
  cap), CLI sends 128000. Pre-existing issue.
- accept-encoding: omp sends identity, CLI sends nothing (HTTP/2).
- connection: omp sends keep-alive (HTTP/1.1), CLI uses HTTP/2.
- sentry-trace: CLI sends per-request trace IDs (telemetry only).
- System prompt content: omp uses its own prompt (expected).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… parsing

Tests cover:
- Tool definitions are encoded in GetChatMessageRequest.tools (field 10)
- Multiple tool definitions (run_command, edit_file) are encoded correctly
- Tool call deltas from response stream are parsed and arguments merged
- Multiple tool calls in a single response are parsed correctly

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Lock the CLI wire contract (flag 0x00) in the history capture helper so
gzip decode regressions fail loudly.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop the stale gzip log field, retarget tool-calling tests at
devin-proto imports, and assert frameBytes after CLI-matched
uncompressed Connect frames.

Co-authored-by: Cursor <cursoragent@cursor.com>
The CLI wire-format alignment entry belonged in [Unreleased], not under
the already-released 18.0.9 section.

Co-authored-by: Jacob <jroth1111@users.noreply.github.com>
…ider

Send uncompressed GetChatMessage frames with Basic auth and identity
headers while keeping AssignModel/GetUserJwt from can1357#8590. Move the
wire-format changelog entry under Unreleased.

Co-authored-by: Jacob <jroth1111@users.noreply.github.com>
Post-can1357#8590 chat is preceded by auth; capture GetChatMessage only and
return a JWT response so Connect-frame assertions stay valid.

Co-authored-by: Jacob <jroth1111@users.noreply.github.com>
@cursor
cursor Bot force-pushed the feat/devin-cli-wire-format-match branch from c43f5dd to 184a344 Compare August 30, 2026 02:21
Rebase onto current main after the Devin CLI provider merge (can1357#8590).
Keep AssignModel router, credit metering, and devinCliMetadata identity
while applying mitmproxy wire-format fixes: raw Connect frames, Basic auth
header, no GetUserJwt preflight, per-install attestation field f, CLI
completion defaults, and omission of fields absent from real CLI traffic.

Update router tests for uncompressed request decoding and move the
packages/ai changelog entry under Unreleased.

Co-authored-by: Cursor <cursoragent@cursor.com>
gwizz and others added 2 commits August 31, 2026 02:14
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep local wire-format resolution: no GetUserJwt preflight, raw Connect
frames, per-install attestation, and router tests updated for can1357#8590.

Co-authored-by: Cursor <cursoragent@cursor.com>
jroth1111 added a commit to jroth1111/oh-my-pi that referenced this pull request Sep 6, 2026
…8534): feat(ai/devin): match Devin CLI wire format from mitmproxy capture
@jroth1111

Copy link
Copy Markdown
Author

Merge update + verification pass (ai typecheck/lint clean). Not superseded: the branch's wire-format delta (raw Connect frames, Basic auth, per-install attestation, CLI completion defaults) remains distinct from upstream's native surface.

One judgment call: the header hunk took upstream's user-agent/connect-accept-encoding (CLI 3000.6.2 alignment, newer than this PR's capture) over the older suppress-Bun-identity lines; HTTP accept-encoding stays identity per the capture. All four blocking threads were already fixed on-branch (flag-0x00 decode, install-derived attestation, options-respecting completion defaults, changelog).

@jroth1111

Copy link
Copy Markdown
Author

Second pass: rebased onto upstream/main, no new review threads. Typecheck clean.

@jroth1111

Copy link
Copy Markdown
Author

Hey @will-bogusz — yes, re-checked the threads against your merged #8590 surface plus the CLI 3000.6.2 alignment. What remains distinct here: raw Connect frames (flag 0x00), the Basic token-token auth header, per-install attestation field, CLI completion defaults, and no GetUserJwt preflight. I took your newer user-agent/connect-accept-encoding headers over this branch's older suppression lines in the latest merge. The four blocking threads (framing decode, install-derived attestation, options-respecting defaults, changelog) are fixed on-branch. Open to followups if you spot anything else from the desktop capture that's still missing.

gwizz and others added 3 commits September 25, 2026 11:33
Upstream reworked the provider (GetUserJwt, gzip, stop defaults, Gemini
schema normalization); the merge keeps the PR's remaining delta: session-
token Basic authorization and the attestation f field.
The capture helpers in devin-history and devin-router read the Connect
frame flag byte before decoding, matching devin-tool-calling and the
gzip-compressed wire format the provider actually emits.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants