Skip to content

Commit b9c1897

Browse files
committed
fix(mothership): accurate stale-observation wording and lease scope notes
- A stale browser observation says it was not run this time, which stays true when an earlier delivery already ran it. - The lifetimes module says what a terminal call leaves running, and that sign-out ends leased tools only. - Tests check responses and recorded outcomes instead of mock calls.
1 parent 1102530 commit b9c1897

5 files changed

Lines changed: 15 additions & 11 deletions

File tree

‎apps/sim/app/api/copilot/confirm/route.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,10 @@ describe('Copilot Confirm API Route', () => {
332332
)
333333

334334
expect(response.status).toBe(409)
335+
expect(await response.json()).toEqual({
336+
error: 'The desktop app holds this tool call; only its own result settles it',
337+
})
335338
expect(recordedOutcome()).toBe(CopilotConfirmOutcome.HeldByDesktop)
336-
expect(completeAsyncToolCall).not.toHaveBeenCalled()
337-
expect(completeClaimedAsyncToolCall).not.toHaveBeenCalled()
338-
expect(publishToolConfirmation).not.toHaveBeenCalled()
339339
})
340340

341341
it('does not publish when another terminal transition wins indeterminate claim reconciliation', async () => {

‎apps/sim/app/workspace/[workspaceId]/home/hooks/desktop-tool-lifetimes.ts‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ interface RunningTurnTools {
88
* Turns with a desktop tool still running in this tab, keyed by the turn's stream id. A tool
99
* outlives the chat view that started it (and any stream reader), so the turn, not the view, owns
1010
* its Stop. A turn is held only while one of its tools runs: each tool releases it as it settles.
11-
* Terminal commands take no lease: Stop ends them through the turn's resource activity, which
12-
* clears the agent's commands in each scope it touched.
11+
* Terminal calls take no lease. A terminal call returns once its operation does (a `run` after
12+
* its wait window), and the command it started keeps running in a terminal tab the user can see
13+
* and control. Stop settles the agent's marks on that tab through the turn's resource activity;
14+
* the process itself is the user's to end.
1315
*/
1416
const runningTurns = new Map<string, RunningTurnTools>()
1517

@@ -52,7 +54,10 @@ export function stopDesktopTools(streamId: string, reason: string): void {
5254
runningTurns.delete(streamId)
5355
}
5456

55-
/** Cancels every running desktop tool in this tab, so none outlives the session that started it. */
57+
/**
58+
* Cancels every leased desktop tool running in this tab (browser actions, local file reads and
59+
* imports), so none outlives the session that started it.
60+
*/
5661
export function stopAllDesktopTools(reason: string): void {
5762
for (const turn of runningTurns.values()) turn.stop.abort(reason)
5863
runningTurns.clear()

‎apps/sim/lib/mothership/tools/client/browser-tool-execution.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1470,7 +1470,7 @@ describe('pre-dispatch drops still resolve the waiter', () => {
14701470
expect(mockReportCompletion).toHaveBeenCalledWith(
14711471
'stale-call-1',
14721472
'error',
1473-
expect.stringContaining('never started'),
1473+
expect.stringContaining('not run this time'),
14741474
expect.objectContaining({ staleEvent: true })
14751475
)
14761476
const [, , message] = mockReportCompletion.mock.calls[0] ?? []

‎apps/sim/lib/mothership/tools/client/browser-tool-execution.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ const OUTCOME_UNKNOWN_MESSAGE =
109109
'The Sim window closed while this browser action was in flight. It may already have taken effect. Do not retry it automatically; take a fresh browser snapshot before deciding what to do.'
110110
const REPLAY_OUTCOME_UNKNOWN_MESSAGE =
111111
'This browser action was recorded before the Sim page reloaded, but its terminal result could not be recovered. It may already have taken effect. Do not retry it automatically; take a fresh browser snapshot before deciding what to do.'
112-
const STALE_OBSERVATION_NOT_STARTED_MESSAGE =
113-
'Not run: this browser observation never started, because it reached the Sim desktop app too late to run safely. Nothing happened in the browser. Do not retry it in this turn; tell the user to keep this chat open in the Sim desktop app, or to ask again later.'
112+
const STALE_OBSERVATION_NOT_RUN_MESSAGE =
113+
'Not run: this browser observation reached the Sim desktop app too late to run safely, so it was not run this time and has no result. An observation changes nothing in the browser. Do not retry it in this turn; tell the user to keep this chat open in the Sim desktop app, or to ask again later.'
114114
const STALE_STATEFUL_OUTCOME_UNKNOWN_MESSAGE =
115115
'This browser action was delivered too late to recover its exact result. It may already have taken effect. Do not retry it automatically; take a fresh browser snapshot before deciding what to do.'
116116
const REPLAY_GUARD_CAPACITY_MESSAGE =
@@ -595,7 +595,7 @@ export async function executeBrowserToolOnClient(
595595
logger.info('Skipping stale browser tool event', { toolCallId, toolName, age })
596596
const observationOnly = OBSERVATION_ONLY_BROWSER_TOOLS[toolName]
597597
const message = observationOnly
598-
? STALE_OBSERVATION_NOT_STARTED_MESSAGE
598+
? STALE_OBSERVATION_NOT_RUN_MESSAGE
599599
: STALE_STATEFUL_OUTCOME_UNKNOWN_MESSAGE
600600
retainAndReportTerminalCompletion(
601601
toolCallId,

‎apps/sim/lib/mothership/tools/client/completion.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ describe('client tool completion reporting', () => {
121121
await expect(
122122
reportClientToolCompletionOnPageExit('tool-1', 'error', 'Browser failed')
123123
).resolves.toBeUndefined()
124-
expect(fetchMock).toHaveBeenCalledOnce()
125124
})
126125

127126
it('rejects a non-success response', async () => {

0 commit comments

Comments
 (0)