Skip to content

feat(web): auto-reconcile external agent sessions on project open - #1

Open
lewismarshall wants to merge 3 commits into
mainfrom
cursor/auto-reconcile-agent-sessions-07d1
Open

feat(web): auto-reconcile external agent sessions on project open#1
lewismarshall wants to merge 3 commits into
mainfrom
cursor/auto-reconcile-agent-sessions-07d1

Conversation

@lewismarshall

@lewismarshall lewismarshall commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What Changed

Added always-on automatic reconciliation of external agent sessions (Claude Code, Codex) when projects become available on a connected environment.

A new useAgentSessionAutoReconcile hook is mounted in the chat layout route. When environment shells bootstrap, it calls the existing idempotent agentSessions.import RPC for every known project, surfacing agent work already on disk in the thread list — without requiring a manual "Import agent sessions…" action.

Related Ideas discussions:

Why

When connecting to a remote environment via T3 Connect, agent sessions already on disk (e.g. Claude CLI history under ~/.claude) do not appear in the project thread list unless the user manually triggers an import. The existing AgentSessionScanner + AgentSessionImporter infrastructure (pingdotgg#5362) handles the heavy lifting, but is only wired into the first-run Welcome Wizard. Post-onboarding, there is no continuous visibility path.

This PR adds the minimal client-side trigger to close that gap. It reuses the existing server-side import RPC, which is fully idempotent (thread IDs are prefixed with import:, and per-source file-identity watermarks prevent re-reading unchanged transcripts).

Server version requirement

Auto-reconcile requires a server build that includes PR pingdotgg#5362 (the agentSessions.scan / agentSessions.import RPCs). Servers older than this (e.g. t3@0.0.38) do not expose these methods.

The hook detects unsupported servers via RpcClientError classification and logs a one-time console.warn explaining the minimum version requirement. It skips further import attempts for that environment but does not mark projects as reconciled, so a server upgrade without full client remount will retry automatically.

workspaceRoot matching

The import passes expectedWorkspaceRoot from the project's workspaceRoot to the server. If the T3 project root differs from the directory Claude/Codex sessions ran in (e.g. /home/box/Evosim vs /workspace/charlie-evosim/Evosim), the server returns AgentSessionImportProjectChangedError and the hook logs a diagnostic warning. Sessions are only imported when paths match.

What shipped

  • useAgentSessionAutoReconcile hook: watches the project list, triggers agentSessions.import once per project per mount cycle
  • selectUnreconciledProjects pure function: identifies projects not yet reconciled without eagerly marking them — the hook marks reconciled only after a definitive outcome
  • classifyImportFailure pure function: classifies import failures into four categories (unsupported-server, interrupted, expected domain error, unexpected defect)
  • isDefinitiveOutcome helper: only expected domain errors and successes are definitive; transient failures (unsupported-server, unexpected, interrupted) allow retry
  • Hook mounted in ChatRouteLayout (_chat.tsx)
  • 21 unit tests (8 for selection logic including retry behavior, 10 for error classification, 4 for isDefinitiveOutcome — 1 test per outcome kind + success omission since success returns "expected" but is handled before classification in the hook)

Reconcile-marking semantics

Outcome Marked reconciled? Rationale
Success (importedCount / skippedCount returned) Yes Import completed or was a no-op
Expected domain error (project not found, workspace mismatch, scan error, auth) Yes Won't resolve without user action
Unsupported server (RpcClientError) No Server upgrade may fix; retry next cycle
Unexpected defect No Transient; retry may succeed
Interrupted (unmount / env switch) No Was cancelled; retry when stable

Error handling behavior

Failure kind Detection Logging
Unsupported server (pre-pingdotgg#5362) RpcClientError One-time console.warn with upgrade instructions; skip environment
Interrupted (unmount) Cause.hasInterruptsOnly Silent skip
Expected domain error Tagged error _tag match console.warn with project context
Unexpected defect Fallthrough console.error with project context
Success result._tag === "Success" console.info with importedCount + skippedCount (always, even when 0)

Out of scope

  • Connect-cloud / account-wide session inbox
  • Live attach to running claude --remote-control
  • Cross-environment merged thread list
  • Settings toggle (the import is already idempotent and silent; gating it adds complexity without benefit)

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • No UI changes — this is an invisible background reconciliation

Maintainer manual QA checklist

Prerequisites: Server must include PR pingdotgg#5362 (agentSessions.import RPC). t3@0.0.38 is insufficient — use a build from main after 2026-09-05. The project's workspaceRoot must match the cwd recorded in Claude/Codex session transcripts.

Success signal: Imported threads have IDs prefixed with import: (visible in devtools/network/DB as import:claudeAgent:<session-uuid>). The visible sidebar title is the human-readable title from the transcript (e.g. "Evosim open issues survey"), NOT the string import:.

  1. Hard reload / new browser session of PR web client against a server with feat(web): first-run welcome wizard with agent setup and project import pingdotgg/t3code#5362
  2. Open existing project whose workspaceRoot matches agent session cwd on disk
  3. Console shows [auto-reconcile] project "..." (...): imported N, skipped M info line
  4. Without using the Import UI, external sessions appear in the thread list with human-readable titles and message history
  5. Re-open / re-reconcile does not duplicate import: threads (idempotent by design)
  6. Opening a thread does not auto-start a provider turn (threads are created with historyImport: true, no session is started)
  7. Mismatched expectedWorkspaceRoot logs a diagnostic warning to the console and skips safely
  8. Old server (pre-feat(web): first-run welcome wizard with agent setup and project import pingdotgg/t3code#5362) logs a one-time warning and does NOT mark projects reconciled — a server upgrade without remount will retry
  9. No claim of live remote-control attach — this imports transcript history only

Test commands and results

$ npx vp test run apps/web/src/hooks/useAgentSessionAutoReconcile.test.ts
 Test Files  1 passed (1)
      Tests  21 passed (21)

$ npx vp test run apps/server/src/project/AgentSessionImporter.test.ts
 Test Files  1 passed (1)
      Tests  12 passed (12)

$ npx vp test run apps/server/src/project/AgentSessionScanner.test.ts
 Test Files  1 passed (1)
      Tests  79 passed (79)

$ npx vp run --filter web typecheck
~/apps/web$ tsgo --noEmit  (clean)

This change was authored by Cursor (Claude Opus 4.6) via T3 Code Cloud Agent.

Open in Web Open in Cursor 

When environment shells bootstrap, automatically import external Claude
Code and Codex sessions for every known project. This surfaces agent
work already on disk in the thread list without requiring a manual
"Import agent sessions…" action.

The new useAgentSessionAutoReconcile hook reuses the existing idempotent
agentSessions.import RPC. Each project is reconciled once per mount
cycle; failures are silently ignored since a missing agent home is not
actionable for the user.

Refs: pingdotgg#6994, pingdotgg#6680

Co-authored-by: Lewis Marshall <lewismarshall@users.noreply.github.com>
cursoragent and others added 2 commits September 6, 2026 17:18
The original hook swallowed all import failures silently (.catch(() => {})),
making E2E debugging impossible when the server doesn't support the
agentSessions.import RPC (e.g. t3@0.0.38 predates PR pingdotgg#5362).

Changes:
- Add classifyImportFailure() that distinguishes four failure kinds:
  unsupported-server (RpcClientError), interrupted, expected domain
  errors, and unexpected defects.
- Log unsupported-server with a one-time console.warn naming the
  required server version and PR pingdotgg#5362. Skip further import attempts
  for that environment.
- Log expected errors (project not found, workspace mismatch, scan
  error, auth) with console.warn including project context.
- Log unexpected errors with console.error for debugging.
- Log successful imports with console.info when importedCount > 0.
- Add 10 new unit tests for classifyImportFailure covering all
  error classifications.

Co-authored-by: Lewis Marshall <lewismarshall@users.noreply.github.com>
selectUnreconciledProjects no longer eagerly adds keys to the reconciled
set. The hook marks a project reconciled only after:

- A successful import (importedCount + skippedCount returned).
- A definitive domain error (project not found, workspace mismatch,
  scan error, auth error) — these won't resolve without user action.

Transient failures (unsupported-server, unexpected defect, interrupted)
leave the project eligible for retry on the next render cycle. This
fixes the scenario where imports fail against an old server (pre-pingdotgg#5362)
and the project is permanently marked done, preventing retry after a
server upgrade without a full client remount.

Also: console.info now logs for every successful import (even when
importedCount is 0) with projectId, workspaceRoot, and skippedCount
for E2E observability.

Co-authored-by: Lewis Marshall <lewismarshall@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.

2 participants