Repository navigation
Conversation
…crypted reasoning
🦋 Changeset detectedLatest commit: 0056aa3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Track the hydrated assistant and verify replay packages/ai-client/src/chat-client.ts, packages/ai-client/tests/chat-client-reasoning-rejoin.test.ts, .changeset/rejoin-keeps-replay-message.md |
ChatClient captures the hydrated assistant ID and removes only that message at a rebuild trigger. Unit tests cover readable and encrypted reasoning with different hydrated tails. The changeset declares a patch release for @tanstack/ai-client. |
Exercise reasoning replay end to end testing/e2e/src/routes/api.join-run-client-tool.ts, testing/e2e/src/routes/join-run-client-tool.tsx, testing/e2e/tests/join-run-client-tool.spec.ts, testing/e2e/README.md |
The route emits reasoning before assistant text for the reason scenario. The page exposes assistant thinking content. The Playwright test checks the replayed answer, thinking text, assistant message count, and loading state after reload. The README description includes the reasoning replay case. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix · Severity of issue fixed: Medium
Possibly related PRs
- TanStack/ai#1893: Its reconnect and
joinRunsupport includes replayed reasoning and partial assistant message tests. - TanStack/ai#1899: Its rejoin path hydrates history with an in-flight assistant message, a history shape covered by this change.
Suggested reviewers: alemtuzlak
Merge Risk: 
Minimal · up to 0056a
-
The rejoin change preserves the replayed answer on the inspected path. No merge-blocking issue remains after normal checks.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passed-
Full details: Title check
-
Explanation
The title names the fix in packages/ai-client: the rejoin path keeps the replay's answer when the replay starts with reasoning. This matches the main change in the changeset, chat-client.ts, and the reasoning-rejoin tests. The title is specific and concise, and it has no file list or vague terms.
Full details: Description check
-
Explanation
The description follows the template. It has the Changes section, the Checklist with each item marked, and the Release Impact section. It also adds Root cause, Possible alternatives, Testing, Linked issues, and Risk/rollback sections. The Changes section covers the resumeInFlightRun change, the helper rename, the unchanged REJOIN_REBUILD_TRIGGERS, the unit test update, the E2E spec, and the docs decision. Testing lists the commands run, the failing and passing results on main and on the branch, and a manual test procedure. The description gives the reasons for the change and the rollback path. Minor gaps: the Changes section does not mention the e2e route and page changes (REASON_OK scenario and thinking display), and the PR objectives name the fix as encrypted reasoning, while the description says both readable and encrypted reasoning. These gaps do not block the check.
Full details: Out of Scope Changes check
-
Explanation
The changes remain within the rejoin fix described by the pull request. The client change targets hydrated-message removal, unit tests cover readable and encrypted reasoning, and the E2E changes cover the reasoning replay path. The changeset supports the published package change. No unrelated change is shown.
-
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 1 functions across 5 files. (2 skipped: 2 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
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.
Comment @coderabbitai help to get the list of available commands.
One table covers readable and encrypted-only reasoning against a hydrate that ends on a user turn or an in-flight partial. Each case checks one assistant message with the thinking part, the tool call and the answer.
|
Thanks for the PR, @aayushbtw! 🙌 @AlemTuzlak will take a look. Automated pre-review checks
Automated triage — a human review follows. |
Rejoining a run lost the answer when the replay started with reasoning. This happens for readable and for encrypted-only reasoning, because
chat()puts metadata on every adapter event. The client now drops only the assistant message that hydrate returned, not the message that the replay already started.🎯 Changes
resumeInFlightRunreads the id of the trailing hydrated assistant message before the replay starts. At the first rebuild trigger, it drops only the message with that id.dropTrailingInFlightAssistant()is nowdropHydratedAssistant(id).REJOIN_REBUILD_TRIGGERSdoes not change.chat()does.join-run-client-tool.spec.tshas a reload in the middle of a run that reasons before it answers.docs/persistence/client-persistence.mdalready says that a replay rebuilds a run that starts with reasoning. This fix makes that statement true again.✅ Checklist
pnpm run test:pr, or these tests do not apply to this pull request.docs/for this change, or this change is not user-facing.pnpm changeset), or this PR does not change a published package.🚀 Release Impact
Root cause
Issue. A reload during a run that starts with reasoning loses the answer. If hydrate ends on a user turn, no assistant message is left. If hydrate ends on an in-flight partial, the stale partial stays and the answer is lost.
Cause.
chat()stamps call metadata on every adapter event (withCallMetadata). Since #1789, aREASONING_MESSAGE_STARTwith metadata creates the assistant message. That event comes before every rebuild trigger. At the first trigger,dropTrailingInFlightAssistant()removed the last assistant message, which was now the replay's own message. #1357 fixed #1422 before #1789 landed, so #1789 opened #1422 again for real streams. Encrypted-only reasoning is one case of this.Fix. Before the replay, the client reads the id of the trailing hydrated assistant message. At the first trigger, it drops only that id. The client does not touch the messages that the replay creates.
Side effect. On
main, anACTIVITY_SNAPSHOTbefore the first trigger made the activity the last message. Then the drop removed nothing, and the stale partial stayed next to the new answer. The drop by id removes the partial. A local run showed this (main:[ 'u1', 'partial', 'act-1', 'reply' ], branch: nopartial). This PR does not add a test for it.Possible alternatives
REASONING_STARTandREASONING_MESSAGE_STARTtoREJOIN_REBUILD_TRIGGERS. That moves the drop before this event. Any other event that creates a message before a trigger still has the same fault.Testing
Commands run
pnpm test:pr: pass (Successfully ran targets test:sherif, test:knip, test:docs, test:kiira, test:maintainer, test:ai-review, test:oxlint, test:lib, test:types, test:build, build for 113 projects).pnpm --filter @tanstack/ai-e2e exec playwright test tests/join-run-client-tool.spec.ts: 3 passed. I did not run the full E2E suite.Unit test,
pnpm --filter @tanstack/ai-client exec vitest run tests/chat-client-reasoning-rejoin.test.ts, withsrc/chat-client.tsfrommain:The same test on this branch:
Two checks on
mainshow that the metadata from #1789 causes the fault:main.main.E2E, with
@tanstack/ai-clientbuilt frommain:The same spec on this branch:
Manual test
main, runpnpm --filter @tanstack/ai-client exec vitest run tests/chat-client-reasoning-rejoin.test.ts. All 4 cases fail.@tanstack/ai-client, then runpnpm --filter @tanstack/ai-e2e exec playwright test tests/join-run-client-tool.spec.ts. The reasoning case passes.How this PR makes testing easy.
tests/chat-client-reasoning-rejoin.test.tsis one table: readable or encrypted-only reasoning, against a hydrate that ends on a user turn or on an in-flight partial. Each case asserts one assistant message with the thinking part (and its signature), the tool call, and the answer. The E2E case reloads a held run and asserts the thinking and the answer after the replay.Linked issues
Follow-up to #1422, which #1357 closed. #1789 opened it again for streams from
chat().Risk / rollback
Low. Only the rejoin path changes. If hydrate ends on an assistant message, the client drops that message, as before. If hydrate ends on a user turn, the client drops nothing. To undo, revert the PR.
Summary by CodeRabbit