Skip to content

feat(web): continue a usage-limited thread with the newly picked model - #16889

Closed
fagnersales wants to merge 1 commit into
pingdotgg:mainfrom
fagnersales:feat/continue-with-new-model
Closed

fagnersales wants to merge 1 commit into
pingdotgg:mainfrom
fagnersales:feat/continue-with-new-model

Conversation

@fagnersales

@fagnersales fagnersales commented Oct 7, 2026 •

Copy link
Copy Markdown

Fixes #17164
Fixes #15555

When a thread stops on a usage limit, the usual way out is to switch to another model. Today that means picking the model and then typing a new message, because the composer's resume button ignores the picker. It continues on the old, still-limited model.

Fix

  • The resume action now sends the composer's current model selection with the manual continuation (message.dispatch with manualContinuationOfRunId + modelSelection). The server already accepts a different model or provider instance here and switches the provider through the usual handoff. No server or contract changes.
  • When the picked model differs from the model of the run being resumed (instance or model), the icon-only resume button becomes a labeled Continue with <model> pill. The collapsed mobile-web composer button uses the same label. When the picked model matches, the button stays the existing icon-only "Resume thread" button.
  • This works for any resumable run (usage-limited or interrupted), because both use the same continuation path.

Surfaces

  • Web and desktop: covered (shared ChatComposer).
  • Mobile: not covered. The native app has no manual resume yet, only the auto-resume/snooze recovery card, so this needs its own change.
  • Providers: no adapter changes. The provider switch uses the existing handoff, and the picker still locks when a thread can't switch providers.

Verification

  • Extended the usage-limit recovery › "manually resumes an usage_limit run" test in runtimeLayer.test.ts. The usage-limited case now resumes on another provider instance and asserts that the new run and thread use that model.
  • tsc --noEmit for apps/web, plus targeted lint and format on the changed files.
  • Recorded in the web client on a dev server (video below). The thread is stopped by a real Claude usage limit and shows the existing icon-only "Resume thread" button. Picking Claude Sonnet 5.5 on another Claude account turns it into Continue with Claude Sonnet 5.5. One click hands off context from Opus 5.5 to Sonnet 5.5 and the thread runs again, with nothing retyped.
continue-with-new-model.mp4

🤖 Generated with Claude Code (Claude Opus 5.5, T3 Code)

When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 7, 2026
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 6199c5d9-2c13-4494-aac2-a2cd88797988
📥 Commits

Reviewing files that changed from the base of the PR and between a8c4802 and 019c017.

📒 Files selected for processing (4)
  • apps/server/src/orchestration-v2/runtimeLayer.test.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/chat/ChatComposer.tsx
  • apps/web/src/components/chat/ComposerPrimaryActions.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

ChatView now retains the resumable run’s model selection and includes the composer’s selection in continuation requests. The composer can show a model-specific Continue label when the selections differ. A runtime test checks selection behavior after usage-limit failures and interruptions.

Changes

Resumable Run Model Selection

Layer / File(s) Summary
Resumable run selection propagation
apps/web/src/components/ChatView.tsx, apps/server/src/orchestration-v2/runtimeLayer.test.ts
ChatView retains the resumable run and passes model selection into the composer and continuation request. The runtime test checks that a usage-limit continuation uses the alternate selection, while an interrupted run retains the original selection.
Model-specific continuation action
apps/web/src/components/chat/ChatComposer.tsx, apps/web/src/components/chat/ComposerPrimaryActions.tsx
When the composer selection differs from the resumable run’s selection and the draft conditions allow it, the action can identify the composer model. ComposerPrimaryActions displays a Continue button with the model name when provided.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 019c0

Model-specific continuation appears consistent with the selected model, and the Continue button retains existing resume behavior. No merge-blocking issue was identified.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 019c0

Continuing with a different provider can change where thread context is sent. The inspected path retains existing validation and transfer controls, and no introduced security weakness was established. Coverage of interruption and recovery after external execution begins remains incomplete.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed caller can request continuation of the addressed thread using a different provider, potentially transferring that thread's handoff context. The inspected path does not establish broader tenant, datastore, infrastructure or credential authority; provider-instance authorization and deployment-wide exposure were not exhaustively audited.

Trust Boundaries and Controls

  • observed — The server treats the supplied run ID and model selection as request inputs, not authoritative state. It resolves the source run within the addressed thread, requires the latest resumable run and immediate-start mode, and rejects archived or deleted threads and pending runtime requests. This establishes continuation-state ownership, not a complete tenant-authorization proof.
  • observed — The UI provider-switch lock is not the sole inspected control. Server transition planning checks target availability and compatibility, and the context-handoff path invokes ensureContextHandoff before preparing transfer contents. These controls already existed before this PR.

Resilience and Maintainability Implications

  • observed — Existing dispatch serializes work per thread and returns stored results for accepted command receipts. EventSink commits domain events, projection updates, effect-outbox entries and the receipt within a SQL transaction, providing containment against partial internal commit and repeated command application.
  • observed — Existing effect processing rechecks cancellation before external work, races execution against cancellation, and uses lease-bound settlement and bounded retries. Provider session-open and thread-load failures have guarded terminal settlement on the final attempt. These mechanisms do not by themselves prove recovery for every failure after external acceptance.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the problem, implementation, affected surfaces, limitations, and verification. It does not include the required Scope and approval section, and the UI verification provides a … Add a Scope and approval section with the triaged issue or explicit maintainer approval, or explain why this focused fix qualifies for an exemption. Add clear before/after screenshots for the web UI change and state any remaining verificati…
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Approvability ✅ Passed PASS. The PR is a focused fix to the existing resume workflow. It changes four application/test files, passes the selected model through the existing continuation path, and adds assertions. It does no…
Title check ✅ Passed The title clearly and concisely describes the main change: continuing a usage-limited thread with the newly selected model.
Full details: Description check

Explanation

The description explains the problem, implementation, affected surfaces, limitations, and verification. It does not include the required Scope and approval section, and the UI verification provides a video but no clear before/after screenshots.

Resolution

Add a Scope and approval section with the triaged issue or explicit maintainer approval, or explain why this focused fix qualifies for an exemption. Add clear before/after screenshots for the web UI change and state any remaining verification limits.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@fagnersales

Copy link
Copy Markdown
Author

I don't think the current design is good enough for T3Code standards but I would love to have some sort of helper like this implemented. Have been reaching limits constantly and having to change to another model and type some random stuff is becoming annoying.

fmajestic pushed a commit to fmajestic/t3code that referenced this pull request Oct 8, 2026
When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Cherry-picked from pingdotgg#16889 (open upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fmajestic pushed a commit to fmajestic/t3code that referenced this pull request Oct 8, 2026
When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Cherry-picked from pingdotgg#16889 (open upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fmajestic pushed a commit to fmajestic/t3code that referenced this pull request Oct 8, 2026
When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Cherry-picked from pingdotgg#16889 (open upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fmajestic pushed a commit to fmajestic/t3code that referenced this pull request Oct 8, 2026
When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Cherry-picked from pingdotgg#16889 (open upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
fmajestic pushed a commit to fmajestic/t3code that referenced this pull request Oct 9, 2026
When a run stops on a usage limit, picking another model in the composer
turns the resume button into "Continue with <model>". Resuming now sends
the composer's model selection, so the thread continues on the new model
without retyping the message.

Cherry-picked from pingdotgg#16889 (open upstream).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AdEx-Partners-DE added a commit to AdEx-Partners-DE/t3code that referenced this pull request Oct 10, 2026
…th the picked model

The explicit account from the limit banner still wins over the composer's
selection, and the pill uses the send button's current disabled condition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AdEx-Partners-DE

Copy link
Copy Markdown
Contributor

I run this in a fork on top of current main and had to rebase it by hand, so here is what it took, in case it saves you time:

  • ChatComposer.tsx conflict: main now reads props.resumeCompactionTokens !== null && !props.keepFullHistory in collapsedComposerPrimaryActionLabel. Keeping that condition and putting the resumeModelName branch in front of it resolves it.
  • ComposerPrimaryActions.tsx no longer compiles after the merge: the new pill uses disabled={sendBlocked}, and sendBlocked is gone on main (error TS2304: Cannot find name 'sendBlocked'). The main button now uses isSendBusy || isSendDisabled || isConnecting || isEnvironmentUnavailable, which works for the pill as well.

With those two changes tsc --noEmit is clean for apps/web, the chat component tests pass, and the extended usage-limit recovery test in runtimeLayer.test.ts passes (Windows 11).

One thing to watch if this lands next to a per-account switch: resumeThread reads the composer's selection unconditionally, so any caller that wants to resume on an explicit target has to take precedence over it. In my fork that is one line (explicit ?? composer selection).

Resolved merge, for reference: AdEx-Partners-DE@bb5dfe3cd

Posted by Claude (AI) on behalf of @AdEx-Partners-DE; not reviewed by a human.

@AdEx-Partners-DE

Copy link
Copy Markdown
Contributor

Follow-up with a real run of this PR (rebased as described above, web client on Windows 11, two Claude subscriptions as separate provider instances):

  1. Started a turn on account A (Claude Haiku 5.5), stopped it mid-answer. Composer showed the icon-only Resume thread.
  2. Picked the same model on account B in the picker. The button turned into Continue with Claude Haiku 5.5. The selection survived a page reload.
  3. Clicked it. A new run started on account B with "Continue where you left off.", a context handoff (full_thread_summary) was created and shown in the timeline, and the run completed with the continued answer.

So the path works end to end across provider instances, not only across models.

One thing I noticed: when only the account differs, the label still names the model ("Continue with Claude Haiku 5.5"), which reads the same as before the switch. Naming the instance in that case ("Continue with ") would say what actually changes.

Posted by Claude (AI) on behalf of @AdEx-Partners-DE; not reviewed by a human.

@juliusmarminge

Copy link
Copy Markdown
Member

Note

Grok responding on behalf of Julius.

Thanks for this, and for the detailed write-up. The core fix here, passing the composer's model selection with Resume, landed in #17867, which closed #15555. So I'm closing this as superseded.

The labeled Continue with <model> button is a separate UI change. If you'd still like it, please open a fresh, smaller PR on top of current main with just that part.

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

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

3 participants