Skip to content

Add provider thread IDs and safe cross-thread reads#4575

Draft
basilk15 wants to merge 1 commit into
pingdotgg:mainfrom
basilk15:agent/add-cross-thread-reading
Draft

Add provider thread IDs and safe cross-thread reads#4575
basilk15 wants to merge 1 commit into
pingdotgg:mainfrom
basilk15:agent/add-cross-thread-reading

Conversation

@basilk15

@basilk15 basilk15 commented Jul 26, 2026

Copy link
Copy Markdown

Summary

  • Persist and expose provider-native thread IDs alongside T3 Code thread IDs without changing the existing T3 ID action.
  • Add explicit Copy Codex Thread ID / Copy Provider Thread ID context-menu actions in both sidebar variants.
  • Register a read-only read_thread MCP tool that resolves either a T3 thread ID or a provider-native ID and returns bounded recent messages/activity without resuming, steering, or mutating the target thread.
  • Preserve the existing preview MCP toolkit and require an explicit thread-read capability on issued same-environment MCP credentials.

Compatibility and safeguards

  • The orchestration field is optional and the existing nullable projection column is reused, so no migration is required.
  • Legacy rows with a SQL NULL provider thread ID continue to decode.
  • Existing Copy Thread ID behavior remains available and distinct.
  • Cross-thread results are bounded (1–200 items per category, default 50), read-only, and limited to the authenticated T3 server environment.

Verification

  • vp run --filter @t3tools/contracts --filter t3 --filter @t3tools/web typecheck
  • Targeted formatting and lint on all 21 changed files (lint under the repository-pinned Node 24.13.1 runtime)
  • 19 focused web/MCP/projection tests passed
  • Focused provider runtime ingestion test passed
  • 5 MCP HTTP/preview registration and transport tests passed with local socket permission
  • Isolated test-t3-app environment launched successfully, migrations completed, and the pairing route was produced. Automated UI interaction could not be completed because the native preview bridge required authentication and the fallback in-app browser reported no available browser surfaces; no alternate browser was used to bypass that authentication constraint.

Note

Add provider thread IDs to sessions and expose a read_thread MCP tool

  • Captures providerThreadId (e.g. Codex, Claude) on thread start and propagates it through session state changes, projections, and the OrchestrationSession contract.
  • Persists provider_thread_id in the projection_thread_sessions table and surfaces it via ProjectionSnapshotQuery.
  • Adds a read_thread MCP tool in a new ThreadControlToolkit that fetches thread metadata and bounded message/activity history by T3 or provider thread ID, enforcing a thread-read capability check.
  • Newly issued MCP credentials include the thread-read capability alongside preview.
  • Adds "Copy Provider Thread ID" context menu items to both Sidebar and SidebarV2 in the web UI.
📊 Macroscope summarized d86586d. 13 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted

🗂️ Filtered Issues

No issues evaluated.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bd4c49e2-66b5-4db4-9529-80f8963e785e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 26, 2026
status: "starting",
providerName: activeSession?.provider ?? preferredProvider,
providerInstanceId: activeSession?.providerInstanceId ?? desiredInstanceId,
...(thread.session?.providerThreadId

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium Layers/ProviderCommandReactor.ts:445

When bindSessionToThread runs after a session restart, it copies providerThreadId from the stale thread.session snapshot taken before startSession was called. If the adapter emits a thread.started event during startSession and runtime ingestion projects the new provider-native thread ID before this thread.session.set dispatch is processed, this write overwrites the correct new providerThreadId with the old one. The thread then remains indexed and displayed under the wrong provider-native ID because no later thread.started event is guaranteed.

This affects three call sites in the diff, all of which copy providerThreadId from thread.session?.providerThreadId. Consider sourcing the providerThreadId from the freshly started session object (or from thread.session only when no newer value is available) rather than unconditionally from the pre-start snapshot.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration/Layers/ProviderCommandReactor.ts around line 445:

When `bindSessionToThread` runs after a session restart, it copies `providerThreadId` from the stale `thread.session` snapshot taken before `startSession` was called. If the adapter emits a `thread.started` event during `startSession` and runtime ingestion projects the new provider-native thread ID before this `thread.session.set` dispatch is processed, this write overwrites the correct new `providerThreadId` with the old one. The thread then remains indexed and displayed under the wrong provider-native ID because no later `thread.started` event is guaranteed.

This affects three call sites in the diff, all of which copy `providerThreadId` from `thread.session?.providerThreadId`. Consider sourcing the `providerThreadId` from the freshly started `session` object (or from `thread.session` only when no newer value is available) rather than unconditionally from the pre-start snapshot.

? binding.resumeCursor.threadId
: undefined;
}
if (binding.provider === "claude") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Medium thread-control/handlers.ts:36

readProviderThreadId checks for binding.provider === "claude", but the Claude driver kind is "claudeAgent". Claude bindings always fall through to the generic sessionId branch, so read_thread can't match a Claude provider-native thread ID stored in resume and returns ThreadReadNotFoundError instead of resolving the thread. Consider comparing against "claudeAgent".

Suggested change
if (binding.provider === "claude") {
if (binding.provider === "claudeAgent") {
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/mcp/toolkits/thread-control/handlers.ts around line 36:

`readProviderThreadId` checks for `binding.provider === "claude"`, but the Claude driver kind is `"claudeAgent"`. Claude bindings always fall through to the generic `sessionId` branch, so `read_thread` can't match a Claude provider-native thread ID stored in `resume` and returns `ThreadReadNotFoundError` instead of resolving the thread. Consider comparing against `"claudeAgent"`.

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant