@opengeni/sdk 7.4.0 → 7.5.0-canary.36927100590001

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 (112) hide show
  1. package/README.md +153 -0
  2. package/dist/adapters/next.js +5 -4
  3. package/dist/adapters/next.js.map +1 -1
  4. package/dist/allowance-refusal.d.ts +2 -0
  5. package/dist/allowance-refusal.js +50 -0
  6. package/dist/allowance-refusal.js.map +1 -0
  7. package/dist/artifacts.js +7 -7
  8. package/dist/browser.d.ts +1 -1
  9. package/dist/browser.js +10 -6
  10. package/dist/chat/fold.d.ts +1 -0
  11. package/dist/chat/index.d.ts +2 -0
  12. package/dist/chat/index.js +104 -82
  13. package/dist/chat/index.js.map +1 -1
  14. package/dist/chat/opengeni.d.ts +10 -3
  15. package/dist/chat/types.d.ts +27 -3
  16. package/dist/chats.d.ts +8 -0
  17. package/dist/{chunk-HZH5DJNN.js → chunk-472T6U2J.js} +4 -4
  18. package/dist/{chunk-GT2I4X7R.js → chunk-5VTRBY4Z.js} +40 -5
  19. package/dist/chunk-5VTRBY4Z.js.map +1 -0
  20. package/dist/{chunk-BX5ETUM7.js → chunk-7GR6TOEV.js} +2 -2
  21. package/dist/{chunk-MV2PNVU4.js → chunk-7WJL2ZWY.js} +6 -1
  22. package/dist/chunk-7WJL2ZWY.js.map +1 -0
  23. package/dist/{chunk-7HIA2AI3.js → chunk-ASMIDUEA.js} +2 -2
  24. package/dist/{chunk-FLOL2IES.js → chunk-CKOUDEOS.js} +2 -2
  25. package/dist/{chunk-VF2IW4M5.js → chunk-CLWOTPX5.js} +100 -3
  26. package/dist/chunk-CLWOTPX5.js.map +1 -0
  27. package/dist/{chunk-OO5LPTO7.js → chunk-LQCTDGG6.js} +52 -1
  28. package/dist/chunk-LQCTDGG6.js.map +1 -0
  29. package/dist/chunk-N2LADGI4.js +124 -0
  30. package/dist/chunk-N2LADGI4.js.map +1 -0
  31. package/dist/{chunk-HGGK2BW2.js → chunk-PH56JDUM.js} +2 -2
  32. package/dist/{chunk-A73QXCBW.js → chunk-U3RYSV5N.js} +430 -17
  33. package/dist/chunk-U3RYSV5N.js.map +1 -0
  34. package/dist/{chunk-6NEPJ7NC.js → chunk-ULUWGJBH.js} +2 -2
  35. package/dist/chunk-UM4ZXXJR.js +9 -0
  36. package/dist/chunk-UM4ZXXJR.js.map +1 -0
  37. package/dist/{chunk-FMLDFJ2Q.js → chunk-YTJK64EV.js} +2 -2
  38. package/dist/{chunk-QSCQUITU.js → chunk-ZKKWTOIZ.js} +120 -20
  39. package/dist/chunk-ZKKWTOIZ.js.map +1 -0
  40. package/dist/client.d.ts +44 -3
  41. package/dist/codex-realtime-controller.js +2 -2
  42. package/dist/core.d.ts +1 -1
  43. package/dist/core.js +9 -7
  44. package/dist/document-authority.js +6 -6
  45. package/dist/editable-artifacts.js +1 -1
  46. package/dist/embedding-client.d.ts +39 -0
  47. package/dist/errors.d.ts +17 -0
  48. package/dist/index.d.ts +11 -7
  49. package/dist/index.js +50 -141
  50. package/dist/index.js.map +1 -1
  51. package/dist/interaction.js +2 -2
  52. package/dist/message-links.d.ts +5 -0
  53. package/dist/model-context.d.ts +9 -1
  54. package/dist/realtime.js +2 -2
  55. package/dist/session-history-import.d.ts +22 -0
  56. package/dist/session-history-import.js +36 -0
  57. package/dist/session-history-import.js.map +1 -0
  58. package/dist/session-proxy-client.d.ts +24 -0
  59. package/dist/session-proxy.d.ts +53 -2
  60. package/dist/session-proxy.js +23 -0
  61. package/dist/site-browser-runtime.gen.d.ts +1 -1
  62. package/dist/site-document.js +1 -1
  63. package/dist/site-document.js.map +1 -1
  64. package/dist/site.js +7 -7
  65. package/dist/tenant-workspaces.d.ts +33 -0
  66. package/dist/tenant-workspaces.js +8 -0
  67. package/dist/tenant-workspaces.js.map +1 -0
  68. package/dist/{tools-FSTY4FAY.js → tools-IWJHYNZE.js} +3 -3
  69. package/dist/tools-IWJHYNZE.js.map +1 -0
  70. package/dist/types.d.ts +303 -12
  71. package/dist/usage-allowances.d.ts +36 -0
  72. package/dist/usage-allowances.js +80 -0
  73. package/dist/usage-allowances.js.map +1 -0
  74. package/dist/workspace-integrations.d.ts +22 -5
  75. package/dist/workspace-integrations.js +11 -1
  76. package/package.json +23 -3
  77. package/src/allowance-refusal.ts +5 -0
  78. package/src/browser.ts +2 -0
  79. package/src/chat/fold.ts +12 -0
  80. package/src/chat/index.ts +2 -0
  81. package/src/chat/opengeni.ts +114 -34
  82. package/src/chat/types.ts +27 -2
  83. package/src/chats.ts +16 -0
  84. package/src/client.ts +165 -15
  85. package/src/core.ts +1 -0
  86. package/src/embedding-client.ts +130 -0
  87. package/src/errors.ts +75 -0
  88. package/src/index.ts +68 -1
  89. package/src/message-links.ts +6 -0
  90. package/src/model-context.ts +27 -1
  91. package/src/session-history-import.ts +84 -0
  92. package/src/session-proxy-client.ts +64 -0
  93. package/src/session-proxy.ts +360 -17
  94. package/src/site-browser-runtime.gen.ts +1 -1
  95. package/src/tenant-workspaces.ts +120 -0
  96. package/src/types.ts +343 -10
  97. package/src/usage-allowances.ts +189 -0
  98. package/src/workspace-integrations.ts +76 -4
  99. package/dist/chunk-A73QXCBW.js.map +0 -1
  100. package/dist/chunk-GT2I4X7R.js.map +0 -1
  101. package/dist/chunk-MV2PNVU4.js.map +0 -1
  102. package/dist/chunk-OO5LPTO7.js.map +0 -1
  103. package/dist/chunk-QSCQUITU.js.map +0 -1
  104. package/dist/chunk-VF2IW4M5.js.map +0 -1
  105. /package/dist/{chunk-HZH5DJNN.js.map → chunk-472T6U2J.js.map} +0 -0
  106. /package/dist/{chunk-BX5ETUM7.js.map → chunk-7GR6TOEV.js.map} +0 -0
  107. /package/dist/{chunk-7HIA2AI3.js.map → chunk-ASMIDUEA.js.map} +0 -0
  108. /package/dist/{chunk-FLOL2IES.js.map → chunk-CKOUDEOS.js.map} +0 -0
  109. /package/dist/{chunk-HGGK2BW2.js.map → chunk-PH56JDUM.js.map} +0 -0
  110. /package/dist/{chunk-6NEPJ7NC.js.map → chunk-ULUWGJBH.js.map} +0 -0
  111. /package/dist/{chunk-FMLDFJ2Q.js.map → chunk-YTJK64EV.js.map} +0 -0
  112. /package/dist/{tools-FSTY4FAY.js.map → session-proxy.js.map} +0 -0
package/README.md CHANGED
@@ -25,6 +25,31 @@ bearer design, but an organization API key belongs on the product server.
25
25
  Browser cookies are accepted cross-origin only from operator-configured trusted
26
26
  origins; arbitrary embedding origins never receive credentialed CORS responses.
27
27
 
28
+ ## Allowance lifecycle recovery
29
+
30
+ `getWorkspaceAllowance(workspaceId)` still returns nullable configuration.
31
+ Use `getWorkspaceAllowanceState(workspaceId)` when you also need the lifecycle
32
+ version: `{ version: 0, config: null }` means never configured, while a positive
33
+ version with null configuration means cleared. Recreation uses that exact
34
+ version as `expectedVersion`.
35
+
36
+ `clearWorkspaceAllowance(workspaceId, { expectedVersion, operationId })` returns
37
+ `{ version }`. The optional operation ID makes a lost response recoverable:
38
+ reuse the same actor, operation ID and exact request. Replay rechecks current
39
+ authority and returns 409 after any later allowance lifecycle change. On 409,
40
+ read the current state and decide whether a new operation is intended; never
41
+ guess a CAS version. The SDK does not automatically retry mutations.
42
+
43
+ The same reads and administration are free functions on the
44
+ `@opengeni/sdk/usage-allowances` subpath for browser code that only has the
45
+ narrow `OpenGeniBrowserClient` (`getMyUsage(client, workspaceId)`,
46
+ `getAllUsage`, `setMemberAllowance`, ...); they call the same routes as the root methods.
47
+
48
+ State reads require the same budget-read authority as configuration reads.
49
+ Full usage additionally requires target workspace access; organization budget
50
+ authority alone does not expose the member roster. Agents cannot read or
51
+ change allowances.
52
+
28
53
  ## Embed the conversation (default)
29
54
 
30
55
  The default product integration is the full OpenGeni conversation:
@@ -71,6 +96,48 @@ const client = new OpenGeniClient({ baseUrl: "/api/opengeni" });
71
96
  </OpenGeniProvider>;
72
97
  ```
73
98
 
99
+ The proxy and `@opengeni/sdk/chat` facade accept `chats`. The proxy defaults to
100
+ `"private"`; the facade does too when a `user` is present. Without a user and with
101
+ `chats` omitted, the facade keeps workspace visibility, session-only agent reach
102
+ and Knowledge authoring off. Explicit `"private"` requires an authenticated user.
103
+ Private chats use private visibility, session-only agent reach and personal
104
+ Knowledge; `"shared"` uses workspace visibility, agent reach and Knowledge.
105
+ The API wire values are `visibility: "private" | "workspace"`; stored visibility
106
+ is `user_private | workspace_shared`. Explicit hook/create fields override defaults.
107
+
108
+ For `"isolated"`, pass the facade and return `{ tenant, user }` from `resolve`.
109
+ `og.workspaceIdFor({ tenant, user }, { isolation: "user" })` provisions a separate
110
+ workspace and that user's external membership. It uses stable onboarding keys,
111
+ so retries do not restore revoked access. Other modes need explicit onboarding.
112
+ The standalone resolver is on the server-only `@opengeni/sdk/tenant-workspaces`
113
+ subpath. Keep tenant and user values host-authenticated.
114
+
115
+ Isolated members get only workspace read, session create/read/control (including
116
+ sending messages), file upload/read, and `mcp_servers:attach` for host-provided
117
+ per-session servers. No admin permissions are included. Pass `memberPermissions`
118
+ to the `OpenGeni` constructor or `createWorkspaceIdResolver` options to replace
119
+ that list, for example to disable file uploads or MCP attachment. The organization
120
+ key must also permit each operation. This is initial onboarding, not a membership
121
+ update: changing the option does not update existing grants or restore revoked access.
122
+ Keep MCP URLs and credentials in the server's `createSession` hook; the browser
123
+ cannot choose them through the proxy.
124
+ An existing onboarding conflict returns the workspace address without retrying
125
+ or changing the grant. The address is not authorization: later `asUser` calls
126
+ still enforce current permissions. Existing isolated users need an explicit
127
+ `updateExternalWorkspaceMember` to gain newly added permissions such as MCP attachment.
128
+
129
+ The facade also accepts `agent` (identity, capabilities, instructions, renderer)
130
+ and defaults its renderer to `"markdown"` when the server admits agent configuration.
131
+ Only an implicit renderer retries once without `agent` on `422 agent_config_not_enabled`,
132
+ and that refusal is cached per facade instance. This avoids a bootstrap request
133
+ and supports older servers that cannot advertise admission. Explicit agent settings
134
+ are never stripped: the 422 names `OPENGENI_AGENT_CONFIG_ADMISSION_ENABLED` for the
135
+ deployment operator. The private default with a user now turns personal Knowledge
136
+ on; pass `memory: false` to keep authoring off.
137
+ Missing organization private-session enablement raises `OpenGeniSetupError`,
138
+ with owner/admin API, SDK and web-app instructions; the proxy preserves that
139
+ actionable error for browser clients.
140
+
74
141
  Framework adapters are thin wrappers over the same web-standard handler (they
75
142
  also accept `createChatHandler` or any `(Request) => Promise<Response>`):
76
143
 
@@ -547,6 +614,83 @@ personal grant, Variable Set, Sandbox Environment, MCP server configuration, pro
547
614
  identity, pin, or workflow. Destination visibility and acknowledgement are
548
615
  idempotency-bound.
549
616
 
617
+ ## Archived session history import (server only)
618
+
619
+ Use `@opengeni/sdk/session-history-import` to migrate historical conversations
620
+ into read-only archives. These functions take `client` first, use its ordinary
621
+ `requestJson` transport, and are not eager client methods or root helper exports:
622
+
623
+ Create requires `sessions:create`; append requires `sessions:control` and the same
624
+ authenticated importer. With `asUser`, the user's permissions apply rather than
625
+ the organization key's permissions.
626
+
627
+ ```ts
628
+ import { OpenGeniClient } from "@opengeni/sdk";
629
+ import {
630
+ importArchivedSession,
631
+ appendArchivedSessionEvents,
632
+ } from "@opengeni/sdk/session-history-import";
633
+
634
+ const og = new OpenGeniClient({
635
+ baseUrl: process.env.OPENGENI_API_BASE_URL!,
636
+ apiKey: process.env.OPENGENI_API_KEY!,
637
+ });
638
+ // Existing tenant mapping + explicitly onboarded original creator.
639
+ const actor = og.asUser(originalExternalUserId, { source });
640
+ const imported = await importArchivedSession(actor, workspaceId, {
641
+ importId: "embedded:thread-42", // Stable; persist the exact request before sending.
642
+ title: "Original conversation",
643
+ createdAt: "2025-04-01T09:30:00Z",
644
+ visibility: "user_private",
645
+ });
646
+ const appended = await appendArchivedSessionEvents(actor, workspaceId, imported.importId, {
647
+ batchId: "thread-42:batch-0001", // Stable; also persist this exact body first.
648
+ offset: imported.nextOffset,
649
+ events: [{
650
+ type: "agent.message.completed",
651
+ createdAt: "2025-04-01T09:31:00Z",
652
+ payload: { text: "Historical answer", channel: "final" },
653
+ }],
654
+ });
655
+ // Persist imported.session.id and appended.nextOffset in the migration ledger.
656
+ ```
657
+
658
+ External mapping equivalents take `(client, source, externalId, ...)`:
659
+ `importExternalWorkspaceArchivedSession` and
660
+ `appendExternalWorkspaceArchivedSessionEvents`. These resolve an existing
661
+ mapping; neither import form creates a workspace or grants membership. A bare
662
+ organization key creates a shared, ownerless archive. Use `asUser` for the
663
+ verified creator/owner; private imports require existing private-session
664
+ enablement. Preserve source visibility rather than widening it on failure.
665
+
666
+ The subpath exports `ImportArchivedSessionRequest/Response`,
667
+ `AppendArchivedSessionEventsRequest/Response`, `ArchivedSessionImportEvent` and
668
+ `SessionImportedArchive` types. Requests accept at most 100 events / 1 MiB
669
+ serialized UTF-8 JSON and 256 KiB per event. IDs and titles are bounded to 200
670
+ characters. Import events default to `[]`; append batches must be non-empty.
671
+ Events carry `{ type, createdAt, turnId?, payload }`: a finite supported historical
672
+ type, ISO timestamp, optional UUID/null correlation and JSON-object payload.
673
+ Source timestamps support at most millisecond precision; normalize finer dates
674
+ explicitly and retain originals in the ledger. Negative-zero JSON is rejected.
675
+
676
+ Exact create replay returns `created: false`; exact append replay returns
677
+ `replayed: true`. `offset` is a zero-based imported-event count, not a timeline
678
+ sequence. Retain the exact actor, mapping, IDs, offset and bodies for uncertain
679
+ retries; new batches use the acknowledged `nextOffset`. Changed key reuse or
680
+ an out-of-order new offset returns 409. Helpers propagate `OpenGeniApiError`,
681
+ including `outcomeUnknown`, and do not automatically retry mutations.
682
+
683
+ Import is timeline-only: completed messages, calls/results and goals may be
684
+ historical facts, but no model-facing history, live goal, pending decision or
685
+ execution is restored. `session.importedArchive` has
686
+ `{ importId, importedAt, readOnly: true }`, independent of personal archive state.
687
+ Render through the unchanged `SessionConversation` and session proxy; never
688
+ offer Send or Steer. Continuing an imported archive is unsupported in v1.
689
+ The proxy does not expose import routes. Re-upload files through existing APIs
690
+ and replace references before import; preserve additional source metadata and
691
+ source/destination IDs in the host ledger. See
692
+ [Migrating from embedded OpenGeni](../../docs/product-integration.md#migrating-from-embedded-opengeni).
693
+
550
694
  ## Connected accounts
551
695
 
552
696
  Authenticated messages use the initiating user's eligible connected accounts.
@@ -588,6 +732,12 @@ const request = await verifyCredentialProviderRequest({
588
732
  });
589
733
  ```
590
734
 
735
+ To check an endpoint now, `testWorkspaceWebhook(client, workspaceId, webhookId)` sends a
736
+ signed `webhook.test` event (acknowledge it with any 2xx) and
737
+ `testWorkspaceCredentialProvider(client, workspaceId)` sends a request with
738
+ `purpose: "test"`; both come from `@opengeni/sdk/workspace-integrations` and return
739
+ what the endpoint answered, naming returned credentials but never their values.
740
+
591
741
  `listWorkspaceSandboxImages` returns the deployment's allowlisted images; set one
592
742
  with `updateWorkspaceSettings(workspaceId, { defaultSandboxImage })`.
593
743
 
@@ -1062,6 +1212,9 @@ await client.cancelSession(workspaceId, sessionId, {
1062
1212
  reason: "host record deleted",
1063
1213
  clientEventId: crypto.randomUUID(),
1064
1214
  });
1215
+ // approvalId is `approvals[].id` from the latest `session.requiresAction`
1216
+ // event (type SessionApprovalRequest: { id, name, arguments }); it is the
1217
+ // pending tool call id, not the event id.
1065
1218
  await client.sendApprovalDecision(workspaceId, sessionId, { approvalId, decision: "approve" });
1066
1219
  ```
1067
1220
 
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  createSessionProxyHandler
3
- } from "../chunk-A73QXCBW.js";
4
- import "../chunk-BX5ETUM7.js";
5
- import "../chunk-MV2PNVU4.js";
6
- import "../chunk-OO5LPTO7.js";
3
+ } from "../chunk-U3RYSV5N.js";
4
+ import "../chunk-UM4ZXXJR.js";
5
+ import "../chunk-7GR6TOEV.js";
6
+ import "../chunk-7WJL2ZWY.js";
7
+ import "../chunk-LQCTDGG6.js";
7
8
 
8
9
  // src/adapters/next.ts
9
10
  function toNextRouteHandlers(handler) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/next.ts"],"sourcesContent":["import { createSessionProxyHandler, type SessionProxyHandlerOptions } from \"../session-proxy\";\n\n/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */\nexport type WebHandler = (request: Request) => Promise<Response>;\n\n/** Route handlers for a Next.js App Router catch-all route. */\nexport type NextRouteHandlers = {\n GET: WebHandler;\n POST: WebHandler;\n PUT: WebHandler;\n PATCH: WebHandler;\n DELETE: WebHandler;\n};\n\n/**\n * Expose a web-standard handler as Next.js App Router route handlers.\n *\n * ```ts\n * // app/api/opengeni/[...path]/route.ts\n * export const dynamic = \"force-dynamic\";\n * export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler);\n * ```\n *\n * Next passes the full request URL (including any `basePath`), so the proxy's\n * own `/v1/` detection routes it; responses, including SSE, stream as-is.\n */\nexport function toNextRouteHandlers(handler: WebHandler): NextRouteHandlers {\n // Next calls route handlers with (request, context); only the request matters.\n const route: WebHandler = async (request) => await handler(request);\n return { GET: route, POST: route, PUT: route, PATCH: route, DELETE: route };\n}\n\n/**\n * `createSessionProxyHandler` as Next.js App Router route handlers, for\n * `app/<mount>/[...path]/route.ts`.\n */\nexport function createSessionProxyRoute(\n target: Parameters<typeof createSessionProxyHandler>[0],\n options: SessionProxyHandlerOptions,\n): NextRouteHandlers {\n return toNextRouteHandlers(createSessionProxyHandler(target, options));\n}\n"],"mappings":";;;;;;;;AA0BO,SAAS,oBAAoB,SAAwC;AAE1E,QAAM,QAAoB,OAAO,YAAY,MAAM,QAAQ,OAAO;AAClE,SAAO,EAAE,KAAK,OAAO,MAAM,OAAO,KAAK,OAAO,OAAO,OAAO,QAAQ,MAAM;AAC5E;AAMO,SAAS,wBACd,QACA,SACmB;AACnB,SAAO,oBAAoB,0BAA0B,QAAQ,OAAO,CAAC;AACvE;","names":[]}
1
+ {"version":3,"sources":["../../src/adapters/next.ts"],"sourcesContent":["import { createSessionProxyHandler, type SessionProxyHandlerOptions } from \"../session-proxy\";\n\n/** Any web-standard handler: `createSessionProxyHandler`, `createChatHandler`, or your own. */\nexport type WebHandler = (request: Request) => Promise<Response>;\n\n/** Route handlers for a Next.js App Router catch-all route. */\nexport type NextRouteHandlers = {\n GET: WebHandler;\n POST: WebHandler;\n PUT: WebHandler;\n PATCH: WebHandler;\n DELETE: WebHandler;\n};\n\n/**\n * Expose a web-standard handler as Next.js App Router route handlers.\n *\n * ```ts\n * // app/api/opengeni/[...path]/route.ts\n * export const dynamic = \"force-dynamic\";\n * export const { GET, POST, PUT, PATCH, DELETE } = toNextRouteHandlers(handler);\n * ```\n *\n * Next passes the full request URL (including any `basePath`), so the proxy's\n * own `/v1/` detection routes it; responses, including SSE, stream as-is.\n */\nexport function toNextRouteHandlers(handler: WebHandler): NextRouteHandlers {\n // Next calls route handlers with (request, context); only the request matters.\n const route: WebHandler = async (request) => await handler(request);\n return { GET: route, POST: route, PUT: route, PATCH: route, DELETE: route };\n}\n\n/**\n * `createSessionProxyHandler` as Next.js App Router route handlers, for\n * `app/<mount>/[...path]/route.ts`.\n */\nexport function createSessionProxyRoute(\n target: Parameters<typeof createSessionProxyHandler>[0],\n options: SessionProxyHandlerOptions,\n): NextRouteHandlers {\n return toNextRouteHandlers(createSessionProxyHandler(target, options));\n}\n"],"mappings":";;;;;;;;;AA0BO,SAAS,oBAAoB,SAAwC;AAE1E,QAAM,QAAoB,OAAO,YAAY,MAAM,QAAQ,OAAO;AAClE,SAAO,EAAE,KAAK,OAAO,MAAM,OAAO,KAAK,OAAO,OAAO,OAAO,QAAQ,MAAM;AAC5E;AAMO,SAAS,wBACd,QACA,SACmB;AACnB,SAAO,oBAAoB,0BAA0B,QAAQ,OAAO,CAAC;AACvE;","names":[]}
@@ -0,0 +1,2 @@
1
+ /** Browser-safe allowance presentation without exposing the server core. */
2
+ export { allowanceExhaustedMessage, parseAllowanceExhaustedRefusal, } from "@opengeni/contracts/allowance-refusal";
@@ -0,0 +1,50 @@
1
+ // ../contracts/src/allowance-refusal.ts
2
+ var MESSAGE_MAX_UTF8_BYTES = 1024;
3
+ function record(value) {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
5
+ }
6
+ function resetTimestamp(value) {
7
+ if (value === null) return true;
8
+ if (typeof value !== "string" || value.length > 64) return false;
9
+ const parts = /^(\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/.exec(
10
+ value
11
+ );
12
+ if (!parts || !Number.isFinite(Date.parse(value))) return false;
13
+ const year = Number(parts[1]);
14
+ const month = Number(parts[2]);
15
+ const day = Number(parts[3]);
16
+ const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
17
+ const days = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
18
+ return day <= days[month - 1];
19
+ }
20
+ function parseAllowanceExhaustedRefusal(value) {
21
+ const source = record(value);
22
+ if (source?.code !== "allowance_exhausted") return null;
23
+ const fields = record(source.details) ?? source;
24
+ const scope = fields.scope;
25
+ const subjectId = scope === "member" ? fields.subjectId : void 0;
26
+ if (typeof source.message !== "string" || scope !== "workspace" && scope !== "member" || !resetTimestamp(fields.resetsAt) || subjectId !== void 0 && (typeof subjectId !== "string" || subjectId.length === 0 || subjectId.length > 1024 || subjectId.includes("\0"))) {
27
+ return null;
28
+ }
29
+ const normalized = source.message.slice(0, MESSAGE_MAX_UTF8_BYTES * 2).replace(/[\u0000-\u001f\u007f]+/g, " ").trim();
30
+ const bytes = new TextEncoder().encode(normalized.slice(0, MESSAGE_MAX_UTF8_BYTES * 2));
31
+ let end = Math.min(bytes.length, MESSAGE_MAX_UTF8_BYTES);
32
+ while (end > 0 && end < bytes.length && (bytes[end] & 192) === 128) end -= 1;
33
+ return {
34
+ code: "allowance_exhausted",
35
+ scope,
36
+ resetsAt: fields.resetsAt,
37
+ ...typeof subjectId === "string" ? { subjectId } : {},
38
+ message: new TextDecoder().decode(bytes.slice(0, end)).trim()
39
+ };
40
+ }
41
+ function allowanceExhaustedMessage(refusal) {
42
+ const remedy = refusal.scope === "workspace" ? "The workspace usage allowance is exhausted. An organization administrator or a full-access organization API key can raise the workspace ceiling or add an allowance grant." : "The member usage allowance is exhausted. A workspace administrator or a full-access organization API key can adjust the member ceiling.";
43
+ const reset = refusal.resetsAt === null ? "This allowance has no automatic reset." : `It resets at ${new Date(refusal.resetsAt).toISOString().slice(0, 16).replace("T", " ")} UTC.`;
44
+ return `${remedy} ${reset}`;
45
+ }
46
+ export {
47
+ allowanceExhaustedMessage,
48
+ parseAllowanceExhaustedRefusal
49
+ };
50
+ //# sourceMappingURL=allowance-refusal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../contracts/src/allowance-refusal.ts"],"sourcesContent":["import type { AllowanceExhaustedRefusal } from \"./usage-allowances\";\n\nconst MESSAGE_MAX_UTF8_BYTES = 1_024;\n\nfunction record(value: unknown): Record<string, unknown> | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\n/** Mirror the canonical offset datetime's calendar/time bounds without loading\n * the schema runtime into synchronous browser/React Native session projection.\n * Contract-parity tests guard this deliberately small, bounded wire leaf.\n */\nfunction resetTimestamp(value: unknown): value is string | null {\n if (value === null) return true;\n if (typeof value !== \"string\" || value.length > 64) return false;\n const parts =\n /^(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])T(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$/.exec(\n value,\n );\n if (!parts || !Number.isFinite(Date.parse(value))) return false;\n const year = Number(parts[1]);\n const month = Number(parts[2]);\n const day = Number(parts[3]);\n const leapYear = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n const days = [31, leapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\n return day <= days[month - 1]!;\n}\n\n/** Select only public refusal fields, including from an API error's details.\n * LimitDecision's allowed:false and transport/SQL diagnostics never cross out.\n */\nexport function parseAllowanceExhaustedRefusal(value: unknown): AllowanceExhaustedRefusal | null {\n const source = record(value);\n if (source?.code !== \"allowance_exhausted\") return null;\n const fields = record(source.details) ?? source;\n const scope = fields.scope;\n const subjectId = scope === \"member\" ? fields.subjectId : undefined;\n if (\n typeof source.message !== \"string\" ||\n (scope !== \"workspace\" && scope !== \"member\") ||\n !resetTimestamp(fields.resetsAt) ||\n (subjectId !== undefined &&\n (typeof subjectId !== \"string\" ||\n subjectId.length === 0 ||\n subjectId.length > 1_024 ||\n subjectId.includes(\"\\0\")))\n ) {\n return null;\n }\n const normalized = source.message\n .slice(0, MESSAGE_MAX_UTF8_BYTES * 2)\n .replace(/[\\u0000-\\u001f\\u007f]+/g, \" \")\n .trim();\n const bytes = new TextEncoder().encode(normalized.slice(0, MESSAGE_MAX_UTF8_BYTES * 2));\n let end = Math.min(bytes.length, MESSAGE_MAX_UTF8_BYTES);\n while (end > 0 && end < bytes.length && (bytes[end]! & 0xc0) === 0x80) end -= 1;\n return {\n code: \"allowance_exhausted\",\n scope,\n resetsAt: fields.resetsAt,\n ...(typeof subjectId === \"string\" ? { subjectId } : {}),\n message: new TextDecoder().decode(bytes.slice(0, end)).trim(),\n };\n}\n\n/** Bounded, fixed remedies: a ceiling is not an exhausted billing source.\n * Never interpolate arbitrary exception prose or a member identifier.\n */\nexport function allowanceExhaustedMessage(refusal: AllowanceExhaustedRefusal): string {\n const remedy =\n refusal.scope === \"workspace\"\n ? \"The workspace usage allowance is exhausted. An organization administrator or a full-access organization API key can raise the workspace ceiling or add an allowance grant.\"\n : \"The member usage allowance is exhausted. A workspace administrator or a full-access organization API key can adjust the member ceiling.\";\n const reset =\n refusal.resetsAt === null\n ? \"This allowance has no automatic reset.\"\n : `It resets at ${new Date(refusal.resetsAt).toISOString().slice(0, 16).replace(\"T\", \" \")} UTC.`;\n return `${remedy} ${reset}`;\n}\n"],"mappings":";AAEA,IAAM,yBAAyB;AAE/B,SAAS,OAAO,OAAgD;AAC9D,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IACrE,QACD;AACN;AAMA,SAAS,eAAe,OAAwC;AAC9D,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAI,QAAO;AAC3D,QAAM,QACJ,sIAAsI;AAAA,IACpI;AAAA,EACF;AACF,MAAI,CAAC,SAAS,CAAC,OAAO,SAAS,KAAK,MAAM,KAAK,CAAC,EAAG,QAAO;AAC1D,QAAM,OAAO,OAAO,MAAM,CAAC,CAAC;AAC5B,QAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AAC7B,QAAM,MAAM,OAAO,MAAM,CAAC,CAAC;AAC3B,QAAM,WAAW,OAAO,MAAM,MAAM,OAAO,QAAQ,KAAK,OAAO,QAAQ;AACvE,QAAM,OAAO,CAAC,IAAI,WAAW,KAAK,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE;AAC5E,SAAO,OAAO,KAAK,QAAQ,CAAC;AAC9B;AAKO,SAAS,+BAA+B,OAAkD;AAC/F,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,QAAQ,SAAS,sBAAuB,QAAO;AACnD,QAAM,SAAS,OAAO,OAAO,OAAO,KAAK;AACzC,QAAM,QAAQ,OAAO;AACrB,QAAM,YAAY,UAAU,WAAW,OAAO,YAAY;AAC1D,MACE,OAAO,OAAO,YAAY,YACzB,UAAU,eAAe,UAAU,YACpC,CAAC,eAAe,OAAO,QAAQ,KAC9B,cAAc,WACZ,OAAO,cAAc,YACpB,UAAU,WAAW,KACrB,UAAU,SAAS,QACnB,UAAU,SAAS,IAAI,IAC3B;AACA,WAAO;AAAA,EACT;AACA,QAAM,aAAa,OAAO,QACvB,MAAM,GAAG,yBAAyB,CAAC,EACnC,QAAQ,2BAA2B,GAAG,EACtC,KAAK;AACR,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,WAAW,MAAM,GAAG,yBAAyB,CAAC,CAAC;AACtF,MAAI,MAAM,KAAK,IAAI,MAAM,QAAQ,sBAAsB;AACvD,SAAO,MAAM,KAAK,MAAM,MAAM,WAAW,MAAM,GAAG,IAAK,SAAU,IAAM,QAAO;AAC9E,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,cAAc,WAAW,EAAE,UAAU,IAAI,CAAC;AAAA,IACrD,SAAS,IAAI,YAAY,EAAE,OAAO,MAAM,MAAM,GAAG,GAAG,CAAC,EAAE,KAAK;AAAA,EAC9D;AACF;AAKO,SAAS,0BAA0B,SAA4C;AACpF,QAAM,SACJ,QAAQ,UAAU,cACd,+KACA;AACN,QAAM,QACJ,QAAQ,aAAa,OACjB,2CACA,gBAAgB,IAAI,KAAK,QAAQ,QAAQ,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE,EAAE,QAAQ,KAAK,GAAG,CAAC;AAC7F,SAAO,GAAG,MAAM,IAAI,KAAK;AAC3B;","names":[]}
package/dist/artifacts.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  OpenGeniClient
3
- } from "./chunk-7HIA2AI3.js";
4
- import "./chunk-HGGK2BW2.js";
5
- import "./chunk-QSCQUITU.js";
6
- import "./chunk-BX5ETUM7.js";
7
- import "./chunk-MV2PNVU4.js";
8
- import "./chunk-FLOL2IES.js";
9
- import "./chunk-OO5LPTO7.js";
3
+ } from "./chunk-ASMIDUEA.js";
4
+ import "./chunk-PH56JDUM.js";
5
+ import "./chunk-ZKKWTOIZ.js";
6
+ import "./chunk-7GR6TOEV.js";
7
+ import "./chunk-7WJL2ZWY.js";
8
+ import "./chunk-CKOUDEOS.js";
9
+ import "./chunk-LQCTDGG6.js";
10
10
  export {
11
11
  OpenGeniClient
12
12
  };
package/dist/browser.d.ts CHANGED
@@ -4,7 +4,7 @@ export type { SessionMessageSearchRequest, SessionMessageSearchMatch, SessionMes
4
4
  export { OpenGeniToolCallError, OpenGeniToolReapprovalRequiredError, OpenGeniToolsClient, } from "./tools.js";
5
5
  export type { OpenGeniGeneratedTools, OpenGeniDynamicToolNode, OpenGeniToolCallOptions, OpenGeniToolFunction, OpenGeniToolsFacade, OpenGeniWorkspaceTools, } from "./tools.js";
6
6
  export type { FetchLike, FetchResponse, GetSessionOptions, OpenGeniClientOptions, OpenGeniRequestOptions, SendMessageInput, SteerMessageResult, TranscribeAudioInput, WorkspaceControlEventPage, } from "./client.js";
7
- export { OpenGeniApiContractMismatchError, OpenGeniApiError, OpenGeniSecureContextRequiredError, OpenGeniSessionListCursorError, OpenGeniStreamError, isRetryableStreamError, } from "./errors.js";
7
+ export { OpenGeniApiContractMismatchError, OpenGeniApiError, OpenGeniSetupError, OpenGeniAllowanceExhaustedError, OpenGeniSecureContextRequiredError, OpenGeniSessionListCursorError, OpenGeniStreamError, isRetryableStreamError, } from "./errors.js";
8
8
  export type { OpenGeniSecureContextRequiredReason } from "./errors.js";
9
9
  export { resolveWorkspaceVoiceInputEnabled } from "./transcription.js";
10
10
  export { OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION } from "./types.js";
package/dist/browser.js CHANGED
@@ -5,32 +5,36 @@ import {
5
5
  OpenGeniToolCallError,
6
6
  OpenGeniToolReapprovalRequiredError,
7
7
  OpenGeniToolsClient
8
- } from "./chunk-FMLDFJ2Q.js";
8
+ } from "./chunk-YTJK64EV.js";
9
9
  import {
10
10
  OpenGeniClient
11
- } from "./chunk-QSCQUITU.js";
12
- import "./chunk-BX5ETUM7.js";
11
+ } from "./chunk-ZKKWTOIZ.js";
12
+ import "./chunk-7GR6TOEV.js";
13
13
  import {
14
14
  OPENGENI_API_CONTRACT_HEADER,
15
15
  OPENGENI_API_CONTRACT_REVISION
16
- } from "./chunk-MV2PNVU4.js";
17
- import "./chunk-FLOL2IES.js";
16
+ } from "./chunk-7WJL2ZWY.js";
17
+ import "./chunk-CKOUDEOS.js";
18
18
  import {
19
+ OpenGeniAllowanceExhaustedError,
19
20
  OpenGeniApiContractMismatchError,
20
21
  OpenGeniApiError,
21
22
  OpenGeniSecureContextRequiredError,
22
23
  OpenGeniSessionListCursorError,
24
+ OpenGeniSetupError,
23
25
  OpenGeniStreamError,
24
26
  isRetryableStreamError
25
- } from "./chunk-OO5LPTO7.js";
27
+ } from "./chunk-LQCTDGG6.js";
26
28
  export {
27
29
  OPENGENI_API_CONTRACT_HEADER,
28
30
  OPENGENI_API_CONTRACT_REVISION,
31
+ OpenGeniAllowanceExhaustedError,
29
32
  OpenGeniApiContractMismatchError,
30
33
  OpenGeniApiError,
31
34
  OpenGeniClient as OpenGeniBrowserClient,
32
35
  OpenGeniSecureContextRequiredError,
33
36
  OpenGeniSessionListCursorError,
37
+ OpenGeniSetupError,
34
38
  OpenGeniStreamError,
35
39
  OpenGeniToolCallError,
36
40
  OpenGeniToolReapprovalRequiredError,
@@ -39,6 +39,7 @@ export declare class ChatTurnFold {
39
39
  turnId: string | null;
40
40
  pending: ChatPending | null;
41
41
  failure: SessionEvent | null;
42
+ private emptyFinalReply;
42
43
  private readonly segments;
43
44
  private readonly openTools;
44
45
  constructor(workspaceId: string, sessionId: string, expectedTurnId: string | null);
@@ -1,3 +1,5 @@
1
+ export type { Chats } from "../chats.js";
2
+ export { OpenGeniSetupError } from "../errors.js";
1
3
  export { Chat, DEFAULT_CHAT_SOURCE, DEFAULT_OPENGENI_BASE_URL, IMPORTED_HISTORY_MAX_CHARS, OpenGeni, formatImportedHistory, } from "./opengeni.js";
2
4
  export { OpenGeniChatError, type ChatAgentAccess, type ChatChunk, type ChatImportedMessage, type ChatMemory, type ChatMessage, type ChatOptions, type ChatPending, type ChatReply, type ChatReplyStatus, type ChatRespondInput, type ChatSendOptions, type ChatSessionListOptions, type ChatSnapshot, type ChatTarget, type ChatToolStatus, type OpenGeniOptions, } from "./types.js";
3
5
  export { CHAT_SESSION_NAMESPACE, chatIdempotencyKey, chatIdentityName, chatSessionId, uuidV5, type ChatUserLabel, } from "./ids.js";