Skip to content

Commit 2f8c689

Browse files
committed
fix(desktop): reach every overdue desktop call, and declare the desktop tool names as const
A 24 h horizon on the lapsed deadline meant a call the backstop missed for a day was never settled. The sweep starts from the few unsettled (pending or running) calls in persistence order, so it needs no horizon to stay small. The desktop tool names are a literal array declared as const, and the lookup set is derived from it.
1 parent cb3ff96 commit 2f8c689

3 files changed

Lines changed: 31 additions & 11 deletions

File tree

‎apps/sim/lib/desktop/executor/bound-turn.integration.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,27 @@ describe.runIf(Boolean(redisUrl))("a turn bound to a desktop's background execut
794794
TURN_WAIT_MS
795795
)
796796

797+
it(
798+
'still settles a call that stayed overdue for days, however long the backstop missed it',
799+
async () => {
800+
const desktop = await signedInDesktop()
801+
const run = await boundRun(desktop)
802+
const toolCallId = generateId()
803+
await db.insert(copilotAsyncToolCalls).values({
804+
runId: run.runId,
805+
toolCallId,
806+
toolName: 'browser_click',
807+
args: { ref: 'e1' },
808+
createdAt: new Date(Date.now() - 2 * 24 * 3_600_000),
809+
})
810+
811+
await runCleanupStaleExecutions()
812+
813+
expect((await storedCall(toolCallId)).status).toBe('failed')
814+
},
815+
TURN_WAIT_MS
816+
)
817+
797818
it(
798819
'settles a call whose window lapsed recently on a run that started long ago',
799820
async () => {

‎apps/sim/lib/desktop/executor/repository.ts‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,12 @@ export type DesktopToolCallDeadlines = NonNullable<
422422
/**
423423
* Bound desktop calls a deadline passed for at least `slackMs` ago: unclaimed past their pickup
424424
* deadline (offered or not), or claimed by the executor with a lapsed lease. A live waiter settles
425-
* these within its 5 s poll, so anything this finds lost its waiter. Only deadlines that lapsed
426-
* within the inbox's horizon are scanned, whatever the run's age, oldest calls first.
425+
* these within its 5 s poll, so anything this finds lost its waiter. The scan starts from the
426+
* few unsettled calls (pending or running), in persistence order, so however long a call stayed
427+
* overdue it is still reached.
427428
*/
428429
export async function listOverdueDesktopToolCalls(input: { slackMs: number; limit: number }) {
429430
const overdue = sql`clock_timestamp() - ${input.slackMs} * interval '1 millisecond'`
430-
const horizon = sql`clock_timestamp() - make_interval(hours => ${DESKTOP_INBOX_HORIZON_HOURS})`
431431
const rows = await db
432432
.select({ toolCallId: copilotAsyncToolCalls.toolCallId })
433433
.from(copilotAsyncToolCalls)
@@ -437,14 +437,13 @@ export async function listOverdueDesktopToolCalls(input: { slackMs: number; limi
437437
isNotNull(copilotRuns.desktopDeviceId),
438438
isDesktopToolCallRow,
439439
or(
440-
and(pickupOverdueAt(overdue), sql`${pickupDeadline} > ${horizon}`),
440+
pickupOverdueAt(overdue),
441441
and(
442442
eq(copilotAsyncToolCalls.status, ASYNC_TOOL_STATUS.running),
443443
inArray(copilotAsyncToolCalls.claimedBy, Object.values(DESKTOP_TOOL_CLAIM_OWNER)),
444444
isNotNull(copilotAsyncToolCalls.executionOwnerToken),
445445
isNull(copilotAsyncToolCalls.executionRevokedAt),
446-
sql`${copilotAsyncToolCalls.executionLeaseExpiresAt} < ${overdue}`,
447-
sql`${copilotAsyncToolCalls.executionLeaseExpiresAt} > ${horizon}`
446+
sql`${copilotAsyncToolCalls.executionLeaseExpiresAt} < ${overdue}`
448447
)
449448
)
450449
)

‎apps/sim/lib/mothership/tools/desktop-tools.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ import { isUserLocalVfsToolCall } from '@/lib/mothership/tools/local-filesystem'
1515
export type DesktopToolClaimOwner =
1616
(typeof DESKTOP_TOOL_CLAIM_OWNER)[keyof typeof DESKTOP_TOOL_CLAIM_OWNER]
1717

18-
const DESKTOP_TOOL_NAMES: ReadonlySet<string> = new Set([
18+
/** The tools that run through the desktop app by name alone; local VFS reads also depend on args. */
19+
export const NAMED_DESKTOP_TOOL_NAMES = [
1920
...CURRENT_BROWSER_TOOL_NAMES,
2021
TERMINAL_TOOL_NAME,
2122
'import_local_files',
2223
'read_local_file',
23-
])
24+
] as const
25+
26+
const DESKTOP_TOOL_NAMES: ReadonlySet<string> = new Set(NAMED_DESKTOP_TOOL_NAMES)
2427

2528
/** Whether a call runs through the desktop app, including VFS reads of a granted local folder. */
2629
export function isDesktopToolCall(toolName: string, args: Record<string, unknown> | undefined) {
@@ -68,9 +71,6 @@ export function isClaimedOnPickup(
6871
)
6972
}
7073

71-
/** The tools that run through the desktop app by name alone; local VFS reads also depend on args. */
72-
export const NAMED_DESKTOP_TOOL_NAMES: readonly string[] = [...DESKTOP_TOOL_NAMES]
73-
7474
/**
7575
* The claim owner a desktop background executor records. It is the per-surface owner the chat
7676
* view's claim records, so file transfer and download admission recognize it, with local reads

0 commit comments

Comments
 (0)