Skip to content

fix(claude-sdk-oauth): ignore volatile top hooks in continuity hashes - #1085

Open
codeg-dev wants to merge 3 commits into
code-yeongyu:mainfrom
codeg-dev:fix/session-sync-volatile-hook-hashes
Open

fix(claude-sdk-oauth): ignore volatile top hooks in continuity hashes#1085
codeg-dev wants to merge 3 commits into
code-yeongyu:mainfrom
codeg-dev:fix/session-sync-volatile-hook-hashes

Conversation

@codeg-dev

@codeg-dev codeg-dev commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-on to #791. Top-of-turn hook injections (omo-memory notice, goal-continuation, mindy-team context blocks, senpi-task usage) convert to user-role bodies. Hashing those bodies makes a rewrite or prepend look like sent_stream_diverged, so decideFromBinding flattens to a cold seed and burns the Anthropic prompt cache even when the real conversation prefix is intact.

This is complementary to #1075 (rolling cache checkpoints after serialize). #1075 cannot help if continuity flattens the session first.

Evidence

  • Stock RED: notice rewrite or prepend -> flatten / sent_stream_diverged.
  • GREEN: bun x vitest --run packages/coding-agent/test/suite/regressions/claude-sdk-oauth-volatile-hook-continuity.test.ts packages/coding-agent/test/suite/regressions/790-claude-sdk-oauth-empty-user-continuity.test.ts (9 passed).

Test plan


Summary by cubic

Prevents false session divergence in claude-sdk-oauth by excluding volatile top-of-turn hook content from continuity hashes. Previously we hashed their converted user bodies, triggering sent_stream_diverged and a flatten; now we hash them by kind while still transmitting them, so only real user edits cause divergence.

  • Detects omo-memory notices, goal-continuation, mindy-team context blocks, and senpi-task usage via customType/provenance and content signatures after convertToLlm.
  • Leaves isTransmittedMessage unchanged; neutralizes hook content inside sentMessageHashes so delta indexing stays correct. Genuine user rewrites and structural prepends still diverge. Regression tests added.

Written for commit 3fd8bde. Summary will update on new commits.

Review in cubic

Top-of-turn injections (memory notice, goal continuation, rule/task
blocks) convert to user-role bodies. Hashing them makes a rewrite or
prepend look like sent_stream_diverged and flatten to a cold seed.
Exclude them from isTransmittedMessage the same way content-less user
messages are excluded (PR code-yeongyu#791). Genuine user rewrites stay fail-closed.

@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: 9aab0085ad

ℹ️ 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".

*/
export function isTransmittedMessage(message: { role: string }): message is SentMessage {
if (message.role !== "user" && message.role !== "toolResult") return false;
if (isVolatileHookMessage(message)) return false;

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 Preserve hook messages in incremental payloads

On any non-flatten resident turn containing one of these hooks—especially an automatic goal-continuation turn—this filter removes the hook from sentMessages(), and session-stream.ts later builds the actual delta payload from that same filtered array via buildDeltaPromptBlocks(messages.slice(from)). The SDK therefore never receives the continuation or updated memory/team/task context (and a hook-only turn can submit an empty user payload), even though the turn may be recorded as synchronized; exclude these messages only from continuity hashing, not from the transport message list.

Useful? React with 👍 / 👎.

…wire

Review P1: isTransmittedMessage also feeds buildDeltaPromptBlocks(messages.slice(from)).
Dropping hooks there would omit a goal-continuation-only turn. Neutralize hook
content inside sentMessageHashes instead so rewrite does not flatten, while
from still indexes the full transmitted list.
@codeg-dev

Copy link
Copy Markdown
Contributor Author

P1 accepted. isTransmittedMessage is restored to the previous contract (content-less user messages only). Hook rewrite is neutralized inside sentMessageHashes as { role, volatileHook: kind } so the slot stays in messages.slice(from) and a goal-continuation-only turn still reaches the SDK.

Structural prepend still reports sent_stream_diverged (fail-closed). Hook content rewrite reattaches.

Pushed on this branch (3fd8bde). Tests: claude-sdk-oauth-volatile-hook-continuity + #790 still 9/9.

…tile hook types

Co-authored-by: Code_G <288527233+codeg-dev@users.noreply.github.com>
Signed-off-by: Code_G <288527233+codeg-dev@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant