gclm-code 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/gc.js +53 -25
- package/bin/install-runtime.js +253 -0
- package/package.json +10 -5
- package/vendor/manifest.json +92 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/package.json +9 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/bridgeClient.ts +1126 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/browserTools.ts +546 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/index.ts +15 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpServer.ts +96 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpSocketClient.ts +493 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/mcpSocketPool.ts +327 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/toolCalls.ts +301 -0
- package/vendor/modules/node_modules/@ant/claude-for-chrome-mcp/src/types.ts +134 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/package.json +9 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/driver-jxa.js +341 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/driver-swift.swift +417 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/implementation.js +204 -0
- package/vendor/modules/node_modules/@ant/computer-use-input/src/index.js +5 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/package.json +11 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/deniedApps.ts +553 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/imageResize.ts +108 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/index.ts +69 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/keyBlocklist.ts +153 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/mcpServer.ts +313 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/pixelCompare.ts +171 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/sentinelApps.ts +43 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/subGates.ts +19 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/toolCalls.ts +3872 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/tools.ts +706 -0
- package/vendor/modules/node_modules/@ant/computer-use-mcp/src/types.ts +635 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/package.json +9 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/driver-jxa.js +108 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/implementation.js +706 -0
- package/vendor/modules/node_modules/@ant/computer-use-swift/src/index.js +7 -0
- package/vendor/modules/node_modules/audio-capture-napi/package.json +8 -0
- package/vendor/modules/node_modules/audio-capture-napi/src/index.ts +226 -0
- package/vendor/modules/node_modules/image-processor-napi/package.json +11 -0
- package/vendor/modules/node_modules/image-processor-napi/src/index.ts +396 -0
- package/vendor/modules/node_modules/modifiers-napi/package.json +8 -0
- package/vendor/modules/node_modules/modifiers-napi/src/index.ts +79 -0
- package/vendor/modules/node_modules/url-handler-napi/package.json +8 -0
- package/vendor/modules/node_modules/url-handler-napi/src/index.ts +62 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ComputerExecutor,
|
|
3
|
+
InstalledApp,
|
|
4
|
+
ScreenshotResult,
|
|
5
|
+
} from "./executor.js";
|
|
6
|
+
|
|
7
|
+
/** `ScreenshotResult` without the base64 blob. The shape hosts persist for
|
|
8
|
+
* cross-respawn `scaleCoord` survival. */
|
|
9
|
+
export type ScreenshotDims = Omit<ScreenshotResult, "base64">;
|
|
10
|
+
|
|
11
|
+
/** Shape mirrors claude-for-chrome-mcp/src/types.ts:1-7 */
|
|
12
|
+
export interface Logger {
|
|
13
|
+
info: (message: string, ...args: unknown[]) => void;
|
|
14
|
+
error: (message: string, ...args: unknown[]) => void;
|
|
15
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
16
|
+
debug: (message: string, ...args: unknown[]) => void;
|
|
17
|
+
silly: (message: string, ...args: unknown[]) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Per-app permission tier. Hardcoded by category at grant time — the
|
|
22
|
+
* approval dialog displays the tier but the user cannot change it (for now).
|
|
23
|
+
*
|
|
24
|
+
* - `"read"` — visible in screenshots, NO interaction (no clicks, no typing).
|
|
25
|
+
* Browsers land here: the model can read a page that's already open, but
|
|
26
|
+
* must use the Claude-in-Chrome MCP for any navigation/clicking. Trading
|
|
27
|
+
* platforms land here too (no CiC alternative — the model asks the user).
|
|
28
|
+
* - `"click"` — visible + plain left-click, scroll. NO typing/keys,
|
|
29
|
+
* NO right/middle-click, NO modifier-clicks, NO drag-drop (all text-
|
|
30
|
+
* injection vectors). Terminals/IDEs land here: the model can click a
|
|
31
|
+
* Run button or scroll test output, but `type("rm -rf /")` is blocked
|
|
32
|
+
* and so is right-click→Paste and dragging text onto the terminal.
|
|
33
|
+
* - `"full"` — visible + click + type/key/paste. Everything else.
|
|
34
|
+
*
|
|
35
|
+
* Enforced in `runInputActionGates` via the frontmost-app check: keyboard
|
|
36
|
+
* actions require `"full"`, mouse actions require `"click"` or higher.
|
|
37
|
+
*/
|
|
38
|
+
export type CuAppPermTier = "read" | "click" | "full";
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A single app the user has approved for the current session. Session-scoped
|
|
42
|
+
* only — there is no "once" or "forever" scope (unlike Chrome's per-domain
|
|
43
|
+
* three-way). CU has no natural "once" unit; one task = hundreds of clicks.
|
|
44
|
+
* Mirrors how `chromeAllowedDomains` is a plain `string[]` with no per-item
|
|
45
|
+
* scope.
|
|
46
|
+
*/
|
|
47
|
+
export interface AppGrant {
|
|
48
|
+
bundleId: string;
|
|
49
|
+
displayName: string;
|
|
50
|
+
/** Epoch ms. For Settings-page display ("Granted 3m ago"). */
|
|
51
|
+
grantedAt: number;
|
|
52
|
+
/** Undefined → `"full"` (back-compat for pre-tier grants persisted in
|
|
53
|
+
* session state). */
|
|
54
|
+
tier?: CuAppPermTier;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Orthogonal to the app allowlist. */
|
|
58
|
+
export interface CuGrantFlags {
|
|
59
|
+
clipboardRead: boolean;
|
|
60
|
+
clipboardWrite: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* When false, the `key` tool rejects combos in `keyBlocklist.ts`
|
|
63
|
+
* (cmd+q, cmd+tab, cmd+space, cmd+shift+q, ctrl+alt+delete). All other
|
|
64
|
+
* key sequences work regardless.
|
|
65
|
+
*/
|
|
66
|
+
systemKeyCombos: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const DEFAULT_GRANT_FLAGS: CuGrantFlags = {
|
|
70
|
+
clipboardRead: false,
|
|
71
|
+
clipboardWrite: false,
|
|
72
|
+
systemKeyCombos: false,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Host picks via GrowthBook JSON feature `chicago_coordinate_mode`, baked
|
|
77
|
+
* into tool param descriptions at server-construction time. The model sees
|
|
78
|
+
* ONE convention and never learns the other exists. `normalized_0_100`
|
|
79
|
+
* sidesteps the Retina scaleFactor bug class entirely.
|
|
80
|
+
*/
|
|
81
|
+
export type CoordinateMode = "pixels" | "normalized_0_100";
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Independent kill switches for subtle/risky ported behaviors. Read from
|
|
85
|
+
* GrowthBook by the host adapter, consulted in `toolCalls.ts`.
|
|
86
|
+
*/
|
|
87
|
+
export interface CuSubGates {
|
|
88
|
+
/** 9×9 exact-byte staleness guard before click. */
|
|
89
|
+
pixelValidation: boolean;
|
|
90
|
+
/** Route `type("foo\nbar")` through clipboard instead of keystroke-by-keystroke. */
|
|
91
|
+
clipboardPasteMultiline: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Ease-out-cubic mouse glide at 60fps, distance-proportional duration
|
|
94
|
+
* (2000 px/sec, capped at 0.5s). Adds up to ~0.5s latency
|
|
95
|
+
* per click. When off, cursor teleports instantly.
|
|
96
|
+
*/
|
|
97
|
+
mouseAnimation: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Pre-action sequence: hide non-allowlisted apps, then defocus us (from the
|
|
100
|
+
* Vercept acquisition). When off, the
|
|
101
|
+
* frontmost gate fires in the normal case and the model gets stuck — this
|
|
102
|
+
* is the A/B-test-the-old-broken-behavior switch.
|
|
103
|
+
*/
|
|
104
|
+
hideBeforeAction: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Auto-resolve the target display before each screenshot when the
|
|
107
|
+
* selected display has no allowed-app windows. When on, `handleScreenshot`
|
|
108
|
+
* uses the atomic backend path; off → sticks with `selectedDisplayId`.
|
|
109
|
+
*/
|
|
110
|
+
autoTargetDisplay: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Stash+clear the clipboard while a tier-"click" app is frontmost.
|
|
113
|
+
* Closes the gap where a click-tier terminal/IDE has a UI Paste button
|
|
114
|
+
* that's plain-left-clickable — without this, the tier "click"
|
|
115
|
+
* keyboard block can be routed around by clicking Paste. Restored when
|
|
116
|
+
* a non-"click" app becomes frontmost, or at turn end.
|
|
117
|
+
*/
|
|
118
|
+
clipboardGuard: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ----------------------------------------------------------------------------
|
|
122
|
+
// Permission request/response (mirror of BridgePermissionRequest, types.ts:77-94)
|
|
123
|
+
// ----------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
/** One entry per app the model asked for, after name → bundle ID resolution. */
|
|
126
|
+
export interface ResolvedAppRequest {
|
|
127
|
+
/** What the model asked for (e.g. "Slack", "com.tinyspeck.slackmacgap"). */
|
|
128
|
+
requestedName: string;
|
|
129
|
+
/** The resolved InstalledApp if found, else undefined (shown greyed in the UI). */
|
|
130
|
+
resolved?: InstalledApp;
|
|
131
|
+
/** Shell-access-equivalent bundle IDs get a UI warning. See sentinelApps.ts. */
|
|
132
|
+
isSentinel: boolean;
|
|
133
|
+
/** Already in the allowlist → skip the checkbox, return in `granted` immediately. */
|
|
134
|
+
alreadyGranted: boolean;
|
|
135
|
+
/** Hardcoded tier for this app (browser→"read", terminal→"click", else "full").
|
|
136
|
+
* The dialog displays this read-only; the renderer passes it through
|
|
137
|
+
* verbatim in the AppGrant. */
|
|
138
|
+
proposedTier: CuAppPermTier;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Payload for the renderer approval dialog. Rides through the existing
|
|
143
|
+
* `ToolPermissionRequest.input: unknown` field
|
|
144
|
+
* (packages/utils/desktop/bridge/common/claude.web.ts:1262) — no IPC schema
|
|
145
|
+
* change needed.
|
|
146
|
+
*/
|
|
147
|
+
export interface CuPermissionRequest {
|
|
148
|
+
requestId: string;
|
|
149
|
+
/** Model-provided reason string. Shown prominently in the approval UI. */
|
|
150
|
+
reason: string;
|
|
151
|
+
apps: ResolvedAppRequest[];
|
|
152
|
+
/** What the model asked for. User can toggle independently of apps. */
|
|
153
|
+
requestedFlags: Partial<CuGrantFlags>;
|
|
154
|
+
/**
|
|
155
|
+
* For the "On Windows, Claude can see all apps..." footnote. Taken from
|
|
156
|
+
* `executor.capabilities.screenshotFiltering` so the renderer doesn't
|
|
157
|
+
* need to know about platforms.
|
|
158
|
+
*/
|
|
159
|
+
screenshotFiltering: "native" | "none";
|
|
160
|
+
/**
|
|
161
|
+
* Present only when TCC permissions are NOT yet granted. When present,
|
|
162
|
+
* the renderer shows a TCC toggle panel (two rows: Accessibility, Screen
|
|
163
|
+
* Recording) INSTEAD OF the app list. Clicking a row's "Request" button
|
|
164
|
+
* triggers the OS prompt; the store polls on window-focus and flips the
|
|
165
|
+
* toggle when the grant is detected. macOS itself prompts the user to
|
|
166
|
+
* restart after granting Screen Recording — we don't.
|
|
167
|
+
*/
|
|
168
|
+
tccState?: {
|
|
169
|
+
accessibility: boolean;
|
|
170
|
+
screenRecording: boolean;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Apps with windows on the CU display that aren't in the requested
|
|
174
|
+
* allowlist. These will be hidden the first time Claude takes an action.
|
|
175
|
+
* Computed at request_access time — may be slightly stale by the time the
|
|
176
|
+
* user clicks Allow, but it's a preview, not a contract. Absent when
|
|
177
|
+
* empty so the renderer can skip the section cleanly.
|
|
178
|
+
*/
|
|
179
|
+
willHide?: Array<{ bundleId: string; displayName: string }>;
|
|
180
|
+
/**
|
|
181
|
+
* `chicagoAutoUnhide` app preference at request time. The renderer picks
|
|
182
|
+
* between "...then restored when Claude is done" and "...will be hidden"
|
|
183
|
+
* copy. Absent when `willHide` is absent (same condition).
|
|
184
|
+
*/
|
|
185
|
+
autoUnhideEnabled?: boolean;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* What the renderer stuffs into `updatedInput._cuGrants` when the user clicks
|
|
190
|
+
* "Allow for this session" (mirror of the `_allowAllSites` sentinel at
|
|
191
|
+
* LocalAgentModeSessionManager.ts:2794).
|
|
192
|
+
*/
|
|
193
|
+
export interface CuPermissionResponse {
|
|
194
|
+
granted: AppGrant[];
|
|
195
|
+
/** Bundle IDs the user unchecked, or apps that weren't installed. */
|
|
196
|
+
denied: Array<{ bundleId: string; reason: "user_denied" | "not_installed" }>;
|
|
197
|
+
flags: CuGrantFlags;
|
|
198
|
+
/**
|
|
199
|
+
* Whether the user clicked Allow in THIS dialog. Only set by the
|
|
200
|
+
* teach-mode handler — regular request_access doesn't need it (the
|
|
201
|
+
* session manager's `result.behavior` gates the merge there). Needed
|
|
202
|
+
* because when all requested apps are already granted (skipDialogGrants
|
|
203
|
+
* non-empty, needDialog empty), Allow and Deny produce identical
|
|
204
|
+
* `{granted:[], denied:[]}` payloads and the tool handler can't tell
|
|
205
|
+
* them apart without this. Undefined → legacy/regular path, do not
|
|
206
|
+
* gate on it.
|
|
207
|
+
*/
|
|
208
|
+
userConsented?: boolean;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ----------------------------------------------------------------------------
|
|
212
|
+
// Host adapter (mirror of ClaudeForChromeContext, types.ts:33-62)
|
|
213
|
+
// ----------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Process-lifetime singleton dependencies. Everything that does NOT vary per
|
|
217
|
+
* tool call. Built once by `apps/desktop/src/main/nest-only/chicago/hostAdapter.ts`.
|
|
218
|
+
* No Electron imports in this package — the host injects everything.
|
|
219
|
+
*/
|
|
220
|
+
export interface ComputerUseHostAdapter {
|
|
221
|
+
serverName: string;
|
|
222
|
+
logger: Logger;
|
|
223
|
+
executor: ComputerExecutor;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Capability-scoped TCC check on macOS. Callers specify which permissions the
|
|
227
|
+
* current tool path actually needs; hosts only request the missing subset
|
|
228
|
+
* when `requestMissing` is left enabled. `request_access` /
|
|
229
|
+
* `request_teach_access` call this with both flags set and
|
|
230
|
+
* `requestMissing:false` so the renderer can show the current state without
|
|
231
|
+
* triggering fresh OS prompts.
|
|
232
|
+
*/
|
|
233
|
+
ensureOsPermissions(
|
|
234
|
+
required?: CuOsPermissionRequirements,
|
|
235
|
+
options?: { requestMissing?: boolean },
|
|
236
|
+
): Promise<CuOsPermissionState>;
|
|
237
|
+
|
|
238
|
+
/** The Settings-page kill switch (`chicagoEnabled` app preference). */
|
|
239
|
+
isDisabled(): boolean;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The `chicagoAutoUnhide` app preference. Consumed by `buildAccessRequest`
|
|
243
|
+
* to populate `CuPermissionRequest.autoUnhideEnabled` so the renderer's
|
|
244
|
+
* "will be hidden" copy can say "then restored" only when true.
|
|
245
|
+
*/
|
|
246
|
+
getAutoUnhideEnabled(): boolean;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Sub-gates re-read on every tool call so GrowthBook flips take effect
|
|
250
|
+
* mid-session without restart.
|
|
251
|
+
*/
|
|
252
|
+
getSubGates(): CuSubGates;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* JPEG decode + crop + raw pixel bytes, for the PixelCompare staleness guard.
|
|
256
|
+
* Injected so this package stays Electron-free. The host implements it via
|
|
257
|
+
* `nativeImage.createFromBuffer(jpeg).crop(rect).toBitmap()` — Chromium's
|
|
258
|
+
* decoders, BSD-licensed, no `.node` binary.
|
|
259
|
+
*
|
|
260
|
+
* Returns null on decode/crop failure — caller treats null as `skipped`,
|
|
261
|
+
* click proceeds (validation failure must never block the action).
|
|
262
|
+
*/
|
|
263
|
+
cropRawPatch(
|
|
264
|
+
jpegBase64: string,
|
|
265
|
+
rect: { x: number; y: number; width: number; height: number },
|
|
266
|
+
): Buffer | null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface CuOsPermissionRequirements {
|
|
270
|
+
accessibility?: boolean;
|
|
271
|
+
screenRecording?: boolean;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export interface CuOsPermissionState {
|
|
275
|
+
granted: boolean;
|
|
276
|
+
accessibility: boolean;
|
|
277
|
+
screenRecording: boolean;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ----------------------------------------------------------------------------
|
|
281
|
+
// Session context (getter/callback bag for bindSessionContext)
|
|
282
|
+
// ----------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Per-session state binding for `bindSessionContext`. Hosts build this once
|
|
286
|
+
* per session with getters that read fresh from their session store and
|
|
287
|
+
* callbacks that write back. The returned dispatcher builds
|
|
288
|
+
* `ComputerUseOverrides` from these getters on every call.
|
|
289
|
+
*
|
|
290
|
+
* Callbacks must be set at construction time — `bindSessionContext` reads
|
|
291
|
+
* them once at bind, not per call.
|
|
292
|
+
*
|
|
293
|
+
* The lock hooks are **async** — `bindSessionContext` awaits them before
|
|
294
|
+
* `handleToolCall`, then passes `checkCuLock: undefined` in overrides so the
|
|
295
|
+
* sync Gate-3 in `handleToolCall` no-ops. Hosts with in-memory sync locks
|
|
296
|
+
* (Cowork) wrap them trivially; hosts with cross-process locks (the CLI's
|
|
297
|
+
* O_EXCL file) call the real async primitive directly.
|
|
298
|
+
*/
|
|
299
|
+
export interface ComputerUseSessionContext {
|
|
300
|
+
// ── Read state fresh per call ──────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
getAllowedApps(): readonly AppGrant[];
|
|
303
|
+
getGrantFlags(): CuGrantFlags;
|
|
304
|
+
/** Per-user auto-deny list (Settings page). Empty array = none. */
|
|
305
|
+
getUserDeniedBundleIds(): readonly string[];
|
|
306
|
+
getSelectedDisplayId(): number | undefined;
|
|
307
|
+
getDisplayPinnedByModel?(): boolean;
|
|
308
|
+
getDisplayResolvedForApps?(): string | undefined;
|
|
309
|
+
getTeachModeActive?(): boolean;
|
|
310
|
+
/** Dims-only fallback when `lastScreenshot` is unset (cross-respawn).
|
|
311
|
+
* `bindSessionContext` reconstructs `{...dims, base64: ""}` so scaleCoord
|
|
312
|
+
* works and pixelCompare correctly skips. */
|
|
313
|
+
getLastScreenshotDims?(): ScreenshotDims | undefined;
|
|
314
|
+
|
|
315
|
+
// ── Write-back callbacks ───────────────────────────────────────────
|
|
316
|
+
|
|
317
|
+
/** Shows the approval dialog. Host routes to its UI, awaits user. The
|
|
318
|
+
* signal is aborted if the tool call finishes before the user answers
|
|
319
|
+
* (MCP timeout, etc.) — hosts dismiss the dialog on abort. */
|
|
320
|
+
onPermissionRequest?(
|
|
321
|
+
req: CuPermissionRequest,
|
|
322
|
+
signal: AbortSignal,
|
|
323
|
+
): Promise<CuPermissionResponse>;
|
|
324
|
+
/** Teach-mode sibling of `onPermissionRequest`. */
|
|
325
|
+
onTeachPermissionRequest?(
|
|
326
|
+
req: CuTeachPermissionRequest,
|
|
327
|
+
signal: AbortSignal,
|
|
328
|
+
): Promise<CuPermissionResponse>;
|
|
329
|
+
/** Called by `bindSessionContext` after merging a permission response into
|
|
330
|
+
* the allowlist (dedupe on bundleId, truthy-only flag spread). Host
|
|
331
|
+
* persists for resume survival. */
|
|
332
|
+
onAllowedAppsChanged?(apps: readonly AppGrant[], flags: CuGrantFlags): void;
|
|
333
|
+
onAppsHidden?(bundleIds: string[]): void;
|
|
334
|
+
/** Reads the session's clipboardGuard stash. undefined → no stash held. */
|
|
335
|
+
getClipboardStash?(): string | undefined;
|
|
336
|
+
/** Writes the clipboardGuard stash. undefined clears it. */
|
|
337
|
+
onClipboardStashChanged?(stash: string | undefined): void;
|
|
338
|
+
onResolvedDisplayUpdated?(displayId: number): void;
|
|
339
|
+
onDisplayPinned?(displayId: number | undefined): void;
|
|
340
|
+
onDisplayResolvedForApps?(sortedBundleIdsKey: string): void;
|
|
341
|
+
/** Called after each screenshot. Host persists for respawn survival. */
|
|
342
|
+
onScreenshotCaptured?(dims: ScreenshotDims): void;
|
|
343
|
+
onTeachModeActivated?(): void;
|
|
344
|
+
onTeachStep?(req: TeachStepRequest): Promise<TeachStepResult>;
|
|
345
|
+
onTeachWorking?(): void;
|
|
346
|
+
|
|
347
|
+
// ── Lock (async) ───────────────────────────────────────────────────
|
|
348
|
+
|
|
349
|
+
/** At most one session uses CU at a time. Awaited by `bindSessionContext`
|
|
350
|
+
* before dispatch. Undefined → no lock gating (proceed). */
|
|
351
|
+
checkCuLock?(): Promise<{ holder: string | undefined; isSelf: boolean }>;
|
|
352
|
+
/** Take the lock. Called when `checkCuLock` returned `holder: undefined`
|
|
353
|
+
* on a non-deferring tool. Host emits enter-CU signals here. */
|
|
354
|
+
acquireCuLock?(): Promise<void>;
|
|
355
|
+
/** Host-specific lock-held error text. Default is the package's generic
|
|
356
|
+
* message. The CLI host includes the holder session-ID prefix. */
|
|
357
|
+
formatLockHeldMessage?(holder: string): string;
|
|
358
|
+
|
|
359
|
+
/** User-abort signal. Passed through to `ComputerUseOverrides.isAborted`
|
|
360
|
+
* for the mid-loop checks in handleComputerBatch / handleType. See that
|
|
361
|
+
* field for semantics. */
|
|
362
|
+
isAborted?(): boolean;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ----------------------------------------------------------------------------
|
|
366
|
+
// Per-call overrides (mirror of PermissionOverrides, types.ts:97-102)
|
|
367
|
+
// ----------------------------------------------------------------------------
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Built FRESH on every tool call by `bindSessionContext` from
|
|
371
|
+
* `ComputerUseSessionContext` getters. This is what lets a singleton MCP
|
|
372
|
+
* server carry per-session state — the state lives on the host's session
|
|
373
|
+
* store, not the server.
|
|
374
|
+
*/
|
|
375
|
+
export interface ComputerUseOverrides {
|
|
376
|
+
allowedApps: AppGrant[];
|
|
377
|
+
grantFlags: CuGrantFlags;
|
|
378
|
+
coordinateMode: CoordinateMode;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* User-configured auto-deny list (Settings → Desktop app → Computer Use).
|
|
382
|
+
* Bundle IDs
|
|
383
|
+
* here are stripped from request_access BEFORE the approval dialog — they
|
|
384
|
+
* never reach the user for approval regardless of tier. The response tells
|
|
385
|
+
* the agent to ask the user to remove the app from their deny list in
|
|
386
|
+
* Settings if access is genuinely needed.
|
|
387
|
+
*
|
|
388
|
+
* Per-USER, persists across restarts (read from appPreferences per call,
|
|
389
|
+
* not session state). Contrast with `allowedApps` which is per-session.
|
|
390
|
+
* Empty array = no user-configured denies (the default).
|
|
391
|
+
*/
|
|
392
|
+
userDeniedBundleIds: readonly string[];
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Display CU operates on; read fresh per call. `scaleCoord` uses the
|
|
396
|
+
* `originX/Y` snapshotted in `lastScreenshot`, so mid-session switches
|
|
397
|
+
* only affect the NEXT screenshot/prepare call.
|
|
398
|
+
*/
|
|
399
|
+
selectedDisplayId?: number;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* The `request_access` tool handler calls this and awaits. The wrapper
|
|
403
|
+
* closure in serverDef.ts (mirroring InternalMcpServerManager.ts:131-177)
|
|
404
|
+
* routes through `handleToolPermission` → IPC → renderer ChicagoApproval.
|
|
405
|
+
* When it resolves, the wrapper side-effectfully mutates
|
|
406
|
+
* `InternalServerContext.cuAllowedApps` BEFORE returning here.
|
|
407
|
+
*
|
|
408
|
+
* Undefined when the session wasn't wired with a permission handler (e.g.
|
|
409
|
+
* a future headless mode). `request_access` returns a tool error in that case.
|
|
410
|
+
*/
|
|
411
|
+
onPermissionRequest?: (req: CuPermissionRequest) => Promise<CuPermissionResponse>;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* For the pixel-validation staleness guard. The model's-last-screenshot,
|
|
415
|
+
* stashed by serverDef.ts after each `screenshot` tool call. Undefined on
|
|
416
|
+
* cold start → pixel validation skipped (click proceeds).
|
|
417
|
+
*/
|
|
418
|
+
lastScreenshot?: ScreenshotResult;
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Fired after every `prepareForAction` with the bundle IDs it just hid.
|
|
422
|
+
* The wrapper closure in serverDef.ts accumulates these into
|
|
423
|
+
* `Session.cuHiddenDuringTurn` via a write-through callback (same pattern
|
|
424
|
+
* as `onCuPermissionUpdated`). At turn end (`sdkMessage.type === "result"`),
|
|
425
|
+
* if the `chicagoAutoUnhide` setting is on, everything in the set is
|
|
426
|
+
* unhidden. Set is cleared regardless of the setting so it doesn't leak
|
|
427
|
+
* across turns.
|
|
428
|
+
*
|
|
429
|
+
* Undefined when the session wasn't wired with a tracker — unhide just
|
|
430
|
+
* doesn't happen.
|
|
431
|
+
*/
|
|
432
|
+
onAppsHidden?: (bundleIds: string[]) => void;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Reads the clipboardGuard stash from session state. `undefined` means no
|
|
436
|
+
* stash is held — `syncClipboardStash` stashes on first entry to click-tier
|
|
437
|
+
* and clears on restore. Sibling of the `cuHiddenDuringTurn` getter pattern
|
|
438
|
+
* — state lives on the host's session, not module-level here.
|
|
439
|
+
*/
|
|
440
|
+
getClipboardStash?: () => string | undefined;
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Writes the clipboardGuard stash to session state. `undefined` clears.
|
|
444
|
+
* Sibling of `onAppsHidden` — the wrapper closure writes through to
|
|
445
|
+
* `Session.cuClipboardStash`. At turn end the host reads + clears it
|
|
446
|
+
* directly and restores via Electron's `clipboard.writeText` (no nest-only
|
|
447
|
+
* import surface).
|
|
448
|
+
*/
|
|
449
|
+
onClipboardStashChanged?: (stash: string | undefined) => void;
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Write the resolver's picked display back to session so teach overlay
|
|
453
|
+
* positioning and subsequent non-resolver calls use the same display.
|
|
454
|
+
* Fired by `handleScreenshot` in the atomic `autoTargetDisplay` path when
|
|
455
|
+
* `resolvePrepareCapture`'s pick differs from `selectedDisplayId`.
|
|
456
|
+
* Fire-and-forget.
|
|
457
|
+
*/
|
|
458
|
+
onResolvedDisplayUpdated?: (displayId: number) => void;
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Set when the model explicitly picked a display via `switch_display`.
|
|
462
|
+
* When true, `handleScreenshot` passes `autoResolve: false` so the backend
|
|
463
|
+
* resolver honors `selectedDisplayId` directly (straight cuDisplayInfo
|
|
464
|
+
* passthrough) instead of running the co-location/chase chain. The
|
|
465
|
+
* resolver's Step 2 ("host + allowed co-located → host") otherwise
|
|
466
|
+
* overrides any `selectedDisplayId` whenever an allowed app shares the
|
|
467
|
+
* host's monitor.
|
|
468
|
+
*/
|
|
469
|
+
displayPinnedByModel?: boolean;
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Write the model's explicit display pick to session. `displayId:
|
|
473
|
+
* undefined` clears both `selectedDisplayId` and the pin (back to auto).
|
|
474
|
+
* Sibling of `onResolvedDisplayUpdated` but also sets the pin flag —
|
|
475
|
+
* the two are semantically distinct (resolver-picked vs model-picked).
|
|
476
|
+
*/
|
|
477
|
+
onDisplayPinned?: (displayId: number | undefined) => void;
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Sorted comma-joined bundle-ID set the display was last auto-resolved
|
|
481
|
+
* for. `handleScreenshot` compares this to the current allowed set and
|
|
482
|
+
* only passes `autoResolve: true` when they differ — so the resolver
|
|
483
|
+
* doesn't yank the display on every screenshot, only when the app set
|
|
484
|
+
* has changed since the last resolve (or manual switch).
|
|
485
|
+
*/
|
|
486
|
+
displayResolvedForApps?: string;
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Records which app set the current display selection was made for. Fired
|
|
490
|
+
* alongside `onResolvedDisplayUpdated` when the resolver picks, so the next
|
|
491
|
+
* screenshot sees a matching set and skips auto-resolve.
|
|
492
|
+
*/
|
|
493
|
+
onDisplayResolvedForApps?: (sortedBundleIdsKey: string) => void;
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Global CU lock — at most one session actively uses CU at a time. Checked
|
|
497
|
+
* in `handleToolCall` after kill-switch/TCC, before dispatch. Every CU tool
|
|
498
|
+
* including `request_access` goes through it.
|
|
499
|
+
*
|
|
500
|
+
* - `holder === undefined` → lock is free, safe to acquire
|
|
501
|
+
* - `isSelf === true` → this session already holds it (no-op, proceed)
|
|
502
|
+
* - `holder !== undefined && !isSelf` → blocked, return tool error
|
|
503
|
+
*
|
|
504
|
+
* `undefined` callback → lock system not wired (e.g. CCD). Proceed without
|
|
505
|
+
* gating — absence of the mechanism ≠ locked out.
|
|
506
|
+
*
|
|
507
|
+
* The host manages release (on session idle/stop/archive) — this package
|
|
508
|
+
* never releases.
|
|
509
|
+
*/
|
|
510
|
+
checkCuLock?: () => { holder: string | undefined; isSelf: boolean };
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Take the lock for this session. `handleToolCall` calls this exactly once
|
|
514
|
+
* per turn, on the FIRST CU tool call when `checkCuLock().holder` is
|
|
515
|
+
* undefined. No-op if already held (defensive — the check should have
|
|
516
|
+
* short-circuited). Host emits an event the overlay listens to.
|
|
517
|
+
*/
|
|
518
|
+
acquireCuLock?: () => void;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* User-abort signal. Checked mid-iteration inside `handleComputerBatch`
|
|
522
|
+
* and `handleType`'s grapheme loop so an in-flight batch/type stops
|
|
523
|
+
* promptly on overlay Stop instead of running to completion after the
|
|
524
|
+
* host has already abandoned the tool result.
|
|
525
|
+
*
|
|
526
|
+
* Undefined → never aborts (e.g. unwired host). Live per-check read —
|
|
527
|
+
* same lazy-getter pattern as `checkCuLock`.
|
|
528
|
+
*/
|
|
529
|
+
isAborted?: () => boolean;
|
|
530
|
+
|
|
531
|
+
// ── Teach mode ───────────────────────────────────────────────────────
|
|
532
|
+
// Wired only when the host's teachModeEnabled gate is on. All five
|
|
533
|
+
// undefined → `request_teach_access` / `teach_step` return tool errors
|
|
534
|
+
// and teach mode is effectively off.
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Sibling of `onPermissionRequest`. Same blocking-await-on-renderer-dialog
|
|
538
|
+
* semantics, but routes to ComputerUseTeachApproval.tsx (which explains
|
|
539
|
+
* the window-hides-during-guide behavior) instead of ComputerUseApproval.
|
|
540
|
+
* The wrapper closure in serverDef.ts writes grants through to session state
|
|
541
|
+
* via `onCuPermissionUpdated` exactly as `onPermissionRequest` does.
|
|
542
|
+
*/
|
|
543
|
+
onTeachPermissionRequest?: (
|
|
544
|
+
req: CuTeachPermissionRequest,
|
|
545
|
+
) => Promise<CuPermissionResponse>;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Called by `handleRequestTeachAccess` after the user approves and at least
|
|
549
|
+
* one app was granted. Host sets `session.teachModeActive = true`, emits
|
|
550
|
+
* `teachModeChanged` → teach controller hides the main window and shows the
|
|
551
|
+
* fullscreen overlay. Cleared by the host on turn end (`transitionTo("idle")`)
|
|
552
|
+
* alongside the CU lock release.
|
|
553
|
+
*/
|
|
554
|
+
onTeachModeActivated?: () => void;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Read by `handleRequestAccess` and `handleRequestTeachAccess` to
|
|
558
|
+
* short-circuit with a clear tool error when teach mode is active. The
|
|
559
|
+
* main window is hidden during teach mode, so permission dialogs render
|
|
560
|
+
* invisibly and handleToolPermission blocks forever on an invisible
|
|
561
|
+
* prompt. Better to tell the model to exit teach mode first. Getter
|
|
562
|
+
* (not a boolean field) because teach mode state lives on the session,
|
|
563
|
+
* not on this per-call overrides object.
|
|
564
|
+
*/
|
|
565
|
+
getTeachModeActive?: () => boolean;
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Called by `handleTeachStep` with the scaled anchor + text. Host stores
|
|
569
|
+
* the resolver, emits `teachStepRequested` → teach controller pushes the
|
|
570
|
+
* payload to the overlay → user reads, clicks Next → IPC → host calls the
|
|
571
|
+
* stored resolver → this promise resolves. `{action: "exit"}` when the user
|
|
572
|
+
* clicks Exit (or the turn is interrupted) — `handleTeachStep` short-circuits
|
|
573
|
+
* without executing actions.
|
|
574
|
+
*
|
|
575
|
+
* Same blocking-promise pattern as `onPermissionRequest`, but resolved by
|
|
576
|
+
* the teach overlay's own preload (not the main renderer's tool-approval UI).
|
|
577
|
+
*/
|
|
578
|
+
onTeachStep?: (req: TeachStepRequest) => Promise<TeachStepResult>;
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Called immediately after `onTeachStep` resolves with "next", before
|
|
582
|
+
* action dispatch begins. Host emits `teachStepWorking` → overlay flips to
|
|
583
|
+
* the spinner state (Next button gone, Exit stays, "Working…" + rotating
|
|
584
|
+
* notch). The next `onTeachStep` call replaces the spinner with the new
|
|
585
|
+
* tooltip content.
|
|
586
|
+
*/
|
|
587
|
+
onTeachWorking?: () => void;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ----------------------------------------------------------------------------
|
|
591
|
+
// Teach mode (guided-tour tooltips with Next-button action execution)
|
|
592
|
+
// ----------------------------------------------------------------------------
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Payload the host pushes to the teach overlay BrowserWindow. Built by
|
|
596
|
+
* `handleTeachStep` in toolCalls.ts from the model's `teach_step` args.
|
|
597
|
+
*
|
|
598
|
+
* `anchorLogical` here is POST-`scaleCoord` — **full-display** logical
|
|
599
|
+
* macOS points (origin = monitor top-left, menu bar included, since
|
|
600
|
+
* cuDisplayInfo returns CGDisplayBounds). The overlay window is positioned
|
|
601
|
+
* at `workArea.{x,y}` (excludes menu bar/Dock), so `updateTeachStep` in
|
|
602
|
+
* teach/window.ts subtracts the workArea offset before IPC so the HTML's
|
|
603
|
+
* CSS coords match.
|
|
604
|
+
*/
|
|
605
|
+
export interface TeachStepRequest {
|
|
606
|
+
explanation: string;
|
|
607
|
+
nextPreview: string;
|
|
608
|
+
/** Full-display logical points. Undefined → overlay centers the tooltip, hides the arrow. */
|
|
609
|
+
anchorLogical?: { x: number; y: number };
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export type TeachStepResult = { action: "next" } | { action: "exit" };
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Payload for the renderer's ComputerUseTeachApproval dialog. Rides through
|
|
616
|
+
* `ToolPermissionRequest.input: unknown` same as `CuPermissionRequest`.
|
|
617
|
+
* Separate type (not a flag on `CuPermissionRequest`) so the two approval
|
|
618
|
+
* components can narrow independently and the teach dialog is free to drop
|
|
619
|
+
* fields it doesn't render (no grant-flag checkboxes in teach mode).
|
|
620
|
+
*/
|
|
621
|
+
export interface CuTeachPermissionRequest {
|
|
622
|
+
requestId: string;
|
|
623
|
+
/** Model-provided reason. Shown in the dialog headline ("guide you through {reason}"). */
|
|
624
|
+
reason: string;
|
|
625
|
+
apps: ResolvedAppRequest[];
|
|
626
|
+
screenshotFiltering: "native" | "none";
|
|
627
|
+
/** Present only when TCC is ungranted — same semantics as `CuPermissionRequest.tccState`. */
|
|
628
|
+
tccState?: {
|
|
629
|
+
accessibility: boolean;
|
|
630
|
+
screenRecording: boolean;
|
|
631
|
+
};
|
|
632
|
+
willHide?: Array<{ bundleId: string; displayName: string }>;
|
|
633
|
+
/** Same semantics as `CuPermissionRequest.autoUnhideEnabled`. */
|
|
634
|
+
autoUnhideEnabled?: boolean;
|
|
635
|
+
}
|