@opengeni/sdk 0.57.0 → 1.0.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 (291) hide show
  1. package/README.md +1017 -26
  2. package/dist/accounts.d.ts +153 -0
  3. package/dist/accounts.js +307 -0
  4. package/dist/accounts.js.map +1 -0
  5. package/dist/adapters/express.d.ts +31 -0
  6. package/dist/adapters/express.js +87 -0
  7. package/dist/adapters/express.js.map +1 -0
  8. package/dist/adapters/hono.d.ts +16 -0
  9. package/dist/adapters/hono.js +8 -0
  10. package/dist/adapters/hono.js.map +1 -0
  11. package/dist/adapters/next.d.ts +29 -0
  12. package/dist/adapters/next.js +22 -0
  13. package/dist/adapters/next.js.map +1 -0
  14. package/dist/allowance-refusal.d.ts +2 -0
  15. package/dist/allowance-refusal.js +50 -0
  16. package/dist/allowance-refusal.js.map +1 -0
  17. package/dist/artifact-catalog.d.ts +31 -0
  18. package/dist/artifact-client.d.ts +23 -11
  19. package/dist/artifacts.d.ts +1 -1
  20. package/dist/artifacts.js +7 -5
  21. package/dist/automations-client.d.ts +17 -0
  22. package/dist/automations.d.ts +3 -0
  23. package/dist/automations.js +84 -0
  24. package/dist/automations.js.map +1 -0
  25. package/dist/browser.d.ts +11 -0
  26. package/dist/browser.js +47 -0
  27. package/dist/browser.js.map +1 -0
  28. package/dist/chat/fold.d.ts +75 -0
  29. package/dist/chat/handler.d.ts +41 -0
  30. package/dist/chat/http.d.ts +67 -0
  31. package/dist/chat/ids.d.ts +34 -0
  32. package/dist/chat/index.d.ts +9 -0
  33. package/dist/chat/index.js +1694 -0
  34. package/dist/chat/index.js.map +1 -0
  35. package/dist/chat/openai.d.ts +30 -0
  36. package/dist/chat/opengeni.d.ts +128 -0
  37. package/dist/chat/types.d.ts +207 -0
  38. package/dist/chat/vercel.d.ts +49 -0
  39. package/dist/chats.d.ts +8 -0
  40. package/dist/{chunk-JDLDDRNE.js → chunk-2UXZUCEZ.js} +291 -34
  41. package/dist/chunk-2UXZUCEZ.js.map +1 -0
  42. package/dist/chunk-4SMEOV3P.js +246 -0
  43. package/dist/chunk-4SMEOV3P.js.map +1 -0
  44. package/dist/{chunk-JRPFWI6T.js → chunk-AH5D5Q5V.js} +31 -35
  45. package/dist/chunk-AH5D5Q5V.js.map +1 -0
  46. package/dist/chunk-APXVSSSY.js +301 -0
  47. package/dist/chunk-APXVSSSY.js.map +1 -0
  48. package/dist/chunk-DUVIYER4.js +639 -0
  49. package/dist/chunk-DUVIYER4.js.map +1 -0
  50. package/dist/{chunk-PKQ377ED.js → chunk-FG3OO5LM.js} +2686 -611
  51. package/dist/chunk-FG3OO5LM.js.map +1 -0
  52. package/dist/chunk-GZYZT22T.js +260 -0
  53. package/dist/chunk-GZYZT22T.js.map +1 -0
  54. package/dist/chunk-HMGADJGZ.js +150 -0
  55. package/dist/chunk-HMGADJGZ.js.map +1 -0
  56. package/dist/{chunk-YKZME56C.js → chunk-KWADQ227.js} +3 -3
  57. package/dist/{chunk-YKZME56C.js.map → chunk-KWADQ227.js.map} +1 -1
  58. package/dist/chunk-LY2K6ELJ.js +126 -0
  59. package/dist/chunk-LY2K6ELJ.js.map +1 -0
  60. package/dist/chunk-MOTJYIYL.js +340 -0
  61. package/dist/chunk-MOTJYIYL.js.map +1 -0
  62. package/dist/{chunk-ST5DDKJP.js → chunk-NC57YMUO.js} +121 -3
  63. package/dist/chunk-NC57YMUO.js.map +1 -0
  64. package/dist/chunk-RWKB6YZG.js +1270 -0
  65. package/dist/chunk-RWKB6YZG.js.map +1 -0
  66. package/dist/chunk-U6CV2SXO.js +195 -0
  67. package/dist/chunk-U6CV2SXO.js.map +1 -0
  68. package/dist/chunk-UM4ZXXJR.js +9 -0
  69. package/dist/chunk-UM4ZXXJR.js.map +1 -0
  70. package/dist/chunk-V6GIY62B.js +229 -0
  71. package/dist/chunk-V6GIY62B.js.map +1 -0
  72. package/dist/chunk-WU6GQG25.js +268 -0
  73. package/dist/chunk-WU6GQG25.js.map +1 -0
  74. package/dist/{chunk-VWE2QIVO.js → chunk-XNG7YGR3.js} +23 -6
  75. package/dist/chunk-XNG7YGR3.js.map +1 -0
  76. package/dist/{chunk-HJ3HHUT5.js → chunk-YIKA2QT4.js} +33 -2
  77. package/dist/chunk-YIKA2QT4.js.map +1 -0
  78. package/dist/chunk-ZKPY6YAJ.js +135 -0
  79. package/dist/chunk-ZKPY6YAJ.js.map +1 -0
  80. package/dist/client.d.ts +740 -94
  81. package/dist/codex-realtime-controller.d.ts +3 -0
  82. package/dist/codex-realtime-controller.js +2 -2
  83. package/dist/codex-realtime-v3.d.ts +13 -2
  84. package/dist/company-brain.d.ts +22 -0
  85. package/dist/company-profile.d.ts +14 -1
  86. package/dist/core.d.ts +9 -3
  87. package/dist/core.js +25 -6
  88. package/dist/deprecation.d.ts +47 -0
  89. package/dist/document-authority-client.d.ts +51 -0
  90. package/dist/document-authority.d.ts +2 -0
  91. package/dist/document-authority.js +12 -0
  92. package/dist/document-authority.js.map +1 -0
  93. package/dist/editable-artifacts/browser-session.d.ts +7 -3
  94. package/dist/editable-artifacts/controller.d.ts +4 -1
  95. package/dist/editable-artifacts/errors.d.ts +1 -1
  96. package/dist/editable-artifacts/http-live-transport.d.ts +6 -2
  97. package/dist/editable-artifacts/index.d.ts +2 -0
  98. package/dist/editable-artifacts/open-failure.d.ts +9 -0
  99. package/dist/editable-artifacts/types.d.ts +6 -3
  100. package/dist/editable-artifacts/worker/kernel-adapter.d.ts +1 -1
  101. package/dist/editable-artifacts-worker.js +44 -17
  102. package/dist/editable-artifacts-worker.js.map +1 -1
  103. package/dist/editable-artifacts.js +240 -121
  104. package/dist/editable-artifacts.js.map +1 -1
  105. package/dist/embedding-client.d.ts +129 -0
  106. package/dist/errors.d.ts +36 -1
  107. package/dist/feedback.d.ts +23 -0
  108. package/dist/gateway-realtime-transport.js +1 -1
  109. package/dist/github-repositories.d.ts +10 -0
  110. package/dist/github-repositories.js +36 -0
  111. package/dist/github-repositories.js.map +1 -0
  112. package/dist/index.d.ts +56 -11
  113. package/dist/index.js +242 -125
  114. package/dist/index.js.map +1 -1
  115. package/dist/interaction.d.ts +84 -1
  116. package/dist/interaction.js +6 -1
  117. package/dist/knowledge.d.ts +7 -0
  118. package/dist/knowledge.js +20 -0
  119. package/dist/knowledge.js.map +1 -0
  120. package/dist/message-links.d.ts +68 -0
  121. package/dist/model-context.d.ts +68 -0
  122. package/dist/model-display.d.ts +2 -0
  123. package/dist/model-display.js +15 -0
  124. package/dist/model-display.js.map +1 -0
  125. package/dist/model-picker-order.d.ts +1 -0
  126. package/dist/model-picker-order.js +10 -0
  127. package/dist/model-picker-order.js.map +1 -0
  128. package/dist/organization-integration-policy.d.ts +26 -0
  129. package/dist/organization-integration-policy.js +26 -0
  130. package/dist/organization-integration-policy.js.map +1 -0
  131. package/dist/organization-private-session-settings.d.ts +6 -0
  132. package/dist/organization-private-session-settings.js +22 -0
  133. package/dist/organization-private-session-settings.js.map +1 -0
  134. package/dist/organization-user-setup.d.ts +8 -0
  135. package/dist/organization-user-setup.js +16 -0
  136. package/dist/organization-user-setup.js.map +1 -0
  137. package/dist/pr-review-client.d.ts +16 -0
  138. package/dist/pr-review.d.ts +4 -0
  139. package/dist/pr-review.js +62 -0
  140. package/dist/pr-review.js.map +1 -0
  141. package/dist/preference-registry.d.ts +9 -1
  142. package/dist/proxy.d.ts +7 -5
  143. package/dist/realtime.d.ts +3 -1
  144. package/dist/realtime.js +6 -6
  145. package/dist/realtime.js.map +1 -1
  146. package/dist/retained-artifacts.d.ts +5 -1
  147. package/dist/session-history-import.d.ts +22 -0
  148. package/dist/session-history-import.js +36 -0
  149. package/dist/session-history-import.js.map +1 -0
  150. package/dist/session-list-entries.d.ts +1 -0
  151. package/dist/session-list-entries.js +7 -0
  152. package/dist/session-list-entries.js.map +1 -0
  153. package/dist/session-message-search.d.ts +53 -0
  154. package/dist/session-proxy-client.d.ts +24 -0
  155. package/dist/session-proxy.d.ts +245 -0
  156. package/dist/session-proxy.js +24 -0
  157. package/dist/session-proxy.js.map +1 -0
  158. package/dist/session-titles.d.ts +2 -0
  159. package/dist/site-browser-entry.d.ts +1 -0
  160. package/dist/site-browser-runtime.gen.d.ts +1 -0
  161. package/dist/site-document.d.ts +5 -0
  162. package/dist/site-document.js +20 -0
  163. package/dist/site-document.js.map +1 -0
  164. package/dist/site-document.test.d.ts +1 -0
  165. package/dist/site-http.d.ts +16 -0
  166. package/dist/site-tool-bridge.d.ts +44 -0
  167. package/dist/site.d.ts +101 -0
  168. package/dist/site.js +49 -0
  169. package/dist/site.js.map +1 -0
  170. package/dist/skills.d.ts +75 -0
  171. package/dist/stream.d.ts +2 -0
  172. package/dist/tenant-workspaces.d.ts +47 -0
  173. package/dist/tenant-workspaces.js +8 -0
  174. package/dist/tenant-workspaces.js.map +1 -0
  175. package/dist/tool-auth.d.ts +87 -0
  176. package/dist/tool-auth.js +23 -0
  177. package/dist/tool-auth.js.map +1 -0
  178. package/dist/tool-display-metadata.d.ts +3 -0
  179. package/dist/tools-FXBLUWAE.js +12 -0
  180. package/dist/tools-FXBLUWAE.js.map +1 -0
  181. package/dist/tools.d.ts +61 -0
  182. package/dist/types.d.ts +2863 -424
  183. package/dist/usage-allowances.d.ts +36 -0
  184. package/dist/usage-allowances.js +80 -0
  185. package/dist/usage-allowances.js.map +1 -0
  186. package/dist/workspace-artifacts.d.ts +33 -5
  187. package/dist/workspace-instruction-policies.d.ts +24 -2
  188. package/dist/workspace-integrations.d.ts +74 -0
  189. package/dist/workspace-integrations.js +65 -0
  190. package/dist/workspace-integrations.js.map +1 -0
  191. package/dist/workspace-learning.d.ts +124 -0
  192. package/dist/workspace-state.d.ts +8 -71
  193. package/package.json +108 -3
  194. package/src/accounts.ts +443 -0
  195. package/src/adapters/express.ts +136 -0
  196. package/src/adapters/hono.ts +18 -0
  197. package/src/adapters/next.ts +42 -0
  198. package/src/allowance-refusal.ts +5 -0
  199. package/src/artifact-catalog.ts +41 -0
  200. package/src/artifact-client.ts +43 -52
  201. package/src/artifacts.ts +7 -1
  202. package/src/automations-client.ts +124 -0
  203. package/src/automations.ts +15 -0
  204. package/src/browser.ts +54 -0
  205. package/src/chat/fold.ts +431 -0
  206. package/src/chat/handler.ts +240 -0
  207. package/src/chat/http.ts +257 -0
  208. package/src/chat/ids.ts +85 -0
  209. package/src/chat/index.ts +87 -0
  210. package/src/chat/openai.ts +439 -0
  211. package/src/chat/opengeni.ts +722 -0
  212. package/src/chat/types.ts +219 -0
  213. package/src/chat/vercel.ts +204 -0
  214. package/src/chats.ts +16 -0
  215. package/src/client.ts +4084 -515
  216. package/src/codex-realtime-controller.ts +235 -5
  217. package/src/codex-realtime-v3.ts +162 -31
  218. package/src/codex-realtime.ts +1 -1
  219. package/src/company-brain.ts +59 -0
  220. package/src/company-profile.ts +17 -1
  221. package/src/core.ts +23 -1
  222. package/src/deprecation.ts +149 -0
  223. package/src/document-authority-client.ts +219 -0
  224. package/src/document-authority.ts +17 -0
  225. package/src/editable-artifacts/browser-session.ts +36 -22
  226. package/src/editable-artifacts/controller.ts +102 -47
  227. package/src/editable-artifacts/errors.ts +2 -0
  228. package/src/editable-artifacts/http-live-transport.ts +62 -37
  229. package/src/editable-artifacts/index.ts +6 -0
  230. package/src/editable-artifacts/open-failure.ts +79 -0
  231. package/src/editable-artifacts/storage.ts +31 -40
  232. package/src/editable-artifacts/types.ts +6 -3
  233. package/src/editable-artifacts/worker/kernel-adapter.ts +23 -7
  234. package/src/editable-artifacts/worker/runtime.ts +26 -13
  235. package/src/editable-artifacts/worker/wire-codec.ts +23 -5
  236. package/src/embedding-client.ts +519 -0
  237. package/src/errors.ts +169 -2
  238. package/src/feedback.ts +20 -0
  239. package/src/gateway-realtime-transport.ts +3 -3
  240. package/src/github-repositories.ts +60 -0
  241. package/src/index.ts +561 -41
  242. package/src/interaction.ts +276 -4
  243. package/src/knowledge.ts +72 -0
  244. package/src/message-links.ts +178 -0
  245. package/src/model-context.ts +112 -0
  246. package/src/model-display.ts +10 -0
  247. package/src/model-picker-order.ts +6 -0
  248. package/src/organization-integration-policy.ts +54 -0
  249. package/src/organization-private-session-settings.ts +33 -0
  250. package/src/organization-user-setup.ts +31 -0
  251. package/src/pr-review-client.ts +92 -0
  252. package/src/pr-review.ts +17 -0
  253. package/src/preference-registry.ts +11 -0
  254. package/src/proxy.ts +17 -6
  255. package/src/realtime.ts +1 -0
  256. package/src/retained-artifacts.ts +101 -0
  257. package/src/session-history-import.ts +84 -0
  258. package/src/session-list-entries.ts +1 -0
  259. package/src/session-message-search.ts +47 -0
  260. package/src/session-proxy-client.ts +64 -0
  261. package/src/session-proxy.ts +1473 -0
  262. package/src/session-titles.ts +10 -0
  263. package/src/site-browser-entry.ts +6 -0
  264. package/src/site-browser-runtime.gen.ts +2 -0
  265. package/src/site-document.test.ts +64 -0
  266. package/src/site-document.ts +18 -0
  267. package/src/site-http.ts +162 -0
  268. package/src/site-tool-bridge.ts +146 -0
  269. package/src/site.ts +585 -0
  270. package/src/skills.ts +69 -0
  271. package/src/sse.ts +8 -2
  272. package/src/stream.ts +38 -4
  273. package/src/tenant-workspaces.ts +148 -0
  274. package/src/tool-auth.ts +301 -0
  275. package/src/tool-display-metadata.ts +19 -0
  276. package/src/tools.ts +396 -0
  277. package/src/types.ts +3346 -501
  278. package/src/usage-allowances.ts +189 -0
  279. package/src/workspace-artifacts.ts +40 -6
  280. package/src/workspace-instruction-policies.ts +26 -1
  281. package/src/workspace-integrations.ts +402 -0
  282. package/src/workspace-learning.ts +138 -0
  283. package/src/workspace-state.ts +8 -82
  284. package/dist/chunk-GU6JF75T.js +0 -119
  285. package/dist/chunk-GU6JF75T.js.map +0 -1
  286. package/dist/chunk-HJ3HHUT5.js.map +0 -1
  287. package/dist/chunk-JDLDDRNE.js.map +0 -1
  288. package/dist/chunk-JRPFWI6T.js.map +0 -1
  289. package/dist/chunk-PKQ377ED.js.map +0 -1
  290. package/dist/chunk-ST5DDKJP.js.map +0 -1
  291. package/dist/chunk-VWE2QIVO.js.map +0 -1
@@ -0,0 +1,1270 @@
1
+ import {
2
+ chatDefaults
3
+ } from "./chunk-UM4ZXXJR.js";
4
+ import {
5
+ mintToolToken,
6
+ resolveToolTokenSecret
7
+ } from "./chunk-U6CV2SXO.js";
8
+ import {
9
+ sessionEventStreamCoveredThrough
10
+ } from "./chunk-APXVSSSY.js";
11
+ import {
12
+ OPENGENI_API_CONTRACT_HEADER,
13
+ OPENGENI_API_CONTRACT_REVISION
14
+ } from "./chunk-YIKA2QT4.js";
15
+ import {
16
+ OpenGeniApiError,
17
+ OpenGeniSetupError
18
+ } from "./chunk-V6GIY62B.js";
19
+
20
+ // src/message-links.ts
21
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
22
+ var EDITABLE_ID = /^[0-9a-f]{32}$/iu;
23
+ var WORKSPACE_SEGMENT = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/u;
24
+ var SANDBOX_SCHEME = "sandbox:";
25
+ var LEGACY_WORKSPACE_PREFIX = "/workspace/";
26
+ var TRAILING_LINE = /:([0-9]+)$/u;
27
+ var TRAILING_INVALID_LINE = /:-(?:[0-9]+)$|:[0-9]+-[0-9]+$/u;
28
+ var CONTROL_CHARACTER = /[\u0000-\u001f\u007f]/u;
29
+ function openGeniLinkScheme(href) {
30
+ const scheme = /^([a-z]+):/iu.exec(href ?? "")?.[1]?.toLowerCase();
31
+ return scheme === "artifact" || scheme === "sandbox" ? scheme : null;
32
+ }
33
+ function isReservedOpenGeniLink(href) {
34
+ if (!href) return false;
35
+ if (openGeniLinkScheme(href) || href.startsWith(LEGACY_WORKSPACE_PREFIX)) return true;
36
+ try {
37
+ return /^\/workspaces\/[^/]+\/artifacts(?:\/|[?#]|$)/u.test(decodeURIComponent(href));
38
+ } catch {
39
+ return /^\/workspaces\/[^/]+\/artifacts/u.test(href);
40
+ }
41
+ }
42
+ function parseSandboxLink(href) {
43
+ if (!href) return null;
44
+ const encodedLocation = openGeniLinkScheme(href) === "sandbox" ? href.slice(SANDBOX_SCHEME.length) : href.startsWith(LEGACY_WORKSPACE_PREFIX) ? href : null;
45
+ if (!encodedLocation || encodedLocation.includes("?") || encodedLocation.includes("#")) {
46
+ return null;
47
+ }
48
+ if (TRAILING_INVALID_LINE.test(encodedLocation)) return null;
49
+ const lineMatch = TRAILING_LINE.exec(encodedLocation);
50
+ const encodedPath = lineMatch ? encodedLocation.slice(0, -lineMatch[0].length) : encodedLocation;
51
+ const line = lineMatch?.[1] ? Number(lineMatch[1]) : null;
52
+ if (line !== null && (!Number.isSafeInteger(line) || line < 1)) return null;
53
+ if (!encodedPath) return null;
54
+ try {
55
+ const path = decodeURIComponent(encodedPath);
56
+ return path && !CONTROL_CHARACTER.test(path) ? { path, line } : null;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ function parseRetainedFileReference(href) {
62
+ if (openGeniLinkScheme(href) !== "artifact") return null;
63
+ if (!href) return null;
64
+ const id = href.slice("artifact:".length);
65
+ return UUID.test(id) ? id.toLowerCase() : null;
66
+ }
67
+ function parseOpenGeniLink(href) {
68
+ if (!href) return null;
69
+ const fileId = parseRetainedFileReference(href);
70
+ if (fileId) return { kind: "file", fileId, workspaceId: null };
71
+ if (openGeniLinkScheme(href) === "sandbox" || href.startsWith(LEGACY_WORKSPACE_PREFIX)) {
72
+ const location = parseSandboxLink(href);
73
+ return location ? { kind: "sandbox-file", ...location } : null;
74
+ }
75
+ if (!href.startsWith("/workspaces/") || href.includes("#")) return null;
76
+ const separator = href.indexOf("?");
77
+ const pathname = separator < 0 ? href : href.slice(0, separator);
78
+ const search = separator < 0 ? void 0 : href.slice(separator + 1);
79
+ let fromSession;
80
+ if (search !== void 0) {
81
+ const params = new URLSearchParams(search);
82
+ const keys = [...params.keys()];
83
+ if (keys.some((key) => key !== "fromSession") || keys.length > 1) return null;
84
+ if (!UUID.test(params.get("fromSession") ?? "")) return null;
85
+ fromSession = params.get("fromSession").toLowerCase();
86
+ }
87
+ const parts = pathname.split("/");
88
+ const workspaceId = parts[2] ?? "";
89
+ if (parts[1] !== "workspaces" || parts[3] !== "artifacts" || !WORKSPACE_SEGMENT.test(workspaceId))
90
+ return null;
91
+ if (parts.length === 6 && parts[4] === "editable" && EDITABLE_ID.test(parts[5] ?? "")) {
92
+ return {
93
+ kind: "editable-artifact",
94
+ artifactId: parts[5].toLowerCase(),
95
+ workspaceId,
96
+ ...fromSession ? { fromSession } : {}
97
+ };
98
+ }
99
+ if (parts.length === 6 && parts[4] === "files" && UUID.test(parts[5] ?? "")) {
100
+ return {
101
+ kind: "file",
102
+ fileId: parts[5].toLowerCase(),
103
+ workspaceId,
104
+ ...fromSession ? { fromSession } : {}
105
+ };
106
+ }
107
+ if (parts.length === 5 && UUID.test(parts[4] ?? "")) {
108
+ return {
109
+ kind: "site",
110
+ artifactId: parts[4].toLowerCase(),
111
+ workspaceId,
112
+ ...fromSession ? { fromSession } : {}
113
+ };
114
+ }
115
+ return null;
116
+ }
117
+ function openGeniConsolePath(target, workspaceId) {
118
+ const workspace = encodeURIComponent(
119
+ target.kind === "sandbox-file" ? workspaceId : target.workspaceId ?? workspaceId
120
+ );
121
+ const search = target.kind !== "sandbox-file" && target.fromSession && UUID.test(target.fromSession) ? `?fromSession=${encodeURIComponent(target.fromSession)}` : "";
122
+ switch (target.kind) {
123
+ case "file":
124
+ return `/workspaces/${workspace}/artifacts/files/${target.fileId}${search}`;
125
+ case "editable-artifact":
126
+ return `/workspaces/${workspace}/artifacts/editable/${target.artifactId}${search}`;
127
+ case "site":
128
+ return `/workspaces/${workspace}/artifacts/${target.artifactId}${search}`;
129
+ case "sandbox-file":
130
+ return null;
131
+ }
132
+ }
133
+ var SESSION_SCOPE_HEADER = "x-opengeni-session-id";
134
+
135
+ // src/proxy.ts
136
+ function formatSseEvent(event) {
137
+ const streamedCoverage = sessionEventStreamCoveredThrough(event);
138
+ const projectedCoverage = event.coveredThrough;
139
+ const coveredThrough = streamedCoverage ?? (typeof projectedCoverage === "number" && Number.isSafeInteger(projectedCoverage) && projectedCoverage >= event.sequence ? projectedCoverage : event.sequence);
140
+ return `id: ${coveredThrough}
141
+ event: ${event.type}
142
+ data: ${JSON.stringify(event)}
143
+
144
+ `;
145
+ }
146
+ function sessionEventsToSseStream(events, options = {}) {
147
+ const encoder = new TextEncoder();
148
+ const iterator = events[Symbol.asyncIterator]();
149
+ let heartbeat;
150
+ let cancelled = false;
151
+ const stopHeartbeat = () => {
152
+ if (heartbeat !== void 0) {
153
+ clearInterval(heartbeat);
154
+ heartbeat = void 0;
155
+ }
156
+ };
157
+ return new ReadableStream({
158
+ start: (controller) => {
159
+ if (options.heartbeatMs !== void 0) {
160
+ heartbeat = setInterval(() => {
161
+ try {
162
+ controller.enqueue(encoder.encode(": ping\n\n"));
163
+ } catch {
164
+ stopHeartbeat();
165
+ }
166
+ }, options.heartbeatMs);
167
+ }
168
+ },
169
+ pull: async (controller) => {
170
+ let result;
171
+ try {
172
+ result = await iterator.next();
173
+ } catch (error) {
174
+ stopHeartbeat();
175
+ throw error;
176
+ }
177
+ if (cancelled) {
178
+ return;
179
+ }
180
+ if (result.done) {
181
+ stopHeartbeat();
182
+ controller.close();
183
+ return;
184
+ }
185
+ controller.enqueue(encoder.encode(formatSseEvent(result.value)));
186
+ },
187
+ cancel: () => {
188
+ cancelled = true;
189
+ stopHeartbeat();
190
+ options.onCancel?.();
191
+ void Promise.resolve(iterator.return?.(void 0)).then(
192
+ () => void 0,
193
+ () => void 0
194
+ );
195
+ }
196
+ });
197
+ }
198
+ function sessionEventsToSseResponse(events, options = {}) {
199
+ return new Response(sessionEventsToSseStream(events, options), {
200
+ headers: {
201
+ "Content-Type": "text/event-stream; charset=utf-8",
202
+ "Cache-Control": "no-cache, no-transform",
203
+ Connection: "keep-alive"
204
+ }
205
+ });
206
+ }
207
+ function resumeSequenceFromRequest(request) {
208
+ const url = new URL(request.url);
209
+ const raw = url.searchParams.get("after") ?? request.headers.get("Last-Event-ID");
210
+ const parsed = raw === null ? 0 : Number(raw);
211
+ return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
212
+ }
213
+ function proxySessionEventStream(client, workspaceId, sessionId, options = {}) {
214
+ const { after, heartbeatMs, signal, ...streamOptions } = options;
215
+ const upstream = new AbortController();
216
+ if (signal?.aborted) {
217
+ upstream.abort();
218
+ } else {
219
+ signal?.addEventListener("abort", () => upstream.abort(), { once: true });
220
+ }
221
+ const resolvedAfter = after instanceof Request ? resumeSequenceFromRequest(after) : after ?? 0;
222
+ const events = client.streamEvents(workspaceId, sessionId, {
223
+ ...streamOptions,
224
+ after: resolvedAfter,
225
+ signal: upstream.signal
226
+ });
227
+ return sessionEventsToSseResponse(events, {
228
+ ...heartbeatMs !== void 0 ? { heartbeatMs } : {},
229
+ onCancel: () => upstream.abort()
230
+ });
231
+ }
232
+
233
+ // src/session-proxy-client.ts
234
+ var SESSION_PROXY_SITE_HTML_MAX_BYTES = 25 * 1024 * 1024;
235
+ var SessionProxySiteHtmlTooLargeError = class extends Error {
236
+ code = "site_html_too_large";
237
+ limitBytes = SESSION_PROXY_SITE_HTML_MAX_BYTES;
238
+ constructor() {
239
+ super(`Site HTML exceeds the ${SESSION_PROXY_SITE_HTML_MAX_BYTES}-byte proxy limit.`);
240
+ this.name = "SessionProxySiteHtmlTooLargeError";
241
+ }
242
+ };
243
+ function getSessionProxyWorkspaceGrant(client, workspaceId, options = {}) {
244
+ return client.requestJson(
245
+ "GET",
246
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/access/grant`,
247
+ void 0,
248
+ {},
249
+ options
250
+ );
251
+ }
252
+ function getSessionProxyArtifactAssociation(client, workspaceId, sessionId, kind, artifactId, options = {}) {
253
+ return client.requestJson(
254
+ "GET",
255
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/sessions/${encodeURIComponent(sessionId)}/artifact-associations/${encodeURIComponent(artifactId)}`,
256
+ void 0,
257
+ { kind },
258
+ options
259
+ );
260
+ }
261
+ function downloadSessionProxySiteHtml(client, workspaceId, artifactId, options) {
262
+ return client.requestJsonResponse(
263
+ `/v1/workspaces/${encodeURIComponent(workspaceId)}/published-artifacts/${encodeURIComponent(artifactId)}/html`,
264
+ { versionId: options.versionId },
265
+ options
266
+ );
267
+ }
268
+
269
+ // src/session-proxy.ts
270
+ var DEFAULT_MAX_BODY_BYTES = 1024 * 1024;
271
+ var SEGMENT = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
272
+ var QUEUE_OPERATIONS = /* @__PURE__ */ new Set(["move", "edit", "steer", "delete"]);
273
+ var CREATE_FIELDS = /* @__PURE__ */ new Set(["initialMessage", "idempotencyKey"]);
274
+ var MODEL_FIELDS = ["model", "reasoningEffort", "latencyMode"];
275
+ var ProxyRejection = class extends Error {
276
+ constructor(status, code, message) {
277
+ super(message);
278
+ this.status = status;
279
+ this.code = code;
280
+ }
281
+ };
282
+ function reject(status, code, message) {
283
+ throw new ProxyRejection(status, code, message);
284
+ }
285
+ function json(body, status = 200, headers = {}) {
286
+ return new Response(JSON.stringify(body), {
287
+ status,
288
+ headers: {
289
+ "Content-Type": "application/json; charset=utf-8",
290
+ "Cache-Control": "no-store",
291
+ ...headers
292
+ }
293
+ });
294
+ }
295
+ function errorJson(status, code, message) {
296
+ return json({ error: { code, message } }, status);
297
+ }
298
+ function isFacade(target) {
299
+ return !("asUser" in target) && "client" in target;
300
+ }
301
+ function createSessionProxyHandler(target, options) {
302
+ const service = isFacade(target) ? target.client : target;
303
+ const defaultSource = isFacade(target) ? target.source : "default";
304
+ const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
305
+ const filesEnabled = options.files ?? true;
306
+ const sandboxFilesEnabled = options.sandboxFiles === true;
307
+ const artifactsEnabled = options.artifacts !== void 0 && options.artifacts !== false;
308
+ const editableLiveUrl = artifactsEnabled ? liveSocketUrl(
309
+ (typeof options.artifacts === "object" ? options.artifacts.editableLiveUrl : void 0) ?? service.apiUrl(EDITABLE_ARTIFACT_LIVE_PATH)
310
+ ) : null;
311
+ const modelSelection = options.modelSelection ?? true;
312
+ const heartbeatMs = options.heartbeatMs ?? 15e3;
313
+ const sessionList = options.sessionList ?? "mine";
314
+ const archiveEnabled = options.archive ?? true;
315
+ const chats = options.chats ?? "private";
316
+ const defaults = chatDefaults(chats);
317
+ const toolServer = options.toolServer ? normalizeToolServer(options.toolServer) : void 0;
318
+ const toolSessions = /* @__PURE__ */ new Map();
319
+ const subjects = /* @__PURE__ */ new Map();
320
+ const subjectOf = (client, key) => {
321
+ let subject = subjects.get(key);
322
+ if (!subject) {
323
+ subject = client.getAccessContext().then((access) => access.subjectId);
324
+ subject.catch(() => subjects.delete(key));
325
+ if (subjects.size >= 1e3) subjects.delete(subjects.keys().next().value);
326
+ subjects.set(key, subject);
327
+ }
328
+ return subject;
329
+ };
330
+ return async (request) => {
331
+ try {
332
+ const method = request.method;
333
+ if (!["GET", "POST", "PUT", "PATCH"].includes(method)) {
334
+ return new Response(null, {
335
+ status: 405,
336
+ headers: { Allow: "GET, POST, PUT, PATCH" }
337
+ });
338
+ }
339
+ const resolved = await options.resolve(request);
340
+ if (resolved instanceof Response) return resolved;
341
+ if (typeof resolved?.user !== "string" || !resolved.user) {
342
+ return errorJson(401, "user_required", "Authentication required.");
343
+ }
344
+ const url = new URL(request.url);
345
+ const segments = routeSegments(url.pathname, options.basePath);
346
+ if (!segments) return errorJson(404, "route_not_allowed", "Not found.");
347
+ if (method !== "GET") {
348
+ const allowed = options.authorizeMutation ? await options.authorizeMutation(request) : request.headers.get("sec-fetch-site") !== "cross-site";
349
+ if (!allowed) return errorJson(403, "mutation_denied", "Request denied.");
350
+ }
351
+ const source = resolved.source ?? defaultSource;
352
+ let workspaceId;
353
+ if ("workspaceId" in resolved && !resolved.workspaceId && !resolved.tenant || "tenant" in resolved && !resolved.tenant && !resolved.workspaceId || resolved.workspaceId === "" || resolved.tenant === "") {
354
+ throw new TypeError(
355
+ "resolve returned an empty tenant or workspaceId. Return a non-empty id, or omit the key for one workspace per user."
356
+ );
357
+ }
358
+ if (chats === "isolated") {
359
+ if (resolved.workspaceId || !isFacade(target) || !target.workspaceIdFor) {
360
+ throw new TypeError(
361
+ 'chats: "isolated" requires the Opengeni facade and a tenant or user resolution.'
362
+ );
363
+ }
364
+ workspaceId = await target.workspaceIdFor(
365
+ { tenant: resolved.tenant, user: resolved.user, source },
366
+ { isolation: "user" }
367
+ );
368
+ } else if (resolved.workspaceId) {
369
+ workspaceId = resolved.workspaceId;
370
+ } else if (resolved.tenant && isFacade(target)) {
371
+ workspaceId = await target.workspaceId({ tenant: resolved.tenant });
372
+ } else if (!("tenant" in resolved) && !("workspaceId" in resolved) && isFacade(target) && target.workspaceIdFor) {
373
+ workspaceId = await target.workspaceIdFor(
374
+ { user: resolved.user, source },
375
+ { isolation: "user" }
376
+ );
377
+ } else {
378
+ throw new TypeError(
379
+ "resolve must return a workspaceId (or a tenant or a user alone when given the Opengeni facade)."
380
+ );
381
+ }
382
+ const client = service.asUser(resolved.user, { source });
383
+ const context = {
384
+ request,
385
+ workspaceId,
386
+ user: resolved.user,
387
+ source,
388
+ client
389
+ };
390
+ const call = { signal: request.signal };
391
+ const toolToken = async () => await mintToolToken({
392
+ audience: toolServer.url,
393
+ user: resolved.user,
394
+ tenant: resolved.tenant,
395
+ workspaceId,
396
+ source,
397
+ secret: toolServer.secret,
398
+ ttlSeconds: toolServer.ttlSeconds
399
+ });
400
+ const toolServerOwner = (sessionId2) => {
401
+ const key = `${workspaceId}\0${sessionId2}`;
402
+ let attached = toolSessions.get(key);
403
+ if (!attached) {
404
+ attached = client.getSession(workspaceId, sessionId2, call).then(
405
+ (session2) => (session2.mcpServers ?? []).some(
406
+ (server) => server.id === toolServer.id && server.url === toolServer.url
407
+ ) ? session2.createdBy?.subjectId ?? null : null
408
+ );
409
+ attached.catch(() => toolSessions.delete(key));
410
+ if (toolSessions.size >= 1e3) toolSessions.delete(toolSessions.keys().next().value);
411
+ toolSessions.set(key, attached);
412
+ }
413
+ return attached;
414
+ };
415
+ const messageExtras = async (input) => {
416
+ const extras = options.beforeForwardMessage ? await options.beforeForwardMessage(input, context) : void 0;
417
+ if (extras instanceof Response || !toolServer || !input.sessionId) return extras;
418
+ const updates = extras?.mcpCredentialUpdates ?? [];
419
+ if (updates.some((update) => update.id === toolServer.id)) return extras;
420
+ const owner = await toolServerOwner(input.sessionId);
421
+ if (!owner || owner !== await subjectOf(client, `${source}\0${resolved.user}`)) {
422
+ return extras;
423
+ }
424
+ return {
425
+ ...extras,
426
+ mcpCredentialUpdates: [
427
+ ...updates,
428
+ {
429
+ id: toolServer.id,
430
+ headers: { Authorization: `Bearer ${await toolToken()}` }
431
+ }
432
+ ]
433
+ };
434
+ };
435
+ const forwardMessage = async (value, input) => {
436
+ const message = sanitizeMessage(value, modelSelection || input.delivery === "submit");
437
+ const extras = await messageExtras(input);
438
+ if (extras instanceof Response) return extras;
439
+ const modelContext = joinContext(
440
+ extras?.modelContext,
441
+ typeof message.modelContext === "string" ? message.modelContext : void 0
442
+ );
443
+ return {
444
+ ...message,
445
+ ...modelContext ? { modelContext } : {},
446
+ ...extras?.mcpCredentialUpdates?.length ? { mcpCredentialUpdates: extras.mcpCredentialUpdates } : {}
447
+ };
448
+ };
449
+ const forwardResponse = async (value, input) => {
450
+ const payload = browserPayload(value);
451
+ const extras = await messageExtras(input);
452
+ if (extras instanceof Response) return extras;
453
+ return {
454
+ ...payload,
455
+ ...extras?.mcpCredentialUpdates?.length ? { mcpCredentialUpdates: extras.mcpCredentialUpdates } : {}
456
+ };
457
+ };
458
+ const query = Object.fromEntries(url.searchParams);
459
+ const read = async (path) => json(await client.requestJson("GET", path, void 0, query, call));
460
+ const [root, ...rest] = segments;
461
+ if (root === "config") {
462
+ if (rest.length === 1 && rest[0] === "client" && method === "GET") {
463
+ const config = await client.requestJson(
464
+ "GET",
465
+ "/v1/config/client",
466
+ void 0,
467
+ { ...query, workspaceId },
468
+ call
469
+ );
470
+ let artifacts = null;
471
+ if (editableLiveUrl) {
472
+ try {
473
+ artifacts = {
474
+ editableLiveUrl,
475
+ cachePartition: await cachePartition(
476
+ await getSessionProxyWorkspaceGrant(client, workspaceId, call),
477
+ workspaceId,
478
+ source
479
+ )
480
+ };
481
+ } catch (error) {
482
+ if (!(error instanceof OpenGeniApiError) || error.status !== 404) throw error;
483
+ }
484
+ }
485
+ const { artifacts: _upstreamArtifacts, ...conversationConfig } = config;
486
+ return json({
487
+ ...conversationConfig,
488
+ apiContractRevision: OPENGENI_API_CONTRACT_REVISION,
489
+ // The resolved workspace: a browser given only the proxy's baseUrl
490
+ // reads it here instead of knowing any Opengeni id.
491
+ workspaceId,
492
+ sandboxFiles: sandboxFilesEnabled,
493
+ ...artifacts ? { artifacts } : {},
494
+ // Explicit true also tells stock UIs to offer end users the model picker.
495
+ ...modelSelection ? options.modelSelection === true ? { modelSelection: true } : {} : { modelSelection: false }
496
+ });
497
+ }
498
+ return errorJson(404, "route_not_allowed", "Not found.");
499
+ }
500
+ if (root !== "workspaces" || rest.length < 1) {
501
+ return errorJson(404, "route_not_allowed", "Not found.");
502
+ }
503
+ const [pathWorkspaceId, area, ...tail] = rest;
504
+ if (pathWorkspaceId !== workspaceId) {
505
+ return errorJson(403, "workspace_not_allowed", "This workspace is not available.");
506
+ }
507
+ const base = `/v1/workspaces/${workspaceId}`;
508
+ if (area === "usage" && tail.length === 1 && tail[0] === "me" && method === "GET") {
509
+ if (Object.keys(query).some((key) => key !== "period")) {
510
+ return errorJson(400, "invalid_usage_query", "Only period is accepted for own usage.");
511
+ }
512
+ return await read(`${base}/usage/me`);
513
+ }
514
+ if (area === void 0 && method === "GET") return await read(base);
515
+ if (area === "model-catalog" && tail.length === 0 && method === "GET") {
516
+ return await read(`${base}/model-catalog`);
517
+ }
518
+ if (area === "live-events" && tail.length === 1 && tail[0] === "stream" && method === "GET") {
519
+ await client.requestJson("GET", base, void 0, {}, call);
520
+ return workspaceLiveStream(client, workspaceId, url.searchParams, request, heartbeatMs);
521
+ }
522
+ if (area === "inference-control" && tail.length === 0 && method === "POST") {
523
+ const body2 = await readJsonBody(request, maxBodyBytes);
524
+ if (body2?.action !== "resume") {
525
+ reject(403, "control_not_allowed", "Only workspace resume is available.");
526
+ }
527
+ return json(await client.requestJson("POST", `${base}/inference-control`, body2));
528
+ }
529
+ if (area === "files" && filesEnabled && method === "POST") {
530
+ if (tail.length === 1 && tail[0] === "uploads") {
531
+ const body2 = await readJsonBody(request, maxBodyBytes);
532
+ return json(
533
+ await client.requestJson("POST", `${base}/files/uploads`, pick(body2, UPLOAD_FIELDS))
534
+ );
535
+ }
536
+ if (tail.length === 3 && tail[0] === "uploads" && tail[2] === "complete") {
537
+ return json(
538
+ await client.requestJson("POST", `${base}/files/uploads/${tail[1]}/complete`)
539
+ );
540
+ }
541
+ if (tail.length === 2 && tail[1] === "download-url") {
542
+ return json(
543
+ await client.requestJson(
544
+ "POST",
545
+ `${base}/files/${tail[0]}/download-url`,
546
+ void 0,
547
+ query,
548
+ call
549
+ )
550
+ );
551
+ }
552
+ return errorJson(404, "route_not_allowed", "Not found.");
553
+ }
554
+ if (area === "editable-artifacts" || area === "published-artifacts") {
555
+ if (!artifactsEnabled) return errorJson(404, "route_not_allowed", "Not found.");
556
+ const [artifactId, ...artifactOp] = tail;
557
+ const route2 = `${method} ${artifactOp.join("/")}`;
558
+ const editable = area === "editable-artifacts";
559
+ const allowed = editable ? route2 === "GET " || route2 === "POST live-ticket" : route2 === "GET " || route2 === "GET html";
560
+ if (!artifactId || !SEGMENT.test(artifactId) || !allowed) {
561
+ return errorJson(404, "route_not_allowed", "Not found.");
562
+ }
563
+ const sessionId2 = request.headers.get(SESSION_SCOPE_HEADER)?.trim() ?? "";
564
+ if (!SEGMENT.test(sessionId2)) {
565
+ reject(400, "session_scope_required", "Artifact reads must name their session.");
566
+ }
567
+ if (options.authorizeSession && !await options.authorizeSession(sessionId2, context)) {
568
+ return errorJson(404, "session_not_found", "Session not found.");
569
+ }
570
+ await getSessionProxyArtifactAssociation(
571
+ client,
572
+ workspaceId,
573
+ sessionId2,
574
+ editable ? "editable" : "site",
575
+ artifactId,
576
+ call
577
+ );
578
+ const item = `${base}/${area}/${artifactId}`;
579
+ if (route2 === "GET ") {
580
+ const replicaId = editable ? query.replicaId : void 0;
581
+ return json(
582
+ await client.requestJson(
583
+ "GET",
584
+ item,
585
+ void 0,
586
+ typeof replicaId === "string" ? { replicaId } : {},
587
+ call
588
+ )
589
+ );
590
+ }
591
+ if (route2 === "POST live-ticket") {
592
+ const body2 = await readJsonBody(request, maxBodyBytes);
593
+ return json(
594
+ await client.requestJson("POST", `${item}/live-ticket`, {
595
+ ...pick(body2, TICKET_FIELDS),
596
+ sourceSessionId: sessionId2
597
+ }),
598
+ 201
599
+ );
600
+ }
601
+ const versionId = query.versionId;
602
+ if (typeof versionId !== "string" || !SEGMENT.test(versionId)) {
603
+ reject(400, "version_required", "versionId is required.");
604
+ }
605
+ const html = await downloadSessionProxySiteHtml(client, workspaceId, artifactId, {
606
+ versionId,
607
+ signal: request.signal
608
+ });
609
+ return new Response(boundedSiteHtml(html.body, request.signal), {
610
+ headers: SITE_HTML_HEADERS
611
+ });
612
+ }
613
+ if (area !== "sessions") return errorJson(404, "route_not_allowed", "Not found.");
614
+ if (tail.length === 0 && method === "GET") {
615
+ if (!sessionList) return errorJson(404, "route_not_allowed", "Not found.");
616
+ if (query.view !== "page") {
617
+ reject(400, "page_view_required", "List sessions with listSessionPage.");
618
+ }
619
+ const listQuery = { ...query };
620
+ if (sessionList === "mine") {
621
+ listQuery.createdByKind = "subject";
622
+ listQuery.createdBySubjectId = await subjectOf(client, `${source}\0${resolved.user}`);
623
+ }
624
+ const page = await client.requestJson(
625
+ "GET",
626
+ `${base}/sessions`,
627
+ void 0,
628
+ listQuery,
629
+ call
630
+ );
631
+ return json(sessionList === "mine" ? { ...page, pinned: [] } : page);
632
+ }
633
+ if (tail.length === 0) {
634
+ if (method !== "POST" || !options.createSession) {
635
+ return errorJson(404, "route_not_allowed", "Not found.");
636
+ }
637
+ const input = createInput(await readJsonBody(request, maxBodyBytes));
638
+ const hooked = await options.createSession(input, context);
639
+ if (hooked instanceof Response) return hooked;
640
+ const created = toolServer ? withToolServer(hooked, toolServer, await toolToken()) : hooked;
641
+ const extras = await messageExtras({ delivery: "create" });
642
+ if (extras instanceof Response) return extras;
643
+ const modelContext = joinContext(extras?.modelContext, created.modelContext);
644
+ return json(
645
+ await client.createSession(workspaceId, {
646
+ ...created,
647
+ visibility: created.visibility ?? defaults.visibility,
648
+ agentAccess: created.agentAccess ?? defaults.agentAccess,
649
+ memoryScope: created.memoryScope ?? defaults.memoryScope,
650
+ ...modelContext ? { modelContext } : {}
651
+ })
652
+ );
653
+ }
654
+ const [sessionId, ...op] = tail;
655
+ if (options.authorizeSession && !await options.authorizeSession(sessionId, context)) {
656
+ return errorJson(404, "session_not_found", "Session not found.");
657
+ }
658
+ const session = `${base}/sessions/${sessionId}`;
659
+ const route = `${method} ${op.join("/")}`;
660
+ const body = method === "GET" ? void 0 : await readJsonBody(request, maxBodyBytes, true);
661
+ const sanitize = (value) => sanitizeMessage(value, modelSelection);
662
+ const saveDraftPolicy = async (message) => {
663
+ if (modelSelection) return message;
664
+ const draft = await client.getComposerDraft(workspaceId, sessionId, call);
665
+ return {
666
+ ...message,
667
+ model: draft.model,
668
+ reasoningEffort: draft.reasoningEffort,
669
+ latencyMode: draft.latencyMode
670
+ };
671
+ };
672
+ const forward = async (path, payload) => payload instanceof Response ? payload : json(await client.requestJson("POST", path, payload));
673
+ switch (route) {
674
+ case "GET ":
675
+ return await read(session);
676
+ case "PUT archive": {
677
+ if (!archiveEnabled) return errorJson(404, "route_not_allowed", "Not found.");
678
+ const archived = body?.archived;
679
+ const expectedVersion = body?.expectedVersion;
680
+ if (typeof archived !== "boolean") reject(400, "invalid_body", "archived is required.");
681
+ return json(
682
+ await client.requestJson("PUT", `${session}/archive`, {
683
+ archived,
684
+ ...typeof expectedVersion === "number" ? { expectedVersion } : {}
685
+ })
686
+ );
687
+ }
688
+ case "PATCH ": {
689
+ const title = body?.title;
690
+ if (typeof title !== "string" || Object.keys(body ?? {}).length !== 1) {
691
+ reject(400, "invalid_body", "Only { title } may be updated.");
692
+ }
693
+ return json(await client.requestJson("PATCH", session, { title }));
694
+ }
695
+ case "GET events":
696
+ return await listEvents(client, `${session}/events`, query, call);
697
+ case "GET events/stream":
698
+ await client.getSession(workspaceId, sessionId, call);
699
+ return proxySessionEventStream(client, workspaceId, sessionId, {
700
+ after: request,
701
+ signal: request.signal,
702
+ heartbeatMs
703
+ });
704
+ case "POST events": {
705
+ const event = clientEvent(body);
706
+ const payload = event.type === "user.message" ? await forwardMessage(event.payload, {
707
+ sessionId,
708
+ delivery: "send"
709
+ }) : await forwardResponse(event.payload, {
710
+ sessionId,
711
+ delivery: "send"
712
+ });
713
+ return await forward(
714
+ `${session}/events`,
715
+ payload instanceof Response ? payload : { ...event, payload }
716
+ );
717
+ }
718
+ case "POST steer":
719
+ return await forward(
720
+ `${session}/steer`,
721
+ await forwardMessage(body, { sessionId, delivery: "steer" })
722
+ );
723
+ case "GET queue":
724
+ return await read(`${session}/queue`);
725
+ case "GET composer-draft":
726
+ return await read(`${session}/composer-draft`);
727
+ case "PUT composer-draft":
728
+ return json(
729
+ await client.requestJson(
730
+ "PUT",
731
+ `${session}/composer-draft`,
732
+ await saveDraftPolicy(sanitize(body))
733
+ )
734
+ );
735
+ case "POST composer-draft/submit": {
736
+ const message = await forwardMessage(body, {
737
+ sessionId,
738
+ delivery: "submit"
739
+ });
740
+ return await forward(`${session}/composer-draft/submit`, message);
741
+ }
742
+ case "POST control": {
743
+ if (body?.action !== "pause" && body?.action !== "resume") {
744
+ reject(403, "control_not_allowed", "Only pause and resume are available.");
745
+ }
746
+ return json(await client.requestJson("POST", `${session}/control`, body));
747
+ }
748
+ case "GET human-input-requests":
749
+ return await read(`${session}/human-input-requests`);
750
+ case "POST fs/read":
751
+ case "POST fs/read-workspace": {
752
+ if (!sandboxFilesEnabled) return errorJson(404, "route_not_allowed", "Not found.");
753
+ return json(
754
+ await client.requestJson("POST", `${session}/fs/read-workspace`, sandboxRead(body))
755
+ );
756
+ }
757
+ }
758
+ if (op.length === 2 && op[0] === "human-input-requests" && method === "GET") {
759
+ return await read(`${session}/human-input-requests/${op[1]}`);
760
+ }
761
+ if (op.length === 3 && op[0] === "queue" && QUEUE_OPERATIONS.has(op[2]) && method === "POST") {
762
+ return json(await client.requestJson("POST", `${session}/queue/${op[1]}/${op[2]}`, body));
763
+ }
764
+ return errorJson(404, "route_not_allowed", "Not found.");
765
+ } catch (error) {
766
+ return errorResponse(error);
767
+ }
768
+ };
769
+ }
770
+ var UPLOAD_FIELDS = ["scope", "filename", "contentType", "sizeBytes", "sha256"];
771
+ function normalizeToolServer(toolServer) {
772
+ const configured = toolServer.url ?? globalThis.process?.env?.OPENGENI_TOOL_SERVER_URL;
773
+ let url;
774
+ try {
775
+ url = new URL(configured ?? "");
776
+ } catch {
777
+ throw new TypeError(
778
+ "toolServer.url (or OPENGENI_TOOL_SERVER_URL) must be an absolute https:// URL."
779
+ );
780
+ }
781
+ if (url.protocol !== "https:") {
782
+ throw new TypeError("toolServer.url must be an absolute https:// URL.");
783
+ }
784
+ const id = toolServer.id ?? "app";
785
+ if (!/^[A-Za-z0-9_-]+$/.test(id)) {
786
+ throw new TypeError("toolServer.id may contain only letters, digits, _ and -.");
787
+ }
788
+ const ask = toolServer.approvals?.ask;
789
+ if (ask !== void 0 && ask !== true && !(Array.isArray(ask) && ask.every(isToolName))) {
790
+ throw new TypeError("toolServer.approvals.ask must be true or a list of tool names.");
791
+ }
792
+ return {
793
+ ...toolServer,
794
+ url: configured,
795
+ id,
796
+ secret: resolveToolTokenSecret(toolServer.secret)
797
+ };
798
+ }
799
+ function isToolName(value) {
800
+ return typeof value === "string" && value.length > 0;
801
+ }
802
+ function withToolServer(created, toolServer, token) {
803
+ const servers = created.mcpServers ?? [];
804
+ if (servers.some((server) => server.id === toolServer.id)) {
805
+ throw new TypeError(
806
+ `createSession already attaches an MCP server with the toolServer id "${toolServer.id}".`
807
+ );
808
+ }
809
+ const ask = toolServer.approvals?.ask;
810
+ const tools = created.tools;
811
+ return {
812
+ ...created,
813
+ mcpServers: [
814
+ ...servers,
815
+ {
816
+ id: toolServer.id,
817
+ ...toolServer.name ? { name: toolServer.name } : {},
818
+ url: toolServer.url,
819
+ headers: { Authorization: `Bearer ${token}` },
820
+ ...ask === true ? { requireApproval: true } : ask?.length ? { requireApproval: ask } : {}
821
+ }
822
+ ],
823
+ // Omitted tools keep workspace defaults; the attachment alone selects the
824
+ // server. An explicit allow-list gets the server's tools on the first request.
825
+ ...Array.isArray(tools) && !tools.some((tool) => tool.id === toolServer.id) ? {
826
+ tools: [...tools, { kind: "mcp", id: toolServer.id, eager: true }]
827
+ } : {}
828
+ };
829
+ }
830
+ var EDITABLE_ARTIFACT_LIVE_PATH = "/v1/editable-artifacts/live";
831
+ var TICKET_FIELDS = [
832
+ "replicaId",
833
+ "modality",
834
+ "liveProtocolVersion",
835
+ "kernelVersion",
836
+ "modelSchemaVersion",
837
+ "snapshotVersion",
838
+ "commandProtocolVersion",
839
+ "committedTransactionProtocolVersion"
840
+ ];
841
+ var SITE_HTML_HEADERS = {
842
+ "Content-Type": "text/html; charset=utf-8",
843
+ "Cache-Control": "private, no-store",
844
+ "Content-Security-Policy": "sandbox allow-scripts",
845
+ "Cross-Origin-Resource-Policy": "same-origin",
846
+ "X-Content-Type-Options": "nosniff",
847
+ "Content-Disposition": 'attachment; filename="site.html"'
848
+ };
849
+ function liveSocketUrl(value) {
850
+ const url = new URL(value);
851
+ if (url.protocol === "https:") url.protocol = "wss:";
852
+ else if (url.protocol === "http:") url.protocol = "ws:";
853
+ if (url.protocol !== "wss:" && url.protocol !== "ws:") {
854
+ throw new TypeError("editableLiveUrl must be an HTTP(S) or WS(S) URL");
855
+ }
856
+ return url.href;
857
+ }
858
+ function boundedSiteHtml(body, signal) {
859
+ const reader = body?.getReader();
860
+ let bytes = 0;
861
+ let settled = false;
862
+ let controller;
863
+ const cleanup = () => signal.removeEventListener("abort", abort);
864
+ const cancel = async (reason) => {
865
+ if (settled) return;
866
+ settled = true;
867
+ cleanup();
868
+ await reader?.cancel(reason).catch(() => void 0);
869
+ reader?.releaseLock();
870
+ };
871
+ const abort = () => {
872
+ if (settled) return;
873
+ controller.error(signal.reason);
874
+ void cancel(signal.reason);
875
+ };
876
+ return new ReadableStream(
877
+ {
878
+ start(value) {
879
+ controller = value;
880
+ if (signal.aborted) abort();
881
+ else signal.addEventListener("abort", abort, { once: true });
882
+ },
883
+ async pull(value) {
884
+ if (settled) return;
885
+ try {
886
+ const chunk = await reader?.read();
887
+ if (settled) return;
888
+ if (!chunk || chunk.done) {
889
+ settled = true;
890
+ cleanup();
891
+ reader?.releaseLock();
892
+ value.close();
893
+ return;
894
+ }
895
+ bytes += chunk.value.byteLength;
896
+ if (bytes > SESSION_PROXY_SITE_HTML_MAX_BYTES) {
897
+ const error = new SessionProxySiteHtmlTooLargeError();
898
+ value.error(error);
899
+ await cancel(error);
900
+ return;
901
+ }
902
+ value.enqueue(chunk.value);
903
+ } catch (error) {
904
+ if (settled) return;
905
+ value.error(error);
906
+ await cancel(error);
907
+ }
908
+ },
909
+ cancel
910
+ },
911
+ // Read only when the downstream consumer requests a chunk.
912
+ { highWaterMark: 0 }
913
+ );
914
+ }
915
+ async function artifactViewerCapability(input) {
916
+ return {
917
+ editableLiveUrl: liveSocketUrl(
918
+ input.editableLiveUrl ?? input.client.apiUrl(EDITABLE_ARTIFACT_LIVE_PATH)
919
+ ),
920
+ cachePartition: await cachePartition(
921
+ input.client.requestJson ? await getSessionProxyWorkspaceGrant(
922
+ input.client,
923
+ input.workspaceId
924
+ ) : await legacyViewerGrant(input.client, input.workspaceId),
925
+ input.workspaceId,
926
+ input.source ?? "default"
927
+ )
928
+ };
929
+ }
930
+ async function cachePartition(grant, workspaceId, source) {
931
+ if (grant.workspaceId !== workspaceId) {
932
+ reject(403, "workspace_not_allowed", "This workspace is not available.");
933
+ }
934
+ const digest = await crypto.subtle.digest(
935
+ "SHA-256",
936
+ new TextEncoder().encode(
937
+ JSON.stringify({
938
+ source,
939
+ subjectId: grant.subjectId,
940
+ accountId: grant.accountId,
941
+ workspaceId,
942
+ permissions: [...grant.permissions].sort(),
943
+ // Effective external grants carry live identity/link revisions. Include
944
+ // them even when a revoke/regrant restores an identical permission set.
945
+ externalActor: grant.metadata?.externalActor ?? null
946
+ })
947
+ )
948
+ );
949
+ return {
950
+ accountId: grant.accountId,
951
+ principalId: grant.subjectId,
952
+ authorizationEpoch: `sha256:${[...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`
953
+ };
954
+ }
955
+ async function legacyViewerGrant(client, workspaceId) {
956
+ const access = await client.getAccessContext();
957
+ const grant = access.workspaceGrants.find((candidate) => candidate.workspaceId === workspaceId);
958
+ if (!grant) reject(403, "workspace_not_allowed", "This workspace is not available.");
959
+ return grant;
960
+ }
961
+ function sandboxRead(body) {
962
+ const path = body?.path;
963
+ const encoding = body?.encoding;
964
+ const maxBytes = body?.maxBytes;
965
+ if (typeof path !== "string" || !path || path.length > 4096) {
966
+ reject(400, "invalid_body", "path is required.");
967
+ }
968
+ if (encoding !== void 0 && encoding !== "utf8" && encoding !== "base64") {
969
+ reject(400, "invalid_body", "encoding must be utf8 or base64.");
970
+ }
971
+ if (maxBytes !== void 0 && (typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 1 || maxBytes > 25 * 1024 * 1024)) {
972
+ reject(400, "invalid_body", "maxBytes must be an integer from 1 to 26214400.");
973
+ }
974
+ return {
975
+ path,
976
+ workspaceOnly: true,
977
+ ...encoding === void 0 ? {} : { encoding },
978
+ ...maxBytes === void 0 ? {} : { maxBytes }
979
+ };
980
+ }
981
+ function routeSegments(pathname, basePath) {
982
+ let rest;
983
+ if (basePath !== void 0) {
984
+ const prefix = basePath.replace(/\/+$/, "");
985
+ if (!pathname.startsWith(`${prefix}/`)) return null;
986
+ rest = pathname.slice(prefix.length);
987
+ } else {
988
+ const index = pathname.indexOf("/v1/");
989
+ if (index < 0) return null;
990
+ rest = pathname.slice(index);
991
+ }
992
+ const raw = rest.replace(/\/+$/, "").split("/").slice(1);
993
+ if (raw[0] !== "v1" || raw.length < 2) return null;
994
+ const segments = [];
995
+ for (const part of raw.slice(1)) {
996
+ let decoded;
997
+ try {
998
+ decoded = decodeURIComponent(part);
999
+ } catch {
1000
+ return null;
1001
+ }
1002
+ if (!SEGMENT.test(decoded)) return null;
1003
+ segments.push(decoded);
1004
+ }
1005
+ return segments;
1006
+ }
1007
+ async function readJsonBody(request, maxBytes, optional = false) {
1008
+ const declared = Number(request.headers.get("content-length") ?? "0");
1009
+ if (declared > maxBytes) reject(413, "body_too_large", "Request body is too large.");
1010
+ const reader = request.body?.getReader();
1011
+ const chunks = [];
1012
+ let total = 0;
1013
+ if (reader) {
1014
+ for (; ; ) {
1015
+ const { done, value } = await reader.read();
1016
+ if (done) break;
1017
+ total += value.byteLength;
1018
+ if (total > maxBytes) {
1019
+ void reader.cancel().catch(() => void 0);
1020
+ reject(413, "body_too_large", "Request body is too large.");
1021
+ }
1022
+ chunks.push(value);
1023
+ }
1024
+ }
1025
+ if (total === 0) {
1026
+ if (optional) return void 0;
1027
+ reject(400, "invalid_body", "A JSON object body is required.");
1028
+ }
1029
+ const contentType = request.headers.get("content-type") ?? "";
1030
+ if (!/^application\/json\b/i.test(contentType)) {
1031
+ reject(415, "unsupported_media_type", "Send application/json.");
1032
+ }
1033
+ const bytes = new Uint8Array(total);
1034
+ let offset = 0;
1035
+ for (const chunk of chunks) {
1036
+ bytes.set(chunk, offset);
1037
+ offset += chunk.byteLength;
1038
+ }
1039
+ let parsed;
1040
+ try {
1041
+ parsed = JSON.parse(new TextDecoder().decode(bytes));
1042
+ } catch {
1043
+ reject(400, "invalid_body", "Body is not valid JSON.");
1044
+ }
1045
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1046
+ reject(400, "invalid_body", "A JSON object body is required.");
1047
+ }
1048
+ return parsed;
1049
+ }
1050
+ function pick(body, fields) {
1051
+ const picked = {};
1052
+ for (const field of fields) {
1053
+ if (body && Object.hasOwn(body, field)) picked[field] = body[field];
1054
+ }
1055
+ return picked;
1056
+ }
1057
+ function createInput(body) {
1058
+ for (const key of Object.keys(body ?? {})) {
1059
+ if (!CREATE_FIELDS.has(key)) {
1060
+ reject(
1061
+ 400,
1062
+ "create_field_not_allowed",
1063
+ `The server chooses session configuration; "${key}" cannot be set from the browser.`
1064
+ );
1065
+ }
1066
+ }
1067
+ const initialMessage = body?.initialMessage;
1068
+ const idempotencyKey = body?.idempotencyKey;
1069
+ if (typeof initialMessage !== "string" || !initialMessage.trim()) {
1070
+ reject(400, "initial_message_required", "initialMessage is required.");
1071
+ }
1072
+ if (idempotencyKey !== void 0 && (typeof idempotencyKey !== "string" || !idempotencyKey)) {
1073
+ reject(400, "invalid_idempotency_key", "idempotencyKey must be a non-empty string.");
1074
+ }
1075
+ return { initialMessage, ...idempotencyKey ? { idempotencyKey } : {} };
1076
+ }
1077
+ function browserPayload(value) {
1078
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1079
+ reject(400, "invalid_body", "A JSON object body is required.");
1080
+ }
1081
+ const input = { ...value };
1082
+ if (Object.hasOwn(input, "mcpCredentialUpdates")) {
1083
+ reject(403, "credential_update_not_allowed", "MCP credentials are server-owned.");
1084
+ }
1085
+ return input;
1086
+ }
1087
+ function sanitizeMessage(value, modelSelection) {
1088
+ const input = browserPayload(value);
1089
+ if (input.resources !== void 0) {
1090
+ if (!Array.isArray(input.resources) || input.resources.some(
1091
+ (resource) => !resource || typeof resource !== "object" || resource.kind !== "file"
1092
+ )) {
1093
+ reject(403, "resource_not_allowed", "Only file attachments may be added from the browser.");
1094
+ }
1095
+ }
1096
+ if (!modelSelection) {
1097
+ for (const field of MODEL_FIELDS) delete input[field];
1098
+ }
1099
+ return input;
1100
+ }
1101
+ function clientEvent(body) {
1102
+ switch (body?.type) {
1103
+ case "user.message":
1104
+ case "user.approvalDecision":
1105
+ case "user.humanInputResponse":
1106
+ return body;
1107
+ default:
1108
+ return reject(403, "event_not_allowed", "This session event type is not available.");
1109
+ }
1110
+ }
1111
+ function joinContext(...parts) {
1112
+ const joined = parts.map((part) => part?.trim()).filter(Boolean).join("\n\n");
1113
+ return joined || void 0;
1114
+ }
1115
+ async function listEvents(client, path, query, call) {
1116
+ if (query.mode === "forensic") {
1117
+ reject(403, "forensic_events_not_allowed", "Forensic event reads are not proxied.");
1118
+ }
1119
+ const upstream = await client.requestJsonResponse(path, query, call);
1120
+ const headers = {};
1121
+ upstream.headers.forEach((value, name) => {
1122
+ const lower = name.toLowerCase();
1123
+ if (lower.startsWith("x-opengeni-") && lower !== OPENGENI_API_CONTRACT_HEADER) {
1124
+ headers[name] = value;
1125
+ }
1126
+ });
1127
+ return new Response(await upstream.text(), {
1128
+ headers: {
1129
+ "Content-Type": "application/json; charset=utf-8",
1130
+ "Cache-Control": "no-store",
1131
+ ...headers
1132
+ }
1133
+ });
1134
+ }
1135
+ function workspaceLiveStream(client, workspaceId, params, request, heartbeatMs) {
1136
+ const cursor = (name) => {
1137
+ const value = Number(params.get(name) ?? "0");
1138
+ return Number.isSafeInteger(value) && value > 0 ? value : 0;
1139
+ };
1140
+ const upstream = new AbortController();
1141
+ if (request.signal.aborted) upstream.abort();
1142
+ else
1143
+ request.signal.addEventListener("abort", () => upstream.abort(), {
1144
+ once: true
1145
+ });
1146
+ const events = client.streamWorkspaceLiveEvents(workspaceId, {
1147
+ controlAfter: cursor("controlAfter"),
1148
+ interactionAfter: cursor("interactionAfter"),
1149
+ signal: upstream.signal
1150
+ });
1151
+ const iterator = events[Symbol.asyncIterator]();
1152
+ const encoder = new TextEncoder();
1153
+ let heartbeat;
1154
+ const stop = () => {
1155
+ if (heartbeat !== void 0) clearInterval(heartbeat);
1156
+ heartbeat = void 0;
1157
+ };
1158
+ const body = new ReadableStream({
1159
+ start: (controller) => {
1160
+ heartbeat = setInterval(() => {
1161
+ try {
1162
+ controller.enqueue(encoder.encode(": ping\n\n"));
1163
+ } catch {
1164
+ stop();
1165
+ }
1166
+ }, heartbeatMs);
1167
+ },
1168
+ pull: async (controller) => {
1169
+ const next = await iterator.next().catch((error) => {
1170
+ stop();
1171
+ throw error;
1172
+ });
1173
+ if (next.done) {
1174
+ stop();
1175
+ controller.close();
1176
+ return;
1177
+ }
1178
+ const event = next.value;
1179
+ controller.enqueue(
1180
+ encoder.encode(
1181
+ `id: ${event.sequence}
1182
+ event: ${event.type}
1183
+ data: ${JSON.stringify(event)}
1184
+
1185
+ `
1186
+ )
1187
+ );
1188
+ },
1189
+ cancel: () => {
1190
+ stop();
1191
+ upstream.abort();
1192
+ void Promise.resolve(iterator.return?.(void 0)).catch(() => void 0);
1193
+ }
1194
+ });
1195
+ return new Response(body, {
1196
+ headers: {
1197
+ "Content-Type": "text/event-stream; charset=utf-8",
1198
+ "Cache-Control": "no-cache, no-transform",
1199
+ Connection: "keep-alive"
1200
+ }
1201
+ });
1202
+ }
1203
+ function errorResponse(error) {
1204
+ if (error instanceof ProxyRejection) return errorJson(error.status, error.code, error.message);
1205
+ if (error instanceof OpenGeniSetupError) {
1206
+ return json(
1207
+ {
1208
+ error: {
1209
+ code: error.code,
1210
+ message: error.message,
1211
+ retryable: false,
1212
+ ...error.correlationId ? { requestId: error.correlationId } : {}
1213
+ }
1214
+ },
1215
+ error.status
1216
+ );
1217
+ }
1218
+ if (error instanceof OpenGeniApiError) {
1219
+ const status = error.status >= 400 && error.status <= 599 ? error.status : 502;
1220
+ if (error.body) {
1221
+ return new Response(error.body, {
1222
+ status,
1223
+ headers: {
1224
+ "Content-Type": "application/json; charset=utf-8",
1225
+ "Cache-Control": "no-store"
1226
+ }
1227
+ });
1228
+ }
1229
+ return json(
1230
+ {
1231
+ error: {
1232
+ code: error.code ?? "opengeni_api_error",
1233
+ message: error.message,
1234
+ retryable: error.retryable,
1235
+ outcomeUnknown: error.outcomeUnknown,
1236
+ ...error.correlationId ? { requestId: error.correlationId } : {},
1237
+ ...error.details ? { details: error.details } : {}
1238
+ }
1239
+ },
1240
+ status
1241
+ );
1242
+ }
1243
+ if (error instanceof Error && error.name === "AbortError") {
1244
+ return errorJson(499, "aborted", "The request was aborted.");
1245
+ }
1246
+ return errorJson(500, "proxy_error", "Request could not be completed.");
1247
+ }
1248
+
1249
+ export {
1250
+ openGeniLinkScheme,
1251
+ isReservedOpenGeniLink,
1252
+ parseSandboxLink,
1253
+ parseRetainedFileReference,
1254
+ parseOpenGeniLink,
1255
+ openGeniConsolePath,
1256
+ SESSION_SCOPE_HEADER,
1257
+ formatSseEvent,
1258
+ sessionEventsToSseStream,
1259
+ sessionEventsToSseResponse,
1260
+ resumeSequenceFromRequest,
1261
+ proxySessionEventStream,
1262
+ SESSION_PROXY_SITE_HTML_MAX_BYTES,
1263
+ SessionProxySiteHtmlTooLargeError,
1264
+ getSessionProxyWorkspaceGrant,
1265
+ getSessionProxyArtifactAssociation,
1266
+ downloadSessionProxySiteHtml,
1267
+ createSessionProxyHandler,
1268
+ artifactViewerCapability
1269
+ };
1270
+ //# sourceMappingURL=chunk-RWKB6YZG.js.map