@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,771 @@
1
+ import { apiResponse, createApiClient, defineActivityGroup, defineApiRoutes, defineChannelFile, defineFill, defineMinorModeFile, defineSessionStore, defineSettingsSection, defineToolRenderer, defineWebPlugin, sessionApiAddress, toolResultTextLines } from "@agimon-ai/po-core/web";
2
+ import { Button, MessageItem, MessageItemBody, MessageItemHeader, MessageItemStatus, MessageLines, collapseLines, toolTone } from "@agimon-ai/po-web-components";
3
+ import { useStore } from "@tanstack/react-store";
4
+ import { useEffect, useState } from "react";
5
+ import { sealedTransport } from "@agimon-ai/po-web-security/browser";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/extensions/(frontend)/_lib/computerUseSettings.ts
8
+ const computerUseSettingsSection = {
9
+ id: "computer-use",
10
+ desktopOnly: true,
11
+ label: "computer use",
12
+ detail: "control whether Po Desktop may expose session-scoped computer control",
13
+ order: 45,
14
+ fields: [{
15
+ id: "enabled",
16
+ label: "enable computer use",
17
+ kind: "toggle",
18
+ keyPath: ["computerUse", "enabled"],
19
+ detail: "Makes the computer-use minor mode available. Each control session still requires native confirmation."
20
+ }]
21
+ };
22
+ //#endregion
23
+ //#region src/extensions/(frontend)/setting/computer-use.web.ts
24
+ var computer_use_web_default$3 = defineSettingsSection({ settingsSections: [computerUseSettingsSection] }.settingsSections[0]);
25
+ //#endregion
26
+ //#region src/extensions/workspaces/(frontend)/setting/computer-use.web.ts
27
+ var computer_use_web_default$2 = defineSettingsSection({ settingsSections: [computerUseSettingsSection] }.settingsSections[0]);
28
+ //#endregion
29
+ //#region src/types/computerUseApi.ts
30
+ const computerUseChannelType = "computer_use_state";
31
+ const COMPUTER_USE_MODE_ID = "computer-use";
32
+ const COMPUTER_USE_STATUS_KEY = "doom-computer-use";
33
+ const COMPUTER_USE_MODE_STATUS_KEY = "doom-computer-use-mode";
34
+ const COMPUTER_USE_DEFAULT_DURATION_MS = 3e5;
35
+ /**
36
+ * Every path this API answers on, below its mount.
37
+ *
38
+ * The typed route table in `./apiRoutes` is built from this, the dispatcher
39
+ * reads that table, and the hub channel addresses the broker with these same
40
+ * strings, so a route cannot move on one side alone.
41
+ */
42
+ const COMPUTER_USE_ROUTES = {
43
+ activate: "/activate",
44
+ artifact: "/artifact",
45
+ agentState: "/agent/state",
46
+ agentObserve: "/agent/observe",
47
+ agentAction: "/agent/action",
48
+ agentStop: "/agent/stop",
49
+ hubState: "/hub/state",
50
+ hubActivation: "/hub/activation",
51
+ hubAuthorization: "/hub/authorization",
52
+ hubNext: "/hub/next",
53
+ hubComplete: "/hub/complete",
54
+ hubStop: "/hub/stop"
55
+ };
56
+ const computerUse = defineSessionStore({
57
+ state: {
58
+ sessionId: "",
59
+ revision: 0,
60
+ wake: 0,
61
+ phase: "inactive"
62
+ },
63
+ targets: []
64
+ });
65
+ function isRecord(value) {
66
+ return typeof value === "object" && value !== null && !Array.isArray(value);
67
+ }
68
+ const computerUseChannel = computerUse.channel({
69
+ channel: computerUseChannelType,
70
+ parse(input) {
71
+ if (!isRecord(input) || !isRecord(input.state) || !Array.isArray(input.targets)) return null;
72
+ if (typeof input.state.phase !== "string" || typeof input.state.revision !== "number") return null;
73
+ return input;
74
+ },
75
+ reduce: (_current, payload) => payload
76
+ });
77
+ //#endregion
78
+ //#region src/types/apiRoutes.ts
79
+ /**
80
+ * This package's routes, as data.
81
+ *
82
+ * No scope and no base path: the build reads both off
83
+ * `src/extensions/workspaces/sessions/(backend)/api/computer-use/`, so the
84
+ * mount is stated once, by the folder that creates it.
85
+ *
86
+ * The paths come from `COMPUTER_USE_ROUTES` rather than being written out
87
+ * again. That table already addressed the broker from the hub channel and from
88
+ * the broker's own dispatcher, and a second copy here is exactly the
89
+ * disagreement this file exists to prevent. The dispatcher now reads this
90
+ * table, so the page, the broker and the hub all resolve one set of strings.
91
+ *
92
+ * Activation, status, cancellation and recording reads are reachable from a browser. The `/agent/` and `/hub/` routes
93
+ * each demand a host-issued bearer token and answer 404 without one; they are
94
+ * declared here because they are this API's surface, not because a page calls
95
+ * them.
96
+ */
97
+ var apiRoutes_default = defineApiRoutes({
98
+ artifact: {
99
+ method: "GET",
100
+ path: COMPUTER_USE_ROUTES.artifact,
101
+ query: ["artifactId", "offset"],
102
+ raw: true
103
+ },
104
+ activationState: {
105
+ method: "GET",
106
+ path: COMPUTER_USE_ROUTES.activate,
107
+ response: apiResponse()
108
+ },
109
+ cancelActivation: {
110
+ method: "DELETE",
111
+ path: COMPUTER_USE_ROUTES.activate,
112
+ response: apiResponse()
113
+ },
114
+ activate: {
115
+ method: "POST",
116
+ path: COMPUTER_USE_ROUTES.activate,
117
+ response: apiResponse()
118
+ },
119
+ agentState: {
120
+ method: "GET",
121
+ path: COMPUTER_USE_ROUTES.agentState,
122
+ response: apiResponse()
123
+ },
124
+ /** Answers whatever the admitted Desktop provider returned, which is runtime-defined JSON. */
125
+ agentObserve: {
126
+ method: "POST",
127
+ path: COMPUTER_USE_ROUTES.agentObserve,
128
+ response: apiResponse()
129
+ },
130
+ agentAction: {
131
+ method: "POST",
132
+ path: COMPUTER_USE_ROUTES.agentAction,
133
+ response: apiResponse()
134
+ },
135
+ agentStop: {
136
+ method: "POST",
137
+ path: COMPUTER_USE_ROUTES.agentStop,
138
+ response: apiResponse()
139
+ },
140
+ hubState: {
141
+ method: "GET",
142
+ path: COMPUTER_USE_ROUTES.hubState,
143
+ response: apiResponse()
144
+ },
145
+ /** Null until a reader has confirmed one; reading it moves the session to `activating`. */
146
+ hubActivation: {
147
+ method: "GET",
148
+ path: COMPUTER_USE_ROUTES.hubActivation,
149
+ response: apiResponse()
150
+ },
151
+ hubAuthorization: {
152
+ method: "GET",
153
+ path: COMPUTER_USE_ROUTES.hubAuthorization,
154
+ response: apiResponse()
155
+ },
156
+ hubNext: {
157
+ method: "GET",
158
+ path: COMPUTER_USE_ROUTES.hubNext,
159
+ response: apiResponse()
160
+ },
161
+ hubComplete: {
162
+ method: "POST",
163
+ path: COMPUTER_USE_ROUTES.hubComplete,
164
+ response: apiResponse()
165
+ },
166
+ hubStop: {
167
+ method: "POST",
168
+ path: COMPUTER_USE_ROUTES.hubStop,
169
+ response: apiResponse()
170
+ }
171
+ });
172
+ const api = createApiClient(apiRoutes_default, {
173
+ scopes: ["session"],
174
+ basePath: "computer-use",
175
+ transport: (input, init) => sealedTransport.fetch(input, init),
176
+ sessionAddress: sessionApiAddress
177
+ });
178
+ //#endregion
179
+ //#region src/extensions/workspaces/sessions/(frontend)/_lib/computerUseRecording.ts
180
+ const MAX_RECORDING_BYTES = 134217728;
181
+ const CHUNK_BYTES = 1048576;
182
+ /** Owns only a bounded sealed download and its disposable browser URL. */
183
+ async function loadComputerUseRecording(sessionId, artifactId, sizeBytes, signal) {
184
+ if (!Number.isSafeInteger(sizeBytes) || sizeBytes < 1 || sizeBytes > MAX_RECORDING_BYTES) throw new Error("Recording exceeds the permitted size.");
185
+ const parts = [];
186
+ for (let offset = 0; offset < sizeBytes; offset += CHUNK_BYTES) {
187
+ signal.throwIfAborted();
188
+ const result = await api.session(sessionId).artifact({
189
+ query: {
190
+ artifactId,
191
+ offset
192
+ },
193
+ signal
194
+ });
195
+ if (!result.ok || result.status !== 206) throw new Error("The recording is unavailable or access was revoked.");
196
+ const end = Math.min(offset + CHUNK_BYTES, sizeBytes) - 1;
197
+ if (result.response.headers.get("content-range") !== `bytes ${offset}-${end}/${sizeBytes}`) throw new Error("The recording response is invalid.");
198
+ const bytes = await result.response.arrayBuffer();
199
+ signal.throwIfAborted();
200
+ if (bytes.byteLength !== end - offset + 1) throw new Error("The recording response is incomplete.");
201
+ parts.push(bytes);
202
+ }
203
+ signal.throwIfAborted();
204
+ const url = URL.createObjectURL(new Blob(parts, { type: "video/mp4" }));
205
+ let disposed = false;
206
+ return {
207
+ url,
208
+ dispose() {
209
+ if (!disposed) {
210
+ disposed = true;
211
+ URL.revokeObjectURL(url);
212
+ }
213
+ }
214
+ };
215
+ }
216
+ //#endregion
217
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/_components/ComputerUsePanel.tsx
218
+ function text(value, fallback) {
219
+ return typeof value === "string" || typeof value === "number" ? String(value) : fallback;
220
+ }
221
+ function ComputerUsePanel({ sessionId, sendSessionFrame }) {
222
+ const session = useStore(computerUse.store, (state) => computerUse.select(state, sessionId));
223
+ const [selected, setSelected] = useState(0);
224
+ const [confirmationTarget, setConfirmationTarget] = useState();
225
+ const [error, setError] = useState();
226
+ const [recordingUrl, setRecordingUrl] = useState();
227
+ const [recordingAccessDenied, setRecordingAccessDenied] = useState(false);
228
+ const send = (payload) => {
229
+ if (sessionId !== null) sendSessionFrame(sessionId, {
230
+ type: computerUseChannelType,
231
+ payload
232
+ });
233
+ };
234
+ useEffect(() => {
235
+ if (sessionId === null) return;
236
+ sendSessionFrame(sessionId, {
237
+ type: computerUseChannelType,
238
+ payload: { action: "status" }
239
+ });
240
+ sendSessionFrame(sessionId, {
241
+ type: computerUseChannelType,
242
+ payload: { action: "targets" }
243
+ });
244
+ }, [sendSessionFrame, sessionId]);
245
+ useEffect(() => {
246
+ if (sessionId === null) return;
247
+ const controller = new AbortController();
248
+ let timer;
249
+ const poll = async () => {
250
+ try {
251
+ const result = await api.session(sessionId).activationState({ signal: controller.signal });
252
+ if (controller.signal.aborted) return;
253
+ setRecordingAccessDenied(!result.ok);
254
+ if (result.ok) computerUse.update(sessionId, (current) => result.data.revision >= current.state.revision ? {
255
+ ...current,
256
+ state: result.data
257
+ } : current);
258
+ } catch {
259
+ if (!controller.signal.aborted) setRecordingAccessDenied(true);
260
+ }
261
+ if (!controller.signal.aborted) timer = setTimeout(() => void poll(), 2e3);
262
+ };
263
+ poll();
264
+ return () => {
265
+ controller.abort();
266
+ clearTimeout(timer);
267
+ };
268
+ }, [sessionId]);
269
+ const artifactId = session.state.artifact?.artifactId;
270
+ const artifactSize = session.state.artifact?.sizeBytes;
271
+ const artifactStatus = session.state.artifact?.status;
272
+ useEffect(() => {
273
+ setRecordingUrl(void 0);
274
+ if (recordingAccessDenied || sessionId === null || artifactId === void 0 || artifactSize === void 0 || artifactStatus !== "ready") return;
275
+ const controller = new AbortController();
276
+ let asset;
277
+ loadComputerUseRecording(sessionId, artifactId, artifactSize, controller.signal).then((loaded) => {
278
+ if (controller.signal.aborted) loaded.dispose();
279
+ else {
280
+ asset = loaded;
281
+ setRecordingUrl(loaded.url);
282
+ }
283
+ }).catch(() => {
284
+ if (!controller.signal.aborted) setError("The recording is unavailable or access was revoked.");
285
+ });
286
+ return () => {
287
+ controller.abort();
288
+ asset?.dispose();
289
+ };
290
+ }, [
291
+ sessionId,
292
+ artifactId,
293
+ artifactSize,
294
+ artifactStatus,
295
+ recordingAccessDenied
296
+ ]);
297
+ if (sessionId === null) return /* @__PURE__ */ jsx("p", {
298
+ className: "px-1 text-sm text-doom-dim",
299
+ children: "Select a session."
300
+ });
301
+ const state = session.state;
302
+ const target = session.targets[selected];
303
+ const artifact = state.artifact;
304
+ const confirm = async (chosenTarget = confirmationTarget) => {
305
+ setError(void 0);
306
+ try {
307
+ const result = await api.session(sessionId).activate({ body: {
308
+ ...chosenTarget === void 0 ? {} : { target: chosenTarget },
309
+ durationMs: COMPUTER_USE_DEFAULT_DURATION_MS
310
+ } });
311
+ if (!result.ok) {
312
+ if (result.status === 0) setError("The session is unreachable.");
313
+ else setError(result.error === "" ? `Activation failed with HTTP ${result.status}.` : result.error);
314
+ return;
315
+ }
316
+ setConfirmationTarget(void 0);
317
+ computerUse.update(sessionId, (current) => ({
318
+ ...current,
319
+ state: result.data
320
+ }));
321
+ send({ action: "status" });
322
+ } catch {
323
+ setError("The activation request is unavailable.");
324
+ }
325
+ };
326
+ return /* @__PURE__ */ jsxs("div", {
327
+ "data-testid": "computer-use-panel",
328
+ className: "flex flex-col gap-2 text-base text-doom-text",
329
+ children: [
330
+ /* @__PURE__ */ jsxs("header", {
331
+ className: "flex items-center justify-between gap-2",
332
+ children: [/* @__PURE__ */ jsx("span", {
333
+ className: "text-doom-dim",
334
+ children: state.phase.replaceAll("_", " ")
335
+ }), /* @__PURE__ */ jsx(Button, {
336
+ size: "sm",
337
+ type: "button",
338
+ className: "min-h-7 rounded border border-doom-border bg-doom-panel px-2 py-1",
339
+ onClick: () => send({ action: "targets" }),
340
+ children: "Refresh targets"
341
+ })]
342
+ }),
343
+ session.busy ? /* @__PURE__ */ jsx("p", {
344
+ className: "rounded border border-doom-yellow px-2 py-1",
345
+ children: "Computer use is busy in another session"
346
+ }) : null,
347
+ state.failure ? /* @__PURE__ */ jsx("p", {
348
+ className: "text-doom-red",
349
+ children: state.failure.message
350
+ }) : null,
351
+ error ? /* @__PURE__ */ jsx("p", {
352
+ className: "text-doom-red",
353
+ children: error
354
+ }) : null,
355
+ (state.phase === "inactive" || state.phase === "failed") && confirmationTarget === void 0 ? /* @__PURE__ */ jsxs("div", {
356
+ className: "flex flex-col gap-2",
357
+ children: [
358
+ /* @__PURE__ */ jsx("label", {
359
+ htmlFor: "computer-use-target",
360
+ children: "Application window"
361
+ }),
362
+ /* @__PURE__ */ jsx("select", {
363
+ id: "computer-use-target",
364
+ value: selected,
365
+ onChange: (event) => setSelected(Number(event.target.value)),
366
+ className: "min-h-7 rounded border border-doom-border bg-doom-panel px-2 py-1",
367
+ children: session.targets.map((item, index) => /* @__PURE__ */ jsx("option", {
368
+ value: index,
369
+ children: text(item.applicationName, text(item.windowTitle, text(item.windowId, "Window")))
370
+ }, text(item.windowId, String(index))))
371
+ }),
372
+ /* @__PURE__ */ jsx(Button, {
373
+ size: "sm",
374
+ type: "button",
375
+ disabled: Boolean(session.busy),
376
+ variant: "primary",
377
+ onClick: () => {
378
+ if (target === void 0) confirm();
379
+ else setConfirmationTarget(Object.freeze({ ...target }));
380
+ },
381
+ children: target === void 0 ? "Choose window on Mac" : "Request activation"
382
+ })
383
+ ]
384
+ }) : null,
385
+ confirmationTarget !== void 0 ? /* @__PURE__ */ jsxs("div", {
386
+ className: "rounded border border-doom-blue p-3",
387
+ children: [
388
+ /* @__PURE__ */ jsx("strong", { children: "Confirm computer control" }),
389
+ /* @__PURE__ */ jsxs("p", {
390
+ className: "my-2 text-doom-text",
391
+ children: [
392
+ text(confirmationTarget.applicationName, "Application"),
393
+ ":",
394
+ " ",
395
+ text(confirmationTarget.windowTitle, text(confirmationTarget.windowId, "Window"))
396
+ ]
397
+ }),
398
+ /* @__PURE__ */ jsx("p", {
399
+ className: "my-2 text-doom-dim",
400
+ children: "Confirm a 5 minute session for this exact application window."
401
+ }),
402
+ /* @__PURE__ */ jsxs("div", {
403
+ className: "flex gap-2",
404
+ children: [/* @__PURE__ */ jsx(Button, {
405
+ size: "sm",
406
+ type: "button",
407
+ variant: "primary",
408
+ onClick: () => void confirm(),
409
+ children: "Confirm"
410
+ }), /* @__PURE__ */ jsx(Button, {
411
+ size: "sm",
412
+ type: "button",
413
+ className: "min-h-7 rounded border border-doom-border bg-doom-panel px-2 py-1",
414
+ onClick: () => setConfirmationTarget(void 0),
415
+ children: "Cancel"
416
+ })]
417
+ })
418
+ ]
419
+ }) : null,
420
+ state.phase === "awaiting_confirmation" || state.phase === "activating" ? /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", { children: "Desktop activation is pending." }), /* @__PURE__ */ jsx(Button, {
421
+ size: "sm",
422
+ type: "button",
423
+ onClick: () => {
424
+ (async () => {
425
+ try {
426
+ const result = await api.session(sessionId).cancelActivation();
427
+ if (!result.ok) setError("The activation could not be cancelled.");
428
+ else computerUse.update(sessionId, (current) => ({
429
+ ...current,
430
+ state: result.data
431
+ }));
432
+ } catch {
433
+ setError("The activation could not be cancelled.");
434
+ }
435
+ })();
436
+ },
437
+ children: "Cancel activation"
438
+ })] }) : null,
439
+ state.phase === "active" || state.phase === "stopping" ? /* @__PURE__ */ jsx(Button, {
440
+ size: "sm",
441
+ type: "button",
442
+ disabled: state.phase === "stopping",
443
+ className: "min-h-7 rounded border border-doom-border bg-doom-panel px-2 py-1",
444
+ onClick: () => send({ action: "stop" }),
445
+ children: "Stop computer use"
446
+ }) : null,
447
+ artifact ? /* @__PURE__ */ jsxs("div", {
448
+ "data-testid": "computer-use-artifact",
449
+ className: "rounded bg-doom-panel p-2",
450
+ children: [
451
+ /* @__PURE__ */ jsx("strong", { children: artifact.status === "ready" ? "Completed recording" : "Recording unavailable" }),
452
+ /* @__PURE__ */ jsxs("p", { children: ["ID: ", artifact.artifactId] }),
453
+ /* @__PURE__ */ jsxs("p", { children: ["Status: ", artifact.status] }),
454
+ artifact.failure ? /* @__PURE__ */ jsx("p", {
455
+ className: "text-doom-red",
456
+ children: artifact.failure.message
457
+ }) : null,
458
+ artifact.completedAt ? /* @__PURE__ */ jsxs("p", { children: ["Completed: ", artifact.completedAt] }) : null,
459
+ artifact.actionCount !== void 0 ? /* @__PURE__ */ jsxs("p", { children: ["Actions: ", artifact.actionCount] }) : null,
460
+ artifact.status === "ready" && artifact.sizeBytes && !recordingUrl ? /* @__PURE__ */ jsx("p", { children: "Loading recording through the secure channel." }) : null,
461
+ artifact.status === "ready" && recordingUrl ? /* @__PURE__ */ jsx("video", {
462
+ className: "mt-2 w-full",
463
+ controls: true,
464
+ preload: "metadata",
465
+ src: recordingUrl,
466
+ children: /* @__PURE__ */ jsx("track", { kind: "captions" })
467
+ }) : null,
468
+ artifact.status === "ready" && recordingUrl ? /* @__PURE__ */ jsx("a", {
469
+ className: "mt-2 inline-block text-doom-blue",
470
+ href: recordingUrl,
471
+ download: "Po-recording.mp4",
472
+ children: "Download recording"
473
+ }) : null
474
+ ]
475
+ }) : null
476
+ ]
477
+ });
478
+ }
479
+ //#endregion
480
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/computerActionToolRender.ts
481
+ const computerActionToolName = "computer_action";
482
+ function computerActionCallSummary(args) {
483
+ return [typeof args.kind === "string" ? args.kind : "", typeof args.elementRef === "string" ? args.elementRef : ""].filter(Boolean).join(" ");
484
+ }
485
+ /**
486
+ * The body as toned lines: what the result's details say, or its text. Pure,
487
+ * so a unit test can pin it without a DOM; the item decides how many to show.
488
+ */
489
+ function computerActionResultLines(result) {
490
+ if (result === null) return [];
491
+ return toolResultTextLines(result.content).map((text) => ({
492
+ text,
493
+ tone: "dim"
494
+ }));
495
+ }
496
+ //#endregion
497
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_components/ComputerActionToolCard.tsx
498
+ /**
499
+ * The computer_action tool's timeline item: Perform one constrained semantic action in the authorized application window. The shell owns
500
+ * the frame, the outcome tone, the status badge, and the expand toggle; this
501
+ * card supplies the header summary and the body lines, and says whether it
502
+ * hides any so the toggle appears only when it reveals something.
503
+ */
504
+ function ComputerActionToolCard({ toolName, args, result, running, isError }) {
505
+ const lines = computerActionResultLines(result);
506
+ const collapsed = collapseLines(lines, 8, false);
507
+ return /* @__PURE__ */ jsx(MessageItem, {
508
+ tone: toolTone({
509
+ running,
510
+ isError
511
+ }),
512
+ expandable: collapsed.hidden > 0,
513
+ children: ({ expanded }) => {
514
+ const view = expanded ? collapseLines(lines, 8, true) : collapsed;
515
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MessageItemHeader, {
516
+ title: toolName,
517
+ children: /* @__PURE__ */ jsx("span", {
518
+ "data-testid": `tool-call-${toolName}`,
519
+ className: "min-w-0 flex-1 truncate text-doom-text",
520
+ children: computerActionCallSummary(args)
521
+ })
522
+ }), view.shown.length > 0 || running || isError ? /* @__PURE__ */ jsxs(MessageItemBody, {
523
+ "data-testid": `tool-result-${toolName}`,
524
+ className: "flex flex-col gap-1",
525
+ children: [
526
+ view.shown.length > 0 ? /* @__PURE__ */ jsx(MessageLines, { lines: view.shown }) : null,
527
+ view.hidden > 0 ? /* @__PURE__ */ jsxs(MessageItemStatus, {
528
+ expands: true,
529
+ children: [view.hidden, " more line(s)"]
530
+ }) : null,
531
+ running ? /* @__PURE__ */ jsx(MessageItemStatus, {
532
+ tone: "running",
533
+ children: "running"
534
+ }) : null,
535
+ isError ? /* @__PURE__ */ jsx(MessageItemStatus, {
536
+ tone: "error",
537
+ children: "failed"
538
+ }) : null
539
+ ]
540
+ }) : null] });
541
+ }
542
+ });
543
+ }
544
+ //#endregion
545
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/computerExecToolRender.ts
546
+ const computerExecToolName = "computer_exec";
547
+ function computerExecCallSummary(args) {
548
+ return typeof args.scriptPath === "string" ? args.scriptPath : "";
549
+ }
550
+ function computerExecResultLines(result) {
551
+ if (result === null) return [];
552
+ return toolResultTextLines(result.content).map((text) => ({
553
+ text,
554
+ tone: "dim"
555
+ }));
556
+ }
557
+ //#endregion
558
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_components/ComputerExecToolCard.tsx
559
+ function ComputerExecToolCard({ toolName, args, result, running, isError }) {
560
+ const lines = computerExecResultLines(result);
561
+ const collapsed = collapseLines(lines, 8, false);
562
+ return /* @__PURE__ */ jsx(MessageItem, {
563
+ tone: toolTone({
564
+ running,
565
+ isError
566
+ }),
567
+ expandable: collapsed.hidden > 0,
568
+ children: ({ expanded }) => {
569
+ const view = expanded ? collapseLines(lines, 8, true) : collapsed;
570
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MessageItemHeader, {
571
+ title: toolName,
572
+ children: /* @__PURE__ */ jsx("span", {
573
+ "data-testid": `tool-call-${toolName}`,
574
+ className: "min-w-0 flex-1 truncate text-doom-text",
575
+ children: computerExecCallSummary(args)
576
+ })
577
+ }), view.shown.length > 0 || running || isError ? /* @__PURE__ */ jsxs(MessageItemBody, {
578
+ "data-testid": `tool-result-${toolName}`,
579
+ className: "flex flex-col gap-1",
580
+ children: [
581
+ view.shown.length > 0 ? /* @__PURE__ */ jsx(MessageLines, { lines: view.shown }) : null,
582
+ view.hidden > 0 ? /* @__PURE__ */ jsxs(MessageItemStatus, {
583
+ expands: true,
584
+ children: [view.hidden, " more line(s)"]
585
+ }) : null,
586
+ running ? /* @__PURE__ */ jsx(MessageItemStatus, {
587
+ tone: "running",
588
+ children: "running"
589
+ }) : null,
590
+ isError ? /* @__PURE__ */ jsx(MessageItemStatus, {
591
+ tone: "error",
592
+ children: "failed"
593
+ }) : null
594
+ ]
595
+ }) : null] });
596
+ }
597
+ });
598
+ }
599
+ //#endregion
600
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_lib/computerStateToolRender.ts
601
+ const computerStateToolName = "computer_state";
602
+ function computerStateCallSummary(_args) {
603
+ return "authorized window";
604
+ }
605
+ /**
606
+ * The body as toned lines: what the result's details say, or its text. Pure,
607
+ * so a unit test can pin it without a DOM; the item decides how many to show.
608
+ */
609
+ function computerStateResultLines(result) {
610
+ if (result === null) return [];
611
+ return toolResultTextLines(result.content).map((text) => ({
612
+ text,
613
+ tone: "dim"
614
+ }));
615
+ }
616
+ //#endregion
617
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/_components/ComputerStateToolCard.tsx
618
+ /**
619
+ * The computer_state tool's timeline item: Observe the authorized application window and semantic accessibility state. The shell owns
620
+ * the frame, the outcome tone, the status badge, and the expand toggle; this
621
+ * card supplies the header summary and the body lines, and says whether it
622
+ * hides any so the toggle appears only when it reveals something.
623
+ */
624
+ function ComputerStateToolCard({ toolName, args, result, running, isError }) {
625
+ const lines = computerStateResultLines(result);
626
+ const collapsed = collapseLines(lines, 8, false);
627
+ return /* @__PURE__ */ jsx(MessageItem, {
628
+ tone: toolTone({
629
+ running,
630
+ isError
631
+ }),
632
+ expandable: collapsed.hidden > 0,
633
+ children: ({ expanded }) => {
634
+ const view = expanded ? collapseLines(lines, 8, true) : collapsed;
635
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MessageItemHeader, {
636
+ title: toolName,
637
+ children: /* @__PURE__ */ jsx("span", {
638
+ "data-testid": `tool-call-${toolName}`,
639
+ className: "min-w-0 flex-1 truncate text-doom-text",
640
+ children: computerStateCallSummary(args)
641
+ })
642
+ }), view.shown.length > 0 || running || isError ? /* @__PURE__ */ jsxs(MessageItemBody, {
643
+ "data-testid": `tool-result-${toolName}`,
644
+ className: "flex flex-col gap-1",
645
+ children: [
646
+ view.shown.length > 0 ? /* @__PURE__ */ jsx(MessageLines, { lines: view.shown }) : null,
647
+ view.hidden > 0 ? /* @__PURE__ */ jsxs(MessageItemStatus, {
648
+ expands: true,
649
+ children: [view.hidden, " more line(s)"]
650
+ }) : null,
651
+ running ? /* @__PURE__ */ jsx(MessageItemStatus, {
652
+ tone: "running",
653
+ children: "running"
654
+ }) : null,
655
+ isError ? /* @__PURE__ */ jsx(MessageItemStatus, {
656
+ tone: "error",
657
+ children: "failed"
658
+ }) : null
659
+ ]
660
+ }) : null] });
661
+ }
662
+ });
663
+ }
664
+ //#endregion
665
+ //#region src/extensions/workspaces/sessions/(frontend)/_legacy/index.ts
666
+ var _legacy_default = {
667
+ minorModes: [{
668
+ name: "computer use",
669
+ modeId: COMPUTER_USE_MODE_ID,
670
+ keys: "c e",
671
+ statusKey: COMPUTER_USE_MODE_STATUS_KEY,
672
+ hideWhenMissing: true,
673
+ desktopOnly: true,
674
+ order: 70
675
+ }],
676
+ activityGroups: [{
677
+ name: "computer-use",
678
+ keys: "c e",
679
+ order: 70,
680
+ statusKey: COMPUTER_USE_STATUS_KEY,
681
+ hideWhenEmpty: true,
682
+ desktopOnly: true,
683
+ marksBackgroundWork: false
684
+ }],
685
+ activitySections: [{
686
+ id: "computer-use",
687
+ component: ComputerUsePanel
688
+ }],
689
+ channels: [computerUseChannel],
690
+ toolRenderers: [
691
+ {
692
+ tools: [computerStateToolName],
693
+ message: ComputerStateToolCard
694
+ },
695
+ {
696
+ tools: [computerActionToolName],
697
+ message: ComputerActionToolCard
698
+ },
699
+ {
700
+ tools: [computerExecToolName],
701
+ message: ComputerExecToolCard
702
+ }
703
+ ]
704
+ };
705
+ //#endregion
706
+ //#region src/extensions/workspaces/sessions/(frontend)/activity-group/computer-use.web.ts
707
+ var computer_use_web_default$1 = defineActivityGroup(_legacy_default.activityGroups[0]);
708
+ //#endregion
709
+ //#region src/extensions/workspaces/sessions/(frontend)/channel/computer-use.web.ts
710
+ var computer_use_web_default = defineChannelFile(_legacy_default.channels[0]);
711
+ //#endregion
712
+ //#region src/extensions/workspaces/sessions/(frontend)/fill/ComputerUse.activity.computer-use.web.tsx
713
+ var ComputerUse_activity_computer_use_web_default = defineFill({
714
+ slot: "activity.computer-use",
715
+ ..._legacy_default.activitySections[0]
716
+ });
717
+ //#endregion
718
+ //#region src/extensions/workspaces/sessions/(frontend)/mode/computer-use/mode.web.ts
719
+ var mode_web_default = defineMinorModeFile(_legacy_default.minorModes[0]);
720
+ //#endregion
721
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/computer-action.web.tsx
722
+ var computer_action_web_default = defineToolRenderer(_legacy_default.toolRenderers[1]);
723
+ //#endregion
724
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/computer-exec.web.tsx
725
+ var computer_exec_web_default = defineToolRenderer(_legacy_default.toolRenderers[2]);
726
+ //#endregion
727
+ //#region src/extensions/workspaces/sessions/(frontend)/tool/computer-state.web.tsx
728
+ var computer_state_web_default = defineToolRenderer(_legacy_default.toolRenderers[0]);
729
+ //#endregion
730
+ //#region generated/web.ts
731
+ const via = (identity, value) => value === void 0 ? void 0 : {
732
+ ...identity,
733
+ ...value
734
+ };
735
+ const webPlugin = defineWebPlugin({
736
+ id: "computer-use",
737
+ global: { get settingsSections() {
738
+ return [via({ id: "computer-use" }, computer_use_web_default$3)];
739
+ } },
740
+ workspace: { get settingsSections() {
741
+ return [via({ id: "computer-use" }, computer_use_web_default$2)];
742
+ } },
743
+ session: {
744
+ get activityGroups() {
745
+ return [via({ name: "computer-use" }, computer_use_web_default$1)];
746
+ },
747
+ get channels() {
748
+ return [via({ channel: "computer_use" }, computer_use_web_default)];
749
+ },
750
+ get fills() {
751
+ return [via({
752
+ slot: "activity.computer-use",
753
+ id: "computer-use"
754
+ }, ComputerUse_activity_computer_use_web_default)];
755
+ },
756
+ get minorModes() {
757
+ return [via({ name: "computer-use" }, mode_web_default)];
758
+ },
759
+ get toolRenderers() {
760
+ return [
761
+ via({ tools: ["computer_action"] }, computer_action_web_default),
762
+ via({ tools: ["computer_exec"] }, computer_exec_web_default),
763
+ via({ tools: ["computer_state"] }, computer_state_web_default)
764
+ ];
765
+ }
766
+ }
767
+ });
768
+ //#endregion
769
+ export { webPlugin };
770
+
771
+ //# sourceMappingURL=web.mjs.map