@opengeni/sdk 3.1.0 → 3.1.2

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 (295) hide show
  1. package/README.md +1037 -71
  2. package/dist/accounts.d.ts +5 -1
  3. package/dist/accounts.js +13 -1
  4. package/dist/accounts.js.map +1 -1
  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/app-links.d.ts +42 -0
  18. package/dist/app-links.js +66 -0
  19. package/dist/app-links.js.map +1 -0
  20. package/dist/artifact-catalog.d.ts +40 -0
  21. package/dist/artifact-client.d.ts +20 -8
  22. package/dist/artifacts.d.ts +1 -1
  23. package/dist/artifacts.js +8 -6
  24. package/dist/automations.js +8 -2
  25. package/dist/automations.js.map +1 -1
  26. package/dist/azure-live-transport.d.ts +32 -0
  27. package/dist/browser.d.ts +7 -2
  28. package/dist/browser.js +27 -5
  29. package/dist/chat/fold.d.ts +75 -0
  30. package/dist/chat/handler.d.ts +41 -0
  31. package/dist/chat/http.d.ts +67 -0
  32. package/dist/chat/ids.d.ts +34 -0
  33. package/dist/chat/index.d.ts +9 -0
  34. package/dist/chat/index.js +1703 -0
  35. package/dist/chat/index.js.map +1 -0
  36. package/dist/chat/openai.d.ts +30 -0
  37. package/dist/chat/opengeni.d.ts +129 -0
  38. package/dist/chat/types.d.ts +207 -0
  39. package/dist/chat/vercel.d.ts +49 -0
  40. package/dist/chats.d.ts +8 -0
  41. package/dist/chunk-4AHR4KGA.js +301 -0
  42. package/dist/chunk-4AHR4KGA.js.map +1 -0
  43. package/dist/chunk-6HZGAIUE.js +1665 -0
  44. package/dist/chunk-6HZGAIUE.js.map +1 -0
  45. package/dist/chunk-7EWT3PV6.js +368 -0
  46. package/dist/chunk-7EWT3PV6.js.map +1 -0
  47. package/dist/{chunk-VYCTL62C.js → chunk-GZQSWAKD.js} +1 -1
  48. package/dist/chunk-GZQSWAKD.js.map +1 -0
  49. package/dist/chunk-JXXCTW5W.js +1392 -0
  50. package/dist/chunk-JXXCTW5W.js.map +1 -0
  51. package/dist/{chunk-YTAWBDO2.js → chunk-KADZ5PGG.js} +28 -2
  52. package/dist/chunk-KADZ5PGG.js.map +1 -0
  53. package/dist/{chunk-YKZME56C.js → chunk-KWADQ227.js} +3 -3
  54. package/dist/{chunk-YKZME56C.js.map → chunk-KWADQ227.js.map} +1 -1
  55. package/dist/{chunk-7ZXBPJZP.js → chunk-NEM5N6C6.js} +3902 -1457
  56. package/dist/chunk-NEM5N6C6.js.map +1 -0
  57. package/dist/{chunk-GW3EJ2GP.js → chunk-O7KANXC3.js} +86 -13
  58. package/dist/chunk-O7KANXC3.js.map +1 -0
  59. package/dist/{chunk-VUQZAB3O.js → chunk-OAKHWFSV.js} +28 -32
  60. package/dist/chunk-OAKHWFSV.js.map +1 -0
  61. package/dist/chunk-P7UWKORG.js +561 -0
  62. package/dist/chunk-P7UWKORG.js.map +1 -0
  63. package/dist/chunk-QGEIL4TR.js +195 -0
  64. package/dist/chunk-QGEIL4TR.js.map +1 -0
  65. package/dist/chunk-RJHCBZF7.js +270 -0
  66. package/dist/chunk-RJHCBZF7.js.map +1 -0
  67. package/dist/chunk-S3D75WSQ.js +340 -0
  68. package/dist/chunk-S3D75WSQ.js.map +1 -0
  69. package/dist/chunk-UM4ZXXJR.js +9 -0
  70. package/dist/chunk-UM4ZXXJR.js.map +1 -0
  71. package/dist/chunk-VPVMLM7Q.js +15 -0
  72. package/dist/chunk-VPVMLM7Q.js.map +1 -0
  73. package/dist/{chunk-C2H7HBNP.js → chunk-WVCJRSGJ.js} +119 -14
  74. package/dist/chunk-WVCJRSGJ.js.map +1 -0
  75. package/dist/chunk-XA7HVIHI.js +126 -0
  76. package/dist/chunk-XA7HVIHI.js.map +1 -0
  77. package/dist/{chunk-TX3EIENU.js → chunk-YDVPI7WT.js} +73 -2
  78. package/dist/chunk-YDVPI7WT.js.map +1 -0
  79. package/dist/chunk-YKIHNLWD.js +153 -0
  80. package/dist/chunk-YKIHNLWD.js.map +1 -0
  81. package/dist/chunk-ZDUBPV33.js +277 -0
  82. package/dist/chunk-ZDUBPV33.js.map +1 -0
  83. package/dist/client.d.ts +1065 -124
  84. package/dist/codex-realtime-controller.d.ts +13 -0
  85. package/dist/codex-realtime-controller.js +4 -2
  86. package/dist/codex-realtime-v3-wire.d.ts +3 -2
  87. package/dist/codex-realtime.d.ts +5 -1
  88. package/dist/company-brain.d.ts +1 -11
  89. package/dist/company-profile.d.ts +13 -0
  90. package/dist/core.d.ts +7 -2
  91. package/dist/core.js +25 -6
  92. package/dist/deprecation.d.ts +47 -0
  93. package/dist/document-authority-client.d.ts +24 -2
  94. package/dist/document-authority.js +7 -5
  95. package/dist/editable-artifacts/index.d.ts +1 -1
  96. package/dist/editable-artifacts/types.d.ts +12 -2
  97. package/dist/editable-artifacts.js +19 -3
  98. package/dist/editable-artifacts.js.map +1 -1
  99. package/dist/embedding-client.d.ts +129 -0
  100. package/dist/errors.d.ts +76 -1
  101. package/dist/feedback.d.ts +23 -0
  102. package/dist/gateway-realtime-transport.js +1 -1
  103. package/dist/github-repositories.d.ts +10 -0
  104. package/dist/github-repositories.js +36 -0
  105. package/dist/github-repositories.js.map +1 -0
  106. package/dist/index.d.ts +64 -11
  107. package/dist/index.js +566 -141
  108. package/dist/index.js.map +1 -1
  109. package/dist/insights-usage.d.ts +33 -0
  110. package/dist/insights-usage.js +7 -0
  111. package/dist/insights-usage.js.map +1 -0
  112. package/dist/interaction.d.ts +103 -71
  113. package/dist/interaction.js +4 -2
  114. package/dist/knowledge.d.ts +7 -0
  115. package/dist/knowledge.js +20 -0
  116. package/dist/knowledge.js.map +1 -0
  117. package/dist/known-tools.d.ts +4 -0
  118. package/dist/message-links.d.ts +68 -0
  119. package/dist/model-context.d.ts +68 -0
  120. package/dist/model-display.d.ts +2 -0
  121. package/dist/model-display.js +17 -0
  122. package/dist/model-display.js.map +1 -0
  123. package/dist/model-picker-order.d.ts +1 -0
  124. package/dist/model-picker-order.js +10 -0
  125. package/dist/model-picker-order.js.map +1 -0
  126. package/dist/organization-integration-policy.d.ts +26 -0
  127. package/dist/organization-integration-policy.js +26 -0
  128. package/dist/organization-integration-policy.js.map +1 -0
  129. package/dist/pr-review-client.d.ts +1 -1
  130. package/dist/pr-review.d.ts +1 -1
  131. package/dist/pr-review.js.map +1 -1
  132. package/dist/preference-registry.d.ts +1 -1
  133. package/dist/prepared-mcp-setup.d.ts +5 -0
  134. package/dist/proxy.d.ts +13 -11
  135. package/dist/realtime.d.ts +4 -2
  136. package/dist/realtime.js +253 -7
  137. package/dist/realtime.js.map +1 -1
  138. package/dist/session-history-import.d.ts +22 -0
  139. package/dist/session-history-import.js +36 -0
  140. package/dist/session-history-import.js.map +1 -0
  141. package/dist/session-list-entries.d.ts +1 -0
  142. package/dist/session-list-entries.js +7 -0
  143. package/dist/session-list-entries.js.map +1 -0
  144. package/dist/session-message-search.d.ts +53 -0
  145. package/dist/session-proxy-client.d.ts +28 -0
  146. package/dist/session-proxy.d.ts +334 -0
  147. package/dist/session-proxy.js +24 -0
  148. package/dist/session-proxy.js.map +1 -0
  149. package/dist/session-titles.d.ts +2 -0
  150. package/dist/session-usage-limits.d.ts +10 -0
  151. package/dist/session-usage-limits.js +43 -0
  152. package/dist/session-usage-limits.js.map +1 -0
  153. package/dist/shared-catalog-loader.d.ts +34 -0
  154. package/dist/site-bridge-host.d.ts +112 -0
  155. package/dist/site-bridge-host.js +285 -0
  156. package/dist/site-bridge-host.js.map +1 -0
  157. package/dist/site-browser-entry.d.ts +1 -0
  158. package/dist/site-browser-runtime.gen.d.ts +1 -0
  159. package/dist/site-data.d.ts +178 -0
  160. package/dist/site-document.d.ts +5 -0
  161. package/dist/site-document.js +20 -0
  162. package/dist/site-document.js.map +1 -0
  163. package/dist/site-document.test.d.ts +1 -0
  164. package/dist/site-http.d.ts +17 -0
  165. package/dist/site-sql.d.ts +31 -0
  166. package/dist/site-tool-bridge.d.ts +57 -0
  167. package/dist/site.d.ts +182 -0
  168. package/dist/site.js +62 -0
  169. package/dist/site.js.map +1 -0
  170. package/dist/skills.d.ts +75 -0
  171. package/dist/sse.d.ts +1 -1
  172. package/dist/stream.d.ts +2 -0
  173. package/dist/tenant-workspaces.d.ts +47 -0
  174. package/dist/tenant-workspaces.js +8 -0
  175. package/dist/tenant-workspaces.js.map +1 -0
  176. package/dist/tool-auth.d.ts +87 -0
  177. package/dist/tool-auth.js +23 -0
  178. package/dist/tool-auth.js.map +1 -0
  179. package/dist/tool-display-metadata.d.ts +3 -0
  180. package/dist/tools-37DCTCNZ.js +12 -0
  181. package/dist/tools-37DCTCNZ.js.map +1 -0
  182. package/dist/tools.d.ts +81 -0
  183. package/dist/transcription.d.ts +1 -1
  184. package/dist/types.d.ts +2848 -573
  185. package/dist/usage-allowances.d.ts +36 -0
  186. package/dist/usage-allowances.js +80 -0
  187. package/dist/usage-allowances.js.map +1 -0
  188. package/dist/vault-types.d.ts +236 -0
  189. package/dist/vaults-api.d.ts +57 -0
  190. package/dist/workspace-artifacts.d.ts +47 -5
  191. package/dist/workspace-instruction-policies.d.ts +2 -2
  192. package/dist/workspace-integrations.d.ts +74 -0
  193. package/dist/workspace-integrations.js +65 -0
  194. package/dist/workspace-integrations.js.map +1 -0
  195. package/dist/workspace-state.d.ts +8 -71
  196. package/package.json +99 -5
  197. package/src/accounts.ts +18 -0
  198. package/src/adapters/express.ts +136 -0
  199. package/src/adapters/hono.ts +18 -0
  200. package/src/adapters/next.ts +42 -0
  201. package/src/allowance-refusal.ts +5 -0
  202. package/src/app-links.ts +117 -0
  203. package/src/artifact-catalog.ts +51 -0
  204. package/src/artifact-client.ts +40 -49
  205. package/src/artifacts.ts +1 -0
  206. package/src/automations-client.ts +8 -2
  207. package/src/azure-live-transport.ts +286 -0
  208. package/src/browser.ts +37 -0
  209. package/src/chat/fold.ts +431 -0
  210. package/src/chat/handler.ts +240 -0
  211. package/src/chat/http.ts +257 -0
  212. package/src/chat/ids.ts +85 -0
  213. package/src/chat/index.ts +88 -0
  214. package/src/chat/openai.ts +439 -0
  215. package/src/chat/opengeni.ts +737 -0
  216. package/src/chat/types.ts +219 -0
  217. package/src/chat/vercel.ts +204 -0
  218. package/src/chats.ts +16 -0
  219. package/src/client.ts +5822 -1847
  220. package/src/codex-realtime-controller.ts +163 -8
  221. package/src/codex-realtime-v3-wire.ts +4 -3
  222. package/src/codex-realtime-v3.ts +12 -3
  223. package/src/codex-realtime.ts +11 -3
  224. package/src/company-brain.ts +0 -40
  225. package/src/company-profile.ts +13 -0
  226. package/src/core.ts +20 -0
  227. package/src/deprecation.ts +149 -0
  228. package/src/document-authority-client.ts +104 -1
  229. package/src/editable-artifacts/controller.ts +36 -2
  230. package/src/editable-artifacts/index.ts +1 -0
  231. package/src/editable-artifacts/types.ts +29 -16
  232. package/src/embedding-client.ts +519 -0
  233. package/src/errors.ts +303 -4
  234. package/src/feedback.ts +20 -0
  235. package/src/gateway-realtime-transport.ts +3 -3
  236. package/src/github-repositories.ts +60 -0
  237. package/src/index.ts +611 -64
  238. package/src/insights-usage.ts +61 -0
  239. package/src/interaction.ts +274 -93
  240. package/src/knowledge.ts +72 -0
  241. package/src/known-tools.ts +279 -0
  242. package/src/message-links.ts +178 -0
  243. package/src/model-context.ts +113 -0
  244. package/src/model-display.ts +11 -0
  245. package/src/model-picker-order.ts +6 -0
  246. package/src/organization-integration-policy.ts +54 -0
  247. package/src/pr-review-client.ts +1 -1
  248. package/src/pr-review.ts +1 -1
  249. package/src/preference-registry.ts +2 -0
  250. package/src/prepared-mcp-setup.ts +123 -0
  251. package/src/proxy.ts +23 -12
  252. package/src/realtime.ts +16 -6
  253. package/src/session-history-import.ts +84 -0
  254. package/src/session-list-entries.ts +1 -0
  255. package/src/session-message-search.ts +47 -0
  256. package/src/session-proxy-client.ts +68 -0
  257. package/src/session-proxy.ts +2098 -0
  258. package/src/session-titles.ts +10 -0
  259. package/src/session-usage-limits.ts +71 -0
  260. package/src/shared-catalog-loader.ts +169 -0
  261. package/src/site-bridge-host.ts +422 -0
  262. package/src/site-browser-entry.ts +6 -0
  263. package/src/site-browser-runtime.gen.ts +2 -0
  264. package/src/site-data.ts +372 -0
  265. package/src/site-document.test.ts +64 -0
  266. package/src/site-document.ts +18 -0
  267. package/src/site-http.ts +171 -0
  268. package/src/site-sql.ts +153 -0
  269. package/src/site-tool-bridge.ts +401 -0
  270. package/src/site.ts +867 -0
  271. package/src/skills.ts +69 -0
  272. package/src/sse.ts +9 -3
  273. package/src/stream.ts +33 -10
  274. package/src/tenant-workspaces.ts +148 -0
  275. package/src/tool-auth.ts +301 -0
  276. package/src/tool-display-metadata.ts +27 -0
  277. package/src/tools.ts +452 -0
  278. package/src/transcription.ts +3 -0
  279. package/src/types.ts +3216 -581
  280. package/src/usage-allowances.ts +189 -0
  281. package/src/vault-types.ts +293 -0
  282. package/src/vaults-api.ts +288 -0
  283. package/src/workspace-artifacts.ts +55 -6
  284. package/src/workspace-instruction-policies.ts +2 -1
  285. package/src/workspace-integrations.ts +402 -0
  286. package/src/workspace-state.ts +8 -82
  287. package/dist/chunk-7ZXBPJZP.js.map +0 -1
  288. package/dist/chunk-C2H7HBNP.js.map +0 -1
  289. package/dist/chunk-C4DJPZRU.js +0 -119
  290. package/dist/chunk-C4DJPZRU.js.map +0 -1
  291. package/dist/chunk-GW3EJ2GP.js.map +0 -1
  292. package/dist/chunk-TX3EIENU.js.map +0 -1
  293. package/dist/chunk-VUQZAB3O.js.map +0 -1
  294. package/dist/chunk-VYCTL62C.js.map +0 -1
  295. package/dist/chunk-YTAWBDO2.js.map +0 -1
@@ -0,0 +1,257 @@
1
+ import { OpenGeniApiError } from "../errors";
2
+ import type { Opengeni } from "./opengeni";
3
+ import { OpenGeniChatError, type ChatImportedMessage, type ChatOptions } from "./types";
4
+
5
+ /** Internal HTTP plumbing shared by the native handler and the protocol adapters. */
6
+
7
+ /**
8
+ * Header a client uses to name the conversation it is on. Every handler reads
9
+ * the client conversation as: the host's resolution, else this header, else
10
+ * the protocol's own field.
11
+ */
12
+ export const CHAT_CONVERSATION_HEADER = "x-opengeni-conversation";
13
+
14
+ const CHAT_ERROR_STATUS: Readonly<Record<string, number>> = {
15
+ memory_scope_requires_user: 400,
16
+ conversation_not_authorized: 403,
17
+ conversation_mismatch: 409,
18
+ };
19
+
20
+ /** What the host's auth hook returns: identity from the host, conversation optional per protocol. */
21
+ export type ChatResolution = Omit<ChatOptions, "conversation"> & {
22
+ conversation?: string | undefined;
23
+ };
24
+
25
+ /**
26
+ * The host's authentication hook. It receives the raw request and returns the
27
+ * tenant/user/conversation the caller is allowed to use, or a `Response` to
28
+ * reject the request. Never derive tenant or user from the request body.
29
+ */
30
+ export type ChatResolve = (
31
+ request: Request,
32
+ ) => Promise<ChatResolution | Response> | ChatResolution | Response;
33
+
34
+ export type ChatErrorSummary = { status: number; code: string; message: string };
35
+
36
+ export function jsonResponse(body: unknown, status = 200, headers: HeadersInit = {}): Response {
37
+ return new Response(JSON.stringify(body), {
38
+ status,
39
+ headers: { "Content-Type": "application/json; charset=utf-8", ...headers },
40
+ });
41
+ }
42
+
43
+ export function errorResponse(status: number, message: string, code: string): Response {
44
+ return jsonResponse(
45
+ { error: { message, code, type: status >= 500 ? "server_error" : "invalid_request_error" } },
46
+ status,
47
+ );
48
+ }
49
+
50
+ export async function readJsonObject(request: Request): Promise<Record<string, unknown> | null> {
51
+ try {
52
+ const parsed: unknown = await request.json();
53
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
54
+ ? (parsed as Record<string, unknown>)
55
+ : null;
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ export async function resolveChatRequest(
62
+ request: Request,
63
+ resolve: ChatResolve,
64
+ ): Promise<{ resolution: ChatResolution; response?: undefined } | { response: Response }> {
65
+ const resolved = await resolve(request);
66
+ if (resolved instanceof Response) return { response: resolved };
67
+ return { resolution: resolved };
68
+ }
69
+
70
+ /** The client-supplied conversation: the header, else the protocol's own field. */
71
+ export function clientConversation(request: Request, protocolField: unknown): string | undefined {
72
+ const header = request.headers.get(CHAT_CONVERSATION_HEADER);
73
+ if (header) return header;
74
+ return typeof protocolField === "string" && protocolField ? protocolField : undefined;
75
+ }
76
+
77
+ /**
78
+ * Open a chat from a resolution plus the conversation the client supplied.
79
+ * The host names the conversation, or the host names the user and the client
80
+ * conversation id is namespaced to that user; anything else is a 400, because
81
+ * an unscoped client id could address any conversation in the workspace.
82
+ */
83
+ export async function openResolvedChat(
84
+ og: Opengeni,
85
+ resolution: ChatResolution,
86
+ clientConversationId: string | undefined,
87
+ ): Promise<
88
+ { chat: Awaited<ReturnType<Opengeni["chat"]>>; response?: undefined } | { response: Response }
89
+ > {
90
+ if (!resolution.conversation && !resolution.user) {
91
+ return {
92
+ response: errorResponse(
93
+ 400,
94
+ "Return a conversation from resolve, or a user so client conversation ids are scoped to that user.",
95
+ "conversation_required",
96
+ ),
97
+ };
98
+ }
99
+ const conversationId = resolution.conversation ?? clientConversationId;
100
+ if (!conversationId) {
101
+ return {
102
+ response: errorResponse(
103
+ 400,
104
+ `A conversation id is required: send the ${CHAT_CONVERSATION_HEADER} header or the protocol's conversation field.`,
105
+ "conversation_required",
106
+ ),
107
+ };
108
+ }
109
+ try {
110
+ const chat = await og.chat({ ...resolution, conversation: conversationId } as ChatOptions);
111
+ return { chat };
112
+ } catch (error) {
113
+ const summary = chatErrorSummary(error);
114
+ return { response: errorResponse(summary.status, summary.message, summary.code) };
115
+ }
116
+ }
117
+
118
+ export function chatErrorSummary(error: unknown): ChatErrorSummary {
119
+ if (error instanceof OpenGeniChatError) {
120
+ const status = CHAT_ERROR_STATUS[error.code] ?? 502;
121
+ return { status, code: error.code, message: error.message };
122
+ }
123
+ if (error instanceof OpenGeniApiError) {
124
+ return {
125
+ status: error.status >= 500 ? 502 : error.status,
126
+ code: error.code ?? "opengeni_api_error",
127
+ message: error.message,
128
+ };
129
+ }
130
+ if (error instanceof Error && error.name === "AbortError") {
131
+ return { status: 499, code: "aborted", message: "The chat request was aborted." };
132
+ }
133
+ return { status: 500, code: "internal_error", message: "Chat request failed." };
134
+ }
135
+
136
+ export function sseHeaders(extra: Record<string, string> = {}): Record<string, string> {
137
+ return {
138
+ "Content-Type": "text/event-stream; charset=utf-8",
139
+ "Cache-Control": "no-cache, no-transform",
140
+ Connection: "keep-alive",
141
+ ...extra,
142
+ };
143
+ }
144
+
145
+ export function sseLine(data: string, event?: string): string {
146
+ return `${event ? `event: ${event}\n` : ""}data: ${data}\n\n`;
147
+ }
148
+
149
+ /**
150
+ * Pull-based text-to-bytes stream over already-formatted SSE blocks. Upstream
151
+ * consumption follows downstream demand; cancelling fires `onCancel` so the
152
+ * producer can abort its Opengeni stream.
153
+ */
154
+ export function sseByteStream(
155
+ blocks: AsyncIterable<string>,
156
+ onCancel?: () => void,
157
+ ): ReadableStream<Uint8Array> {
158
+ const encoder = new TextEncoder();
159
+ const iterator = blocks[Symbol.asyncIterator]();
160
+ let cancelled = false;
161
+ return new ReadableStream<Uint8Array>({
162
+ pull: async (controller) => {
163
+ const result = await iterator.next();
164
+ if (cancelled) return;
165
+ if (result.done) {
166
+ controller.close();
167
+ return;
168
+ }
169
+ controller.enqueue(encoder.encode(result.value));
170
+ },
171
+ cancel: () => {
172
+ cancelled = true;
173
+ onCancel?.();
174
+ void Promise.resolve(iterator.return?.(undefined)).then(
175
+ () => undefined,
176
+ () => undefined,
177
+ );
178
+ },
179
+ });
180
+ }
181
+
182
+ /** Text from an OpenAI-style message content: a string or `[{ type, text }]` parts. */
183
+ export function messageContentText(content: unknown, partTypes: string[]): string | null {
184
+ if (typeof content === "string") return content;
185
+ if (!Array.isArray(content)) return null;
186
+ const texts = content
187
+ .map((part) => {
188
+ if (!part || typeof part !== "object") return null;
189
+ const record = part as Record<string, unknown>;
190
+ return partTypes.includes(String(record.type)) && typeof record.text === "string"
191
+ ? record.text
192
+ : null;
193
+ })
194
+ .filter((text): text is string => text !== null);
195
+ return texts.length > 0 ? texts.join("\n") : null;
196
+ }
197
+
198
+ function lastUserMessageIndex(messages: unknown[]): number {
199
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
200
+ const message = messages[index];
201
+ if (message && typeof message === "object" && (message as { role?: unknown }).role === "user") {
202
+ return index;
203
+ }
204
+ }
205
+ return -1;
206
+ }
207
+
208
+ function messageText(
209
+ message: unknown,
210
+ partTypes: string[],
211
+ partsField: "parts" | "content",
212
+ ): string | null {
213
+ if (!message || typeof message !== "object") return null;
214
+ const record = message as Record<string, unknown>;
215
+ return (
216
+ messageContentText(record[partsField], partTypes) ??
217
+ messageContentText(record.content, partTypes)
218
+ );
219
+ }
220
+
221
+ /** The last user-role message's text from an OpenAI/Vercel-style `messages` array. */
222
+ export function lastUserMessageText(
223
+ messages: unknown,
224
+ partTypes: string[],
225
+ partsField: "parts" | "content",
226
+ ): string | null {
227
+ if (!Array.isArray(messages)) return null;
228
+ const index = lastUserMessageIndex(messages);
229
+ return index < 0 ? null : messageText(messages[index], partTypes, partsField);
230
+ }
231
+
232
+ /**
233
+ * Every user/assistant/system message before the last user message, as
234
+ * imported history for the first create. Items without a role or text
235
+ * (tool calls, files) are skipped.
236
+ */
237
+ export function importedHistoryBefore(
238
+ messages: unknown,
239
+ partTypes: string[],
240
+ partsField: "parts" | "content",
241
+ ): ChatImportedMessage[] {
242
+ if (!Array.isArray(messages)) return [];
243
+ const lastUser = lastUserMessageIndex(messages);
244
+ if (lastUser < 0) return [];
245
+ const imported: ChatImportedMessage[] = [];
246
+ for (const message of messages.slice(0, lastUser)) {
247
+ const role = (message as { role?: unknown } | null)?.role;
248
+ if (role !== "user" && role !== "assistant" && role !== "system") continue;
249
+ const text = messageText(message, partTypes, partsField);
250
+ if (text) imported.push({ role, text });
251
+ }
252
+ return imported;
253
+ }
254
+
255
+ export function unixSeconds(): number {
256
+ return Math.floor(Date.now() / 1000);
257
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Deterministic chat identities. A conversation id maps to exactly one
3
+ * session id per workspace, so any process can address the same session
4
+ * without storing a mapping, and a double-submitted create collapses through
5
+ * the idempotency key. The optional legacy label argument is retained only to
6
+ * reproduce historical addresses. New chat calls do not use it: authorization
7
+ * is independent of addressing, so collaborators can share a conversation.
8
+ */
9
+
10
+ /** The end-user label a conversation is namespaced to: the product `source` plus the opaque user id. */
11
+ export type ChatUserLabel = { source: string; id: string };
12
+
13
+ /** Fixed RFC 4122 namespace for chat session ids. Never change it. */
14
+ export const CHAT_SESSION_NAMESPACE = "7c1e6d3a-5b2f-4e8a-9d4c-0f3b6a8e2c17";
15
+
16
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
17
+
18
+ /** RFC 4122 version 5 (SHA-1) UUID of `name` inside `namespace`. */
19
+ export async function uuidV5(name: string, namespace: string): Promise<string> {
20
+ const namespaceBytes = parseUuid(namespace);
21
+ const nameBytes = new TextEncoder().encode(name);
22
+ const input = new Uint8Array(namespaceBytes.length + nameBytes.length);
23
+ input.set(namespaceBytes, 0);
24
+ input.set(nameBytes, namespaceBytes.length);
25
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-1", input)).slice(0, 16);
26
+ digest[6] = ((digest[6] ?? 0) & 0x0f) | 0x50;
27
+ digest[8] = ((digest[8] ?? 0) & 0x3f) | 0x80;
28
+ return formatUuid(digest);
29
+ }
30
+
31
+ /**
32
+ * The session id of `conversation` in `workspaceId`: RFC 4122 v5 of the JSON
33
+ * tuple `[workspaceId, conversation]`, or `[workspaceId, source, id, conversation]`
34
+ * when the conversation belongs to an end user. JSON encoding keeps the tuple
35
+ * unambiguous: an id containing `:` or any other delimiter can never make two
36
+ * different (user, conversation) pairs share a session.
37
+ */
38
+ export async function chatSessionId(
39
+ workspaceId: string,
40
+ conversation: string,
41
+ user?: ChatUserLabel | undefined,
42
+ ): Promise<string> {
43
+ return await uuidV5(chatIdentityName(workspaceId, conversation, user), CHAT_SESSION_NAMESPACE);
44
+ }
45
+
46
+ /** The exact v5 name behind {@link chatSessionId}; exported for tests and audits. */
47
+ export function chatIdentityName(
48
+ workspaceId: string,
49
+ conversation: string,
50
+ user?: ChatUserLabel | undefined,
51
+ ): string {
52
+ return JSON.stringify(
53
+ user ? [workspaceId, user.source, user.id, conversation] : [workspaceId, conversation],
54
+ );
55
+ }
56
+
57
+ /**
58
+ * The create idempotency key for a chat session: `chat:<sessionId>`. The
59
+ * session id already encodes the workspace, user label, and conversation as an
60
+ * unambiguous tuple, so the key inherits that and stays bounded.
61
+ */
62
+ export function chatIdempotencyKey(sessionId: string): string {
63
+ return `chat:${sessionId}`;
64
+ }
65
+
66
+ export function isUuid(value: string): boolean {
67
+ return UUID_PATTERN.test(value);
68
+ }
69
+
70
+ function parseUuid(value: string): Uint8Array {
71
+ if (!UUID_PATTERN.test(value)) {
72
+ throw new TypeError(`Invalid UUID namespace: ${value}`);
73
+ }
74
+ const hex = value.replace(/-/g, "");
75
+ const bytes = new Uint8Array(16);
76
+ for (let index = 0; index < 16; index += 1) {
77
+ bytes[index] = Number.parseInt(hex.slice(index * 2, index * 2 + 2), 16);
78
+ }
79
+ return bytes;
80
+ }
81
+
82
+ function formatUuid(bytes: Uint8Array): string {
83
+ const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
84
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
85
+ }
@@ -0,0 +1,88 @@
1
+ // @opengeni/sdk/chat: the one-option-object chat facade, a server handler for
2
+ // a product's own chat endpoint, and protocol adapters (native SSE, Vercel AI
3
+ // SDK UI message stream, OpenAI Chat Completions and Responses).
4
+ export type { Chats } from "../chats";
5
+ export { OpenGeniSetupError } from "../errors";
6
+
7
+ export {
8
+ Chat,
9
+ DEFAULT_CHAT_SOURCE,
10
+ DEFAULT_OPENGENI_BASE_URL,
11
+ IMPORTED_HISTORY_MAX_CHARS,
12
+ OpenGeni,
13
+ Opengeni,
14
+ formatImportedHistory,
15
+ } from "./opengeni";
16
+ export {
17
+ OpenGeniChatError,
18
+ type ChatAgentAccess,
19
+ type ChatChunk,
20
+ type ChatImportedMessage,
21
+ type ChatMemory,
22
+ type ChatMessage,
23
+ type ChatOptions,
24
+ type ChatPending,
25
+ type ChatReply,
26
+ type ChatReplyStatus,
27
+ type ChatRespondInput,
28
+ type ChatSendOptions,
29
+ type ChatSessionListOptions,
30
+ type ChatSnapshot,
31
+ type ChatTarget,
32
+ type WorkspaceTarget,
33
+ type ChatToolStatus,
34
+ type OpenGeniOptions,
35
+ } from "./types";
36
+ export {
37
+ CHAT_SESSION_NAMESPACE,
38
+ chatIdempotencyKey,
39
+ chatIdentityName,
40
+ chatSessionId,
41
+ uuidV5,
42
+ type ChatUserLabel,
43
+ } from "./ids";
44
+ export {
45
+ ChatTurnFold,
46
+ approvalPending,
47
+ humanInputPending,
48
+ type ChatFoldStep,
49
+ type ChatTurnTerminal,
50
+ } from "./fold";
51
+ export {
52
+ CHAT_CONVERSATION_HEADER,
53
+ CHAT_FORMAT_HEADER,
54
+ chatChunksToSseBlocks,
55
+ chatChunksToSseResponse,
56
+ chatChunksToSseStream,
57
+ createChatHandler,
58
+ handleNativeChatRequest,
59
+ handleNativeHistoryRequest,
60
+ handleNativeRespondRequest,
61
+ parseChatChunkStream,
62
+ respondInputFromBody,
63
+ type ChatHandlerFormat,
64
+ type ChatHandlerOptions,
65
+ type ChatResolution,
66
+ type ChatResolve,
67
+ } from "./handler";
68
+ export {
69
+ UI_MESSAGE_STREAM_HEADER,
70
+ UI_MESSAGE_STREAM_VERSION,
71
+ chatChunksToUIMessageStream,
72
+ handleVercelChatRequest,
73
+ lastUIMessageText,
74
+ uiMessageStreamBlocks,
75
+ uiMessageStreamParts,
76
+ uiMessageStreamResponse,
77
+ type UIMessageStreamOptions,
78
+ } from "./vercel";
79
+ export {
80
+ chatCompletionBlocks,
81
+ chatCompletionObject,
82
+ decodeResponseId,
83
+ encodeResponseId,
84
+ handleChatCompletionsRequest,
85
+ handleResponsesRequest,
86
+ responsesBlocks,
87
+ responsesInputText,
88
+ } from "./openai";