Skip to content

Commit 8f57a2e

Browse files
committed
fix(desktop): say why local files are out of reach, and drop non-null assertions in tests
When the app has no usable account storage (signing out, switching account, storage unavailable), a local file call now says so. It no longer tells the model a setting is off and to ask the user to switch it on.
1 parent 6ecaee0 commit 8f57a2e

3 files changed

Lines changed: 41 additions & 7 deletions

File tree

‎apps/desktop/src/main/desktop-executor/executor.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ interface Deferred<T> {
2222
}
2323

2424
function deferred<T>(): Deferred<T> {
25-
let resolve!: (value: T) => void
26-
let reject!: (error: unknown) => void
25+
let resolve: (value: T) => void = () => {}
26+
let reject: (error: unknown) => void = () => {}
2727
const promise = new Promise<T>((res, rej) => {
2828
resolve = res
2929
reject = rej

‎apps/desktop/src/main/desktop-executor/runner.test.ts‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { TerminalToolResponse } from '@sim/terminal-protocol'
22
import { afterEach, describe, expect, it, vi } from 'vitest'
33
import type { ClaimedDesktopCall } from '@/main/desktop-executor/protocol'
4-
import { createDesktopToolRunner } from '@/main/desktop-executor/runner'
4+
import { createDesktopToolRunner, type DesktopToolRunnerDeps } from '@/main/desktop-executor/runner'
55

66
function terminalCall(toolCallId: string, operation: string): ClaimedDesktopCall {
77
return {
@@ -14,7 +14,7 @@ function terminalCall(toolCallId: string, operation: string): ClaimedDesktopCall
1414
}
1515
}
1616

17-
function runnerWithTerminal(executeTool: (toolCallId: string) => Promise<TerminalToolResponse>) {
17+
function runner(overrides: Partial<DesktopToolRunnerDeps> = {}) {
1818
return createDesktopToolRunner({
1919
preferences: () => ({ browserEnabled: true, terminalEnabled: true }),
2020
accountDataAvailable: () => true,
@@ -24,12 +24,19 @@ function runnerWithTerminal(executeTool: (toolCallId: string) => Promise<Termina
2424
hasSession: () => true,
2525
restoreScope: vi.fn(),
2626
},
27+
terminal: { executeTool: vi.fn(), cancelTool: vi.fn(async () => true) },
28+
localFiles: { read: vi.fn() },
29+
localFilesystem: { handle: vi.fn(), vfsRoot: () => 'user-local/x--1' },
30+
...overrides,
31+
})
32+
}
33+
34+
function runnerWithTerminal(executeTool: (toolCallId: string) => Promise<TerminalToolResponse>) {
35+
return runner({
2736
terminal: {
2837
executeTool: (_scope, toolCallId) => executeTool(toolCallId),
2938
cancelTool: vi.fn(async () => true),
3039
},
31-
localFiles: { read: vi.fn() },
32-
localFilesystem: { handle: vi.fn(), vfsRoot: () => 'user-local/x--1' },
3340
})
3441
}
3542

@@ -94,3 +101,23 @@ describe('background terminal calls', () => {
94101
expect(started).toEqual(['wedged'])
95102
})
96103
})
104+
105+
describe('local file calls', () => {
106+
it('names a passing storage state, not a setting, when local files are out of reach', async () => {
107+
const completion = await runner({ accountDataAvailable: () => false }).run(
108+
{
109+
toolCallId: 'read-1',
110+
toolName: 'read_local_file',
111+
args: { path: '~/notes.txt' },
112+
chatId: 'chat-b',
113+
workspaceId: 'ws-1',
114+
executionToken: 'token-read-1',
115+
},
116+
new AbortController().signal
117+
)
118+
119+
expect(completion.data).toMatchObject({ notStarted: true })
120+
expect(completion.message).toContain('cannot reach local files')
121+
expect(completion.message).not.toContain('settings')
122+
})
123+
})

‎apps/desktop/src/main/desktop-executor/runner.ts‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ function surfaceOff(surface: string): DesktopToolCompletion {
5151
return { status: 'error', message, data: { error: message, notStarted: true } }
5252
}
5353

54+
/** Local files are out of reach while the app has no usable account storage, a passing state. */
55+
function localAccessUnavailable(): DesktopToolCompletion {
56+
const message =
57+
'Not run: this action never started, because the Sim desktop app cannot reach local files right now (it is signing out, switching account, or its storage is unavailable). Nothing happened on the user’s computer. Do not retry it in this turn; tell the user, who can ask again once the app is ready.'
58+
return { status: 'error', message, data: { error: message, notStarted: true } }
59+
}
60+
5461
function unsupported(toolName: string): DesktopToolCompletion {
5562
const message = `Not run: this action never started, because this version of the Sim desktop app cannot run ${toolName} in the background. Nothing happened on the user’s computer. Do not retry it in this turn; tell the user to update the Sim desktop app.`
5663
return { status: 'error', message, data: { error: message, notStarted: true } }
@@ -222,7 +229,7 @@ export function createDesktopToolRunner(deps: DesktopToolRunnerDeps): DesktopToo
222229
try {
223230
if (isCurrentBrowserToolName(call.toolName)) return await runBrowser(call, call.toolName)
224231
if (call.toolName === 'terminal') return await runTerminal(call, signal)
225-
if (!deps.accountDataAvailable()) return surfaceOff('local file access')
232+
if (!deps.accountDataAvailable()) return localAccessUnavailable()
226233
if (call.toolName === 'read_local_file') {
227234
return localFileReadCompletion(await deps.localFiles.read(call))
228235
}

0 commit comments

Comments
 (0)