Skip to content

fix(chat): keep desktop tools running in chats left mid-turn - #8311

Open
waleedlatif1 wants to merge 3 commits into
stagingfrom
fix/desktop-chat-switch-stops-stream
Open

waleedlatif1 wants to merge 3 commits into
stagingfrom
fix/desktop-chat-switch-stops-stream

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Switching chats in the desktop app stalled the chat you left. Its server run keeps going, but browser, terminal, and local filesystem tools only run in the desktop client, which finds them through the chat's stream. With no reader, the server waits up to an hour on the next such call
  • Leaving a chat mid-turn now hands its desktop tools to a background relay (detached-client-tools.ts). It reads the stream from where the view stopped and starts each call until the run ends or the chat's view reads the stream again
  • The relay lives at module scope, since switching chats remounts the chat surface (<Home key={chatId}>)
  • Like the view's own reconnect, the relay reads each connection as batch-then-tail. Calls that already have a result are settled before any call frame replays, so any cursor is a safe starting point. It learns a new chat's id from the stream and retries with capped backoff until the run ends
  • Only turns the server admitted are detached. A send still awaiting admission keeps the existing withdraw-and-hand-off on unmount
  • When a relay takes the turn, leaving no longer aborts the turn's controller, so a browser action or local file read already in flight finishes and reports instead of being cancelled or dropped
  • Extracted the client-tool start rule (resolveClientToolStart) so the view and the relay classify calls identically. It replaces four copy-pasted blocks in handleToolEvent, with no behavior change
  • Executors own exactly-once. Local filesystem gets an LRUCache guard in its lazily loaded executor, matching the browser and terminal executors. The terminal executor now reports stale calls it never ran instead of dropping them silently
  • Workflow runs are unchanged (the server runs them itself when no client picks them up). Text-only turns were never affected. Web is unaffected (the relay only runs in the desktop app)

Type of Change

  • Bug fix

Testing

  • Reproduced against a local orchestrator: a text-only turn keeps running after its client disconnects, and client-tool calls have no pickup path once the reader is gone
  • Ran the real relay module against live left turns. It resumed exactly after the view's cursor (including cursor 0 on a brand-new chat), received every later event exactly once, and stopped at completion. It survived 6 consecutive failures and recovered. A 401 ends it after one request, and hand-back by stream id stops it
  • Chat stream and client tool suites pass (231 tests). Type-check, lint, and all 51 audits pass

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing (new tests pass the test-audit authoring gate)
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercel Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
docs Skipped Skipped Sep 25, 2026 11:52pm UTC

Request Review

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

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.

All reported issues were addressed across 7 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts Outdated
Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Outdated
Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Outdated
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Keeps desktop tools running when user leaves chat mid-turn.

The PR is not yet safe to merge because a stale replay in another tab can fail a terminal command that is still running.

Findings

  1. P1 Stale replay fails running command ▶
  2. P2 Duplicate filesystem failure reports ▶

Summary

The PR hands desktop-only tools to a background stream relay when a user leaves a chat, then hands stream reading back when the chat reopens. It also shares client-tool start logic and moves filesystem deduplication into its executor. The latest changes add batch timeouts, stream-specific hand-back, and stale-terminal error reporting.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Chat view reads turn] -->|Leave chat| B[Detached stream relay]
  B -->|Tool call| C[Desktop tool executor]
  C -->|Completion| D[Server-side waiter]
  B -->|Reopen and acquire reader| A
Loading

Reviews (3) · Last reviewed commit: "fix(chat): detach only admitted turns an..."

Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Outdated
Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts Outdated
Comment thread apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts Outdated
Comment thread apps/sim/lib/mothership/tools/client/launch-local-filesystem-tool.ts Outdated
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

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

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.

1 issue found across 7 files

Confidence score: 3/5

  • In apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts, a terminal call first seen more than two minutes after emission can be silently dropped, permanently stalling the turn; explicitly settle stale calls as failed.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts:104">
P1: A detached relay can permanently stall a turn when it first sees a terminal call more than two minutes after emission. The terminal executor drops stale events without reporting a failure, so settle stale calls with an error (as the browser executor does) instead of returning silently.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

@waleedlatif1
waleedlatif1 force-pushed the fix/desktop-chat-switch-stops-stream branch from c938242 to b60c919 Compare September 25, 2026 23:52
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cubic-dev-ai review this PR

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review this PR

@waleedlatif1 I have started the AI code review. It will take a few minutes to complete.

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

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.

2 issues found across 8 files

Confidence score: 3/5

  • In apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts, organization-owned detached chats can have user-local filesystem tool events consumed without starting the tool or reporting an error; provide the required workspaceId or handle the event explicitly.
  • In apps/sim/lib/mothership/tools/client/local-filesystem.ts, the LRU can evict an in-flight call ID, allowing a replay to trigger duplicate bridge work; keep active IDs pinned until completion.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/sim/lib/mothership/tools/client/local-filesystem.ts">

<violation number="1" location="apps/sim/lib/mothership/tools/client/local-filesystem.ts:346">
P2: This finite LRU can evict a local-filesystem call while it is still running, defeating the exactly-once guarantee and causing duplicate bridge work when the detached relay or remounted view replays it. Keep active IDs non-evictable, or make eviction completion-aware instead of assuming every evicted call has settled.</violation>
</file>

<file name="apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts">

<violation number="1" location="apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts:106">
P1: Organization-owned detached chats pass no `workspaceId`, but `launchLocalFilesystemTool` refuses user-local filesystem calls without one. The relay then consumes the tool event without starting it or reporting an error, leaving the server run stalled; allow user-local execution without a workspace context or explicitly settle unsupported calls.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

return
case 'localFilesystem':
launchLocalFilesystemTool(toolCallId, toolName, args, {
workspaceId: turn.workspaceId,

@cubic-dev-ai cubic-dev-ai Bot Sep 25, 2026 •

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.

P1: Organization-owned detached chats pass no workspaceId, but launchLocalFilesystemTool refuses user-local filesystem calls without one. The relay then consumes the tool event without starting it or reporting an error, leaving the server run stalled; allow user-local execution without a workspace context or explicitly settle unsupported calls.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/app/workspace/[workspaceId]/home/hooks/stream/detached-client-tools.ts, line 106:

<comment>Organization-owned detached chats pass no `workspaceId`, but `launchLocalFilesystemTool` refuses user-local filesystem calls without one. The relay then consumes the tool event without starting it or reporting an error, leaving the server run stalled; allow user-local execution without a workspace context or explicitly settle unsupported calls.</comment>

<file context>
@@ -0,0 +1,243 @@
+      return
+    case 'localFilesystem':
+      launchLocalFilesystemTool(toolCallId, toolName, args, {
+        workspaceId: turn.workspaceId,
+        chatId,
+      })
</file context>
Fix with cubic

* user leaves can both start it, and a remounted view replays calls still
* running. Bounded: a call evicted behind this many newer ones is long settled.
*/
const executedToolCallIds = new LRUCache<string, true>({ max: 500 })

@cubic-dev-ai cubic-dev-ai Bot Sep 25, 2026 •

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.

P2: This finite LRU can evict a local-filesystem call while it is still running, defeating the exactly-once guarantee and causing duplicate bridge work when the detached relay or remounted view replays it. Keep active IDs non-evictable, or make eviction completion-aware instead of assuming every evicted call has settled.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/sim/lib/mothership/tools/client/local-filesystem.ts, line 346:

<comment>This finite LRU can evict a local-filesystem call while it is still running, defeating the exactly-once guarantee and causing duplicate bridge work when the detached relay or remounted view replays it. Keep active IDs non-evictable, or make eviction completion-aware instead of assuming every evicted call has settled.</comment>

<file context>
@@ -337,12 +338,21 @@ async function execute(
+ * user leaves can both start it, and a remounted view replays calls still
+ * running. Bounded: a call evicted behind this many newer ones is long settled.
+ */
+const executedToolCallIds = new LRUCache<string, true>({ max: 500 })
+
 export function executeLocalFilesystemTool(
</file context>
Fix with cubic

Comment on lines +127 to +131
void reportClientToolCompletion(
toolCallId,
ASYNC_TOOL_CONFIRMATION_STATUS.error,
STALE_EVENT_MESSAGE,
{ error: STALE_EVENT_MESSAGE, staleEvent: true }

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.

P1 Stale replay fails running command If a terminal command is still running after two minutes, opening the same chat in another desktop tab can replay its call there as stale. That tab has no record of executing the command, so this code reports an error. If the error reaches the server before the executing tab reports success, the command is marked failed and its successful result is discarded.

}

import('@/lib/mothership/tools/client/local-filesystem').then(
(m) => m.executeLocalFilesystemTool(toolCallId, toolName, args, context),

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.

P2 Duplicate filesystem failure reports The exactly-once guard now runs only after the executor chunk loads. If the chat view and relay launch the same call and that chunk fails to load, both report the failure. The second report retries against an already-settled call, causing avoidable requests and error logs during a load failure.

This branch was previously deployed

1 inactive deployment
Preview — b60c9194 Deployed Sep 25, 2026 by vercel[bot]
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