@agimon-ai/po-computer-use 0.0.0-alpha.0

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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +49 -0
  3. package/dist/apiContracts.cjs +242 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +237 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/extensionService-Bp0q295i.cjs +504 -0
  10. package/dist/extensionService-Bp0q295i.cjs.map +1 -0
  11. package/dist/extensionService-DXvF7esO.mjs +434 -0
  12. package/dist/extensionService-DXvF7esO.mjs.map +1 -0
  13. package/dist/extensions/mcp-ui.mjs +47 -0
  14. package/dist/extensions/mcp-ui.mjs.map +1 -0
  15. package/dist/extensions/mcp.cjs +66 -0
  16. package/dist/extensions/mcp.cjs.map +1 -0
  17. package/dist/extensions/mcp.d.mts +5 -0
  18. package/dist/extensions/mcp.d.mts.map +1 -0
  19. package/dist/extensions/mcp.mjs +61 -0
  20. package/dist/extensions/mcp.mjs.map +1 -0
  21. package/dist/extensions/server.cjs +1394 -0
  22. package/dist/extensions/server.cjs.map +1 -0
  23. package/dist/extensions/server.d.mts +5 -0
  24. package/dist/extensions/server.d.mts.map +1 -0
  25. package/dist/extensions/server.mjs +1388 -0
  26. package/dist/extensions/server.mjs.map +1 -0
  27. package/dist/extensions/web.mjs +771 -0
  28. package/dist/extensions/web.mjs.map +1 -0
  29. package/dist/index.cjs +140 -0
  30. package/dist/index.cjs.map +1 -0
  31. package/dist/index.d.mts +325 -0
  32. package/dist/index.d.mts.map +1 -0
  33. package/dist/index.mjs +129 -0
  34. package/dist/index.mjs.map +1 -0
  35. package/docs/computer-scripts.md +72 -0
  36. package/llms.txt +12 -0
  37. package/package.json +147 -0
  38. package/src/prompts/po-use-computer-use/SKILL.md +34 -0
  39. package/src/types/computerUse.ts +132 -0
  40. package/src/types/computerUseApi.ts +83 -0
@@ -0,0 +1,1394 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_extensionService = require("../extensionService-Bp0q295i.cjs");
6
+ let node_path = require("node:path");
7
+ node_path = require_extensionService.__toESM(node_path, 1);
8
+ let _agimon_ai_po_core_serverFacet = require("@agimon-ai/po-core/serverFacet");
9
+ let _agimon_ai_po_core_extensionFile = require("@agimon-ai/po-core/extensionFile");
10
+ let _agimon_ai_po_minor_mode = require("@agimon-ai/po-minor-mode");
11
+ let node_crypto = require("node:crypto");
12
+ let _agimon_ai_po_core_packageApi = require("@agimon-ai/po-core/packageApi");
13
+ let _agimon_ai_po_core_web = require("@agimon-ai/po-core/web");
14
+ //#region src/types/computerUseApi.ts
15
+ const API_BASE_PATH = "computer-use";
16
+ const computerUseChannelType = "computer_use_state";
17
+ const COMPUTER_USE_MODE_ID = "computer-use";
18
+ const COMPUTER_USE_STATUS_KEY = "doom-computer-use";
19
+ const COMPUTER_USE_CONFIRMATION_WINDOW_MS = 12e4;
20
+ /**
21
+ * Every path this API answers on, below its mount.
22
+ *
23
+ * The typed route table in `./apiRoutes` is built from this, the dispatcher
24
+ * reads that table, and the hub channel addresses the broker with these same
25
+ * strings, so a route cannot move on one side alone.
26
+ */
27
+ const COMPUTER_USE_ROUTES = {
28
+ activate: "/activate",
29
+ artifact: "/artifact",
30
+ agentState: "/agent/state",
31
+ agentObserve: "/agent/observe",
32
+ agentAction: "/agent/action",
33
+ agentStop: "/agent/stop",
34
+ hubState: "/hub/state",
35
+ hubActivation: "/hub/activation",
36
+ hubAuthorization: "/hub/authorization",
37
+ hubNext: "/hub/next",
38
+ hubComplete: "/hub/complete",
39
+ hubStop: "/hub/stop"
40
+ };
41
+ const COMPUTER_USE_WAKE_LIMIT = 1e6;
42
+ //#endregion
43
+ //#region src/services/mcpTools/index.ts
44
+ const COMPUTER_USE_MCP_TOOLS_SERVICE = "doom/computer-use-mcp-tools";
45
+ function mountMcpTools(tools) {
46
+ return (context) => {
47
+ context.plugin((providerContext) => {
48
+ providerContext.provide(COMPUTER_USE_MCP_TOOLS_SERVICE, {
49
+ get: (name) => tools.find((tool) => tool.name === name),
50
+ names: () => tools.map((tool) => tool.name)
51
+ });
52
+ });
53
+ };
54
+ }
55
+ //#endregion
56
+ //#region src/models/computerUseMode.ts
57
+ function modeState(state, enabled = false) {
58
+ const phase = state?.phase ?? "inactive";
59
+ const modeEnabled = enabled || phase !== "inactive" && phase !== "failed";
60
+ return {
61
+ activation: modeEnabled ? phase === "stopping" ? "deactivating" : phase === "awaiting_confirmation" || phase === "activating" ? "activating" : "active" : "inactive",
62
+ condition: phase === "awaiting_confirmation" ? "blocked" : phase === "failed" ? "failed" : phase === "activating" || phase === "stopping" ? "queued" : "ready",
63
+ ...modeEnabled && phase === "inactive" ? { detail: "configure computer use in Activity" } : {},
64
+ ...phase === "awaiting_confirmation" ? { detail: "awaiting confirmation in Activity" } : {},
65
+ actions: [
66
+ {
67
+ id: "activate",
68
+ enabled: !modeEnabled,
69
+ ...!modeEnabled ? {} : { disabledReason: "Computer use is already enabled." }
70
+ },
71
+ {
72
+ id: "deactivate",
73
+ enabled: modeEnabled,
74
+ ...modeEnabled ? {} : { disabledReason: "Computer use is not enabled." }
75
+ },
76
+ {
77
+ id: "doctor",
78
+ enabled: true
79
+ }
80
+ ]
81
+ };
82
+ }
83
+ //#endregion
84
+ //#region src/services/computerToolOutput/index.ts
85
+ const MAX_TEXT_BYTES = 262144;
86
+ const MAX_IMAGE_DATA_LENGTH = 8388608;
87
+ function content(value, observation) {
88
+ const text = JSON.stringify(value);
89
+ if (Buffer.byteLength(text) > MAX_TEXT_BYTES) throw new Error("Computer-use text response exceeds the size limit.");
90
+ const screenshot = observation.screenshot;
91
+ if (screenshot && (screenshot.mimeType !== "image/png" || screenshot.data.length > MAX_IMAGE_DATA_LENGTH)) throw new Error("Computer-use screenshot exceeds the image limit.");
92
+ return [{
93
+ type: "text",
94
+ text
95
+ }, ...screenshot ? [{
96
+ type: "image",
97
+ mimeType: screenshot.mimeType,
98
+ data: screenshot.data
99
+ }] : []];
100
+ }
101
+ function computerObservationOutput(observation) {
102
+ return {
103
+ content: content({
104
+ ...observation,
105
+ screenshot: void 0
106
+ }, observation),
107
+ details: observation
108
+ };
109
+ }
110
+ function computerScriptOutput(result) {
111
+ try {
112
+ return {
113
+ content: content({
114
+ ...result,
115
+ observation: {
116
+ ...result.observation,
117
+ screenshot: void 0
118
+ }
119
+ }, result.observation),
120
+ details: result
121
+ };
122
+ } catch (error) {
123
+ if (result.metrics === void 0) throw error;
124
+ throw new require_extensionService.ComputerScriptExecutionError(error instanceof Error ? error.message : String(error), result.metrics.actions, false);
125
+ }
126
+ }
127
+ function computerScriptFailure(error) {
128
+ const details = {
129
+ error: (error instanceof Error ? error.message : String(error)).slice(0, 1024),
130
+ ...error instanceof require_extensionService.ComputerScriptExecutionError ? {
131
+ completedActions: error.completedActions,
132
+ outcomeUncertain: error.outcomeUncertain
133
+ } : {}
134
+ };
135
+ return {
136
+ content: [{
137
+ type: "text",
138
+ text: JSON.stringify(details)
139
+ }],
140
+ details,
141
+ isError: true
142
+ };
143
+ }
144
+ //#endregion
145
+ //#region src/types/apiRoutes.ts
146
+ /**
147
+ * This package's routes, as data.
148
+ *
149
+ * No scope and no base path: the build reads both off
150
+ * `src/extensions/workspaces/sessions/(backend)/api/computer-use/`, so the
151
+ * mount is stated once, by the folder that creates it.
152
+ *
153
+ * The paths come from `COMPUTER_USE_ROUTES` rather than being written out
154
+ * again. That table already addressed the broker from the hub channel and from
155
+ * the broker's own dispatcher, and a second copy here is exactly the
156
+ * disagreement this file exists to prevent. The dispatcher now reads this
157
+ * table, so the page, the broker and the hub all resolve one set of strings.
158
+ *
159
+ * Activation, status, cancellation and recording reads are reachable from a browser. The `/agent/` and `/hub/` routes
160
+ * each demand a host-issued bearer token and answer 404 without one; they are
161
+ * declared here because they are this API's surface, not because a page calls
162
+ * them.
163
+ */
164
+ var apiRoutes_default = (0, _agimon_ai_po_core_web.defineApiRoutes)({
165
+ artifact: {
166
+ method: "GET",
167
+ path: COMPUTER_USE_ROUTES.artifact,
168
+ query: ["artifactId", "offset"],
169
+ raw: true
170
+ },
171
+ activationState: {
172
+ method: "GET",
173
+ path: COMPUTER_USE_ROUTES.activate,
174
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
175
+ },
176
+ cancelActivation: {
177
+ method: "DELETE",
178
+ path: COMPUTER_USE_ROUTES.activate,
179
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
180
+ },
181
+ activate: {
182
+ method: "POST",
183
+ path: COMPUTER_USE_ROUTES.activate,
184
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
185
+ },
186
+ agentState: {
187
+ method: "GET",
188
+ path: COMPUTER_USE_ROUTES.agentState,
189
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
190
+ },
191
+ /** Answers whatever the admitted Desktop provider returned, which is runtime-defined JSON. */
192
+ agentObserve: {
193
+ method: "POST",
194
+ path: COMPUTER_USE_ROUTES.agentObserve,
195
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
196
+ },
197
+ agentAction: {
198
+ method: "POST",
199
+ path: COMPUTER_USE_ROUTES.agentAction,
200
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
201
+ },
202
+ agentStop: {
203
+ method: "POST",
204
+ path: COMPUTER_USE_ROUTES.agentStop,
205
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
206
+ },
207
+ hubState: {
208
+ method: "GET",
209
+ path: COMPUTER_USE_ROUTES.hubState,
210
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
211
+ },
212
+ /** Null until a reader has confirmed one; reading it moves the session to `activating`. */
213
+ hubActivation: {
214
+ method: "GET",
215
+ path: COMPUTER_USE_ROUTES.hubActivation,
216
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
217
+ },
218
+ hubAuthorization: {
219
+ method: "GET",
220
+ path: COMPUTER_USE_ROUTES.hubAuthorization,
221
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
222
+ },
223
+ hubNext: {
224
+ method: "GET",
225
+ path: COMPUTER_USE_ROUTES.hubNext,
226
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
227
+ },
228
+ hubComplete: {
229
+ method: "POST",
230
+ path: COMPUTER_USE_ROUTES.hubComplete,
231
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
232
+ },
233
+ hubStop: {
234
+ method: "POST",
235
+ path: COMPUTER_USE_ROUTES.hubStop,
236
+ response: (0, _agimon_ai_po_core_web.apiResponse)()
237
+ }
238
+ });
239
+ //#endregion
240
+ //#region src/services/computerUseApi/index.ts
241
+ function jsonError(message, status) {
242
+ return Response.json({ error: message }, { status });
243
+ }
244
+ function record$1(value) {
245
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
246
+ }
247
+ async function body(request) {
248
+ try {
249
+ return record$1(await request.json()) ?? {};
250
+ } catch {
251
+ return {};
252
+ }
253
+ }
254
+ function semanticAction(value) {
255
+ if (!(typeof value.snapshotId === "string" && value.snapshotId.length > 0 && typeof value.elementRef === "string" && value.elementRef.length > 0)) return false;
256
+ const allowed = (keys) => Object.keys(value).every((key) => keys.includes(key));
257
+ if (value.kind === "press" || value.kind === "focus") return allowed([
258
+ "kind",
259
+ "snapshotId",
260
+ "elementRef"
261
+ ]);
262
+ if (value.kind === "set_value") return typeof value.value === "string" && allowed([
263
+ "kind",
264
+ "snapshotId",
265
+ "elementRef",
266
+ "value"
267
+ ]);
268
+ if (value.kind === "scroll") return [
269
+ "up",
270
+ "down",
271
+ "left",
272
+ "right"
273
+ ].includes(value.direction) && (value.amount === "line" || value.amount === "page") && allowed([
274
+ "kind",
275
+ "snapshotId",
276
+ "elementRef",
277
+ "direction",
278
+ "amount"
279
+ ]);
280
+ return false;
281
+ }
282
+ function artifactView(value) {
283
+ const input = record$1(value);
284
+ if (typeof input?.artifactId !== "string" || input.status !== "ready" && input.status !== "failed") return void 0;
285
+ const safeUrl = (candidate) => typeof candidate === "string" && candidate.startsWith("/api/") ? candidate : void 0;
286
+ const downloadUrl = safeUrl(input.downloadUrl);
287
+ const previewUrl = safeUrl(input.previewUrl);
288
+ const artifactFailure = record$1(input.failure);
289
+ const safeFailure = typeof artifactFailure?.code === "string" && typeof artifactFailure.message === "string" ? {
290
+ code: artifactFailure.code.slice(0, 128),
291
+ message: artifactFailure.message.slice(0, 512)
292
+ } : void 0;
293
+ return {
294
+ artifactId: input.artifactId,
295
+ status: input.status,
296
+ ...typeof input.sizeBytes === "number" && Number.isSafeInteger(input.sizeBytes) && input.sizeBytes > 0 ? { sizeBytes: input.sizeBytes } : {},
297
+ ...downloadUrl === void 0 ? {} : { downloadUrl },
298
+ ...previewUrl === void 0 ? {} : { previewUrl },
299
+ ...typeof input.actionCount === "number" ? { actionCount: input.actionCount } : {},
300
+ ...typeof input.completedAt === "string" ? { completedAt: input.completedAt } : {},
301
+ ...safeFailure === void 0 ? {} : { failure: safeFailure }
302
+ };
303
+ }
304
+ var ComputerUseRequestBroker = class ComputerUseRequestBroker {
305
+ sessionId;
306
+ internalToken;
307
+ hubToken;
308
+ directEvents;
309
+ requestTimeoutMs;
310
+ revision = 0;
311
+ wake = 0;
312
+ phase = "inactive";
313
+ activation;
314
+ grantId;
315
+ expiresAt;
316
+ actionSequence = 0;
317
+ expiryTimer;
318
+ failure;
319
+ artifact;
320
+ pending;
321
+ closed = false;
322
+ desktop;
323
+ beforeActivate;
324
+ listeners = /* @__PURE__ */ new Set();
325
+ unsubscribeDesktop;
326
+ static DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
327
+ constructor(options) {
328
+ this.sessionId = options.sessionId ?? "unknown";
329
+ this.internalToken = options.internalToken;
330
+ this.hubToken = options.hubToken;
331
+ this.directEvents = options.directEvents;
332
+ this.requestTimeoutMs = options.requestTimeoutMs ?? ComputerUseRequestBroker.DEFAULT_REQUEST_TIMEOUT_MS;
333
+ this.desktop = options.desktop;
334
+ this.beforeActivate = options.beforeActivate;
335
+ this.unsubscribeDesktop = options.desktop?.subscribe(() => {
336
+ clearTimeout(this.expiryTimer);
337
+ if (!options.desktop?.available) {
338
+ this.rejectPending("Desktop disconnected.");
339
+ this.phase = "failed";
340
+ this.grantId = void 0;
341
+ this.failure = {
342
+ code: "desktop_unavailable",
343
+ message: "Desktop disconnected."
344
+ };
345
+ } else if (this.phase !== "inactive" && this.phase !== "failed") {
346
+ this.phase = "stopping";
347
+ this.rejectPending("Control was stopped by Desktop.");
348
+ }
349
+ this.changed();
350
+ });
351
+ }
352
+ state() {
353
+ return Object.freeze({
354
+ sessionId: this.sessionId,
355
+ revision: this.revision,
356
+ wake: this.wake,
357
+ phase: this.phase,
358
+ ...this.activation === void 0 ? {} : {
359
+ requestId: this.activation.requestId,
360
+ target: this.activation.target,
361
+ durationMs: this.activation.durationSeconds * 1e3
362
+ },
363
+ ...this.expiresAt === void 0 ? {} : { expiresAt: this.expiresAt },
364
+ ...this.failure === void 0 ? {} : { failure: this.failure },
365
+ ...this.artifact === void 0 ? {} : { artifact: this.artifact }
366
+ });
367
+ }
368
+ /** Tools and the API share this broker. No socket, credential discovery, or second state store. */
369
+ sessionClient() {
370
+ const result = async (response) => {
371
+ const value = await response.json();
372
+ if (!response.ok) throw new Error(value.error ?? `Computer-use request failed (${response.status}).`);
373
+ return value;
374
+ };
375
+ const requireDesktop = () => {
376
+ if (this.closed || !this.desktop?.available || this.desktop.enabled === false) throw new Error("Desktop computer use is unavailable.");
377
+ };
378
+ return {
379
+ state: async () => this.state(),
380
+ subscribeStatus: (listener) => {
381
+ this.listeners.add(listener);
382
+ return () => {
383
+ this.listeners.delete(listener);
384
+ };
385
+ },
386
+ observe: async (signal, options = {}) => {
387
+ requireDesktop();
388
+ return result(await this.enqueue("observe", options, signal));
389
+ },
390
+ act: async (action, signal) => {
391
+ requireDesktop();
392
+ if (!semanticAction(action)) throw new Error("A valid semantic action is required.");
393
+ return result(await this.enqueue("act", action, signal));
394
+ },
395
+ stop: async (signal) => {
396
+ signal?.throwIfAborted();
397
+ if (this.phase !== "inactive" && this.phase !== "failed") {
398
+ this.phase = "stopping";
399
+ this.rejectPending("Computer use was stopped.");
400
+ this.changed();
401
+ }
402
+ return this.state();
403
+ }
404
+ };
405
+ }
406
+ changed() {
407
+ this.revision += 1;
408
+ this.wake = (this.wake + 1) % COMPUTER_USE_WAKE_LIMIT;
409
+ const state = this.state();
410
+ for (const listener of this.listeners) listener(state);
411
+ this.directEvents.publish(computerUseChannelType, this.sessionId, state);
412
+ }
413
+ authorized(request, token) {
414
+ return token !== void 0 && request.headers.get("authorization") === `Bearer ${token}`;
415
+ }
416
+ async requestActivation(request) {
417
+ if (!this.desktop?.available || !(this.desktop.authorizeActivation?.(request.headers) ?? this.desktop.authorize(request.headers))) return jsonError("Not found.", 404);
418
+ if (this.desktop.enabled === false) return jsonError("Enable computer use in global Desktop settings first.", 409);
419
+ const stamped = (0, _agimon_ai_po_core_packageApi.doomApiCallerFrom)(request.headers);
420
+ const caller = stamped?.locality === "remote" ? stamped : {
421
+ locality: "local",
422
+ stepUp: "not-required"
423
+ };
424
+ if (this.phase !== "inactive" && this.phase !== "failed") return jsonError("Computer use is already busy.", 409);
425
+ const input = await body(request);
426
+ const target = record$1(input.target);
427
+ const durationMs = input.durationMs;
428
+ if (target === void 0 && (input.target !== void 0 || caller.locality !== "remote") || target !== void 0 && (typeof target.windowId !== "string" || typeof target.bundleId !== "string")) return jsonError("A Desktop target is required, or a remote request must leave selection to the Mac.", 400);
429
+ if (typeof durationMs !== "number" || !Number.isInteger(durationMs) || durationMs < 1e3 || durationMs > 18e5) return jsonError("The requested duration is invalid.", 400);
430
+ try {
431
+ this.desktop.claim(request.headers);
432
+ await this.beforeActivate?.();
433
+ } catch (error) {
434
+ return jsonError(error instanceof Error ? error.message : String(error), 409);
435
+ }
436
+ if (this.closed || !this.desktop.available || this.phase !== "inactive" && this.phase !== "failed") return jsonError("Computer use is unavailable or already busy.", 409);
437
+ const createdAt = Date.now();
438
+ this.activation = Object.freeze({
439
+ requestId: (0, node_crypto.randomUUID)(),
440
+ ...target === void 0 ? {} : { target: Object.freeze({ ...target }) },
441
+ durationSeconds: durationMs / 1e3,
442
+ createdAt,
443
+ confirmationExpiresAt: createdAt + COMPUTER_USE_CONFIRMATION_WINDOW_MS,
444
+ caller: Object.freeze({ ...caller })
445
+ });
446
+ this.phase = "awaiting_confirmation";
447
+ this.failure = void 0;
448
+ this.artifact = void 0;
449
+ this.changed();
450
+ return Response.json(this.state(), { status: 202 });
451
+ }
452
+ rejectPending(message) {
453
+ const pending = this.pending;
454
+ if (pending === void 0) return;
455
+ this.pending = void 0;
456
+ pending.dispose();
457
+ pending.reject(new Error(message));
458
+ }
459
+ stopAfterUncertainRequest(message) {
460
+ if (this.phase === "active") {
461
+ this.phase = "stopping";
462
+ this.changed();
463
+ }
464
+ this.rejectPending(message);
465
+ }
466
+ async enqueue(operation, payload, signal) {
467
+ if (this.phase !== "active" || this.grantId === void 0) return jsonError("Computer use is not active.", 409);
468
+ if (this.pending !== void 0) return jsonError("Another computer-use request is live.", 409);
469
+ if (signal?.aborted === true) return jsonError("The computer-use request was cancelled.", 499);
470
+ const sequence = operation === "act" ? ++this.actionSequence : void 0;
471
+ const request = {
472
+ id: (0, node_crypto.randomUUID)(),
473
+ operation,
474
+ grantId: this.grantId,
475
+ ...sequence === void 0 ? {} : { sequence },
476
+ ...payload === void 0 ? {} : { payload }
477
+ };
478
+ const completion = new Promise((resolve, reject) => {
479
+ const onAbort = () => this.stopAfterUncertainRequest("The computer-use request was cancelled.");
480
+ const timer = setTimeout(() => this.stopAfterUncertainRequest("The computer-use request timed out."), this.requestTimeoutMs);
481
+ timer.unref?.();
482
+ signal?.addEventListener("abort", onAbort, { once: true });
483
+ this.pending = {
484
+ request,
485
+ resolve,
486
+ reject,
487
+ dispose: () => {
488
+ clearTimeout(timer);
489
+ signal?.removeEventListener("abort", onAbort);
490
+ }
491
+ };
492
+ });
493
+ this.changed();
494
+ try {
495
+ return Response.json(await completion);
496
+ } catch (error) {
497
+ return jsonError(error instanceof Error ? error.message : String(error), 502);
498
+ }
499
+ }
500
+ async fetch(request) {
501
+ if (this.closed) return jsonError("Computer-use broker is closed.", 503);
502
+ const url = new URL(request.url);
503
+ const path = url.pathname;
504
+ if (request.method === "GET" && path === apiRoutes_default.artifact.path) {
505
+ if (!(this.desktop?.authorizeRecording?.(request.headers) ?? this.desktop?.authorize(request.headers)) || !this.desktop?.fetchRecording) return jsonError("Not found.", 404);
506
+ const artifactId = url.searchParams.get("artifactId") ?? "";
507
+ const offsetText = url.searchParams.get("offset") ?? "";
508
+ const offset = Number(offsetText);
509
+ if (!/^[0-9]+$/u.test(offsetText) || !Number.isSafeInteger(offset) || offset < 0 || offset > 134217728) return jsonError("Invalid recording offset.", 400);
510
+ return this.desktop.fetchRecording(artifactId, `bytes=${offset}-${offset + 1048576 - 1}`);
511
+ }
512
+ if (path === apiRoutes_default.activate.path && request.method === "GET") {
513
+ if (!this.desktop?.authorizePending?.(request.headers) && !(this.desktop?.authorizeRecording?.(request.headers) ?? this.desktop?.authorize(request.headers))) return jsonError("Not found.", 404);
514
+ return Response.json(this.state());
515
+ }
516
+ if (path === apiRoutes_default.activate.path && request.method === "DELETE") {
517
+ if (!this.desktop?.authorizePending?.(request.headers)) return jsonError("Not found.", 404);
518
+ if (this.phase !== "awaiting_confirmation" && this.phase !== "activating") return jsonError("No activation is pending.", 409);
519
+ this.phase = "stopping";
520
+ this.rejectPending("The activation was cancelled.");
521
+ this.changed();
522
+ return Response.json(this.state(), { status: 202 });
523
+ }
524
+ if (request.method === "POST" && path === apiRoutes_default.activate.path) return this.requestActivation(request);
525
+ const agent = path.startsWith("/agent/");
526
+ const hub = path.startsWith("/hub/");
527
+ if (agent && !this.authorized(request, this.internalToken) || hub && !this.authorized(request, this.hubToken)) return jsonError("Not found.", 404);
528
+ if (request.method === "GET" && (path === apiRoutes_default.agentState.path || path === apiRoutes_default.hubState.path)) return Response.json(this.state());
529
+ if (request.method === "POST" && path === apiRoutes_default.agentObserve.path) return this.enqueue("observe", void 0, request.signal);
530
+ if (request.method === "POST" && path === apiRoutes_default.agentAction.path) {
531
+ const action = await body(request);
532
+ if (!semanticAction(action)) return jsonError("A valid semantic action is required.", 400);
533
+ return this.enqueue("act", action, request.signal);
534
+ }
535
+ if (request.method === "POST" && path === apiRoutes_default.agentStop.path) {
536
+ if (this.phase !== "active") return jsonError("Computer use is not active.", 409);
537
+ this.phase = "stopping";
538
+ this.rejectPending("The computer-use request was stopped.");
539
+ this.changed();
540
+ return Response.json(this.state(), { status: 202 });
541
+ }
542
+ if (request.method === "GET" && path === apiRoutes_default.hubActivation.path) {
543
+ if (this.activation === void 0 || this.phase !== "awaiting_confirmation") return Response.json(null);
544
+ this.phase = "activating";
545
+ this.changed();
546
+ return Response.json(this.activation);
547
+ }
548
+ if (request.method === "GET" && path === apiRoutes_default.hubAuthorization.path) return Response.json(this.grantId === void 0 ? null : {
549
+ grantId: this.grantId,
550
+ expiresAt: this.expiresAt
551
+ });
552
+ if (request.method === "GET" && path === apiRoutes_default.hubNext.path) return Response.json(this.pending?.request ?? null);
553
+ if (request.method === "POST" && path === apiRoutes_default.hubComplete.path) {
554
+ const input = await body(request);
555
+ const id = typeof input.id === "string" ? input.id : void 0;
556
+ if (this.pending === void 0 || this.pending.request.id !== id) return jsonError("The request is stale.", 409);
557
+ const pending = this.pending;
558
+ this.pending = void 0;
559
+ pending.dispose();
560
+ if (typeof input.error === "string") {
561
+ this.phase = "stopping";
562
+ pending.reject(new Error(input.error));
563
+ } else pending.resolve(input.result);
564
+ this.changed();
565
+ return Response.json(this.state());
566
+ }
567
+ if (request.method === "POST" && path === apiRoutes_default.hubStop.path) {
568
+ const input = await body(request);
569
+ if (this.phase === "activating") {
570
+ if (typeof input.error === "string") {
571
+ this.phase = "failed";
572
+ this.failure = {
573
+ code: "desktop_unavailable",
574
+ message: input.error.slice(0, 512)
575
+ };
576
+ this.activation = void 0;
577
+ } else {
578
+ const host = record$1(input.host);
579
+ if (typeof host?.grantId !== "string" || !Number.isFinite(host.expiresAt) || host.expiresAt <= Date.now()) return jsonError("Desktop did not issue a complete grant.", 502);
580
+ const selectedTarget = record$1(host.target);
581
+ if (this.activation !== void 0 && selectedTarget !== void 0) this.activation = Object.freeze({
582
+ ...this.activation,
583
+ target: Object.freeze({ ...selectedTarget })
584
+ });
585
+ this.grantId = host.grantId;
586
+ this.expiresAt = host.expiresAt;
587
+ this.actionSequence = 0;
588
+ this.phase = "active";
589
+ clearTimeout(this.expiryTimer);
590
+ this.expiryTimer = setTimeout(() => {
591
+ if (this.closed || this.phase !== "active") return;
592
+ this.phase = "stopping";
593
+ this.rejectPending("The computer-use grant expired.");
594
+ this.changed();
595
+ }, Math.max(0, this.expiresAt - Date.now()));
596
+ this.expiryTimer.unref?.();
597
+ }
598
+ } else {
599
+ clearTimeout(this.expiryTimer);
600
+ this.rejectPending("The computer-use session stopped.");
601
+ this.phase = "inactive";
602
+ this.grantId = void 0;
603
+ this.expiresAt = void 0;
604
+ this.activation = void 0;
605
+ this.artifact = artifactView(input.artifact) ?? this.artifact;
606
+ }
607
+ this.changed();
608
+ return Response.json(this.state());
609
+ }
610
+ return jsonError("Not found.", 404);
611
+ }
612
+ close() {
613
+ if (this.closed) return;
614
+ this.closed = true;
615
+ clearTimeout(this.expiryTimer);
616
+ this.unsubscribeDesktop?.();
617
+ this.listeners.clear();
618
+ this.rejectPending("Computer-use broker closed.");
619
+ this.grantId = void 0;
620
+ this.expiresAt = void 0;
621
+ }
622
+ };
623
+ function createComputerUseApi(options) {
624
+ return new ComputerUseRequestBroker(options);
625
+ }
626
+ //#endregion
627
+ //#region src/services/computerUseServer/index.ts
628
+ /** This package's identity for mode, activity and restriction ownership, not a status key. */
629
+ const SOURCE = "@agimon-ai/po-computer-use";
630
+ const SCRIPT_PATHS_ENV = "PO_COMPUTER_USE_SCRIPT_PATHS";
631
+ const GUIDANCE = "[COMPUTER USE ACTIVE]\nUse computer_state before acting and after any action that may change the interface. Use only element refs and snapshot ids returned by computer_state. Never infer coordinates or bypass secure elements.";
632
+ function output(value, isError = false) {
633
+ return {
634
+ content: [{
635
+ type: "text",
636
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
637
+ }],
638
+ details: value,
639
+ ...isError ? { isError: true } : {}
640
+ };
641
+ }
642
+ function createComputerUseServer(host, apiContext) {
643
+ if (!apiContext?.computerUse?.available || !apiContext.directEvents) return {
644
+ tools: [],
645
+ commands: []
646
+ };
647
+ const desktop = apiContext.computerUse;
648
+ const broker = createComputerUseApi({
649
+ sessionId: host.context.sessionId,
650
+ internalToken: apiContext.internalToken,
651
+ hubToken: apiContext.hubToken,
652
+ directEvents: apiContext.directEvents,
653
+ desktop,
654
+ beforeActivate: async () => {
655
+ if (!modeSelected()) throw new Error("Enable computer use before authorizing an application.");
656
+ const activity = await host.context.session.activity();
657
+ if (!activity.isIdle || activity.hasPendingMessages) throw new Error("Wait for the agent and its queued prompts to finish before granting computer use.");
658
+ }
659
+ });
660
+ const client = broker.sessionClient();
661
+ const allowedScriptPaths = (host.context.environment[SCRIPT_PATHS_ENV] ?? "").split(node_path.default.delimiter).filter((entry) => entry.length > 0);
662
+ const dependencies = {
663
+ client,
664
+ scriptRunner: new require_extensionService.ComputerScriptRunner({
665
+ client,
666
+ allowedScriptPaths,
667
+ scriptRoot: host.context.cwd
668
+ }),
669
+ service: new require_extensionService.DefaultComputerUseExtensionService(client)
670
+ };
671
+ let state = broker.state();
672
+ const restrictionListeners = /* @__PURE__ */ new Set();
673
+ let stopActivity;
674
+ let modeOwner;
675
+ const modeSelected = () => (host.context.selection.state?.["minor-mode"] ?? []).includes(COMPUTER_USE_MODE_ID);
676
+ const publishMode = () => modeOwner?.publish();
677
+ const applyState = (next) => {
678
+ state = next;
679
+ host.context.client.setStatus(COMPUTER_USE_STATUS_KEY, next === void 0 || next.phase === "inactive" ? void 0 : `computer use: ${next.phase}`);
680
+ publishMode();
681
+ for (const listener of restrictionListeners) listener();
682
+ };
683
+ const selectMode = async (enabled) => {
684
+ const modes = (host.context.selection.state?.["minor-mode"] ?? []).filter((mode) => mode !== COMPUTER_USE_MODE_ID);
685
+ await host.changeSelection({
686
+ axis: "state",
687
+ key: "minor-mode",
688
+ values: enabled ? [...modes, COMPUTER_USE_MODE_ID] : modes
689
+ });
690
+ publishMode();
691
+ };
692
+ const refresh = async (signal) => {
693
+ if (!client) {
694
+ applyState(void 0);
695
+ return;
696
+ }
697
+ try {
698
+ applyState(await client.state(signal));
699
+ } catch {
700
+ applyState(void 0);
701
+ }
702
+ };
703
+ const requireActive = () => {
704
+ if (!desktop.available || desktop.enabled === false || !modeSelected() || state?.phase !== "active") throw new Error("Computer use is not active for this session.");
705
+ return client;
706
+ };
707
+ modeOwner = (0, _agimon_ai_po_minor_mode.defineMinorMode)({
708
+ descriptor: {
709
+ source: SOURCE,
710
+ id: COMPUTER_USE_MODE_ID,
711
+ label: "Computer Use",
712
+ description: "Session-scoped semantic control through Po Desktop.",
713
+ order: 450,
714
+ actions: [
715
+ {
716
+ id: "activate",
717
+ label: "Activate",
718
+ description: "Enable computer use for this session.",
719
+ contexts: ["headless"],
720
+ parameters: []
721
+ },
722
+ {
723
+ id: "deactivate",
724
+ label: "Deactivate",
725
+ description: "Stop this session computer-use run.",
726
+ contexts: ["headless"],
727
+ parameters: []
728
+ },
729
+ {
730
+ id: "doctor",
731
+ label: "Doctor",
732
+ description: "Report Desktop availability and session state.",
733
+ contexts: ["headless"],
734
+ parameters: []
735
+ }
736
+ ]
737
+ },
738
+ state: () => {
739
+ const mode = modeState(state, modeSelected());
740
+ return desktop.enabled === false ? {
741
+ ...mode,
742
+ condition: "blocked",
743
+ detail: "Enable computer use in global Desktop settings.",
744
+ actions: mode.actions.map((action) => ({
745
+ ...action,
746
+ enabled: false,
747
+ disabledReason: "Enable computer use in global Desktop settings."
748
+ }))
749
+ } : mode;
750
+ },
751
+ async handleAction(_runtime, actionId, _argumentsValue, execution) {
752
+ execution.signal.throwIfAborted();
753
+ if (actionId === "activate") {
754
+ if (!desktop.available || desktop.enabled === false) throw new Error("Enable computer use in global Desktop settings first.");
755
+ await selectMode(true);
756
+ await refresh(execution.signal);
757
+ return { message: "Computer use is ready to configure in Activity." };
758
+ }
759
+ if (actionId === "doctor") {
760
+ await refresh(execution.signal);
761
+ return { message: client ? `Computer use is ${state?.phase ?? "unavailable"}.` : "Po Desktop session API is unavailable." };
762
+ }
763
+ if (actionId === "deactivate") {
764
+ if (client && state?.phase !== void 0 && state.phase !== "inactive" && state.phase !== "failed") await client.stop(execution.signal);
765
+ await selectMode(false);
766
+ await refresh(execution.signal);
767
+ return { message: "Computer use deactivated." };
768
+ }
769
+ throw new Error(`Unknown computer-use action: ${actionId}`);
770
+ }
771
+ }).createOwner(void 0);
772
+ const unsubscribeState = client.subscribeStatus?.(applyState);
773
+ return {
774
+ api: [{
775
+ basePath: API_BASE_PATH,
776
+ start: () => broker
777
+ }],
778
+ services: [(context) => {
779
+ const fiber = context.plugin((0, _agimon_ai_po_minor_mode.serverMinorModes)([modeOwner]));
780
+ context.effect(() => desktop.subscribe(() => {
781
+ if (!desktop.available) fiber.dispose();
782
+ }));
783
+ }],
784
+ toolRestrictions: [{
785
+ source: SOURCE,
786
+ restrict: () => ({ excludedTools: desktop.available && desktop.enabled !== false && modeSelected() && state?.phase === "active" ? [] : [
787
+ "computer_state",
788
+ "computer_action",
789
+ "computer_exec"
790
+ ] }),
791
+ subscribe: (listener) => {
792
+ restrictionListeners.add(listener);
793
+ return () => {
794
+ restrictionListeners.delete(listener);
795
+ };
796
+ }
797
+ }],
798
+ activities: [{
799
+ when: {
800
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
801
+ attribution: {
802
+ kind: "minor",
803
+ mode: COMPUTER_USE_MODE_ID
804
+ }
805
+ },
806
+ name: SOURCE,
807
+ async start() {
808
+ await refresh();
809
+ stopActivity = () => {
810
+ client.stop();
811
+ };
812
+ return () => {
813
+ stopActivity?.();
814
+ stopActivity = void 0;
815
+ };
816
+ }
817
+ }],
818
+ tools: [
819
+ {
820
+ when: {
821
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
822
+ attribution: {
823
+ kind: "minor",
824
+ mode: COMPUTER_USE_MODE_ID
825
+ }
826
+ },
827
+ name: "computer_state",
828
+ label: "Computer State",
829
+ description: "Observe the authorized application window and return semantic accessibility state.",
830
+ parameters: {
831
+ type: "object",
832
+ properties: { includeScreenshot: { type: "boolean" } },
833
+ additionalProperties: false
834
+ },
835
+ executionMode: "serial",
836
+ async execute(_toolCallId, _parameters, signal) {
837
+ try {
838
+ return computerObservationOutput(await requireActive().observe(signal, { includeScreenshot: _parameters.includeScreenshot !== false }));
839
+ } catch (error) {
840
+ return output(error instanceof Error ? error.message : String(error), true);
841
+ }
842
+ }
843
+ },
844
+ {
845
+ when: {
846
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
847
+ attribution: {
848
+ kind: "minor",
849
+ mode: COMPUTER_USE_MODE_ID
850
+ }
851
+ },
852
+ name: "computer_action",
853
+ label: "Computer Action",
854
+ description: "Perform one constrained semantic action in the authorized application window.",
855
+ parameters: {
856
+ type: "object",
857
+ properties: {
858
+ kind: {
859
+ type: "string",
860
+ enum: [
861
+ "press",
862
+ "focus",
863
+ "set_value",
864
+ "scroll"
865
+ ]
866
+ },
867
+ snapshotId: {
868
+ type: "string",
869
+ minLength: 1
870
+ },
871
+ elementRef: {
872
+ type: "string",
873
+ minLength: 1
874
+ },
875
+ value: { type: "string" },
876
+ direction: {
877
+ type: "string",
878
+ enum: [
879
+ "up",
880
+ "down",
881
+ "left",
882
+ "right"
883
+ ]
884
+ },
885
+ amount: {
886
+ type: "string",
887
+ enum: ["line", "page"]
888
+ }
889
+ },
890
+ required: [
891
+ "kind",
892
+ "snapshotId",
893
+ "elementRef"
894
+ ],
895
+ additionalProperties: false
896
+ },
897
+ executionMode: "serial",
898
+ async execute(_toolCallId, parameters, signal) {
899
+ try {
900
+ const result = await requireActive().act(parameters, signal);
901
+ await refresh(signal);
902
+ return output(result);
903
+ } catch (error) {
904
+ return output(error instanceof Error ? error.message : String(error), true);
905
+ }
906
+ }
907
+ },
908
+ {
909
+ when: {
910
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
911
+ attribution: {
912
+ kind: "minor",
913
+ mode: COMPUTER_USE_MODE_ID
914
+ }
915
+ },
916
+ name: "computer_exec",
917
+ label: "Computer Script",
918
+ description: "Run a reusable function with only the authorized program API. Relative helpers are supported. Full Node requires trusted:true and an explicitly allowlisted path.",
919
+ parameters: {
920
+ type: "object",
921
+ properties: {
922
+ scriptPath: {
923
+ type: "string",
924
+ minLength: 1
925
+ },
926
+ input: {},
927
+ trusted: { type: "boolean" },
928
+ includeScreenshot: { type: "boolean" }
929
+ },
930
+ required: ["scriptPath", "input"],
931
+ additionalProperties: false
932
+ },
933
+ executionMode: "serial",
934
+ async execute(_toolCallId, parameters, signal) {
935
+ try {
936
+ requireActive();
937
+ if (!dependencies.scriptRunner) throw new Error("Computer script execution is unavailable.");
938
+ const input = parameters;
939
+ return computerScriptOutput(await dependencies.scriptRunner.execute(input.scriptPath, input.input, signal, input));
940
+ } catch (error) {
941
+ return computerScriptFailure(error);
942
+ }
943
+ }
944
+ }
945
+ ],
946
+ commands: [{
947
+ name: require_extensionService.COMMAND_NAME,
948
+ description: require_extensionService.COMMAND_DESCRIPTION,
949
+ async execute(args, execution) {
950
+ const result = await dependencies.service.execute();
951
+ await execution.client.notify({
952
+ body: result.message,
953
+ level: result.level
954
+ });
955
+ if (args.trim() === "deactivate" && client && state?.phase !== "inactive" && state?.phase !== "failed") {
956
+ await client.stop();
957
+ await host.changeSelection({
958
+ axis: "state",
959
+ key: "minor-mode",
960
+ values: (execution.selection.state?.["minor-mode"] ?? []).filter((mode) => mode !== COMPUTER_USE_MODE_ID)
961
+ });
962
+ await refresh();
963
+ }
964
+ }
965
+ }],
966
+ hooks: [{
967
+ when: {
968
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
969
+ attribution: {
970
+ kind: "minor",
971
+ mode: COMPUTER_USE_MODE_ID
972
+ }
973
+ },
974
+ event: "before_agent_start",
975
+ handle(event) {
976
+ if (state?.phase !== "active") return void 0;
977
+ return { systemPrompt: `${typeof event.systemPrompt === "string" ? event.systemPrompt : ""}\n\n${GUIDANCE}`.trim() };
978
+ }
979
+ }, {
980
+ when: {
981
+ state: { "minor-mode": COMPUTER_USE_MODE_ID },
982
+ attribution: {
983
+ kind: "minor",
984
+ mode: COMPUTER_USE_MODE_ID
985
+ }
986
+ },
987
+ event: "session_shutdown",
988
+ handle: async () => {
989
+ stopActivity?.();
990
+ if (client && state?.phase !== "inactive" && state?.phase !== "failed") await client.stop();
991
+ }
992
+ }],
993
+ onDispose() {
994
+ stopActivity?.();
995
+ stopActivity = void 0;
996
+ unsubscribeState?.();
997
+ broker.close();
998
+ restrictionListeners.clear();
999
+ host.context.client.setStatus(COMPUTER_USE_STATUS_KEY, void 0);
1000
+ }
1001
+ };
1002
+ }
1003
+ //#endregion
1004
+ //#region src/extensions/workspaces/sessions/(backend)/_runtime/index.server.ts
1005
+ var index_server_default = (({ agent, host }) => agent ? createComputerUseServer(agent, host.context) : {
1006
+ tools: [],
1007
+ commands: []
1008
+ });
1009
+ //#endregion
1010
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
1011
+ var root_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoot)((context) => {
1012
+ const value = index_server_default(context);
1013
+ return {
1014
+ value,
1015
+ services: [...value.services ?? [], ...value.tools?.length ? [mountMcpTools(value.tools)] : []],
1016
+ activities: value.activities,
1017
+ onStop: value.onStop,
1018
+ onDispose: value.onDispose
1019
+ };
1020
+ });
1021
+ //#endregion
1022
+ //#region src/services/webComputerUseAvailability/index.ts
1023
+ var MissingComputerUseApiError = class extends Error {};
1024
+ function computerUseSessionApiError(status, value) {
1025
+ const error = typeof value === "object" && value !== null && !Array.isArray(value) ? value.error : void 0;
1026
+ const message = typeof error === "string" ? error : `HTTP ${String(status)}`;
1027
+ return status === 404 && message === `No API 'computer-use' in this session.` ? new MissingComputerUseApiError(message) : new Error(message);
1028
+ }
1029
+ //#endregion
1030
+ //#region src/services/webComputerUseChannel/index.ts
1031
+ /** Computer use runs on the Po Desktop host, so every other client declines it. */
1032
+ const DESKTOP_UNAVAILABLE = "Po Desktop computer use is unavailable.";
1033
+ function record(value) {
1034
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1035
+ }
1036
+ function command(value) {
1037
+ const input = record(value);
1038
+ return input?.action === "status" || input?.action === "targets" || input?.action === "stop" || input?.action === "artifacts" ? { action: input.action } : void 0;
1039
+ }
1040
+ async function responseJson(response) {
1041
+ const value = await response.json();
1042
+ if (!response.ok) throw computerUseSessionApiError(response.status, value);
1043
+ return value;
1044
+ }
1045
+ function sessionState(value) {
1046
+ const input = record(value);
1047
+ if (typeof input?.sessionId !== "string" || typeof input.revision !== "number" || typeof input.wake !== "number" || ![
1048
+ "inactive",
1049
+ "awaiting_confirmation",
1050
+ "activating",
1051
+ "active",
1052
+ "stopping",
1053
+ "failed"
1054
+ ].includes(input.phase)) return void 0;
1055
+ return input;
1056
+ }
1057
+ function createComputerUseChannel() {
1058
+ let receiveCommand;
1059
+ return {
1060
+ frameType: computerUseChannelType,
1061
+ lifecycle: "hub",
1062
+ start(host) {
1063
+ const scopes = /* @__PURE__ */ new Map();
1064
+ const latest = /* @__PURE__ */ new Map();
1065
+ const targets = /* @__PURE__ */ new Map();
1066
+ const grants = /* @__PURE__ */ new Map();
1067
+ const subscriptions = /* @__PURE__ */ new Map();
1068
+ const processing = /* @__PURE__ */ new Set();
1069
+ const rerun = /* @__PURE__ */ new Map();
1070
+ const requests = /* @__PURE__ */ new Map();
1071
+ let activeSessionId;
1072
+ let closed = false;
1073
+ const sessionRequest = async (scope, path, method = "GET", value) => host.requestSessionApi(scope, {
1074
+ basePath: API_BASE_PATH,
1075
+ path,
1076
+ method,
1077
+ ...value === void 0 ? {} : { body: JSON.stringify(value) }
1078
+ });
1079
+ const publish = (scope, state) => {
1080
+ if (state.phase === "active" || state.phase === "activating" || state.phase === "stopping") activeSessionId = scope.sessionId;
1081
+ else if (activeSessionId === scope.sessionId) {
1082
+ activeSessionId = void 0;
1083
+ grants.delete(scope.sessionId);
1084
+ }
1085
+ const payload = {
1086
+ state,
1087
+ targets: targets.get(scope.sessionId) ?? [],
1088
+ ...activeSessionId !== void 0 && activeSessionId !== scope.sessionId ? { busy: true } : {}
1089
+ };
1090
+ latest.set(scope.sessionId, payload);
1091
+ host.publish(scope.sessionId, payload);
1092
+ };
1093
+ const refresh = async (scope) => {
1094
+ const state = sessionState(await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubState)));
1095
+ if (state === void 0) throw new Error("Computer-use API returned an invalid session state.");
1096
+ publish(scope, state);
1097
+ return state;
1098
+ };
1099
+ const completePending = async (scope, pending) => {
1100
+ if (!host.computerUse?.available) throw new Error(DESKTOP_UNAVAILABLE);
1101
+ const controller = new AbortController();
1102
+ requests.set(scope.sessionId, controller);
1103
+ try {
1104
+ const result = await host.computerUse.request(scope, {
1105
+ operation: pending.operation,
1106
+ signal: controller.signal,
1107
+ payload: pending.operation === "act" ? {
1108
+ grantId: pending.grantId,
1109
+ sequence: pending.sequence,
1110
+ action: pending.payload
1111
+ } : {
1112
+ grantId: pending.grantId,
1113
+ includeScreenshot: record(pending.payload)?.includeScreenshot !== false
1114
+ }
1115
+ });
1116
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, "POST", {
1117
+ id: pending.id,
1118
+ result
1119
+ }));
1120
+ } catch (error) {
1121
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubComplete, "POST", {
1122
+ id: pending.id,
1123
+ error: error instanceof Error ? error.message : String(error)
1124
+ }));
1125
+ } finally {
1126
+ if (requests.get(scope.sessionId) === controller) requests.delete(scope.sessionId);
1127
+ }
1128
+ };
1129
+ const stopSession = async (scope) => {
1130
+ requests.get(scope.sessionId)?.abort();
1131
+ const authorization = await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization));
1132
+ let artifact;
1133
+ if (authorization !== null && host.computerUse?.available) try {
1134
+ artifact = record(await host.computerUse.request(scope, {
1135
+ operation: "stop",
1136
+ payload: { grantId: authorization.grantId }
1137
+ }))?.artifact;
1138
+ } catch (error) {
1139
+ const message = error instanceof Error ? error.message : String(error);
1140
+ artifact = {
1141
+ artifactId: (0, node_crypto.randomUUID)(),
1142
+ status: "failed",
1143
+ failure: {
1144
+ code: "recording_failed",
1145
+ message: message.slice(0, 512)
1146
+ }
1147
+ };
1148
+ host.onNotice(`computer-use Desktop stop was already unavailable (${message})`);
1149
+ }
1150
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, "POST", { artifact }));
1151
+ grants.delete(scope.sessionId);
1152
+ if (activeSessionId === scope.sessionId) activeSessionId = void 0;
1153
+ };
1154
+ const processScope = async (scope, announced) => {
1155
+ if (closed || !scopes.has(scope.sessionId)) return;
1156
+ if (processing.has(scope.sessionId)) {
1157
+ rerun.set(scope.sessionId, announced);
1158
+ return;
1159
+ }
1160
+ processing.add(scope.sessionId);
1161
+ try {
1162
+ const state = announced ?? await refresh(scope);
1163
+ if (state.phase === "active") {
1164
+ if (!grants.has(scope.sessionId)) {
1165
+ const authorization = await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubAuthorization));
1166
+ if (authorization !== null) grants.set(scope.sessionId, authorization.grantId);
1167
+ }
1168
+ const expired = typeof state.expiresAt === "number" && state.expiresAt <= Date.now();
1169
+ let owned = false;
1170
+ if (!expired && host.computerUse?.available) try {
1171
+ owned = record(await host.computerUse.request(scope, { operation: "status" }))?.ownedBySession === true;
1172
+ } catch {
1173
+ owned = false;
1174
+ }
1175
+ if (expired || !owned) {
1176
+ await stopSession(scope);
1177
+ await refresh(scope);
1178
+ return;
1179
+ }
1180
+ }
1181
+ if (state.phase === "awaiting_confirmation" && (activeSessionId === void 0 || activeSessionId === scope.sessionId)) {
1182
+ const activation = await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubActivation));
1183
+ if (activation !== null && !host.computerUse?.available) {
1184
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, "POST", { error: DESKTOP_UNAVAILABLE }));
1185
+ await refresh(scope);
1186
+ return;
1187
+ }
1188
+ if (activation !== null && host.computerUse?.available) {
1189
+ activeSessionId = scope.sessionId;
1190
+ const controller = new AbortController();
1191
+ requests.set(scope.sessionId, controller);
1192
+ try {
1193
+ const hostResult = await host.computerUse.request(scope, {
1194
+ operation: "activate",
1195
+ payload: activation,
1196
+ signal: controller.signal
1197
+ });
1198
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, "POST", { host: hostResult }));
1199
+ const grantId = record(hostResult)?.grantId;
1200
+ if (typeof grantId === "string") grants.set(scope.sessionId, grantId);
1201
+ } catch (error) {
1202
+ activeSessionId = void 0;
1203
+ await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubStop, "POST", { error: error instanceof Error ? error.message : String(error) }));
1204
+ } finally {
1205
+ if (requests.get(scope.sessionId) === controller) requests.delete(scope.sessionId);
1206
+ }
1207
+ await refresh(scope);
1208
+ }
1209
+ }
1210
+ if (state.phase === "stopping") {
1211
+ await stopSession(scope);
1212
+ await refresh(scope);
1213
+ }
1214
+ const pending = await responseJson(await sessionRequest(scope, COMPUTER_USE_ROUTES.hubNext));
1215
+ if (pending !== null) await completePending(scope, pending);
1216
+ } catch (error) {
1217
+ if (error instanceof MissingComputerUseApiError) latest.delete(scope.sessionId);
1218
+ else host.onNotice(`computer-use state handling failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`);
1219
+ } finally {
1220
+ processing.delete(scope.sessionId);
1221
+ if (!closed && scopes.has(scope.sessionId) && rerun.has(scope.sessionId)) {
1222
+ const next = rerun.get(scope.sessionId);
1223
+ rerun.delete(scope.sessionId);
1224
+ queueMicrotask(() => {
1225
+ processScope(scope, next);
1226
+ });
1227
+ }
1228
+ }
1229
+ };
1230
+ const onState = (scope, payload) => {
1231
+ const state = sessionState(payload);
1232
+ if (state === void 0 || state.sessionId !== scope.sessionId) return;
1233
+ if (state.phase === "stopping" || state.phase === "failed" || state.phase === "inactive") requests.get(scope.sessionId)?.abort();
1234
+ publish(scope, state);
1235
+ processScope(scope, state);
1236
+ };
1237
+ receiveCommand = (scope, payload) => {
1238
+ const parsed = command(payload);
1239
+ if (parsed === void 0) return;
1240
+ (async () => {
1241
+ try {
1242
+ if (parsed.action === "targets") {
1243
+ const result = host.computerUse?.available ? await host.computerUse.request(scope, { operation: "targets" }) : [];
1244
+ targets.set(scope.sessionId, Array.isArray(result) ? result.filter((item) => record(item) !== void 0) : []);
1245
+ } else if (parsed.action === "stop") await stopSession(scope);
1246
+ await refresh(scope);
1247
+ processScope(scope);
1248
+ } catch (error) {
1249
+ if (error instanceof MissingComputerUseApiError) latest.delete(scope.sessionId);
1250
+ else host.onNotice(`computer-use command failed for ${scope.sessionId} (${error instanceof Error ? error.message : String(error)})`);
1251
+ }
1252
+ })();
1253
+ };
1254
+ return {
1255
+ payloadFor: (scope) => latest.get(scope.sessionId),
1256
+ sessionAdded(scope) {
1257
+ scopes.set(scope.sessionId, scope);
1258
+ const unsubscribe = host.directEvents.subscribe(computerUseChannelType, scope.sessionId, (payload) => onState(scope, payload), { replayLatest: true });
1259
+ subscriptions.set(scope.sessionId, unsubscribe);
1260
+ processScope(scope);
1261
+ },
1262
+ sessionRemoved(sessionId) {
1263
+ requests.get(sessionId)?.abort();
1264
+ requests.delete(sessionId);
1265
+ rerun.delete(sessionId);
1266
+ const scope = scopes.get(sessionId);
1267
+ const grantId = grants.get(sessionId);
1268
+ subscriptions.get(sessionId)?.();
1269
+ subscriptions.delete(sessionId);
1270
+ if (scope !== void 0 && grantId !== void 0 && host.computerUse?.available) host.computerUse.request(scope, {
1271
+ operation: "stop",
1272
+ payload: { grantId }
1273
+ }).catch((error) => host.onNotice(`computer-use session cleanup failed (${error instanceof Error ? error.message : String(error)})`));
1274
+ scopes.delete(sessionId);
1275
+ latest.delete(sessionId);
1276
+ targets.delete(sessionId);
1277
+ grants.delete(sessionId);
1278
+ processing.delete(sessionId);
1279
+ if (activeSessionId === sessionId) activeSessionId = void 0;
1280
+ },
1281
+ close() {
1282
+ closed = true;
1283
+ for (const controller of requests.values()) controller.abort();
1284
+ requests.clear();
1285
+ rerun.clear();
1286
+ for (const unsubscribe of subscriptions.values()) unsubscribe();
1287
+ subscriptions.clear();
1288
+ if (host.computerUse?.available) for (const [sessionId, grantId] of grants) {
1289
+ const scope = scopes.get(sessionId);
1290
+ if (scope !== void 0) host.computerUse.request(scope, {
1291
+ operation: "stop",
1292
+ payload: { grantId }
1293
+ }).catch(() => void 0);
1294
+ }
1295
+ scopes.clear();
1296
+ latest.clear();
1297
+ targets.clear();
1298
+ grants.clear();
1299
+ }
1300
+ };
1301
+ },
1302
+ receive(scope, payload, connection) {
1303
+ if (connection.desktopAuthorized !== true && connection.computerUseAuthorized !== true) return;
1304
+ receiveCommand?.(scope, payload);
1305
+ }
1306
+ };
1307
+ }
1308
+ //#endregion
1309
+ //#region src/extensions/(backend)/channel/computer-use.server.ts
1310
+ var computer_use_server_default$2 = (0, _agimon_ai_po_core_extensionFile.defineChannel)(createComputerUseChannel);
1311
+ //#endregion
1312
+ //#region src/extensions/workspaces/sessions/(backend)/api/computer-use/route.server.ts
1313
+ var route_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.api?.find((api) => api.basePath === "computer-use"), { cardinality: "optional" });
1314
+ //#endregion
1315
+ //#region src/extensions/workspaces/sessions/(backend)/command/computer-use.server.ts
1316
+ var computer_use_server_default$1 = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.commands.find((command) => command.name === "computer-use"), { cardinality: "optional" });
1317
+ //#endregion
1318
+ //#region src/extensions/workspaces/sessions/(backend)/hook/before-agent-start.server.ts
1319
+ var before_agent_start_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "before_agent_start"), { cardinality: "optional" });
1320
+ //#endregion
1321
+ //#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
1322
+ var session_shutdown_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.hooks?.find((hook) => hook.event === "session_shutdown"), { cardinality: "optional" });
1323
+ //#endregion
1324
+ //#region src/extensions/workspaces/sessions/(backend)/tool-restriction/computer-use.server.ts
1325
+ var computer_use_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.toolRestrictions?.[0], { cardinality: "optional" });
1326
+ //#endregion
1327
+ //#region src/extensions/workspaces/sessions/(backend)/tool/00-computer-state.server.ts
1328
+ var _00_computer_state_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.tools.find((tool) => tool.name === "computer_state"), { cardinality: "optional" });
1329
+ //#endregion
1330
+ //#region src/extensions/workspaces/sessions/(backend)/tool/10-computer-action.server.ts
1331
+ var _10_computer_action_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.tools.find((tool) => tool.name === "computer_action"), { cardinality: "optional" });
1332
+ //#endregion
1333
+ //#region src/extensions/workspaces/sessions/(backend)/tool/20-computer-exec.server.ts
1334
+ var _20_computer_exec_server_default = (0, _agimon_ai_po_core_extensionFile.defineRoutedContribution)((context) => context.root.tools.find((tool) => tool.name === "computer_exec"), { cardinality: "optional" });
1335
+ //#endregion
1336
+ //#region generated/server.ts
1337
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
1338
+ /* v8 ignore next -- generated normalization branch */
1339
+ const defined = (values) => values.filter((value) => value !== void 0);
1340
+ const via = (identity, value) => value === void 0 ? void 0 : {
1341
+ ...identity,
1342
+ ...value
1343
+ };
1344
+ const facet = (0, _agimon_ai_po_core_serverFacet.defineServerPlugin)({
1345
+ name: "@agimon-ai/po-computer-use",
1346
+ providerOverrides: (0, _agimon_ai_po_core_serverFacet.composeServerProviderOverrides)(root_server_default),
1347
+ global: () => ({ get channels() {
1348
+ return [() => via({ frameType: "computer_use" }, computer_use_server_default$2())];
1349
+ } }),
1350
+ workspace: () => ({ get channels() {
1351
+ return [() => via({ frameType: "computer_use" }, computer_use_server_default$2())];
1352
+ } }),
1353
+ session: async (context) => {
1354
+ const scopeSession = await root_server_default(context);
1355
+ const contextSession = {
1356
+ ...context,
1357
+ root: scopeSession.value
1358
+ };
1359
+ return {
1360
+ ...(0, _agimon_ai_po_core_extensionFile.composeRootHooks)(scopeSession),
1361
+ get api() {
1362
+ return defined([at(route_server_default, contextSession)]);
1363
+ },
1364
+ get channels() {
1365
+ return [() => via({ frameType: "computer_use" }, computer_use_server_default$2())];
1366
+ },
1367
+ get commands() {
1368
+ return defined([via({ name: "computer-use" }, at(computer_use_server_default$1, contextSession))]);
1369
+ },
1370
+ get hooks() {
1371
+ return defined([via({ event: "before_agent_start" }, at(before_agent_start_server_default, contextSession)), via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession))]);
1372
+ },
1373
+ get toolRestrictions() {
1374
+ return defined([at(computer_use_server_default, contextSession)]);
1375
+ },
1376
+ get tools() {
1377
+ return defined([
1378
+ via({ name: "00_computer_state" }, at(_00_computer_state_server_default, contextSession)),
1379
+ via({ name: "10_computer_action" }, at(_10_computer_action_server_default, contextSession)),
1380
+ via({ name: "20_computer_exec" }, at(_20_computer_exec_server_default, contextSession))
1381
+ ]);
1382
+ },
1383
+ services: [...scopeSession.services ?? []],
1384
+ get activities() {
1385
+ return [...scopeSession.activities ?? []];
1386
+ }
1387
+ };
1388
+ }
1389
+ });
1390
+ //#endregion
1391
+ exports.default = facet;
1392
+ exports.facet = facet;
1393
+
1394
+ //# sourceMappingURL=server.cjs.map