Skip to content

fix(devin): replay the assistant's reasoning instead of dropping it - #4426

Merged
lidge-jun merged 1 commit into
devfrom
codex/260912-devin-thinking-replay
Sep 12, 2026
Merged

fix(devin): replay the assistant's reasoning instead of dropping it#4426
lidge-jun merged 1 commit into
devfrom
codex/260912-devin-thinking-replay

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Summary

The adapter asserted that Cognition has no reasoning-replay field and dropped every assistant thinking block, so a reasoning model restarted its chain on each turn of a tool loop — and paid for it again.

The field exists. Two independent clients of the same service write it on the assistant prompt: #11 thinking, #12 signature, #18 signature_type. This change carries the assistant's own reasoning there.

The response side was half-missing too. delta_thinking (#9) was decoded but delta_signature (#10) was not, so even once the prompt could carry a signature there would never be one to carry and the replay would always have been unsigned. #10 now surfaces as a thinking_signature event, which closes the round trip.

Reasoning still stays out of the replayed assistant text, which is what the original comment was right about: folding chain-of-thought into the visible content makes the model treat it as something it said to the user. It rides its own field instead. A turn that produced only reasoning is now replayed rather than skipped, since dropping it is exactly what forces the next turn to re-derive the same chain.

Devin-only. No shared or cross-provider code is touched.

Verification

Field numbers were read from two independent read-only reference clones in scratch (can1357/oh-my-pi and jc01rho/CLIProxyAPIPlus), which agree. Neither is vendored.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Summary by CodeRabbit

  • Bug Fixes

    • Assistant reasoning is now preserved and replayed across Devin conversations.
    • Turns containing only reasoning are no longer omitted from conversation history.
    • Reasoning signatures are retained between turns, improving continuity in multi-step interactions.
  • Tests

    • Added coverage to verify reasoning preservation, hidden reasoning metadata, and signature handling during conversation replay.

The adapter asserted that Cognition has no reasoning-replay field and
dropped every assistant thinking block, so a reasoning model restarted its
chain on each turn of a tool loop and paid for it again.

The field exists. Two independent clients of the same service write it on
the assistant prompt: #11 thinking, #12 signature, #18 signature_type.

The response side was half-missing too. delta_thinking (#9) was decoded
but delta_signature (#10) was not, so even once the prompt could carry a
signature there was never one to carry: the replay would always have been
unsigned. #10 now surfaces as a thinking_signature event.

Reasoning still stays out of the replayed assistant TEXT, which is what
the original comment was right about — folding chain-of-thought into the
visible content makes the model treat it as something it said to the user.
It rides its own field instead. A turn that produced only reasoning is now
replayed rather than skipped, since dropping it is what forces the next
turn to re-derive the same chain.
@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 12, 2026 16:01
@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 99080bdc-de42-4b88-94df-fa0f1996c900

📥 Commits

Reviewing files that changed from the base of the PR and between 92e21bc and d2f3dff.

📒 Files selected for processing (3)
  • src/adapters/devin.ts
  • src/adapters/devin/cloud-direct/chat.ts
  • tests/providers/devin-hardening.test.ts

📝 Walkthrough

Walkthrough

The Devin adapter now preserves assistant reasoning and signatures. It maps reasoning into chat history, encodes it in prompt fields 11, 12, and 18, decodes response field 10, and forwards signatures for later replay.

Changes

Devin reasoning replay

Layer / File(s) Summary
Reasoning history and prompt encoding
src/adapters/devin.ts, src/adapters/devin/cloud-direct/chat.ts
Assistant reasoning and its last signature are stored separately from visible text. Reasoning-only turns remain in history. Prompt encoding forwards reasoning, signature, and signature type to fields 11, 12, and 18.
Reasoning signature event propagation
src/adapters/devin/cloud-direct/chat.ts, src/adapters/devin.ts
Cloud response field 10 decodes as reasoning_signature. runTurn emits the signature as a thinking_signature adapter event.
Reasoning replay validation
tests/providers/devin-hardening.test.ts
Tests verify history mapping, reasoning-only turns, prompt field encoding, and signature event decoding.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant AssistantMessage
  participant mapOneMessage
  participant ChatHistoryItem
  participant encodeChatMessagePrompt
  participant decodeChatFrame
  participant runTurn
  AssistantMessage->>mapOneMessage: provide thinking blocks and signature
  mapOneMessage->>ChatHistoryItem: store reasoning separately from content
  encodeChatMessagePrompt->>ChatHistoryItem: encode fields 11, 12, and 18
  decodeChatFrame->>runTurn: deliver reasoning_signature from field 10
  runTurn->>ChatHistoryItem: emit thinking_signature for later replay
Loading
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/260912-devin-thinking-replay

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T16:14:37.347115Z d2f3dff Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed labels Sep 12, 2026
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Deterministic hygiene checks failed.

  • missing_coauthor_credit — This pull request says it reimplements, supersedes, carries, or rebases another author's pull request, but no Co-authored-by trailer names that author. Prose in a commit body is not read by anything; the trailer is what GitHub counts. Add it to the description or a commit, or obtain attribution-approved. Paths: #10, #12.

@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • hygiene: missing_coauthor_credit.

What to do

  • Fix missing_coauthor_credit — This pull request says it reimplements, supersedes, carries, or rebases another author's pull request, but no Co-authored-by trailer names that author. Prose in a commit body is not read by anything; the trailer is what GitHub counts. Add it to the description or a commit, or obtain attribution-approved. Paths: #10, #12.

Automatic draft conversion failed (token cannot change draft status). Please convert this pull request to a draft manually. The required enforce-target check will keep failing until every issue above is resolved.

@github-actions
github-actions Bot marked this pull request as draft September 12, 2026 16:02
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 71 / 80

설명

지금 dev HEAD는 92e21bc8e이다. 바로 전에 합쳐진 #4423이 Connect 트레일러 코드를 HTTP 상태로 옮겼고, #4419가 사용량(필드 7)·상태 분류를 고쳤다. 그런데 Devin cloud-direct 어댑터에는 아직 다른 구멍이 남아 있다. src/adapters/devin.tsassistantText 주석이 "Cognition에는 reasoning replay 필드가 없다"고 단정하고, assistant의 thinking 블록을 히스토리에서 통째로 버렸다. 그래서 추론 모델이 도구 루프의 매 턴마다 같은 사고 사슬을 처음부터 다시 만들고, 그 토큰 비용도 다시 낸다.

이 PR은 그 단정이 틀렸다는 쪽에 선다. 같은 서비스를 쓰는 다른 클라이언트 두 곳이 어시스턴트 프롬프트에 thinking(필드 11), signature(필드 12), signature_type(필드 18)을 쓴다고 보고, 그 경로를 연다. assistantThinkingthinking 파트를 모아 텍스트는 필드 11로, 서명은 마지막에 서명이 있는 블록만 필드 12로 실어 보낸다. 보이는 assistant 텍스트에는 여전히 생각을 넣지 않는다. 예전 주석이 맞았던 부분이다. 생각을 본문에 넣으면 모델이 그걸 사용자에게 말한 말처럼 다시 읽는다.

응답 쪽도 반쪽이었다. src/adapters/devin/cloud-direct/chat.tsdecodeChatFrame은 필드 9(delta_thinking)만 reasoning으로 풀고, 필드 10(delta_signature)은 무시했다. 그래서 프롬프트에 서명을 넣을 자리가 생겨도 넣을 값이 없었다. 이제 필드 10을 reasoning_signature로 풀고, 어댑터가 thinking_signature 이벤트로 올린다. 브리지·terminal-guard는 이미 Anthropic과 같은 이름으로 서명을 thinking 파트에 붙이므로, 다음 턴 assistantThinking이 그걸 다시 읽을 수 있다. 생각만 있고 본문·도구 호출이 없는 턴도 더 이상 스킵하지 않는다. 그걸 버리면 바로 다음 턴이 같은 사슬을 다시 만들기 때문이다.

범위는 Devin 어댑터와 cloud-direct 인코더/디코더, 그리고 tests/providers/devin-hardening.test.ts의 네 줄짜리 회귀만이다. 공유 코어나 다른 프로바이더는 안 건드린다. #4420(effort 접미사)과도 파일이 거의 안 겹친다. 로컬에서 hardening·adapter 테스트 44개가 통과했고 tsc도 깨끗하다고 적혀 있다. 풀 스위트는 CI에 맡긴다.

다만 위생 검사(intake: hygiene-blocked)가 missing_coauthor_credit로 빨갛다. 원인은 남의 PR을 가져온 게 아니라, 커밋/본문에 "carries" 같은 동사와 프로토버프 필드 번호 #10/#12가 같은 문장 창에 있어서 게이트가 PR #10·#12의 공동저자 트레일러를 요구한 것이다. PR 11을 resolve하다 404가 난 로그도 같은 오탐이다. 필드 번호 표기를 field 10처럼 바꾸면 풀린다.

라인 문제

src/adapters/devin.ts assistantThinking - signature_type(프롬프트 필드 18)을 ChatHistoryItem/encodeChatMessagePrompt까지는 열어 두었는데, 매퍼는 thinkingsignature만 채운다. 다른 클라이언트가 필드 18을 쓴다고 본문에서 말했으면, 값을 모르면 상수라도 넣을지·생략이 안전한지 한 줄로 밝혀야 한다. 지금 테스트는 필드 11·12만 단언한다.

src/adapters/devin.ts mapOneMessage 스킵 조건 - !reasoning.thinking이면 서명만 있는 턴도 버린다. 실측에서 서명만 오는 프레임이 없다면 괜찮지만, 필드 10만 오고 필드 9가 비는 경우가 있으면 라운드트립이 다시 깨진다.

src/adapters/devin.ts 여러 thinking 블록 - 텍스트는 줄바꿈으로 이어 붙이고 서명은 "마지막에 서명이 있는 블록"만 고른다. 앞 블록 서명과 뒤 블록 텍스트가 짝이 안 맞을 수 있다. Anthropic식 블록 단위 재현이 필요하면 블록별로 나눠 실어야 한다.

tests/providers/devin-hardening.test.ts - 인코더가 필드 18을 쓰는지, thinking_signature 이벤트가 어댑터 emit까지 가는지는 핀이 없다. 매핑·디코드 단위만 있다.

위생 본문/커밋 - 프로토버프 필드를 #10 형태로 쓰면 pr-carry-attribution이 PR 인용으로 읽는다. field 10 / 필드 10으로 고치거나, carries 동사와 필드 번호를 같은 문장에 두지 말아야 한다.

메인테이너의 판단이 필요한 지점

  • 필드 18 signature_type을 지금 생략한 채 머지할지, 참조 클라이언트의 실제 문자열을 확인한 뒤 채울지
  • 위생 오탐을 본문만 고쳐서 풀지, attribution-approved로 한 번 넘길지
  • fix(devin): map Connect trailer codes onto the status core acts on #4423 직후 하드닝 열차에 이어서 넣을지, CI 풀 스위트·위생 초록을 기다릴지

너의 추천

위생 본문에서 필드 번호를 #N이 아닌 field N으로 고쳐 intake: hygiene-blocked를 먼저 풀어라. 그다음 hardening/adapter 테스트와 타입체크가 초록이면 #4423 후속으로 머지하는 편이 맞다. 필드 18은 모르면 이번엔 생략을 문서 한 줄로 남기고, 라이브 프레임에서 값이 보이면 바로 후속 PR로 채워라. #4420과는 순서를 바꿔도 된다.

이 댓글은 grok-bot이 작성했습니다

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2f3dff4af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/adapters/devin.ts
const thinking = blocks.map(b => b.thinking).filter(Boolean).join("\n");
// Only one signature can ride the prompt, so take the last block that has
// one: that is the block the turn actually ended on.
const signature = blocks.filter(b => b.signature).at(-1)?.signature;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate signatures on Devin provenance

When history contains unsigned reasoning or reasoning produced by another provider, OcxThinkingContent.signature is not necessarily a Cognition signature: src/responses/parser.ts can populate it with JSON.stringify(reasoning), while Anthropic histories carry their own opaque signatures. Forwarding every truthy value as Devin field #12 therefore pairs the text with an invalid attestation, so a same-provider unsigned continuation or provider-switched conversation can have its replay ignored or rejected. Preserve Devin provenance in the internal event/history contract and emit #12 only when that provenance matches the current Devin destination.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment thread src/adapters/devin.ts
Comment on lines +184 to +186
function assistantThinking(
message: OcxAssistantMessage,
): { thinking?: string; signature?: string } {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Synchronize the adapter ownership docs

This changes Devin adapter and transport replay semantics without updating any of the structure documents mapped to src/adapters/ in structure/INDEX.md. The scoped repository rule requires every document listed for a changed source area to be updated in the same change, so the relevant ownership documentation must be synchronized before landing.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

Comment thread src/adapters/devin.ts
Comment on lines +191 to +194
const thinking = blocks.map(b => b.thinking).filter(Boolean).join("\n");
// Only one signature can ride the prompt, so take the last block that has
// one: that is the block the turn actually ended on.
const signature = blocks.filter(b => b.signature).at(-1)?.signature;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep each signature paired with its thinking block

When one assistant message contains multiple thinking blocks, this concatenates every block's text but attaches only the last available signature. The parser deliberately preserves multiple individually signed reasoning items in one assistant turn, and each opaque signature attests only its original block, so the resulting #11/#12 pair is invalid and Cognition cannot reliably replay it. Select a single matching block—typically the final signed block—or otherwise preserve block boundaries instead of combining signed payloads.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

@lidge-jun
lidge-jun marked this pull request as ready for review September 12, 2026 16:10
@lidge-jun
lidge-jun merged commit ad50c4a into dev Sep 12, 2026
25 of 28 checks passed
@lidge-jun
lidge-jun deleted the codex/260912-devin-thinking-replay branch September 12, 2026 16:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d2f3dff4af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/adapters/devin.ts
*/
function assistantThinking(
message: OcxAssistantMessage,
): { thinking?: string; signature?: string } {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Populate signature_type on replayed Devin prompts

For Cognition models that require field #18 to identify the signature scheme, every signed replay produced through this adapter still omits it: assistantThinking can return only thinking and signature, so ChatHistoryItem.signature_type remains undefined and the encoder never emits the newly documented #18 field. This leaves the #11/#12 replay in a shape that differs from both verified clients and can cause the signature to be ignored or rejected, defeating the tool-loop reasoning replay; preserve or infer the verified Devin signature type alongside the signature and extend the wire test to assert #18.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working intake: hygiene-blocked Deterministic PR hygiene checks failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant