feat(desktop): device registry, inbox and leased claims for a background executor - #8644
Conversation
|
@cubic-dev-ai review this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
|
|
@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 review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 27 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
e0e3fb7 to
48b2051
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 33 files
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 33 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
…und executor Adds the server half of the desktop background executor's device protocol, behind the mothership-desktop-background-executor flag (off by default), built on the existing tool-execution primitives rather than new ones: - desktop_devices binds an install id to a user and the Better Auth session that registered it; copilot_runs.desktop_device_id records which device a turn's desktop tools run on (nothing sets it yet). - Claims, renewals and results reuse claimDesktopToolCall, renewSimToolExecutionLease and the execution-owner fence. A background executor's claim is the desktop claim plus an execution lease, only on a run bound to that device; a result is accepted after its lease lapsed until something else settles the call. - /api/copilot/confirm and the device's complete share one sealing and settlement function, so a device result is sealed, settled and published exactly like a chat view's. - POST /api/desktop/devices registers; GET /api/desktop/inbox lists calls, approval_needed items (from permission_requested_at) and cancel items; GET /api/desktop/inbox/stream is an SSE doorbell over Redis pub/sub. - Presence is driven by the device: every pull, lease renewal and stream open refreshes a 45 s key, and only its TTL removes it.
1fe23de to
4cebfa2
Compare
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 33 files
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 33 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
Summary
First Phase 1 PR for the desktop background executor. It adds the server half of the protocol a Sim desktop app will use to keep running a chat's desktop tools after the user leaves the chat.
staging. feat(desktop): bind turns to a desktop and enforce its deadlines from the row #8650 follows this PR.mothership-desktop-background-executor. The flag is user-targeted in AppConfig, falls back toMSHIP_DESKTOP_BACKGROUND_EXECUTOR, and is off by default.What is reused, not rewritten
Claim, lease and fence. The device claim is the desktop claim,
claimDesktopToolCall(run-locked throughclaimUnderRunAdmission, so it serializes with Stop), with anexecutor: { deviceId, ownerToken }option. It keeps everything the desktop claim already checks (apendingcall, allowed by the permission gate, under the per-surface claim owner) and adds two things:ownerToken, like Sim's own executions.Renewal is
renewSimToolExecutionLeasewith adesktopoption (the call must still be running on the device's run, so a call Stop settled cannot be renewed). The device's result goes throughmarkAsyncToolStatus's execution-owner fence, which gains a lease mode:lease: 'any'records a late but real result until something else settles the call.One sealing and settlement path. Sealing is fix(mothership): refuse desktop claims for unapproved or stopped calls #8643's
sealClientToolSettlement. The CAS-then-publish step (settleClientToolCall, with a guard that picks the CAS) is extracted from/api/copilot/confirmintorequest/tools/client-settlement.server.ts. Confirm and the device's complete both use it.Approvals. Approvals come from fix(mothership): refuse desktop claims for unapproved or stopped calls #8643's
permission_requested_at: a call isapproval_neededwhile it is requested and undecided, and claimable once allowed. A claim before that answers 403, as/api/desktop/tool/authorizedoes.Stop. Stop is fix(mothership): refuse desktop claims for unapproved or stopped calls #8643's sealed settlement in the Stop transaction. A device result racing it waits on the row lock and is answered
superseded.Classifier. The device's tools are fix(mothership): refuse desktop claims for unapproved or stopped calls #8643's
lib/mothership/tools/desktop-tools.ts, which gains the name list the inbox query narrows on and the executor's claim owner.Transport.
createSSEStream,createPubSubChannel,isFeatureEnabled, application operations and the route builders are used as they are.What is new
0397_desktop_devices, generated with drizzle-kit, expand-only):desktop_devicesbinds an install id to a user and to the Better Auth session that registered it.ON DELETE SET NULLmeans signing out disconnects the device.copilot_runs.desktop_device_idhas an FK addedNOT VALID(every existing row is NULL) and a partial index builtCONCURRENTLY.desktop:presence:<id>(45 s TTL).desktop_tool_call.claimed,desktop_tool_call.completed, resourcedesktop_device) with the tool name, chat and call id, never the arguments or output. A duplicate or superseded result changed nothing and is not audited.Device contract (for Phase 2)
All routes authenticate with the cookie of the session the device registered under. Any other session gets 401.
POST /api/desktop/devices {deviceId(uuid), name, appVersion, platform, capabilities:{executor, browser, terminal, localFiles}}{enabled, protocolVersion, leaseMs:60000, leaseRenewMs:20000, reconcileMs:10000}.enabled:falseis the kill switch. A 409 means the id belongs to another account: generate a new one. Registering from a new session rebinds the device, and the old session gets 401.GET /api/desktop/inbox/stream?deviceIdevent: inbox_changed {reason: call/approval/cancel}. Pull the inbox on each event. Reconnect on close or onrotate.GET /api/desktop/inbox?deviceId{items}. Pull on connect, on each doorbell, on wake, and everyreconcileMs; the pull also keeps the device online. Claimcallitems in list order. Notify onapproval_needed. Stop the local action for eachcancel, then post its result to acknowledge it.POST /api/desktop/tool/claim {deviceId, toolCallId}{toolName, args, chatId, workspaceId, executionToken}. Run the call in the chat's scope (chatId) with the server's args only. 403: not approved (yet). 409: the turn ended or was stopped. 404: no longer waiting for this device. On any 4xx, drop the call.POST /api/desktop/tool/lease {deviceId, toolCallId, executionToken}, everyleaseRenewMs{renewed:true}. A 410 means the call was stopped, settled or lapsed: stop the action.POST /api/desktop/tool/complete {deviceId, toolCallId, executionToken, status, message?, data?}{outcome: recorded / duplicate / superseded, status}. Every 200 acknowledges the outbox entry. Retry a 5xx with backoff.Test plan
lib/desktop/application/executor.integration.ts(real PostgreSQL and Redis), 23 tests. They cover:requestRunStop: claim refused, renewal 410, a cancel item, and a late success answeredsuperseded;duplicate; the claim and the recorded result are audited once each, without the result's content; a late result after a lapsed lease is recorded;superseded;pg_blocking_pids);desktop-tool-authorization.integration.ts(11 tests),async-runs/*.integration.ts, and confirm's unit tests.scripts/test-desktop-inbox-e2e.ts, run against a local Next.js app with PostgreSQL and Redis, passes 10/10 checks over 33 HTTP requests:lint,type-check,check:audits,check:migrations,docs-manifest:check, the block registry check,drizzle-kit generate(no drift), andbun run test(37,229 passed).Notes