Skip to content

fix desktop terminal attach latency#3847

Merged
Kitenite merged 1 commit into
mainfrom
trpc-throttle-investigati
Apr 29, 2026
Merged

fix desktop terminal attach latency#3847
Kitenite merged 1 commit into
mainfrom
trpc-throttle-investigati

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 28, 2026

Description

Fixes V2 terminal attach latency during workspace switches by removing renderer-side waits on terminal session tRPC before opening terminal WebSockets.

Terminal panes now mount xterm and connect the WebSocket immediately. The WebSocket URL carries workspaceId and themeType, allowing host-service to create or attach the terminal session on open. This avoids tRPC head-of-line blocking when workspace switch work and terminal creation happen at the same time.

V2 presets and pending terminal launches no longer pre-create terminal sessions through tRPC. They add terminal panes with a transient initialCommand; TerminalPane sends that command as the first WebSocket frame after open, and host-service queues it behind the same shell-ready gate used by session creation.

Server-side automation dispatch now uses the explicit terminal.launchSession({ workspaceId, terminalId?, initialCommand, themeType? }) API. The old terminal.ensureSession procedure has been removed.

The terminal session list cache is invalidated from terminal connection state when the WebSocket reaches open, with the dedupe reset when the socket leaves open so reconnects refresh the list again.

Type of Change

  • Bug fix
  • New feature
  • Documentation
  • Refactor
  • Other

Testing

  • bun --filter @superset/desktop typecheck
  • bun --filter @superset/host-service typecheck
  • bun --filter @superset/trpc typecheck
  • bunx @biomejs/biome@2.4.2 check packages/host-service/src/trpc/router/terminal/terminal.ts packages/trpc/src/router/automation/dispatch.ts

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Terminal WebSocket URL now includes workspaceId and resolved themeType. TerminalPane no longer awaits a tRPC session call; it connects immediately via terminalRuntimeRegistry. Registry adds onServerAttach hooks and transport notifies server-attach when a title arrives. Session-list invalidation is deduplicated per (workspaceId, terminalId, instanceId, websocketUrl).

Changes

Cohort / File(s) Summary
TerminalPane (connection & lifecycle)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
Builds WebSocket URL with workspaceId and resolved themeType. Removed workspaceTrpc.terminal.ensureSession flow and related async/error/reconnect logic. Now mounts and connects via terminalRuntimeRegistry immediately; session invalidation moved to registry-driven server-attach/title events and deduplicated by (workspaceId, terminalId, terminalInstanceId, websocketUrl).
Terminal runtime registry
apps/desktop/src/renderer/lib/terminal/terminal-runtime-registry.ts
Added onServerAttach(terminalId, listener, instanceId?) to subscribe to server-attach events per terminal/instance. Uses entry creation helper and returns a remover closure.
Terminal WebSocket transport
apps/desktop/src/renderer/lib/terminal/terminal-ws-transport.ts
Added serverAttachListeners: Set<() => void> to transport, initializes/clears it on create/dispose, and invokes listeners when "title" messages update the terminal title (via notifyServerAttach).

Sequence Diagram(s)

sequenceDiagram
    participant Pane as TerminalPane
    participant Registry as terminalRuntimeRegistry
    participant Transport as TerminalTransport
    participant Server as WebSocket Server
    participant TRPC as workspaceTrpc/cache

    Pane->>Registry: mount(wsConfig)
    Pane->>Registry: connect(websocketUrl with workspaceId & themeType)
    Registry->>Transport: open WebSocket
    Transport->>Server: WebSocket OPEN -> session created
    Server-->>Transport: "title" message (terminal title set)
    Transport-->>Registry: notifyServerAttach()
    Registry-->>Pane: onServerAttach callback (title present)
    Pane->>TRPC: invalidate('terminal.listSessions') [dedup key: workspaceId,terminalId,instanceId,websocketUrl]
    Transport-->>Registry: onStateChange("closed") -> clear dedupe marker
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I hopped to the socket, theme in paw,
sent workspace along without a pause,
the server named the shell and gave a cheer,
listeners woke — the session list drew near,
I munched a byte and twitched my whiskers with applause. 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing desktop terminal attach latency through removal of renderer-side wait on tRPC before WebSocket connection.
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.
Description check ✅ Passed The pull request description includes all required template sections with comprehensive technical details about the changes and testing performed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch trpc-throttle-investigati

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 28, 2026

Greptile Summary

This PR reduces terminal attach latency by eliminating the blocking ensureSession tRPC round-trip before the WebSocket connects. Session creation is now delegated to the server-side WebSocket handler (triggered by workspaceId and themeType query params added to the URL), and the session-list invalidation is decoupled into a 250 ms setTimeout.

Confidence Score: 5/5

Safe to merge; only P2 style findings remain.

The core change is correct — removing the tRPC blocking call and moving session creation to the WS handler. Both remaining findings are P2: a stale comment and a heuristic delay for cache invalidation. Neither affects correctness or reliability in a breaking way.

No files require special attention.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx Removes the blocking ensureSession tRPC call before WebSocket connect, moves session creation to the server-side WebSocket handler via URL params; two minor P2 issues: a stale comment and a heuristic 250 ms invalidation timer.

Sequence Diagram

sequenceDiagram
    participant R as React (TerminalPane)
    participant WS as WebSocket (server)
    participant DB as Session Store

    note over R,DB: Before (slow path)
    R->>WS: tRPC ensureSession (HTTP)
    WS-->>DB: create/verify session
    DB-->>WS: ok
    WS-->>R: {status: active}
    R->>WS: connect() WebSocket
    R->>DB: invalidateTerminalSessions

    note over R,DB: After (fast path)
    R->>WS: connect() WebSocket (workspaceId + themeType in URL)
    WS->>DB: create/verify session on open
    R->>DB: invalidateTerminalSessions (250 ms timer, fire-and-forget)
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx, line 158-162 (link)

    P2 Stale comment references removed ensureSession

    The reconnect effect's comment still says "we let the ensureSession path above open it", but ensureSession was deleted in this PR. The comment now describes a non-existent code path and will mislead future readers.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
    Line: 158-162
    
    Comment:
    **Stale comment references removed `ensureSession`**
    
    The reconnect effect's comment still says _"we let the `ensureSession` path above open it"_, but `ensureSession` was deleted in this PR. The comment now describes a non-existent code path and will mislead future readers.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
Line: 158-162

Comment:
**Stale comment references removed `ensureSession`**

The reconnect effect's comment still says _"we let the `ensureSession` path above open it"_, but `ensureSession` was deleted in this PR. The comment now describes a non-existent code path and will mislead future readers.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
Line: 146-150

Comment:
**Magic 250 ms heuristic for session-list invalidation**

The old code called `invalidateTerminalSessions` only after `ensureSession` confirmed the session was `"active"`, so the cache was refreshed at the right moment. The 250 ms timer fires unconditionally — on a slow connection the WebSocket handshake may not have completed yet, leaving the session list stale until the next automatic refetch cycle. Consider invalidating inside a WebSocket `onOpen` / connection-state callback instead so timing is driven by the actual event rather than an arbitrary delay.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix desktop terminal attach latency" | Re-trigger Greptile

Comment on lines +146 to +150
const invalidateTimer = window.setTimeout(() => {
void invalidateTerminalSessionsRef.current({
workspaceId: sessionWorkspaceId,
themeType: initialThemeTypeRef.current,
})
.then((result) => {
if (result.status === "active") {
void invalidateTerminalSessionsRef.current({
workspaceId: sessionWorkspaceId,
});
return;
}
if (cancelled) return;
const details = result.error
? `: ${result.error}`
: " for an unknown reason";
terminalRuntimeRegistry
.getTerminal(terminalId, terminalInstanceId)
?.writeln(
`\r\n[terminal] Failed to create terminal session${details}`,
);
})
.catch((err) => {
console.error("[TerminalPane] ensureSession failed:", err);
if (cancelled) return;
const message = err instanceof Error ? err.message : String(err);
terminalRuntimeRegistry
.getTerminal(terminalId, terminalInstanceId)
?.writeln(
`\r\n[terminal] terminal.ensureSession request failed: ${message}`,
);
})
.finally(() => {
if (cancelled) return;
terminalRuntimeRegistry.connect(
terminalId,
websocketUrlRef.current,
terminalInstanceId,
);
});
}, 250);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Magic 250 ms heuristic for session-list invalidation

The old code called invalidateTerminalSessions only after ensureSession confirmed the session was "active", so the cache was refreshed at the right moment. The 250 ms timer fires unconditionally — on a slow connection the WebSocket handshake may not have completed yet, leaving the session list stale until the next automatic refetch cycle. Consider invalidating inside a WebSocket onOpen / connection-state callback instead so timing is driven by the actual event rather than an arbitrary delay.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
Line: 146-150

Comment:
**Magic 250 ms heuristic for session-list invalidation**

The old code called `invalidateTerminalSessions` only after `ensureSession` confirmed the session was `"active"`, so the cache was refreshed at the right moment. The 250 ms timer fires unconditionally — on a slow connection the WebSocket handshake may not have completed yet, leaving the session list stale until the next automatic refetch cycle. Consider invalidating inside a WebSocket `onOpen` / connection-state callback instead so timing is driven by the actual event rather than an arbitrary delay.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx (1)

146-150: Consider extracting the 250ms delay as a named constant.

The magic number could be clearer with a descriptive constant name explaining its purpose (e.g., SESSION_CACHE_INVALIDATION_DELAY_MS). This is a minor readability nit.

💡 Optional refactor
+const SESSION_CACHE_INVALIDATION_DELAY_MS = 250;
+
 // ... in the effect:
-const invalidateTimer = window.setTimeout(() => {
+const invalidateTimer = window.setTimeout(() => {
   void invalidateTerminalSessionsRef.current({
     workspaceId: sessionWorkspaceId,
   });
-}, 250);
+}, SESSION_CACHE_INVALIDATION_DELAY_MS);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx
around lines 146 - 150, Extract the magic number 250ms into a clearly named
constant (e.g., SESSION_CACHE_INVALIDATION_DELAY_MS) and replace the inline
literal in the window.setTimeout call inside TerminalPane (the invalidateTimer
setup that calls invalidateTerminalSessionsRef.current with workspaceId). Define
the constant near the top of this module or component so its purpose is clear
and reuseable, and keep the existing behavior (250) as the constant value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx:
- Around line 146-150: Extract the magic number 250ms into a clearly named
constant (e.g., SESSION_CACHE_INVALIDATION_DELAY_MS) and replace the inline
literal in the window.setTimeout call inside TerminalPane (the invalidateTimer
setup that calls invalidateTerminalSessionsRef.current with workspaceId). Define
the constant near the top of this module or component so its purpose is clear
and reuseable, and keep the existing behavior (250) as the constant value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c84091ab-4c8e-4311-9113-4a3827b52059

📥 Commits

Reviewing files that changed from the base of the PR and between e7c785a and 4304a21.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

No issues found across 1 file

@Kitenite Kitenite force-pushed the trpc-throttle-investigati branch 2 times, most recently from c255c0e to e8fb4bc Compare April 29, 2026 00:19
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/`$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx:
- Around line 150-178: The dedupe key stored in lastInvalidatedOpenSessionRef
never gets cleared, so after a close->open cycle invalidateIfOpen (used with
terminalRuntimeRegistry.onStateChange) will short-circuit forever; modify the
state-change handling so that when
terminalRuntimeRegistry.getConnectionState(...) reports a non-"open" state
(e.g., "closed"), you clear lastInvalidatedOpenSessionRef.current (or set it to
null) so the next open will regenerate the invalidateKey and call
invalidateTerminalSessionsRef.current; update the callback registered with
terminalRuntimeRegistry.onStateChange (used in the effect) to clear the ref on
close and keep the existing invalidateIfOpen behavior on open.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b149196b-1729-466e-a8f5-f27686c956d4

📥 Commits

Reviewing files that changed from the base of the PR and between 4304a21 and c255c0e.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/hooks/usePaneRegistry/components/TerminalPane/TerminalPane.tsx

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@Kitenite Kitenite merged commit 69ca191 into main Apr 29, 2026
12 of 13 checks passed
@Kitenite Kitenite deleted the trpc-throttle-investigati branch April 29, 2026 04:31
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