openwork-server 0.18.50 → 0.18.56

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 (92) hide show
  1. package/README.md +1 -1
  2. package/bin/openwork-server.mjs +28 -22
  3. package/bin/platform.mjs +28 -0
  4. package/dist/opencode-plugin-deps-1.18.30.tgz +0 -0
  5. package/dist/opencode-plugins/openwork-context-v2.js +84 -0
  6. package/dist/opencode-plugins/openwork-extensions-preview.js +589 -268
  7. package/dist/opencode-plugins/openwork-gateway-quota-v2.js +12 -1
  8. package/dist/opencode-plugins/openwork-gateway-quota.js +12 -1
  9. package/dist/opencode-plugins/openwork-mcp-results-v2.js +154 -0
  10. package/dist/opencode-plugins/openwork-office-attachments.js +135 -130
  11. package/dist/opencode-plugins/openwork-provider-adapters.js +2 -2
  12. package/dist/opencode-plugins/openwork-provider-filters-v2.js +69 -0
  13. package/dist/opencode-plugins/openwork-read-transport.js +2 -0
  14. package/dist/opencode-plugins/openwork-spreadsheets.js +136 -2
  15. package/dist/openwork-server.mjs +114735 -0
  16. package/dist/pdfium.wasm +0 -0
  17. package/package.json +7 -1
  18. package/web/assets/{abnfDiagram-VCTEODGH-BgnyE_Jl.js → abnfDiagram-VCTEODGH-Dr1YMfll.js} +1 -1
  19. package/web/assets/{app-CyCqD289.js → app-D9Nvta88.js} +474 -424
  20. package/web/assets/{arc-jSgX49-e.js → arc-7SoIBvT7.js} +1 -1
  21. package/web/assets/{architectureDiagram-5GKGNRK7-DD_aGn4v.js → architectureDiagram-5GKGNRK7-BZwevfH9.js} +1 -1
  22. package/web/assets/{artifact-code-view-DFIq0k-c.js → artifact-code-view-WLwX_I1h.js} +1 -1
  23. package/web/assets/{artifact-spreadsheet-editor-DFVCYrL7.js → artifact-spreadsheet-editor-BvEnxKsm.js} +1 -1
  24. package/web/assets/{artifact-text-editor-DBngc6bx.js → artifact-text-editor-BHyVrNyR.js} +1 -1
  25. package/web/assets/{blockDiagram-OSKFZWR5-CBcZLjLp.js → blockDiagram-OSKFZWR5-Cvqr9DPi.js} +1 -1
  26. package/web/assets/{c4Diagram-7LVT6UL2-DMrhFCLj.js → c4Diagram-7LVT6UL2-CWTWC9C1.js} +1 -1
  27. package/web/assets/channel-BkgEJbR-.js +1 -0
  28. package/web/assets/{chunk-2Q5K7J3B-Ce1X-FJQ.js → chunk-2Q5K7J3B-DpJkzZ5x.js} +1 -1
  29. package/web/assets/{chunk-5VM5RSS4-BS48ZtVI.js → chunk-5VM5RSS4-BtTBnr9m.js} +1 -1
  30. package/web/assets/{chunk-F27PBJKO-COdFZJ-M.js → chunk-F27PBJKO-BbAvFJAg.js} +1 -1
  31. package/web/assets/{chunk-HLEWEB6X-Cdw4ABX3.js → chunk-HLEWEB6X-DX5mKBrj.js} +1 -1
  32. package/web/assets/{chunk-JWPE2WC7-FVVDYZmE.js → chunk-JWPE2WC7-CugB1e5O.js} +1 -1
  33. package/web/assets/{chunk-POPQ4Y6H-C7acdUF3.js → chunk-POPQ4Y6H-BQ-bWmmo.js} +1 -1
  34. package/web/assets/{chunk-SVP7TREG-BUun0UWD.js → chunk-SVP7TREG-szCI_Kz9.js} +1 -1
  35. package/web/assets/{chunk-XXDRQBXY-DLgpJmat.js → chunk-XXDRQBXY-C5UTO899.js} +1 -1
  36. package/web/assets/{chunk-ZLD2IHE6-BdMQ5Cr0.js → chunk-ZLD2IHE6-D5v_jiDg.js} +1 -1
  37. package/web/assets/classDiagram-CYGNFDIV-BHINh3-f.js +1 -0
  38. package/web/assets/classDiagram-v2-TLXNO2FR-BHINh3-f.js +1 -0
  39. package/web/assets/{cose-bilkent-JH36ORCC-CqWy3Swy.js → cose-bilkent-JH36ORCC-BhG_zAfo.js} +1 -1
  40. package/web/assets/{cynefin-OW5HDTMX-DslesE5Y.js → cynefin-OW5HDTMX-DSTjm6jO.js} +1 -1
  41. package/web/assets/{cynefinDiagram-5FMLGOSQ-B54uZJn_.js → cynefinDiagram-5FMLGOSQ-Dqkgb6tF.js} +1 -1
  42. package/web/assets/{dagre-OS7QT2EB-Bkahh4gZ.js → dagre-OS7QT2EB-BZMseQyA.js} +1 -1
  43. package/web/assets/{diagram-S7CK7UJ4-CXQl7SfF.js → diagram-S7CK7UJ4-Cs2hsmEJ.js} +1 -1
  44. package/web/assets/{diagram-UQ7AKVKN-BA0V8tjK.js → diagram-UQ7AKVKN-CDnRP_yz.js} +1 -1
  45. package/web/assets/{diagram-VSXAHHWV-DlcR8qeO.js → diagram-VSXAHHWV-DxKXQXAC.js} +1 -1
  46. package/web/assets/{diagram-VX7I27RA-ewm16zDI.js → diagram-VX7I27RA-BJKoseDf.js} +1 -1
  47. package/web/assets/{diagram-Z3DM3KII-Bc2lRc88.js → diagram-Z3DM3KII-DDcMvRxo.js} +1 -1
  48. package/web/assets/{ebnfDiagram-PWID7BFC-JwCaf6cT.js → ebnfDiagram-PWID7BFC-C2uUm-CC.js} +1 -1
  49. package/web/assets/{erDiagram-2YWLMYGG-DOqO-9Yf.js → erDiagram-2YWLMYGG-B0Mdv5WO.js} +1 -1
  50. package/web/assets/{flowDiagram-T62WH6J4-CCZxvIjW.js → flowDiagram-T62WH6J4-CUnt9AMo.js} +1 -1
  51. package/web/assets/{ganttDiagram-EL5Y4UJY-BczG7RIu.js → ganttDiagram-EL5Y4UJY-CENsUToC.js} +1 -1
  52. package/web/assets/{gitGraphDiagram-WWUBYQGX-GOecDN3g.js → gitGraphDiagram-WWUBYQGX-CrbKh94B.js} +1 -1
  53. package/web/assets/index-CyyMNIC_.css +1 -0
  54. package/web/assets/{infoDiagram-FKFFQAWI-BbgIVsQp.js → infoDiagram-FKFFQAWI-B-U-x_qU.js} +1 -1
  55. package/web/assets/{ishikawaDiagram-5VMMS53U-xutQo5Ip.js → ishikawaDiagram-5VMMS53U-jNVSJSu1.js} +1 -1
  56. package/web/assets/{journeyDiagram-EYS64GPL-BIbzPGaa.js → journeyDiagram-EYS64GPL-De3j0gPh.js} +1 -1
  57. package/web/assets/{kanban-definition-UXKFOSKX-BCTiRzck.js → kanban-definition-UXKFOSKX-Bbc8wjhq.js} +1 -1
  58. package/web/assets/{layout-Wpfi54TB.js → layout-BIjP2nup.js} +1 -1
  59. package/web/assets/{linear-DLJHPikh.js → linear-lvQZOiHM.js} +1 -1
  60. package/web/assets/{mermaid.core-CDRJkis7.js → mermaid.core-LJPf4T7o.js} +5 -5
  61. package/web/assets/{mindmap-definition-THT77NOG-DtphSFs_.js → mindmap-definition-THT77NOG-DiboHsg8.js} +1 -1
  62. package/web/assets/{overlay-zA9qjYXH.js → overlay-DA5SOee-.js} +1 -1
  63. package/web/assets/{pegDiagram-XKGWAZYB-CzbjpqK7.js → pegDiagram-XKGWAZYB-DO_edVtr.js} +1 -1
  64. package/web/assets/{pieDiagram-E7YTZNPT-tjxtWWT7.js → pieDiagram-E7YTZNPT-bPvBuBuG.js} +1 -1
  65. package/web/assets/{quadrantDiagram-AXDQQJYC-CFC1qey-.js → quadrantDiagram-AXDQQJYC-BGyrQhlH.js} +1 -1
  66. package/web/assets/{railroadDiagram-O6MQD6OU-73_BXhNP.js → railroadDiagram-O6MQD6OU-XhPIuoOE.js} +1 -1
  67. package/web/assets/{requirementDiagram-IS5BZ75X-B8i4BL2A.js → requirementDiagram-IS5BZ75X-atdASt4G.js} +1 -1
  68. package/web/assets/{sankeyDiagram-P5KCCOFB-Dll4Naj0.js → sankeyDiagram-P5KCCOFB-BV9prEKy.js} +1 -1
  69. package/web/assets/{sequenceDiagram-WJ2MYXX4-B8Rvjhb5.js → sequenceDiagram-WJ2MYXX4-Csfm-fZ-.js} +1 -1
  70. package/web/assets/{sizeCapture-INFHLROL-h8YDF-c3.js → sizeCapture-INFHLROL-CLmH-AjF.js} +1 -1
  71. package/web/assets/{stateDiagram-XQSTLZYL-COorU6Ga.js → stateDiagram-XQSTLZYL-yiSt2fJK.js} +1 -1
  72. package/web/assets/stateDiagram-v2-IH3M54BS-BEhxOujx.js +1 -0
  73. package/web/assets/{swimlanes-V6O3JKXN-CAghInFw.js → swimlanes-V6O3JKXN-BpXNggx9.js} +1 -1
  74. package/web/assets/swimlanesDiagram-JKAHXJPX-DWqwFv9F.js +8 -0
  75. package/web/assets/{timeline-definition-24CTP7MA-Dum2N-ex.js → timeline-definition-24CTP7MA-C8X71OnL.js} +1 -1
  76. package/web/assets/{vennDiagram-4TSXK5OY-D-JeJumX.js → vennDiagram-4TSXK5OY-BZo-99ra.js} +1 -1
  77. package/web/assets/{wardleyDiagram-VM6X3IG4-d99FKQIG.js → wardleyDiagram-VM6X3IG4-BHwH6ZLg.js} +1 -1
  78. package/web/assets/{workspace-file-tree-CAuhntUr.js → workspace-file-tree-C8iSfvPt.js} +3 -3
  79. package/web/assets/{xychartDiagram-S5SC5T6Z-DdmevfkD.js → xychartDiagram-S5SC5T6Z-BieEi319.js} +1 -1
  80. package/web/ext-github.svg +1 -0
  81. package/web/ext-hubspot.svg +1 -0
  82. package/web/ext-slack.svg +1 -0
  83. package/web/index.html +4 -3
  84. package/web/overlay.html +3 -3
  85. package/dist/bin/openwork-server +0 -4
  86. package/web/assets/channel-BE8JEd1U.js +0 -1
  87. package/web/assets/classDiagram-CYGNFDIV-Cy8V0GW-.js +0 -1
  88. package/web/assets/classDiagram-v2-TLXNO2FR-Cy8V0GW-.js +0 -1
  89. package/web/assets/index-BZgplq0g.css +0 -1
  90. package/web/assets/stateDiagram-v2-IH3M54BS-BAu05WuT.js +0 -1
  91. package/web/assets/swimlanesDiagram-JKAHXJPX-CpJNl3pn.js +0 -8
  92. /package/web/assets/{index-_gYIQM_z.js → index-D6FUvVB1.js} +0 -0
@@ -45,98 +45,9 @@ var __export = (target, all) => {
45
45
  };
46
46
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
47
47
 
48
- // src/opencode-plugins/openwork-extensions-preview.ts
49
- import { randomUUID } from "node:crypto";
50
- import { realpath } from "node:fs/promises";
51
-
52
- // src/errors.ts
53
- class ApiError extends Error {
54
- status;
55
- code;
56
- details;
57
- constructor(status, code, message, details) {
58
- super(message);
59
- this.status = status;
60
- this.code = code;
61
- this.details = details;
62
- }
63
- }
64
-
65
- // src/opencode-plugins/openwork-ui-bridge.ts
66
- import { readFile } from "node:fs/promises";
67
- import { join } from "node:path";
68
- import { homedir, platform } from "node:os";
69
- var cachedBridge = null;
70
- var cachedBridgeAt = 0;
71
- var cachedBridgeDiscovery;
72
- var BRIDGE_CACHE_MS = 2000;
73
- var BRIDGE_TIMEOUT_MS = 5000;
74
- function userAppDataDir() {
75
- if (platform() === "darwin")
76
- return join(homedir(), "Library", "Application Support");
77
- if (platform() === "win32")
78
- return process.env.APPDATA || join(homedir(), "AppData", "Roaming");
79
- return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
80
- }
81
- function uiControlDiscoveryPaths() {
82
- const explicit = process.env.OPENWORK_UI_CONTROL_DISCOVERY?.trim();
83
- if (explicit)
84
- return [explicit];
85
- return [
86
- join(userAppDataDir(), "com.differentai.openwork", "openwork-ui-control.json"),
87
- join(userAppDataDir(), "com.differentai.openwork.dev", "openwork-ui-control.json")
88
- ].filter((p) => Boolean(p));
89
- }
90
- async function discoverUiBridge() {
91
- const discovery = process.env.OPENWORK_UI_CONTROL_DISCOVERY?.trim();
92
- if (cachedBridge && cachedBridgeDiscovery === discovery && Date.now() - cachedBridgeAt < BRIDGE_CACHE_MS)
93
- return cachedBridge;
94
- cachedBridge = null;
95
- for (const candidate of uiControlDiscoveryPaths()) {
96
- try {
97
- const raw = await readFile(candidate, "utf8");
98
- const parsed = JSON.parse(raw);
99
- if (typeof parsed.baseUrl === "string" && typeof parsed.token === "string") {
100
- const url = new URL(parsed.baseUrl);
101
- if (url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || url.username || url.password)
102
- continue;
103
- cachedBridge = { baseUrl: parsed.baseUrl, token: parsed.token };
104
- cachedBridgeAt = Date.now();
105
- cachedBridgeDiscovery = discovery;
106
- return cachedBridge;
107
- }
108
- } catch {}
109
- }
110
- return null;
111
- }
112
- async function uiBridgeRequest(path, options = {}) {
113
- const bridge = await discoverUiBridge();
114
- if (!bridge)
115
- return { ok: false, code: "browser_unavailable", dispatched: false, error: "The built-in browser is unavailable. Open the desktop app on the same machine as this task's server." };
116
- try {
117
- const response = await fetch(`${bridge.baseUrl}${path}`, {
118
- method: options.method || "GET",
119
- redirect: "error",
120
- signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(options.timeoutMs ?? BRIDGE_TIMEOUT_MS)]) : AbortSignal.timeout(options.timeoutMs ?? BRIDGE_TIMEOUT_MS),
121
- headers: {
122
- Authorization: `Bearer ${bridge.token}`,
123
- ...options.body ? { "Content-Type": "application/json" } : {}
124
- },
125
- ...options.body ? { body: JSON.stringify(options.body) } : {}
126
- });
127
- const text = await response.text();
128
- try {
129
- return JSON.parse(text);
130
- } catch {
131
- return { ok: false, code: "browser_disconnected", mayHaveChangedState: true, retrySafe: false, error: "The browser returned no readable receipt. Inspect the page before retrying an action." };
132
- }
133
- } catch {
134
- cachedBridge = null;
135
- cachedBridgeAt = 0;
136
- cachedBridgeDiscovery = undefined;
137
- return { ok: false, code: "browser_disconnected", mayHaveChangedState: true, retrySafe: false, error: "The browser connection was interrupted. An action may already have run. Reconnect, list this conversation's tabs and observe before deciding what remains." };
138
- }
139
- }
48
+ // src/opencode-plugins/openwork-read-transport.ts
49
+ import { AsyncLocalStorage } from "node:async_hooks";
50
+ var openworkReadTransport = new AsyncLocalStorage;
140
51
 
141
52
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
142
53
  var exports_external = {};
@@ -13670,6 +13581,279 @@ function date4(params) {
13670
13581
 
13671
13582
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/external.js
13672
13583
  config(en_default());
13584
+ // src/opencode-plugins/session-activity.ts
13585
+ var engineSessionStatusesSchema = exports_external.record(exports_external.string(), exports_external.object({ type: exports_external.string() }).passthrough());
13586
+ var enginePendingRequestsSchema = exports_external.array(exports_external.object({ sessionID: exports_external.string() }).passthrough());
13587
+ function sessionActivityFrom(statuses, permissions, questions, sessionId, descendantIds = [], unknownDescendants = 0) {
13588
+ const parsedStatuses = engineSessionStatusesSchema.safeParse(statuses);
13589
+ const parsedPermissions = enginePendingRequestsSchema.safeParse(permissions);
13590
+ const parsedQuestions = enginePendingRequestsSchema.safeParse(questions);
13591
+ const probesComplete = parsedStatuses.success && parsedPermissions.success && parsedQuestions.success;
13592
+ const waiting = new Set([
13593
+ ...parsedPermissions.success ? parsedPermissions.data.map((request) => request.sessionID) : [],
13594
+ ...parsedQuestions.success ? parsedQuestions.data.map((request) => request.sessionID) : []
13595
+ ]);
13596
+ const statusFor = (id) => {
13597
+ const type = parsedStatuses.success ? parsedStatuses.data[id]?.type : undefined;
13598
+ if (type === "error")
13599
+ return "error";
13600
+ if (waiting.has(id) || type === "waiting")
13601
+ return "waiting";
13602
+ if (type === "busy" || type === "running")
13603
+ return "busy";
13604
+ if (type === "retry" || type === "compacting" || type === "thinking" || type === "responding")
13605
+ return type;
13606
+ return probesComplete && (type === undefined || type === "idle") ? "idle" : "unknown";
13607
+ };
13608
+ const descendantActivity = { busy: 0, waiting: 0, unknown: unknownDescendants };
13609
+ for (const id of new Set(descendantIds)) {
13610
+ if (id === sessionId)
13611
+ continue;
13612
+ const status = statusFor(id);
13613
+ if (status === "unknown")
13614
+ descendantActivity.unknown += 1;
13615
+ else if (status === "waiting")
13616
+ descendantActivity.waiting += 1;
13617
+ else if (status !== "idle" && status !== "error")
13618
+ descendantActivity.busy += 1;
13619
+ }
13620
+ const own = statusFor(sessionId);
13621
+ return {
13622
+ status: own !== "error" && descendantActivity.waiting > 0 ? "waiting" : own,
13623
+ working: own !== "idle" && own !== "error" && own !== "unknown" || descendantActivity.busy > 0 || descendantActivity.waiting > 0,
13624
+ descendantActivity,
13625
+ inventoryComplete: probesComplete && own !== "unknown" && descendantActivity.unknown === 0
13626
+ };
13627
+ }
13628
+
13629
+ // src/opencode-v2-read-adapter.ts
13630
+ function isRecord(value) {
13631
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13632
+ }
13633
+ function data(value) {
13634
+ return isRecord(value) && "data" in value ? value.data : value;
13635
+ }
13636
+ function items(value) {
13637
+ const rows = data(value);
13638
+ if (!Array.isArray(rows))
13639
+ throw new Error("Invalid engine list response");
13640
+ return rows.filter(isRecord);
13641
+ }
13642
+ function session(value) {
13643
+ const row = data(value);
13644
+ if (!isRecord(row))
13645
+ throw new Error("Invalid engine session response");
13646
+ const info = isRecord(row.info) ? row.info : row;
13647
+ const home = info.openworkHomeDirectory;
13648
+ return { ...info, directory: home ?? (isRecord(info.location) ? info.location.directory : undefined) };
13649
+ }
13650
+ async function pages(read, path, limit) {
13651
+ const url2 = new URL(path, "http://engine");
13652
+ url2.searchParams.set("limit", String(limit ?? 200));
13653
+ const rows = [];
13654
+ const cursors = new Set;
13655
+ while (true) {
13656
+ const result = await read(url2.pathname + url2.search);
13657
+ rows.push(...items(result));
13658
+ const next = isRecord(result) && isRecord(result.cursor) ? result.cursor.next : undefined;
13659
+ if (limit !== undefined || typeof next !== "string" || !next)
13660
+ return rows;
13661
+ if (cursors.has(next))
13662
+ throw new Error("Invalid engine pagination cursor");
13663
+ cursors.add(next);
13664
+ url2.searchParams.set("cursor", next);
13665
+ }
13666
+ }
13667
+ function createV2ReadAdapter(read) {
13668
+ return async (path) => {
13669
+ const match = /^\/workspace\/([^/]+)\/opencode(\/.*)$/.exec(path);
13670
+ if (!match)
13671
+ return read(path);
13672
+ const base = `/workspace/${match[1]}/opencode2`;
13673
+ const native = (path2) => read(base + path2);
13674
+ const url2 = new URL(match[2], "http://engine");
13675
+ if (url2.pathname === "/session/status") {
13676
+ const active = data(await native("/api/session/active"));
13677
+ if (!isRecord(active))
13678
+ throw new Error("Invalid engine activity response");
13679
+ return Object.fromEntries(Object.entries(active).map(([id, value]) => [
13680
+ id,
13681
+ { type: isRecord(value) && value.type === "running" ? "busy" : "idle" }
13682
+ ]));
13683
+ }
13684
+ if (url2.pathname === "/session") {
13685
+ const rows2 = await pages(native, "/api/session");
13686
+ return rows2.filter((row) => url2.searchParams.get("roots") !== "true" || !row.parentID).map(session);
13687
+ }
13688
+ if (url2.pathname === "/provider") {
13689
+ const [models, providers] = await Promise.all([native("/api/model").then(items), native("/api/provider").then(items)]);
13690
+ const names = new Map(providers.map((provider) => [provider.id, provider.name]));
13691
+ const catalog = new Map;
13692
+ for (const model of models) {
13693
+ if (typeof model.id !== "string" || typeof model.providerID !== "string")
13694
+ continue;
13695
+ const name = names.get(model.providerID);
13696
+ const provider = catalog.get(model.providerID) ?? {
13697
+ id: model.providerID,
13698
+ name: typeof name === "string" ? name : model.providerID,
13699
+ models: {}
13700
+ };
13701
+ provider.models[model.id] = { name: typeof model.name === "string" ? model.name : model.id };
13702
+ catalog.set(model.providerID, provider);
13703
+ }
13704
+ return { all: [...catalog.values()], connected: [...catalog.keys()] };
13705
+ }
13706
+ if (url2.pathname === "/question")
13707
+ return items(await native("/api/form/request"));
13708
+ const target = /^\/session\/([^/]+)(\/message|\/children)?$/.exec(url2.pathname);
13709
+ if (!target)
13710
+ throw new Error("Unsupported native engine read");
13711
+ if (!target[2])
13712
+ return session(await native(`/api/session/${target[1]}`));
13713
+ if (target[2] === "/children") {
13714
+ return (await pages(native, "/api/session")).filter((row) => row.parentID === decodeURIComponent(target[1])).map(session);
13715
+ }
13716
+ const limit = url2.searchParams.get("limit");
13717
+ const rows = await pages(native, `/api/session/${target[1]}/message`, limit === null ? undefined : Number(limit));
13718
+ return rows.filter((row) => row.type === "user" || row.type === "assistant").sort((a, b) => Number(isRecord(a.time) ? a.time.created : 0) - Number(isRecord(b.time) ? b.time.created : 0)).map((row) => ({
13719
+ info: { id: row.id, role: row.type, time: row.time, ...row.error ? { error: row.error } : {} },
13720
+ parts: Array.isArray(row.content) ? row.content.filter((part) => isRecord(part) && part.type === "text") : typeof row.text === "string" ? [{ type: "text", text: row.text }] : []
13721
+ }));
13722
+ };
13723
+ }
13724
+ async function readV2SessionActivity(read, workspaceId, sessionId) {
13725
+ const base = `/workspace/${encodeURIComponent(workspaceId)}/opencode2`;
13726
+ const native = (path) => read(base + path);
13727
+ const [listed, statuses] = await Promise.all([
13728
+ pages(native, "/api/session").catch(() => null),
13729
+ native("/api/session/active").then(data).catch(() => null)
13730
+ ]);
13731
+ const ids = new Set([sessionId]);
13732
+ if (listed) {
13733
+ for (let changed = true;changed; ) {
13734
+ changed = false;
13735
+ for (const row of listed) {
13736
+ if (typeof row.id !== "string" || typeof row.parentID !== "string" || !ids.has(row.parentID) || ids.has(row.id))
13737
+ continue;
13738
+ ids.add(row.id);
13739
+ changed = true;
13740
+ }
13741
+ }
13742
+ }
13743
+ const permissions = [];
13744
+ const questions = [];
13745
+ let complete = listed !== null;
13746
+ const pending = [...ids];
13747
+ await Promise.all(Array.from({ length: Math.min(4, pending.length) }, async () => {
13748
+ for (let id = pending.shift();id; id = pending.shift()) {
13749
+ try {
13750
+ const [p, q] = await Promise.all([
13751
+ native(`/api/session/${encodeURIComponent(id)}/permission`),
13752
+ native(`/api/session/${encodeURIComponent(id)}/form`)
13753
+ ]);
13754
+ permissions.push(...items(p).map((item) => ({ ...item, sessionID: id })));
13755
+ questions.push(...items(q).map((item) => ({ ...item, sessionID: id })));
13756
+ } catch {
13757
+ complete = false;
13758
+ }
13759
+ }
13760
+ }));
13761
+ return sessionActivityFrom(statuses, complete ? permissions : null, complete ? questions : null, sessionId, [...ids].filter((id) => id !== sessionId), listed ? 0 : 1);
13762
+ }
13763
+
13764
+ // src/opencode-plugins/openwork-extensions-preview.ts
13765
+ import { randomUUID } from "node:crypto";
13766
+ import { realpath } from "node:fs/promises";
13767
+
13768
+ // src/errors.ts
13769
+ class ApiError extends Error {
13770
+ status;
13771
+ code;
13772
+ details;
13773
+ constructor(status, code, message, details) {
13774
+ super(message);
13775
+ this.status = status;
13776
+ this.code = code;
13777
+ this.details = details;
13778
+ }
13779
+ }
13780
+
13781
+ // src/opencode-plugins/openwork-ui-bridge.ts
13782
+ import { readFile } from "node:fs/promises";
13783
+ import { join } from "node:path";
13784
+ import { homedir, platform } from "node:os";
13785
+ var cachedBridge = null;
13786
+ var cachedBridgeAt = 0;
13787
+ var cachedBridgeDiscovery;
13788
+ var BRIDGE_CACHE_MS = 2000;
13789
+ var BRIDGE_TIMEOUT_MS = 5000;
13790
+ function userAppDataDir() {
13791
+ if (platform() === "darwin")
13792
+ return join(homedir(), "Library", "Application Support");
13793
+ if (platform() === "win32")
13794
+ return process.env.APPDATA || join(homedir(), "AppData", "Roaming");
13795
+ return process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
13796
+ }
13797
+ function uiControlDiscoveryPaths() {
13798
+ const explicit = process.env.OPENWORK_UI_CONTROL_DISCOVERY?.trim();
13799
+ if (explicit)
13800
+ return [explicit];
13801
+ return [
13802
+ join(userAppDataDir(), "com.differentai.openwork", "openwork-ui-control.json"),
13803
+ join(userAppDataDir(), "com.differentai.openwork.dev", "openwork-ui-control.json")
13804
+ ].filter((p) => Boolean(p));
13805
+ }
13806
+ async function discoverUiBridge() {
13807
+ const discovery = process.env.OPENWORK_UI_CONTROL_DISCOVERY?.trim();
13808
+ if (cachedBridge && cachedBridgeDiscovery === discovery && Date.now() - cachedBridgeAt < BRIDGE_CACHE_MS)
13809
+ return cachedBridge;
13810
+ cachedBridge = null;
13811
+ for (const candidate of uiControlDiscoveryPaths()) {
13812
+ try {
13813
+ const raw = await readFile(candidate, "utf8");
13814
+ const parsed = JSON.parse(raw);
13815
+ if (typeof parsed.baseUrl === "string" && typeof parsed.token === "string") {
13816
+ const url2 = new URL(parsed.baseUrl);
13817
+ if (url2.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]"].includes(url2.hostname) || url2.username || url2.password)
13818
+ continue;
13819
+ cachedBridge = { baseUrl: parsed.baseUrl, token: parsed.token };
13820
+ cachedBridgeAt = Date.now();
13821
+ cachedBridgeDiscovery = discovery;
13822
+ return cachedBridge;
13823
+ }
13824
+ } catch {}
13825
+ }
13826
+ return null;
13827
+ }
13828
+ async function uiBridgeRequest(path, options = {}) {
13829
+ const bridge = await discoverUiBridge();
13830
+ if (!bridge)
13831
+ return { ok: false, code: "browser_unavailable", dispatched: false, error: "The built-in browser is unavailable. Open the desktop app on the same machine as this task's server." };
13832
+ try {
13833
+ const response = await fetch(`${bridge.baseUrl}${path}`, {
13834
+ method: options.method || "GET",
13835
+ redirect: "error",
13836
+ signal: options.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(options.timeoutMs ?? BRIDGE_TIMEOUT_MS)]) : AbortSignal.timeout(options.timeoutMs ?? BRIDGE_TIMEOUT_MS),
13837
+ headers: {
13838
+ Authorization: `Bearer ${bridge.token}`,
13839
+ ...options.body ? { "Content-Type": "application/json" } : {}
13840
+ },
13841
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
13842
+ });
13843
+ const text = await response.text();
13844
+ try {
13845
+ return JSON.parse(text);
13846
+ } catch {
13847
+ return { ok: false, code: "browser_disconnected", mayHaveChangedState: true, retrySafe: false, error: "The browser returned no readable receipt. Inspect the page before retrying an action." };
13848
+ }
13849
+ } catch {
13850
+ cachedBridge = null;
13851
+ cachedBridgeAt = 0;
13852
+ cachedBridgeDiscovery = undefined;
13853
+ return { ok: false, code: "browser_disconnected", mayHaveChangedState: true, retrySafe: false, error: "The browser connection was interrupted. An action may already have run. Reconnect, list this conversation's tabs and observe before deciding what remains." };
13854
+ }
13855
+ }
13856
+
13673
13857
  // src/opencode-plugins/gmail-attachment-fulfillment.ts
13674
13858
  var draftSchema = exports_external.object({
13675
13859
  to: exports_external.string().trim().min(3).max(320),
@@ -13702,7 +13886,7 @@ var receiptSchema = exports_external.object({
13702
13886
  size: exports_external.number().int().nonnegative()
13703
13887
  })).optional()
13704
13888
  });
13705
- function isRecord(value) {
13889
+ function isRecord2(value) {
13706
13890
  return typeof value === "object" && value !== null && !Array.isArray(value);
13707
13891
  }
13708
13892
  function normalizeJson(value) {
@@ -13752,11 +13936,11 @@ var CLOUD_NO_WRITE = new Set([
13752
13936
  "mcp_membership_revoked:403"
13753
13937
  ]);
13754
13938
  function noWriteMessage(error48) {
13755
- if (!isRecord(error48) || typeof error48.message !== "string")
13939
+ if (!isRecord2(error48) || typeof error48.message !== "string")
13756
13940
  return;
13757
13941
  if (LOCAL_NO_WRITE.has(`${error48.code}:${error48.status}`))
13758
13942
  return `${error48.message} No draft created.`;
13759
- if (error48.code === "cloud_upload_failed" && isRecord(error48.details) && CLOUD_NO_WRITE.has(`${error48.details.upstreamCode}:${error48.status}`)) {
13943
+ if (error48.code === "cloud_upload_failed" && isRecord2(error48.details) && CLOUD_NO_WRITE.has(`${error48.details.upstreamCode}:${error48.status}`)) {
13760
13944
  return `${error48.message} No draft created.`;
13761
13945
  }
13762
13946
  }
@@ -13776,18 +13960,18 @@ function createGmailAttachmentFulfillment(dependencies, factoryContext = {}) {
13776
13960
  return call;
13777
13961
  }
13778
13962
  const fulfill = async (input, output) => {
13779
- if (!isRecord(input) || input.tool !== "openwork-cloud_execute_capability")
13963
+ if (!isRecord2(input) || input.tool !== "openwork-cloud_execute_capability")
13780
13964
  return;
13781
- if (!isRecord(input.args) || typeof input.args.name !== "string")
13965
+ if (!isRecord2(input.args) || typeof input.args.name !== "string")
13782
13966
  return;
13783
13967
  const native = NATIVE_GMAIL.exec(input.args.name);
13784
13968
  const connectionId = native?.[1];
13785
- if (!connectionId || !isRecord(output) || output.isError === true)
13969
+ if (!connectionId || !isRecord2(output) || output.isError === true)
13786
13970
  return;
13787
13971
  if (!Array.isArray(output.content) || output.content.length !== 1)
13788
13972
  return;
13789
13973
  const part = output.content[0];
13790
- if (!isRecord(part) || part.type !== "text" || typeof part.text !== "string")
13974
+ if (!isRecord2(part) || part.type !== "text" || typeof part.text !== "string")
13791
13975
  return;
13792
13976
  if (!markerSchema.safeParse(normalizeJson(part.text)).success)
13793
13977
  return;
@@ -13822,7 +14006,7 @@ function createGmailAttachmentFulfillment(dependencies, factoryContext = {}) {
13822
14006
  }
13823
14007
  if (call.controller.signal.aborted)
13824
14008
  throw new Error(UNKNOWN_OUTCOME);
13825
- if (isRecord(result) && result.ok === false) {
14009
+ if (isRecord2(result) && result.ok === false) {
13826
14010
  throw new Error(UNKNOWN_OUTCOME);
13827
14011
  }
13828
14012
  const receipt = receiptSchema.safeParse(result);
@@ -13838,17 +14022,17 @@ function createGmailAttachmentFulfillment(dependencies, factoryContext = {}) {
13838
14022
  };
13839
14023
  return Object.assign(fulfill, {
13840
14024
  before: async (input, output) => {
13841
- if (!isRecord(input) || input.tool !== "openwork-cloud_execute_capability" || typeof input.sessionID !== "string" || typeof input.callID !== "string" || !isRecord(output) || !isRecord(output.args) || typeof output.args.name !== "string" || !NATIVE_GMAIL.test(output.args.name))
14025
+ if (!isRecord2(input) || input.tool !== "openwork-cloud_execute_capability" || typeof input.sessionID !== "string" || typeof input.callID !== "string" || !isRecord2(output) || !isRecord2(output.args) || typeof output.args.name !== "string" || !NATIVE_GMAIL.test(output.args.name))
13842
14026
  return;
13843
14027
  const body = normalizeJson(output.args.body);
13844
- if (isRecord(body) && Array.isArray(body.attachments))
14028
+ if (isRecord2(body) && Array.isArray(body.attachments))
13845
14029
  invocation(input.sessionID, input.callID);
13846
14030
  },
13847
14031
  event: async (input) => {
13848
- if (!isRecord(input) || !isRecord(input.event) || input.event.type !== "session.status")
14032
+ if (!isRecord2(input) || !isRecord2(input.event) || input.event.type !== "session.status")
13849
14033
  return;
13850
14034
  const props = input.event.properties;
13851
- if (!isRecord(props) || typeof props.sessionID !== "string" || !isRecord(props.status))
14035
+ if (!isRecord2(props) || typeof props.sessionID !== "string" || !isRecord2(props.status))
13852
14036
  return;
13853
14037
  if (props.status.type === "busy")
13854
14038
  idleSessions.delete(props.sessionID);
@@ -13868,51 +14052,6 @@ function createGmailAttachmentFulfillment(dependencies, factoryContext = {}) {
13868
14052
  });
13869
14053
  }
13870
14054
 
13871
- // src/opencode-plugins/session-activity.ts
13872
- var engineSessionStatusesSchema = exports_external.record(exports_external.string(), exports_external.object({ type: exports_external.string() }).passthrough());
13873
- var enginePendingRequestsSchema = exports_external.array(exports_external.object({ sessionID: exports_external.string() }).passthrough());
13874
- function sessionActivityFrom(statuses, permissions, questions, sessionId, descendantIds = [], unknownDescendants = 0) {
13875
- const parsedStatuses = engineSessionStatusesSchema.safeParse(statuses);
13876
- const parsedPermissions = enginePendingRequestsSchema.safeParse(permissions);
13877
- const parsedQuestions = enginePendingRequestsSchema.safeParse(questions);
13878
- const probesComplete = parsedStatuses.success && parsedPermissions.success && parsedQuestions.success;
13879
- const waiting = new Set([
13880
- ...parsedPermissions.success ? parsedPermissions.data.map((request) => request.sessionID) : [],
13881
- ...parsedQuestions.success ? parsedQuestions.data.map((request) => request.sessionID) : []
13882
- ]);
13883
- const statusFor = (id) => {
13884
- const type = parsedStatuses.success ? parsedStatuses.data[id]?.type : undefined;
13885
- if (type === "error")
13886
- return "error";
13887
- if (waiting.has(id) || type === "waiting")
13888
- return "waiting";
13889
- if (type === "busy" || type === "running")
13890
- return "busy";
13891
- if (type === "retry" || type === "compacting" || type === "thinking" || type === "responding")
13892
- return type;
13893
- return probesComplete && (type === undefined || type === "idle") ? "idle" : "unknown";
13894
- };
13895
- const descendantActivity = { busy: 0, waiting: 0, unknown: unknownDescendants };
13896
- for (const id of new Set(descendantIds)) {
13897
- if (id === sessionId)
13898
- continue;
13899
- const status = statusFor(id);
13900
- if (status === "unknown")
13901
- descendantActivity.unknown += 1;
13902
- else if (status === "waiting")
13903
- descendantActivity.waiting += 1;
13904
- else if (status !== "idle" && status !== "error")
13905
- descendantActivity.busy += 1;
13906
- }
13907
- const own = statusFor(sessionId);
13908
- return {
13909
- status: own !== "error" && descendantActivity.waiting > 0 ? "waiting" : own,
13910
- working: own !== "idle" && own !== "error" && own !== "unknown" || descendantActivity.busy > 0 || descendantActivity.waiting > 0,
13911
- descendantActivity,
13912
- inventoryComplete: probesComplete && own !== "unknown" && descendantActivity.unknown === 0
13913
- };
13914
- }
13915
-
13916
14055
  // ../../packages/types/src/openwork-affordance.ts
13917
14056
  var openworkAffordanceKindSchema = exports_external.enum(["query", "command", "guidance"]);
13918
14057
  var openworkProviderKindSchema = exports_external.enum(["builtin", "extension", "mcp", "connect"]);
@@ -14216,15 +14355,18 @@ var automationExecutionThreadSchema = exports_external.object({
14216
14355
  var automationExecutionTargetSchema = exports_external.enum(["desktop", "cloud"]);
14217
14356
  var AUTOMATION_MODEL_ATTENTION_CAPABILITY = "model_attention_v1";
14218
14357
  var REMOTE_SESSION_DESKTOP_RUNNER_CAPABILITY = "remote_session_v1";
14358
+ var REMOTE_SESSION_CONTROL_RUNNER_CAPABILITY = "remote_session_control_v1";
14219
14359
  var automationDesktopRunnerCapabilitySchema = exports_external.enum([
14220
14360
  AUTOMATION_MODEL_ATTENTION_CAPABILITY,
14221
- REMOTE_SESSION_DESKTOP_RUNNER_CAPABILITY
14361
+ REMOTE_SESSION_DESKTOP_RUNNER_CAPABILITY,
14362
+ REMOTE_SESSION_CONTROL_RUNNER_CAPABILITY
14222
14363
  ]);
14364
+ var AUTOMATION_DESKTOP_RUNNER_CAPABILITY_LIMIT = 3;
14223
14365
  var automationDesktopRunnerRegistrationSchema = exports_external.object({
14224
14366
  runnerId: idSchema.min(8),
14225
14367
  protocolVersion: exports_external.literal(1),
14226
14368
  supportedExecutionTargets: exports_external.array(exports_external.literal("desktop")).length(1),
14227
- capabilities: exports_external.array(automationDesktopRunnerCapabilitySchema).max(2).default([]),
14369
+ capabilities: exports_external.array(automationDesktopRunnerCapabilitySchema).max(AUTOMATION_DESKTOP_RUNNER_CAPABILITY_LIMIT).default([]),
14228
14370
  appVersion: exports_external.string().trim().min(1).max(80),
14229
14371
  platform: exports_external.enum(["darwin", "win32", "linux"]),
14230
14372
  concurrency: exports_external.number().int().min(1).max(4)
@@ -14240,10 +14382,11 @@ var automationRunnerNotificationSchema = exports_external.object({
14240
14382
  }).strict();
14241
14383
  var automationRunnerWorkItemSchema = exports_external.union([
14242
14384
  exports_external.object({ runId: idSchema, executionTarget: exports_external.literal("desktop") }),
14243
- exports_external.object({ kind: exports_external.literal("remote_session_create"), commandId: idSchema })
14385
+ exports_external.object({ kind: exports_external.literal("remote_session_create"), commandId: idSchema }),
14386
+ exports_external.object({ kind: exports_external.literal("remote_session_request"), requestId: idSchema })
14244
14387
  ]);
14245
14388
  var automationRunnerWorkResponseSchema = exports_external.object({
14246
- items: exports_external.array(automationRunnerWorkItemSchema).max(9)
14389
+ items: exports_external.array(automationRunnerWorkItemSchema).max(14)
14247
14390
  });
14248
14391
  var remoteSessionCommandAssignmentSchema = exports_external.object({
14249
14392
  commandId: idSchema,
@@ -14289,6 +14432,127 @@ var remoteSessionCommandCompleteResponseSchema = exports_external.object({
14289
14432
  workspaceId: exports_external.string().max(240).nullable()
14290
14433
  })
14291
14434
  });
14435
+ var REMOTE_SESSION_FINAL_TEXT_MAX_LENGTH = 20000;
14436
+ var remoteSessionStatusSchema = exports_external.enum(["running", "waiting", "idle", "error"]);
14437
+ var remoteSessionWaitingForSchema = exports_external.enum(["permission", "question"]);
14438
+ var remoteSessionEngineSchema = exports_external.enum(["v1", "v2"]);
14439
+ var remoteSessionModelSchema = exports_external.object({
14440
+ providerId: idSchema,
14441
+ modelId: idSchema,
14442
+ variant: exports_external.string().trim().min(1).max(60).nullable().optional()
14443
+ });
14444
+ var remoteSessionCommandSessionReportSchema = exports_external.object({
14445
+ status: remoteSessionStatusSchema,
14446
+ waitingFor: remoteSessionWaitingForSchema.nullable().optional(),
14447
+ engine: remoteSessionEngineSchema.optional(),
14448
+ model: remoteSessionModelSchema.nullable().optional(),
14449
+ finalText: exports_external.string().max(REMOTE_SESSION_FINAL_TEXT_MAX_LENGTH).optional(),
14450
+ error: remoteSessionCommandErrorSchema.nullable().optional(),
14451
+ messageCount: exports_external.number().int().min(0).optional(),
14452
+ observedAt: timestampSchema
14453
+ });
14454
+ var remoteSessionCommandSessionReportResponseSchema = exports_external.object({ ok: exports_external.literal(true) });
14455
+ var REMOTE_SESSION_REQUEST_RESULT_MAX_BYTES = 256 * 1024;
14456
+ var REMOTE_SESSION_TRANSCRIPT_TEXT_MAX_LENGTH = 20000;
14457
+ var REMOTE_SESSION_TOOL_SUMMARY_MAX_LENGTH = 2000;
14458
+ var REMOTE_SESSION_TRANSCRIPT_PAGE_MAX = 100;
14459
+ var remoteSessionRequestActionSchema = exports_external.enum(["read", "send", "stop"]);
14460
+ var remoteSessionMessageIdSchema = exports_external.string().regex(/^msg_[a-zA-Z0-9]+$/).max(160);
14461
+ var remoteSessionReadInputSchema = exports_external.object({
14462
+ from: exports_external.enum(["start", "end"]),
14463
+ cursor: exports_external.string().trim().min(1).max(160).nullable(),
14464
+ limit: exports_external.number().int().min(1).max(REMOTE_SESSION_TRANSCRIPT_PAGE_MAX)
14465
+ });
14466
+ var remoteSessionSendInputSchema = exports_external.object({
14467
+ prompt: exports_external.string().min(1).max(1e5),
14468
+ messageId: remoteSessionMessageIdSchema.nullable(),
14469
+ model: remoteSessionModelSchema.nullable()
14470
+ });
14471
+ var remoteSessionStopInputSchema = exports_external.object({
14472
+ messageId: exports_external.string().trim().min(1).max(160).nullable()
14473
+ });
14474
+ var remoteSessionRequestInputSchema = exports_external.discriminatedUnion("action", [
14475
+ exports_external.object({ action: exports_external.literal("read"), input: remoteSessionReadInputSchema }),
14476
+ exports_external.object({ action: exports_external.literal("send"), input: remoteSessionSendInputSchema }),
14477
+ exports_external.object({ action: exports_external.literal("stop"), input: remoteSessionStopInputSchema })
14478
+ ]);
14479
+ var remoteSessionRequestAssignmentBase = {
14480
+ requestId: idSchema,
14481
+ kind: exports_external.literal("remote_session_request"),
14482
+ commandId: idSchema,
14483
+ sessionId: exports_external.string().trim().min(1).max(240),
14484
+ workspaceId: exports_external.string().trim().min(1).max(240),
14485
+ engine: remoteSessionEngineSchema.nullable(),
14486
+ expiresAt: timestampSchema
14487
+ };
14488
+ var remoteSessionRequestAssignmentSchema = exports_external.discriminatedUnion("action", [
14489
+ exports_external.object({ ...remoteSessionRequestAssignmentBase, action: exports_external.literal("read"), input: remoteSessionReadInputSchema }),
14490
+ exports_external.object({ ...remoteSessionRequestAssignmentBase, action: exports_external.literal("send"), input: remoteSessionSendInputSchema }),
14491
+ exports_external.object({ ...remoteSessionRequestAssignmentBase, action: exports_external.literal("stop"), input: remoteSessionStopInputSchema })
14492
+ ]);
14493
+ var remoteSessionRequestClaimResponseSchema = exports_external.object({
14494
+ assignment: remoteSessionRequestAssignmentSchema
14495
+ });
14496
+ var boundedSummarySchema = exports_external.string().max(REMOTE_SESSION_TOOL_SUMMARY_MAX_LENGTH).nullable();
14497
+ var remoteSessionTranscriptToolCallSchema = exports_external.object({
14498
+ id: exports_external.string().max(240),
14499
+ name: exports_external.string().max(240),
14500
+ status: exports_external.string().max(60).nullable(),
14501
+ input: boundedSummarySchema,
14502
+ output: boundedSummarySchema,
14503
+ error: boundedSummarySchema,
14504
+ truncated: exports_external.boolean()
14505
+ });
14506
+ var remoteSessionTranscriptMessageSchema = exports_external.object({
14507
+ id: exports_external.string().max(240),
14508
+ role: exports_external.enum(["user", "assistant"]),
14509
+ createdAt: timestampSchema.nullable(),
14510
+ text: exports_external.string().max(REMOTE_SESSION_TRANSCRIPT_TEXT_MAX_LENGTH),
14511
+ truncated: exports_external.boolean(),
14512
+ toolCalls: exports_external.array(remoteSessionTranscriptToolCallSchema).max(200),
14513
+ error: remoteSessionCommandErrorSchema.nullable()
14514
+ });
14515
+ var remoteSessionReadResultSchema = exports_external.object({
14516
+ title: exports_external.string().max(240).nullable(),
14517
+ status: remoteSessionStatusSchema,
14518
+ waitingFor: remoteSessionWaitingForSchema.nullable(),
14519
+ lastError: remoteSessionCommandErrorSchema.nullable(),
14520
+ messageCount: exports_external.number().int().min(0),
14521
+ from: exports_external.enum(["start", "end"]),
14522
+ messages: exports_external.array(remoteSessionTranscriptMessageSchema).max(REMOTE_SESSION_TRANSCRIPT_PAGE_MAX),
14523
+ nextCursor: exports_external.string().max(160).nullable()
14524
+ });
14525
+ var remoteSessionSendResultSchema = exports_external.object({
14526
+ messageId: exports_external.string().max(160).nullable(),
14527
+ alreadyPresent: exports_external.boolean()
14528
+ });
14529
+ var remoteSessionStopResultSchema = exports_external.object({
14530
+ stopped: exports_external.boolean(),
14531
+ reason: exports_external.enum(["different_turn"]).nullable()
14532
+ });
14533
+ var remoteSessionRequestResultSchema = exports_external.discriminatedUnion("action", [
14534
+ exports_external.object({ action: exports_external.literal("read"), result: remoteSessionReadResultSchema }),
14535
+ exports_external.object({ action: exports_external.literal("send"), result: remoteSessionSendResultSchema }),
14536
+ exports_external.object({ action: exports_external.literal("stop"), result: remoteSessionStopResultSchema })
14537
+ ]);
14538
+ var remoteSessionRequestCompleteRequestSchema = exports_external.discriminatedUnion("status", [
14539
+ exports_external.object({
14540
+ status: exports_external.literal("done"),
14541
+ outcome: remoteSessionRequestResultSchema,
14542
+ error: exports_external.never().optional()
14543
+ }),
14544
+ exports_external.object({
14545
+ status: exports_external.literal("failed"),
14546
+ outcome: exports_external.never().optional(),
14547
+ error: remoteSessionCommandErrorSchema
14548
+ })
14549
+ ]).refine((body) => body.status !== "done" || new TextEncoder().encode(JSON.stringify(body.outcome)).byteLength <= REMOTE_SESSION_REQUEST_RESULT_MAX_BYTES, { message: `The result must be at most ${REMOTE_SESSION_REQUEST_RESULT_MAX_BYTES} bytes`, path: ["outcome"] });
14550
+ var remoteSessionRequestCompleteResponseSchema = exports_external.object({
14551
+ request: exports_external.object({ id: idSchema, status: exports_external.enum(["done", "failed"]) })
14552
+ });
14553
+ var remoteSessionRequestPendingResponseSchema = exports_external.object({
14554
+ items: exports_external.array(exports_external.object({ kind: exports_external.literal("remote_session_request"), requestId: idSchema })).max(5)
14555
+ });
14292
14556
  var automationDesktopRunnerAssignmentSchema = exports_external.object({
14293
14557
  executionTarget: exports_external.literal("desktop"),
14294
14558
  runId: idSchema,
@@ -14544,14 +14808,14 @@ var OPENWORK_CLOUD_CONNECTION_INSTRUCTION = `The OpenWork Cloud connection is ve
14544
14808
  var OPENWORK_CONNECT_SIGN_IN_INSTRUCTION = `${OPENWORK_EXTENSION_DISCOVERY_INSTRUCTION} OpenWork Cloud is not signed in or no desired agent access configuration exists for this workspace. Direct the user to sign in to OpenWork and connect the service in Settings → Connect.`;
14545
14809
  var OPENWORK_CONNECT_DISABLED_INSTRUCTION = `${OPENWORK_EXTENSION_DISCOVERY_INSTRUCTION} OpenWork Cloud agent access is explicitly disabled for this workspace. Explain that the user can enable agent access in Settings → Connect.`;
14546
14810
  var OPENWORK_CLOUD_MCP_NAME = "openwork-cloud";
14547
- function isRecord2(value) {
14811
+ function isRecord3(value) {
14548
14812
  return typeof value === "object" && value !== null && !Array.isArray(value);
14549
14813
  }
14550
14814
  function readString(value) {
14551
14815
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
14552
14816
  }
14553
14817
  function getRecordProperty(value, key) {
14554
- return isRecord2(value) ? value[key] : undefined;
14818
+ return isRecord3(value) ? value[key] : undefined;
14555
14819
  }
14556
14820
  function readNestedString(value, keys) {
14557
14821
  let current = value;
@@ -14561,9 +14825,9 @@ function readNestedString(value, keys) {
14561
14825
  }
14562
14826
  function readContext(input) {
14563
14827
  const context = getRecordProperty(input, "context");
14564
- const session = getRecordProperty(input, "session");
14565
- const directory = readNestedString(input, ["directory"]) ?? readNestedString(context, ["directory"]) ?? readNestedString(session, ["directory"]);
14566
- const worktree = readNestedString(input, ["worktree"]) ?? readNestedString(context, ["worktree"]) ?? readNestedString(session, ["worktree"]);
14828
+ const session2 = getRecordProperty(input, "session");
14829
+ const directory = readNestedString(input, ["directory"]) ?? readNestedString(context, ["directory"]) ?? readNestedString(session2, ["directory"]);
14830
+ const worktree = readNestedString(input, ["worktree"]) ?? readNestedString(context, ["worktree"]) ?? readNestedString(session2, ["worktree"]);
14567
14831
  const workspaceId = readNestedString(input, ["workspaceId"]) ?? readNestedString(input, ["workspaceID"]) ?? readNestedString(context, ["workspaceId"]) ?? readNestedString(context, ["workspaceID"]);
14568
14832
  return {
14569
14833
  ...directory ? { directory } : {},
@@ -14611,7 +14875,7 @@ async function parseResponse(response) {
14611
14875
  }
14612
14876
  }
14613
14877
  function getStringProperty(value, key) {
14614
- if (!isRecord2(value))
14878
+ if (!isRecord3(value))
14615
14879
  return null;
14616
14880
  const property = value[key];
14617
14881
  return typeof property === "string" ? property : null;
@@ -14624,11 +14888,11 @@ function readEngineDirectory(input, fallback) {
14624
14888
  return context.directory ?? context.worktree ?? readString(fallback);
14625
14889
  }
14626
14890
  function engineStatusPayload(result) {
14627
- if (!isRecord2(result))
14891
+ if (!isRecord3(result))
14628
14892
  return result;
14629
- const data = result.data;
14630
- if (data !== undefined)
14631
- return data;
14893
+ const data2 = result.data;
14894
+ if (data2 !== undefined)
14895
+ return data2;
14632
14896
  if (result.error !== undefined)
14633
14897
  throw new Error("OpenCode MCP status request failed");
14634
14898
  const responseOk = getRecordProperty(result.response, "ok");
@@ -14845,7 +15109,7 @@ function sessionContribution() {
14845
15109
  id: "session.search",
14846
15110
  kind: "query",
14847
15111
  title: "Find sessions",
14848
- description: "Search session titles and transcripts without changing the visible workbench. Every root session's title is matched; transcripts are scanned for the `scanLimit` newest sessions only, so when the result's `truncated` is true, retry with a larger `scanLimit` (max 500). Title and phrase matches rank first, then newest `updatedAt`. Each result carries `createdAt`, `archived` and `parentId`.",
15112
+ description: "Search session titles and transcripts without changing the visible workbench. Every root session's title is matched; transcripts are scanned for the `scanLimit` newest sessions only, so when the result's `truncated` is true, retry with a larger `scanLimit` (max 500). Title and phrase matches rank first, then newest `updatedAt`. Each result carries `createdAt`, `archived`, `parentId` and `engine` (v1 or v2, the engine that holds the session).",
14849
15113
  provider,
14850
15114
  arguments: [
14851
15115
  argument("query", "string", true, "Text to find in session titles or messages."),
@@ -14864,7 +15128,7 @@ function sessionContribution() {
14864
15128
  id: "session.read",
14865
15129
  kind: "query",
14866
15130
  title: "Read a session transcript",
14867
- description: "Read messages from a session without opening it. The result also carries `createdAt`, `archived`, `parentId`, `status` (idle, busy, retry, waiting), `working` (check it before session.archive), and `model` ({ providerId, modelId, variant, displayName?, providerName? } the session is bound to, variant being its reasoning effort; null before a model is bound). Pass `summary: true` to get only the first user message and the last assistant message (what was asked, what was concluded) in one call. Both modes include `lastError` ({ code, message } or null) from the latest assistant info.error before text filtering: end reads inspect only the fetched newest `count` messages; start/summary inspect the whole transcript, even outside displayed text. Null means no assistant error observed in that window; a later assistant without an error clears it. Codes are allowlisted error names (otherwise UnknownError); messages are fixed labels under 160 characters, never arbitrary provider details. Snapshot errors only: event-only failures, including pre-assistant model-not-found, are not observable here.",
15131
+ description: "Read messages from a session without opening it. Finds the session on either engine. The result also carries `engine` (v1 or v2), `createdAt`, `archived`, `parentId`, `status` (idle, busy, retry, waiting), `working` (check it before session.archive), and `model` ({ providerId, modelId, variant, displayName?, providerName? } the session is bound to, variant being its reasoning effort; null before a model is bound). Pass `summary: true` to get only the first user message and the last assistant message (what was asked, what was concluded) in one call. Both modes include `lastError` ({ code, message } or null) from the latest assistant info.error before text filtering: end reads inspect only the fetched newest `count` messages; start/summary inspect the whole transcript, even outside displayed text. Null means no assistant error observed in that window; a later assistant without an error clears it. Codes are allowlisted error names (otherwise UnknownError); messages are fixed labels under 160 characters, never arbitrary provider details. Snapshot errors only: event-only failures, including pre-assistant model-not-found, are not observable here.",
14868
15132
  provider,
14869
15133
  arguments: [
14870
15134
  argument("sessionId", "string", true, "Session id returned by session.search."),
@@ -15123,18 +15387,18 @@ A dispatch receipt or a website callback returning does not prove the requested
15123
15387
  If sign-in, CAPTCHA or a sensitive input is needed, call browser_handoff. Ask the user to sign in directly in the browser and resume there; never request passwords, cookies, tokens or one-time codes in chat. Do not put page content or authentication data into logs or evidence.
15124
15388
  Models without vision should use site tools and text observations. When a task requires visual interpretation they cannot perform, request user help. No model selection changes permission or session boundaries.`;
15125
15389
  var WEBMCP_EXECUTION_TIMEOUT_MS = 125000;
15126
- function isRecord3(value) {
15390
+ function isRecord4(value) {
15127
15391
  return typeof value === "object" && value !== null && !Array.isArray(value);
15128
15392
  }
15129
15393
  var MAX_PRESERVED_MCP_APP_RESULT_BYTES = 1024 * 1024;
15130
15394
  function preserveMcpResult(output) {
15131
- if (!isRecord3(output) || !Array.isArray(output.content))
15395
+ if (!isRecord4(output) || !Array.isArray(output.content))
15132
15396
  return;
15133
15397
  const appResult = {
15134
15398
  content: output.content,
15135
15399
  ...typeof output.isError === "boolean" ? { isError: output.isError } : {},
15136
15400
  ...output.structuredContent !== undefined ? { structuredContent: output.structuredContent } : {},
15137
- ...isRecord3(output._meta) ? { _meta: output._meta } : {}
15401
+ ...isRecord4(output._meta) ? { _meta: output._meta } : {}
15138
15402
  };
15139
15403
  try {
15140
15404
  if (new TextEncoder().encode(JSON.stringify(appResult)).byteLength > MAX_PRESERVED_MCP_APP_RESULT_BYTES)
@@ -15142,7 +15406,7 @@ function preserveMcpResult(output) {
15142
15406
  } catch {
15143
15407
  return;
15144
15408
  }
15145
- const existing = isRecord3(output.metadata) ? output.metadata : {};
15409
+ const existing = isRecord4(output.metadata) ? output.metadata : {};
15146
15410
  output.metadata = {
15147
15411
  ...existing,
15148
15412
  openworkMcpApp: appResult
@@ -15154,7 +15418,7 @@ var affordanceExternalWriteEffects = { data: "write", ui: "none", external: true
15154
15418
  var affordanceWriteNavigateEffects = { data: "write", ui: "navigate", external: false };
15155
15419
  var affordanceProposalEffects = { data: "none", ui: "none", external: false };
15156
15420
  function affordanceResult(id, result, effects) {
15157
- if (isRecord3(result) && result.ok === false) {
15421
+ if (isRecord4(result) && result.ok === false) {
15158
15422
  return {
15159
15423
  ok: false,
15160
15424
  id,
@@ -15169,7 +15433,7 @@ function unavailableAffordance(id, error48) {
15169
15433
  return { ok: false, id, error: error48, code: "unavailable" };
15170
15434
  }
15171
15435
  function optionalStringProperty(value, key) {
15172
- if (!isRecord3(value))
15436
+ if (!isRecord4(value))
15173
15437
  return;
15174
15438
  const property = value[key];
15175
15439
  return typeof property === "string" && property.trim().length > 0 ? property : undefined;
@@ -15178,15 +15442,15 @@ function isOpenWorkEngineMcpStatusFunction(value) {
15178
15442
  return typeof value === "function";
15179
15443
  }
15180
15444
  function readEngineMcpStatusClient(value) {
15181
- const client = isRecord3(value) ? value.client : undefined;
15182
- const mcp = isRecord3(client) ? client.mcp : undefined;
15183
- const status = isRecord3(mcp) ? mcp.status : undefined;
15445
+ const client = isRecord4(value) ? value.client : undefined;
15446
+ const mcp = isRecord4(client) ? client.mcp : undefined;
15447
+ const status = isRecord4(mcp) ? mcp.status : undefined;
15184
15448
  if (!isOpenWorkEngineMcpStatusFunction(status))
15185
15449
  return;
15186
15450
  return { mcp: { status: (request) => status.call(mcp, request) } };
15187
15451
  }
15188
15452
  function normalizeOpenCodeContext(value) {
15189
- const nested = isRecord3(value) && isRecord3(value.context) ? value.context : value;
15453
+ const nested = isRecord4(value) && isRecord4(value.context) ? value.context : value;
15190
15454
  const agent = optionalStringProperty(nested, "agent");
15191
15455
  const sessionID = optionalStringProperty(nested, "sessionID");
15192
15456
  const messageID = optionalStringProperty(nested, "messageID");
@@ -15219,6 +15483,9 @@ async function uiControlRequest(kind, input) {
15219
15483
  }
15220
15484
  }
15221
15485
  async function serverGet(path) {
15486
+ const transport = openworkReadTransport.getStore();
15487
+ if (transport)
15488
+ return transport.get(path);
15222
15489
  const { url: url2, token } = requireOpenWorkServer2();
15223
15490
  const response = await fetch(`${url2}${path}`, {
15224
15491
  headers: { Authorization: `Bearer ${token}` }
@@ -15241,8 +15508,8 @@ async function readEngineMcpDescriptors(client, directory) {
15241
15508
  return [];
15242
15509
  try {
15243
15510
  const result = await client.mcp.status(directory ? { query: { directory } } : undefined);
15244
- const payload = isRecord3(result) && result.data !== undefined ? result.data : result;
15245
- if (!isRecord3(payload))
15511
+ const payload = isRecord4(result) && result.data !== undefined ? result.data : result;
15512
+ if (!isRecord4(payload))
15246
15513
  return [];
15247
15514
  return Object.entries(payload).map(([name, entry]) => {
15248
15515
  const status = typeof entry === "string" ? entry : optionalStringProperty(entry, "status");
@@ -15260,7 +15527,7 @@ async function readOpenworkAgentContext(engineMcpStatusClient, engineMcpStatusDi
15260
15527
  ]);
15261
15528
  const contributions = buildOpenworkProviderContributions(skills, mcps);
15262
15529
  const providerAffordances = contributions.flatMap((contribution) => contribution.affordances);
15263
- const uiContext = isRecord3(uiResult) && isRecord3(uiResult.context) ? uiResult.context : null;
15530
+ const uiContext = isRecord4(uiResult) && isRecord4(uiResult.context) ? uiResult.context : null;
15264
15531
  if (!uiContext) {
15265
15532
  return {
15266
15533
  ok: true,
@@ -15297,7 +15564,7 @@ async function queryOpenworkAffordance(rawArgs) {
15297
15564
  return unavailableAffordance(request.id, "This affordance declares a dedicated Connect executor. Call the tool named in openwork_context.");
15298
15565
  }
15299
15566
  const result = await uiControlRequest("query", request);
15300
- return isRecord3(result) && typeof result.ok === "boolean" ? result : unavailableAffordance(request.id, "OpenWork UI query returned an invalid response.");
15567
+ return isRecord4(result) && typeof result.ok === "boolean" ? result : unavailableAffordance(request.id, "OpenWork UI query returned an invalid response.");
15301
15568
  }
15302
15569
  async function executeOpenworkAffordance(rawArgs, context) {
15303
15570
  const request = openworkAffordanceRequestSchema2.parse(rawArgs);
@@ -15324,7 +15591,7 @@ async function executeOpenworkAffordance(rawArgs, context) {
15324
15591
  return unavailableAffordance(request.id, "This affordance declares a dedicated Connect executor. Call the tool named in openwork_context.");
15325
15592
  }
15326
15593
  const result = await uiControlRequest("command", { ...request, ...affordanceOrigin(context) });
15327
- return isRecord3(result) && typeof result.ok === "boolean" ? result : unavailableAffordance(request.id, "OpenWork UI command returned an invalid response.");
15594
+ return isRecord4(result) && typeof result.ok === "boolean" ? result : unavailableAffordance(request.id, "OpenWork UI command returned an invalid response.");
15328
15595
  }
15329
15596
  function affordanceOrigin(context) {
15330
15597
  const sessionId = context.sessionID?.trim();
@@ -15346,46 +15613,47 @@ function buildSessionSnippet(text, index, length) {
15346
15613
  function workspaceLabel(workspace) {
15347
15614
  return workspace.displayName?.trim() || workspace.name?.trim() || workspace.path?.trim() || workspace.id;
15348
15615
  }
15349
- function sessionTitle(session) {
15350
- return session.title?.trim() || session.id;
15616
+ function sessionTitle(session2) {
15617
+ return session2.title?.trim() || session2.id;
15351
15618
  }
15352
- function sessionUpdatedAt(session) {
15353
- return session.time?.updated ?? session.time?.created ?? 0;
15619
+ function sessionUpdatedAt(session2) {
15620
+ return session2.time?.updated ?? session2.time?.created ?? 0;
15354
15621
  }
15355
- function sessionCreatedAt(session) {
15356
- return session.time?.created ?? session.time?.updated ?? 0;
15622
+ function sessionCreatedAt(session2) {
15623
+ return session2.time?.created ?? session2.time?.updated ?? 0;
15357
15624
  }
15358
- function sessionArchived(session) {
15359
- const archived = session.time?.archived;
15625
+ function sessionArchived(session2) {
15626
+ const archived = session2.time?.archived;
15360
15627
  return typeof archived === "number" && archived > 0;
15361
15628
  }
15362
- function sessionMetadata(workspace, session) {
15629
+ function sessionMetadata(workspace, session2, engine) {
15363
15630
  return {
15364
15631
  workspaceId: workspace.id,
15632
+ engine,
15365
15633
  workspace: workspaceLabel(workspace),
15366
- sessionId: session.id,
15367
- title: sessionTitle(session),
15368
- createdAt: sessionCreatedAt(session),
15369
- updatedAt: sessionUpdatedAt(session),
15370
- archived: sessionArchived(session),
15371
- parentId: session.parentID ?? null
15634
+ sessionId: session2.id,
15635
+ title: sessionTitle(session2),
15636
+ createdAt: sessionCreatedAt(session2),
15637
+ updatedAt: sessionUpdatedAt(session2),
15638
+ archived: sessionArchived(session2),
15639
+ parentId: session2.parentID ?? null
15372
15640
  };
15373
15641
  }
15374
- function sessionPassesFilters(session, args) {
15375
- const createdAt = sessionCreatedAt(session);
15642
+ function sessionPassesFilters(session2, args) {
15643
+ const createdAt = sessionCreatedAt(session2);
15376
15644
  if (args.createdAfter !== undefined && createdAt < sessionTimestampMs(args.createdAfter))
15377
15645
  return false;
15378
15646
  if (args.createdBefore !== undefined && createdAt > sessionTimestampMs(args.createdBefore))
15379
15647
  return false;
15380
15648
  const archived = args.archived ?? "include";
15381
- if (archived === "exclude" && sessionArchived(session))
15649
+ if (archived === "exclude" && sessionArchived(session2))
15382
15650
  return false;
15383
- if (archived === "only" && !sessionArchived(session))
15651
+ if (archived === "only" && !sessionArchived(session2))
15384
15652
  return false;
15385
15653
  return true;
15386
15654
  }
15387
- function sessionModelOf(session) {
15388
- const model = session.model;
15655
+ function sessionModelOf(session2) {
15656
+ const model = session2.model;
15389
15657
  if (!model)
15390
15658
  return null;
15391
15659
  const variant = model.variant?.trim();
@@ -15432,19 +15700,19 @@ function findTextMatch(text, queryLower, mode) {
15432
15700
  }
15433
15701
  return Number.isFinite(firstIndex) ? { index: firstIndex, length: firstLength, phrase: false } : null;
15434
15702
  }
15435
- function titleSearchResult(workspace, session, queryLower, mode) {
15436
- const text = `${sessionTitle(session)} ${workspaceLabel(workspace)}`;
15703
+ function titleSearchResult(workspace, session2, engine, queryLower, mode) {
15704
+ const text = `${sessionTitle(session2)} ${workspaceLabel(workspace)}`;
15437
15705
  const match = findTextMatch(text, queryLower, mode);
15438
15706
  if (!match)
15439
15707
  return null;
15440
15708
  return {
15441
- ...sessionMetadata(workspace, session),
15709
+ ...sessionMetadata(workspace, session2, engine),
15442
15710
  kind: "title",
15443
15711
  phrase: match.phrase,
15444
15712
  snippet: buildSessionSnippet(text, match.index, match.length)
15445
15713
  };
15446
15714
  }
15447
- function messageSearchResult(workspace, session, messages, queryLower, mode) {
15715
+ function messageSearchResult(workspace, session2, engine, messages, queryLower, mode) {
15448
15716
  let fallback = null;
15449
15717
  for (const [index, message] of messages.entries()) {
15450
15718
  const role = message.info.role;
@@ -15457,7 +15725,7 @@ function messageSearchResult(workspace, session, messages, queryLower, mode) {
15457
15725
  if (!match)
15458
15726
  continue;
15459
15727
  const result = {
15460
- ...sessionMetadata(workspace, session),
15728
+ ...sessionMetadata(workspace, session2, engine),
15461
15729
  kind: "message",
15462
15730
  phrase: match.phrase,
15463
15731
  role,
@@ -15488,13 +15756,35 @@ function filterWorkspaces(workspaces, workspaceId) {
15488
15756
  return labels.includes(query);
15489
15757
  });
15490
15758
  }
15491
- async function listWorkspaceSessions(workspace, limit) {
15759
+ function ownEngineReader() {
15760
+ const transport = openworkReadTransport.getStore();
15761
+ if (!transport)
15762
+ return { engine: "v1", get: serverGet };
15763
+ const activity = transport.activity;
15764
+ return {
15765
+ engine: transport.engine ?? "v1",
15766
+ get: (path) => transport.get(path),
15767
+ ...activity ? { activity: (workspaceId, sessionId) => activity(workspaceId, sessionId) } : {}
15768
+ };
15769
+ }
15770
+ function engineReaders() {
15771
+ const own = ownEngineReader();
15772
+ const transport = openworkReadTransport.getStore();
15773
+ if (transport)
15774
+ return transport.other ? [own, transport.other] : [own];
15775
+ return [own, {
15776
+ engine: "v2",
15777
+ get: createV2ReadAdapter(serverGet),
15778
+ activity: (workspaceId, sessionId) => readV2SessionActivity(serverGet, workspaceId, sessionId)
15779
+ }];
15780
+ }
15781
+ async function listWorkspaceSessions(reader, workspace, limit) {
15492
15782
  const query = new URLSearchParams({ roots: "true", limit: String(limit) });
15493
- return exports_external.array(sessionInfoSchema).parse(await serverGet(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session?${query.toString()}`));
15783
+ return exports_external.array(sessionInfoSchema).parse(await reader.get(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session?${query.toString()}`));
15494
15784
  }
15495
- async function assertSessionInWorkspace(workspace, session) {
15785
+ async function assertSessionInWorkspace(workspace, session2) {
15496
15786
  const workspacePath = workspace.path?.trim();
15497
- const sessionDirectory = session.directory?.trim();
15787
+ const sessionDirectory = session2.directory?.trim();
15498
15788
  if (!workspacePath || !sessionDirectory)
15499
15789
  return;
15500
15790
  const [root, dir] = await Promise.all([
@@ -15505,26 +15795,26 @@ async function assertSessionInWorkspace(workspace, session) {
15505
15795
  const normalizedDir = normalizeDirPath(dir);
15506
15796
  if (normalizedDir === normalizedRoot || normalizedDir.startsWith(`${normalizedRoot}/`))
15507
15797
  return;
15508
- throw new Error(`Session ${session.id} not found in workspace ${workspaceLabel(workspace)}`);
15798
+ throw new Error(`Session ${session2.id} not found in workspace ${workspaceLabel(workspace)}`);
15509
15799
  }
15510
- async function readWorkspaceSession(workspace, sessionId) {
15511
- const session = sessionInfoSchema.parse(await serverGet(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(sessionId)}`));
15512
- await assertSessionInWorkspace(workspace, session);
15513
- return session;
15800
+ async function readWorkspaceSession(workspace, sessionId, reader = ownEngineReader()) {
15801
+ const session2 = sessionInfoSchema.parse(await reader.get(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(sessionId)}`));
15802
+ await assertSessionInWorkspace(workspace, session2);
15803
+ return session2;
15514
15804
  }
15515
15805
  var MAX_SESSION_DESCENDANTS = 256;
15516
15806
  var sessionChildrenSchema = exports_external.array(exports_external.object({
15517
15807
  id: exports_external.string().trim().min(1),
15518
15808
  time: exports_external.object({ archived: exports_external.number().optional() }).optional()
15519
15809
  }).passthrough());
15520
- async function readSessionDescendantIds(base, sessionId) {
15810
+ async function readSessionDescendantIds(get, base, sessionId) {
15521
15811
  const queue = [sessionId];
15522
15812
  const seen = new Set(queue);
15523
15813
  const ids = [];
15524
15814
  let unknown2 = 0;
15525
15815
  let index = 0;
15526
15816
  for (;index < queue.length && index < MAX_SESSION_DESCENDANTS; index += 1) {
15527
- const parsed = sessionChildrenSchema.safeParse(await serverGet(`${base}/session/${encodeURIComponent(queue[index])}/children`).catch(() => null));
15817
+ const parsed = sessionChildrenSchema.safeParse(await get(`${base}/session/${encodeURIComponent(queue[index])}/children`).catch(() => null));
15528
15818
  if (!parsed.success) {
15529
15819
  unknown2 += 1;
15530
15820
  continue;
@@ -15545,32 +15835,34 @@ async function readSessionDescendantIds(base, sessionId) {
15545
15835
  }
15546
15836
  return { ids, unknown: unknown2 };
15547
15837
  }
15548
- async function readSessionActivity(workspace, session) {
15838
+ async function readSessionActivity(reader, workspace, session2) {
15839
+ if (reader.activity)
15840
+ return reader.activity(workspace.id, session2.id);
15549
15841
  const base = `/workspace/${encodeURIComponent(workspace.id)}/opencode`;
15550
- const probe = (path) => serverGet(`${base}${path}`).catch(() => null);
15842
+ const probe = (path) => reader.get(`${base}${path}`).catch(() => null);
15551
15843
  const [statuses, permissions, questions, descendants] = await Promise.all([
15552
15844
  probe("/session/status"),
15553
15845
  probe("/permission"),
15554
15846
  probe("/question"),
15555
- session.time?.archived ? { ids: [], unknown: 0 } : readSessionDescendantIds(base, session.id)
15847
+ session2.time?.archived ? { ids: [], unknown: 0 } : readSessionDescendantIds((path) => reader.get(path), base, session2.id)
15556
15848
  ]);
15557
- return sessionActivityFrom(statuses, permissions, questions, session.id, descendants.ids, descendants.unknown);
15849
+ return sessionActivityFrom(statuses, permissions, questions, session2.id, descendants.ids, descendants.unknown);
15558
15850
  }
15559
- async function readSessionMessages(workspace, sessionId, limit) {
15851
+ async function readSessionMessages(reader, workspace, sessionId, limit) {
15560
15852
  const query = limit === undefined ? "" : `?${new URLSearchParams({ limit: String(limit) }).toString()}`;
15561
- return exports_external.array(sessionMessageSchema).parse(await serverGet(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(sessionId)}/message${query}`));
15853
+ return exports_external.array(sessionMessageSchema).parse(await reader.get(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(sessionId)}/message${query}`));
15562
15854
  }
15563
- async function forEachWithConcurrency(items, concurrency, run) {
15855
+ async function forEachWithConcurrency(items2, concurrency, run) {
15564
15856
  let index = 0;
15565
15857
  const worker = async () => {
15566
- while (index < items.length) {
15567
- const item = items[index];
15858
+ while (index < items2.length) {
15859
+ const item = items2[index];
15568
15860
  index += 1;
15569
15861
  if (item !== undefined)
15570
15862
  await run(item);
15571
15863
  }
15572
15864
  };
15573
- await Promise.all(Array.from({ length: Math.min(Math.max(1, concurrency), Math.max(1, items.length)) }, () => worker()));
15865
+ await Promise.all(Array.from({ length: Math.min(Math.max(1, concurrency), Math.max(1, items2.length)) }, () => worker()));
15574
15866
  }
15575
15867
  async function searchOpenWorkSessions(rawArgs) {
15576
15868
  const parsed = sessionSearchArgsSchema.safeParse(rawArgs);
@@ -15588,34 +15880,45 @@ async function searchOpenWorkSessions(rawArgs) {
15588
15880
  }
15589
15881
  const sessions = [];
15590
15882
  const workspaceErrors = [];
15883
+ const readers = engineReaders();
15591
15884
  await Promise.all(workspaces.map(async (workspace) => {
15592
- try {
15593
- const items = await listWorkspaceSessions(workspace, SESSION_SEARCH_TITLE_LIST_LIMIT);
15594
- for (const session of items)
15595
- if (sessionPassesFilters(session, args))
15596
- sessions.push({ workspace, session });
15597
- } catch (error48) {
15598
- workspaceErrors.push({ workspaceId: workspace.id, workspace: workspaceLabel(workspace), error: unknownErrorMessage(error48) });
15599
- }
15885
+ const listed = await Promise.all(readers.map((reader, index) => listWorkspaceSessions(reader, workspace, SESSION_SEARCH_TITLE_LIST_LIMIT).catch((error48) => {
15886
+ if (index === 0)
15887
+ workspaceErrors.push({ workspaceId: workspace.id, workspace: workspaceLabel(workspace), error: unknownErrorMessage(error48) });
15888
+ return [];
15889
+ })));
15890
+ const seen = new Set;
15891
+ listed.forEach((items2, index) => {
15892
+ const reader = readers[index];
15893
+ if (!reader)
15894
+ return;
15895
+ for (const session2 of items2) {
15896
+ if (seen.has(session2.id))
15897
+ continue;
15898
+ seen.add(session2.id);
15899
+ if (sessionPassesFilters(session2, args))
15900
+ sessions.push({ workspace, session: session2, reader });
15901
+ }
15902
+ });
15600
15903
  }));
15601
15904
  sessions.sort((left, right) => sessionUpdatedAt(right.session) - sessionUpdatedAt(left.session));
15602
15905
  const sessionsToScan = sessions.slice(0, scanLimit);
15603
15906
  const matches = [];
15604
15907
  const titleMatched = new Set;
15605
- for (const { workspace, session } of sessions.slice(scanLimit)) {
15606
- const titleMatch = titleSearchResult(workspace, session, queryLower, mode);
15908
+ for (const { workspace, session: session2, reader } of sessions.slice(scanLimit)) {
15909
+ const titleMatch = titleSearchResult(workspace, session2, reader.engine, queryLower, mode);
15607
15910
  if (!titleMatch)
15608
15911
  continue;
15609
- titleMatched.add(session.id);
15912
+ titleMatched.add(session2.id);
15610
15913
  matches.push(titleMatch);
15611
15914
  }
15612
- await forEachWithConcurrency(sessionsToScan, SESSION_SEARCH_CONCURRENCY, async ({ workspace, session }) => {
15613
- const titleMatch = titleSearchResult(workspace, session, queryLower, mode);
15915
+ await forEachWithConcurrency(sessionsToScan, SESSION_SEARCH_CONCURRENCY, async ({ workspace, session: session2, reader }) => {
15916
+ const titleMatch = titleSearchResult(workspace, session2, reader.engine, queryLower, mode);
15614
15917
  if (titleMatch)
15615
- titleMatched.add(session.id);
15918
+ titleMatched.add(session2.id);
15616
15919
  try {
15617
- const messages = await readSessionMessages(workspace, session.id, messageLimit);
15618
- const messageMatch = messageSearchResult(workspace, session, messages, queryLower, mode);
15920
+ const messages = await readSessionMessages(reader, workspace, session2.id, messageLimit);
15921
+ const messageMatch = messageSearchResult(workspace, session2, reader.engine, messages, queryLower, mode);
15619
15922
  if (messageMatch)
15620
15923
  matches.push(messageMatch);
15621
15924
  else if (titleMatch)
@@ -15654,7 +15957,7 @@ function lastAssistantError(messages) {
15654
15957
  const error48 = [...messages].reverse().find((message) => message.info.role === "assistant")?.info.error;
15655
15958
  if (error48 === undefined || error48 === null)
15656
15959
  return null;
15657
- if (isRecord3(error48) && typeof error48.name === "string") {
15960
+ if (isRecord4(error48) && typeof error48.name === "string") {
15658
15961
  const message = assistantErrorMessages.get(error48.name);
15659
15962
  if (message !== undefined)
15660
15963
  return { code: error48.name, message };
@@ -15670,8 +15973,16 @@ function readableMessages(messages) {
15670
15973
  text: messageText(message)
15671
15974
  })).filter((message) => message.text.trim().length > 0);
15672
15975
  }
15673
- async function readWorkspaceModels(workspace) {
15674
- return openworkCatalogModels(openworkEngineProviderCatalogSchema.parse(await serverGet(`/workspace/${encodeURIComponent(workspace.id)}/opencode/provider`)));
15976
+ async function readWorkspaceModels(reader, workspace) {
15977
+ return openworkCatalogModels(openworkEngineProviderCatalogSchema.parse(await reader.get(`/workspace/${encodeURIComponent(workspace.id)}/opencode/provider`)));
15978
+ }
15979
+ async function readSessionOnEitherEngine(workspace, sessionId, readers) {
15980
+ for (const reader of readers) {
15981
+ try {
15982
+ return { session: await readWorkspaceSession(workspace, sessionId, reader), reader };
15983
+ } catch {}
15984
+ }
15985
+ return null;
15675
15986
  }
15676
15987
  async function readOpenWorkSession(rawArgs) {
15677
15988
  const parsed = sessionReadArgsSchema.safeParse(rawArgs);
@@ -15685,19 +15996,26 @@ async function readOpenWorkSession(rawArgs) {
15685
15996
  if (!workspaces.length) {
15686
15997
  return { ok: false, error: args.workspaceId ? `No workspace matched ${args.workspaceId}` : "No OpenWork workspaces are available" };
15687
15998
  }
15999
+ const readers = engineReaders();
15688
16000
  for (const workspace of workspaces) {
15689
16001
  try {
15690
- const session = await readWorkspaceSession(workspace, args.sessionId);
16002
+ const located = await readSessionOnEitherEngine(workspace, args.sessionId, readers);
16003
+ if (!located) {
16004
+ if (args.workspaceId)
16005
+ break;
16006
+ continue;
16007
+ }
16008
+ const { session: session2, reader } = located;
15691
16009
  const needsFullTranscript = summary || from === "start";
15692
16010
  const [messages, activity, catalog] = await Promise.all([
15693
- readSessionMessages(workspace, args.sessionId, needsFullTranscript ? undefined : count),
15694
- readSessionActivity(workspace, session),
15695
- readWorkspaceModels(workspace).catch(() => [])
16011
+ readSessionMessages(reader, workspace, args.sessionId, needsFullTranscript ? undefined : count),
16012
+ readSessionActivity(reader, workspace, session2),
16013
+ readWorkspaceModels(reader, workspace).catch(() => [])
15696
16014
  ]);
15697
16015
  const lastError = lastAssistantError(messages);
15698
16016
  const readable = readableMessages(messages);
15699
16017
  const metadata = {
15700
- ...sessionMetadata(workspace, session),
16018
+ ...sessionMetadata(workspace, session2, reader.engine),
15701
16019
  ...activity,
15702
16020
  lastError
15703
16021
  };
@@ -15705,7 +16023,7 @@ async function readOpenWorkSession(rawArgs) {
15705
16023
  return {
15706
16024
  ok: true,
15707
16025
  ...metadata,
15708
- model: labelOpenworkSessionModel(sessionModelOf(session), catalog),
16026
+ model: labelOpenworkSessionModel(sessionModelOf(session2), catalog),
15709
16027
  totalMessages: readable.length,
15710
16028
  firstUser: readable.find((message) => message.role === "user") ?? null,
15711
16029
  lastAssistant: [...readable].reverse().find((message) => message.role === "assistant") ?? null
@@ -15715,7 +16033,7 @@ async function readOpenWorkSession(rawArgs) {
15715
16033
  return {
15716
16034
  ok: true,
15717
16035
  ...metadata,
15718
- model: labelOpenworkSessionModel(sessionModelOf(session), catalog),
16036
+ model: labelOpenworkSessionModel(sessionModelOf(session2), catalog),
15719
16037
  from,
15720
16038
  returned: window.length,
15721
16039
  requested: count,
@@ -15756,26 +16074,26 @@ async function sendToOpenWorkSession(rawArgs, context) {
15756
16074
  const located = await locateOpenWorkSession(args.sessionId, args.workspaceId);
15757
16075
  if ("error" in located)
15758
16076
  return { ok: false, error: located.error };
15759
- const { workspace, session } = located;
16077
+ const { workspace, session: session2 } = located;
15760
16078
  const messageId = createSendMessageId();
15761
- await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(session.id)}/prompt_async`, { messageID: messageId, parts: [{ type: "text", text: args.text }] });
16079
+ await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(session2.id)}/prompt_async`, { messageID: messageId, parts: [{ type: "text", text: args.text }] });
15762
16080
  const result = {
15763
16081
  ok: true,
15764
16082
  accepted: true,
15765
- sessionId: session.id,
16083
+ sessionId: session2.id,
15766
16084
  workspaceId: workspace.id,
15767
16085
  workspace: workspaceLabel(workspace),
15768
- title: sessionTitle(session),
16086
+ title: sessionTitle(session2),
15769
16087
  messageId
15770
16088
  };
15771
16089
  if (args.reveal !== true)
15772
16090
  return result;
15773
16091
  const opened = await uiControlRequest("command", {
15774
16092
  id: "session.open",
15775
- args: { sessionId: session.id },
16093
+ args: { sessionId: session2.id },
15776
16094
  ...affordanceOrigin(context)
15777
16095
  });
15778
- return { ...result, revealed: isRecord3(opened) && opened.ok === true };
16096
+ return { ...result, revealed: isRecord4(opened) && opened.ok === true };
15779
16097
  }
15780
16098
  function serverUrl2() {
15781
16099
  return String(process.env.OPENWORK_SERVER_URL || "").replace(/\/$/, "");
@@ -15876,7 +16194,7 @@ async function createOpenWorkSessions(rawArgs, context) {
15876
16194
  let models;
15877
16195
  try {
15878
16196
  catalog = [];
15879
- if (args.model || args.sessions.some((session) => session.model)) {
16197
+ if (args.model || args.sessions.some((session2) => session2.model)) {
15880
16198
  const envelope = openworkAffordanceResultSchema.safeParse(await uiControlRequest("query", {
15881
16199
  id: "models.list",
15882
16200
  args: { workspaceId: workspace.id }
@@ -15890,23 +16208,23 @@ async function createOpenWorkSessions(rawArgs, context) {
15890
16208
  catalog = result.models;
15891
16209
  }
15892
16210
  const defaultModel = args.model ? resolveOpenworkModel(args.model, catalog) : undefined;
15893
- models = args.sessions.map((session) => session.model ? resolveOpenworkModel(session.model, catalog) : defaultModel);
16211
+ models = args.sessions.map((session2) => session2.model ? resolveOpenworkModel(session2.model, catalog) : defaultModel);
15894
16212
  } catch (error48) {
15895
16213
  return { ok: false, error: unknownErrorMessage(error48) };
15896
16214
  }
15897
16215
  let createdOnEngine = false;
15898
- const results = await Promise.all(args.sessions.map(async (session, index) => {
16216
+ const results = await Promise.all(args.sessions.map(async (session2, index) => {
15899
16217
  const inputTitle = argumentAtPath(rawArgs, ["sessions", index, "title"]);
15900
16218
  const titleTruncated = typeof inputTitle === "string" && inputTitle.trim().length > 120;
15901
16219
  const model = models[index];
15902
16220
  try {
15903
- const payload = sessionInfoSchema.parse(await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session`, { title: session.title, ...model ? { model: engineSessionCreateModel(model) } : {} }));
16221
+ const payload = sessionInfoSchema.parse(await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session`, { title: session2.title, ...model ? { model: engineSessionCreateModel(model) } : {} }));
15904
16222
  createdOnEngine = true;
15905
- await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(payload.id)}/prompt_async`, { ...model ? enginePromptModel(model) : {}, parts: [{ type: "text", text: session.prompt }] });
16223
+ await postJson(`/workspace/${encodeURIComponent(workspace.id)}/opencode/session/${encodeURIComponent(payload.id)}/prompt_async`, { ...model ? enginePromptModel(model) : {}, parts: [{ type: "text", text: session2.prompt }] });
15906
16224
  return {
15907
16225
  ok: true,
15908
16226
  sessionId: payload.id,
15909
- title: session.title,
16227
+ title: session2.title,
15910
16228
  titleTruncated,
15911
16229
  started: true,
15912
16230
  model: labelOpenworkSessionModel(sessionModelOf(payload), catalog),
@@ -15915,7 +16233,7 @@ async function createOpenWorkSessions(rawArgs, context) {
15915
16233
  } catch (error48) {
15916
16234
  return {
15917
16235
  ok: false,
15918
- title: session.title,
16236
+ title: session2.title,
15919
16237
  titleTruncated,
15920
16238
  error: unknownErrorMessage(error48)
15921
16239
  };
@@ -15950,6 +16268,9 @@ function proposeAutomation(rawArgs, context) {
15950
16268
  };
15951
16269
  }
15952
16270
  async function postJson(path, body, signal, gmailAttachment = false) {
16271
+ const transport = openworkReadTransport.getStore();
16272
+ if (transport)
16273
+ return transport.post(path, body, signal);
15953
16274
  if (gmailAttachment && (!serverUrl2() || !serverToken2())) {
15954
16275
  throw new ApiError(409, "gmail_host_unavailable", "OpenWork host transport is unavailable. Run this tool from OpenWork.");
15955
16276
  }
@@ -15966,7 +16287,7 @@ async function postJson(path, body, signal, gmailAttachment = false) {
15966
16287
  const payload = await parseResponse2(response);
15967
16288
  if (!response.ok) {
15968
16289
  if (gmailAttachment) {
15969
- throw new ApiError(response.status, getStringProperty2(payload, "code") ?? "gmail_attachment_http_error", errorMessage2(payload, "OpenWork extension call failed"), isRecord3(payload) ? payload.details : undefined);
16290
+ throw new ApiError(response.status, getStringProperty2(payload, "code") ?? "gmail_attachment_http_error", errorMessage2(payload, "OpenWork extension call failed"), isRecord4(payload) ? payload.details : undefined);
15970
16291
  }
15971
16292
  throw new Error(errorMessage2(payload, "OpenWork extension call failed"));
15972
16293
  }
@@ -15992,7 +16313,7 @@ var OpenWorkExtensionsPreview = async (factoryInput, _options, dependencies) =>
15992
16313
  event: fulfillGmailAttachments.event,
15993
16314
  dispose: fulfillGmailAttachments.dispose,
15994
16315
  "chat.headers": async (input, output) => {
15995
- if (input.model.providerID !== "openwork")
16316
+ if (input.model.providerID !== "openwork" && input.model.providerID !== "openwork-free")
15996
16317
  return;
15997
16318
  output.headers["x-openwork-session-id"] = input.sessionID;
15998
16319
  output.headers["x-openwork-task-id"] = input.message.id;