@opengeni/react 0.37.0 → 0.41.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 (71) hide show
  1. package/README.md +88 -2
  2. package/dist/{chunk-7CJOAW3V.js → chunk-EB67J347.js} +1 -135
  3. package/dist/chunk-EB67J347.js.map +1 -0
  4. package/dist/chunk-EKZH6IDG.js +141 -0
  5. package/dist/chunk-EKZH6IDG.js.map +1 -0
  6. package/dist/{chunk-FSPDND3P.js → chunk-JALF5FI3.js} +8 -6
  7. package/dist/{chunk-FSPDND3P.js.map → chunk-JALF5FI3.js.map} +1 -1
  8. package/dist/{chunk-UCZPNXV3.js → chunk-KR2SK5GJ.js} +1292 -711
  9. package/dist/chunk-KR2SK5GJ.js.map +1 -0
  10. package/dist/{chunk-IP22SLO6.js → chunk-OMCFRWHL.js} +28 -441
  11. package/dist/chunk-OMCFRWHL.js.map +1 -0
  12. package/dist/{chunk-HJ4OQVGW.js → chunk-Q2NCKWTK.js} +10 -5
  13. package/dist/chunk-Q2NCKWTK.js.map +1 -0
  14. package/dist/{chunk-ALA3UGWB.js → chunk-SXIQK54Z.js} +8 -6
  15. package/dist/{chunk-ALA3UGWB.js.map → chunk-SXIQK54Z.js.map} +1 -1
  16. package/dist/chunk-WZT5G5OR.js +433 -0
  17. package/dist/chunk-WZT5G5OR.js.map +1 -0
  18. package/dist/client.d.ts +2 -0
  19. package/dist/components/chat-composer.d.ts +5 -1
  20. package/dist/components/human-input-form.d.ts +16 -6
  21. package/dist/components/human-input-surface.d.ts +19 -0
  22. package/dist/components/machine-input-display.d.ts +12 -0
  23. package/dist/components/message-timeline.d.ts +6 -1
  24. package/dist/components/session-chrome.d.ts +1 -0
  25. package/dist/components/tip-follow.d.ts +30 -0
  26. package/dist/composer.js +14 -11
  27. package/dist/hooks/use-composer.d.ts +1 -1
  28. package/dist/index.d.ts +4 -0
  29. package/dist/index.js +2246 -2236
  30. package/dist/index.js.map +1 -1
  31. package/dist/machines.js +4 -3
  32. package/dist/realtime/dropdown-menu.d.ts +16 -0
  33. package/dist/realtime/realtime-control.d.ts +126 -0
  34. package/dist/realtime.d.ts +11 -0
  35. package/dist/realtime.js +1023 -0
  36. package/dist/realtime.js.map +1 -0
  37. package/dist/session-context.d.ts +7 -1
  38. package/dist/session-ui.d.ts +2 -0
  39. package/dist/session-ui.js +5 -3
  40. package/dist/session.js +4 -3
  41. package/dist/timeline/compute-label.d.ts +2 -0
  42. package/dist/timeline/index.d.ts +1 -0
  43. package/package.json +7 -2
  44. package/src/client.ts +13 -0
  45. package/src/components/chat-composer.tsx +11 -2
  46. package/src/components/composer-transcription-control.tsx +2 -2
  47. package/src/components/composer.tsx +14 -5
  48. package/src/components/human-input-form.tsx +506 -186
  49. package/src/components/human-input-surface.tsx +80 -0
  50. package/src/components/machine-input-display.ts +83 -0
  51. package/src/components/message-timeline.tsx +348 -308
  52. package/src/components/model-policy-picker.tsx +8 -3
  53. package/src/components/sandbox-files.tsx +1 -1
  54. package/src/components/session-chrome.tsx +3 -1
  55. package/src/components/tip-follow.ts +127 -6
  56. package/src/hooks/use-composer.ts +16 -13
  57. package/src/index.ts +4 -0
  58. package/src/realtime/dropdown-menu.tsx +123 -0
  59. package/src/realtime/realtime-control.tsx +1178 -0
  60. package/src/realtime.ts +27 -0
  61. package/src/session-context.ts +16 -0
  62. package/src/session-ui.ts +2 -0
  63. package/src/timeline/compute-label.tsx +18 -0
  64. package/src/timeline/index.ts +3 -0
  65. package/src/timeline/tool-renderers.tsx +232 -43
  66. package/styles/index.css +61 -0
  67. package/styles/tokens.css +2 -0
  68. package/dist/chunk-7CJOAW3V.js.map +0 -1
  69. package/dist/chunk-HJ4OQVGW.js.map +0 -1
  70. package/dist/chunk-IP22SLO6.js.map +0 -1
  71. package/dist/chunk-UCZPNXV3.js.map +0 -1
package/README.md CHANGED
@@ -13,7 +13,8 @@ The default root import (`@opengeni/react`) is the clean sandbox-agnostic
13
13
  surface — the chat/timeline hooks and components plus the sandbox workspace
14
14
  suite. Advanced chat-composer composition lives under
15
15
  `@opengeni/react/composer`; Connected-Machine UI lives under
16
- `@opengeni/react/machines`. (The root barrel still re-exports the machines
16
+ `@opengeni/react/machines`; realtime voice controls live under the lazily
17
+ loadable `@opengeni/react/realtime` subpath. (The root barrel still re-exports the machines
17
18
  island for back-compat, deprecated per #144.)
18
19
 
19
20
  Design-system-first: every visual decision routes through CSS-variable tokens
@@ -118,6 +119,89 @@ export function App() {
118
119
  }
119
120
  ```
120
121
 
122
+ ## Realtime composer controls (`@opengeni/react/realtime`)
123
+
124
+ The realtime subpath is the exact OpenGeni composer experience: model catalog
125
+ and selection, split-button motion, start/stop/retry states, microphone and
126
+ audio mute controls, diagnostics, recovery, and the same copy, ARIA, classes,
127
+ and styling used by the web console. It is deliberately separate from the root
128
+ entry so SSR and non-realtime consumers do not eagerly load browser media or
129
+ transport code.
130
+
131
+ For an existing session, place one public component in the composer's action
132
+ slot. It resolves `client` and `workspaceId` from `OpenGeniProvider` and reuses
133
+ the host's existing session/event projection rather than opening a duplicate
134
+ stream:
135
+
136
+ ```tsx
137
+ import { ChatComposer } from "@opengeni/react";
138
+ import { SessionRealtimeControl } from "@opengeni/react/realtime";
139
+
140
+ <ChatComposer
141
+ composer={composer}
142
+ effectiveControl={effectiveControl}
143
+ actionsStart={
144
+ <SessionRealtimeControl
145
+ sessionId={sessionId}
146
+ sessionStatus={sessionStatus}
147
+ effectiveControl={effectiveControl}
148
+ events={events}
149
+ eventsReady={!initialLoading}
150
+ codexConnected={codexConnected}
151
+ />
152
+ }
153
+ />;
154
+ ```
155
+
156
+ For a new-session composer, create the session with `startMode: "realtime"`,
157
+ navigate to it, and pass the selected model to the same existing-session
158
+ control's `realtimeAutostartModel` prop:
159
+
160
+ ```tsx
161
+ import { NewSessionRealtimeControl } from "@opengeni/react/realtime";
162
+
163
+ <NewSessionRealtimeControl
164
+ codexConnected={codexConnected}
165
+ onStart={async (model) => {
166
+ const session = await client.createSession(workspaceId, {
167
+ requestedSessionId: crypto.randomUUID(),
168
+ startMode: "realtime",
169
+ idempotencyKey: crypto.randomUUID(),
170
+ });
171
+ navigateToSession(session.id, { realtimeAutostartModel: model });
172
+ return true;
173
+ }}
174
+ />;
175
+ ```
176
+
177
+ Proxy-backed hosts can pass explicit `client` and `workspaceId` overrides. The
178
+ exported `EmbeddedRealtimeSessionClientLike` requires only catalog, begin,
179
+ Codex/Gateway negotiation, activation, heartbeat, ledger sync, and end. For
180
+ custom layouts, use `useSessionRealtime`, `useRealtimeModelSelection`,
181
+ `RealtimeVoiceControl`, and `RealtimeModelPickerMenu`; the batteries-included
182
+ wrappers remain the recommended path.
183
+
184
+ The reference consumer is `demo/realtime.html`. Run `bun run demo` from
185
+ `packages/react`, then open `http://localhost:3100/realtime.html?mode=mock` for
186
+ deterministic selection/start/mute/stop/reconnect/error testing. Use
187
+ `?mode=live&workspaceId=…&sessionId=…` against the web server's same-origin
188
+ `/demo-api` proxy for a real local OpenGeni environment. Configure
189
+ `OPENGENI_DEMO_API_URL` and, only on the server, optional demo API/access
190
+ credentials; the browser receives neither credential. Prefer the deployment's
191
+ normal browser authentication. If the proxy needs a server credential, create a
192
+ dedicated tenant-scoped, least-privilege key for the reference demo and never
193
+ reuse a deployment-wide runtime secret. Helm deployments can mount a Secret
194
+ containing only `api-key` and/or `access-key` with
195
+ `web.demoApiCredentialsSecret`; those values are mounted read-only rather than
196
+ exposed as container environment variables. Local non-Kubernetes servers may
197
+ instead use `OPENGENI_DEMO_API_KEY` and/or `OPENGENI_DEMO_ACCESS_KEY`.
198
+
199
+ Microphone capture works on `localhost` or a secure HTTPS origin. A remote HTTP
200
+ deployment cannot request microphone access. The live page exercises catalog
201
+ loading, realtime-first creation, Codex Live, Gateway models, mute, recovery,
202
+ delegation/context timeline updates, structured questions, and stop/restart
203
+ through published package APIs only.
204
+
121
205
  ## Composer customization (`@opengeni/react/composer`)
122
206
 
123
207
  Use `ChatComposer` for the standard layout and its `controlsStart`, `header`,
@@ -418,4 +502,6 @@ what the surfaces you mount need:
418
502
  `bun run demo` (from this package) serves a harness that drives the real hooks
419
503
  and components against a scripted mock client — a manager ops-channel narrative
420
504
  with streaming, tool calls, and a worker spawn, plus fleet and scheduled-task
421
- views and a dark/light toggle. `bun run demo:build` is part of the repo gate.
505
+ views and a dark/light toggle. `realtime.html` is the public-package reference
506
+ consumer described above, with deterministic mock and same-origin live modes.
507
+ `bun run demo:build` is part of the repo gate.
@@ -1,127 +1,3 @@
1
- // src/session-context.ts
2
- import { createContext, useContext } from "react";
3
- var OpenGeniContext = createContext(null);
4
- var NOOP_REGISTER_RECONCILER = () => () => void 0;
5
- var NOOP_RECONCILE_SESSION = async () => void 0;
6
- function useEmbeddedClientRefinement(override, requiredMethods, hookName) {
7
- const context = useContext(OpenGeniContext);
8
- const candidate = override.client ?? context?.client;
9
- const workspaceId = override.workspaceId ?? context?.workspaceId;
10
- if (!candidate || !workspaceId) {
11
- throw new Error(
12
- "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
13
- );
14
- }
15
- const methods = candidate;
16
- const missing = requiredMethods.filter((method) => typeof methods[method] !== "function");
17
- if (missing.length > 0) {
18
- throw new Error(
19
- `@opengeni/react: ${hookName} requires client method${missing.length === 1 ? "" : "s"} ${missing.join(", ")}.`
20
- );
21
- }
22
- return {
23
- client: candidate,
24
- workspaceId,
25
- workspaceControlEvent: context?.workspaceControlEvent ?? null,
26
- workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
27
- registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
28
- reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION
29
- };
30
- }
31
- function eventClientMethods(override, directMethods) {
32
- const hasSharedEventFeed = "events" in override && override.events !== void 0;
33
- return hasSharedEventFeed ? directMethods : ["getSession", "streamEvents", ...directMethods];
34
- }
35
- function useOpenGeni(override = {}) {
36
- const context = useContext(OpenGeniContext);
37
- const client = override.client ?? context?.client;
38
- const workspaceId = override.workspaceId ?? context?.workspaceId;
39
- if (!client || !workspaceId) {
40
- throw new Error(
41
- "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
42
- );
43
- }
44
- return {
45
- client,
46
- workspaceId,
47
- workspaceControlEvent: context?.workspaceControlEvent ?? null,
48
- workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
49
- registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
50
- reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION
51
- };
52
- }
53
- function useEmbeddedSession(override = {}) {
54
- return useEmbeddedClientRefinement(
55
- override,
56
- [
57
- "getSession",
58
- "listEvents",
59
- "streamEvents",
60
- "getComposerDraft",
61
- "saveComposerDraft",
62
- "sendMessage",
63
- "steerMessage",
64
- "getQueue",
65
- "moveQueueItem",
66
- "editQueueItem",
67
- "steerQueueItem",
68
- "deleteQueueItem",
69
- "pauseSession",
70
- "resumeSession",
71
- "sendApprovalDecision"
72
- ],
73
- "session hooks"
74
- );
75
- }
76
- function useEmbeddedSessionRead(override = {}) {
77
- return useEmbeddedClientRefinement(
78
- override,
79
- eventClientMethods(override, ["getSession", "updateSession"]),
80
- "useSession"
81
- );
82
- }
83
- function useEmbeddedGoal(override = {}) {
84
- return useEmbeddedClientRefinement(
85
- override,
86
- eventClientMethods(override, ["getGoal", "updateGoal", "deleteGoal"]),
87
- "useGoal"
88
- );
89
- }
90
- function useEmbeddedSessionLineage(override = {}) {
91
- return useEmbeddedClientRefinement(
92
- override,
93
- eventClientMethods(override, ["getSessionLineage"]),
94
- "useSessionLineage"
95
- );
96
- }
97
- function useEmbeddedFileAttachments(override = {}) {
98
- return useEmbeddedClientRefinement(override, ["uploadFile"], "useFileAttachments");
99
- }
100
- function useEmbeddedHumanInputSession(override = {}) {
101
- return useEmbeddedClientRefinement(
102
- override,
103
- eventClientMethods(override, ["listHumanInputRequests", "submitHumanInputResponse"]),
104
- "useHumanInputRequests"
105
- );
106
- }
107
- function useEmbeddedSessionMcpApprovalPolicy(override = {}) {
108
- return useEmbeddedClientRefinement(
109
- override,
110
- eventClientMethods(override, ["getSession", "updateSessionMcpApprovalPolicy"]),
111
- "useSessionMcpApprovalPolicy"
112
- );
113
- }
114
- function useOpenGeniClient(override = {}) {
115
- const context = useContext(OpenGeniContext);
116
- const client = override.client ?? context?.client;
117
- if (!client) {
118
- throw new Error(
119
- "@opengeni/react: no OpenGeni client available. Wrap the tree in <OpenGeniProvider> or pass { client } to the hook."
120
- );
121
- }
122
- return client;
123
- }
124
-
125
1
  // src/hooks/internal.ts
126
2
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
127
3
  function usePolledValue(load, options = {}) {
@@ -374,19 +250,9 @@ function useDebouncedCallback(callback, delayMs = 150) {
374
250
  }
375
251
 
376
252
  export {
377
- OpenGeniContext,
378
- useOpenGeni,
379
- useEmbeddedSession,
380
- useEmbeddedSessionRead,
381
- useEmbeddedGoal,
382
- useEmbeddedSessionLineage,
383
- useEmbeddedFileAttachments,
384
- useEmbeddedHumanInputSession,
385
- useEmbeddedSessionMcpApprovalPolicy,
386
- useOpenGeniClient,
387
253
  usePolledValue,
388
254
  useMutationRunner,
389
255
  useSessionEventTrigger,
390
256
  useDebouncedCallback
391
257
  };
392
- //# sourceMappingURL=chunk-7CJOAW3V.js.map
258
+ //# sourceMappingURL=chunk-EB67J347.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/internal.ts"],"sourcesContent":["import type { SessionEvent } from \"@opengeni/sdk\";\nimport { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport type { EmbeddedSessionEventClientLike } from \"../client\";\n\nexport type AsyncListState<T> = {\n data: T | null;\n loading: boolean;\n error: Error | null;\n refresh: () => Promise<void>;\n};\n\n/**\n * Shared fetch + optional polling loop for the list/read hooks. Stale\n * responses (superseded by a newer load or an unmount) are dropped.\n */\nexport function usePolledValue<T>(\n load: (signal?: AbortSignal) => Promise<T>,\n options: { pollIntervalMs?: number | undefined; enabled?: boolean | undefined } = {},\n): AsyncListState<T> {\n const enabled = options.enabled ?? true;\n const pollIntervalMs = options.pollIntervalMs;\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(enabled);\n const [error, setError] = useState<Error | null>(null);\n const generation = useRef(0);\n const activeLoadRef = useRef(load);\n useLayoutEffect(() => {\n activeLoadRef.current = load;\n }, [load]);\n const requestAbortRef = useRef<AbortController | null>(null);\n const [stateIdentity, setStateIdentity] = useState<{ load: typeof load }>(() => ({ load }));\n\n // A new loader identity means a new query (different session/workspace/...):\n // drop the previous result instead of showing it as the new query's data.\n useEffect(() => {\n if (stateIdentity.load !== load) {\n setStateIdentity({ load });\n setData(null);\n setError(null);\n }\n }, [load, stateIdentity.load]);\n\n const run = useCallback(async () => {\n // A callback retained by a completed mutation from the previous query must\n // not supersede or settle the current query's request.\n if (activeLoadRef.current !== load) return;\n const ticket = ++generation.current;\n requestAbortRef.current?.abort();\n const requestAbort = new AbortController();\n requestAbortRef.current = requestAbort;\n try {\n const result = await load(requestAbort.signal);\n if (\n ticket === generation.current &&\n activeLoadRef.current === load &&\n !requestAbort.signal.aborted\n ) {\n setData(result);\n setError(null);\n setLoading(false);\n }\n } catch (cause) {\n if (\n ticket === generation.current &&\n activeLoadRef.current === load &&\n !requestAbort.signal.aborted\n ) {\n setError(cause instanceof Error ? cause : new Error(String(cause)));\n setLoading(false);\n }\n } finally {\n if (requestAbortRef.current === requestAbort) {\n requestAbortRef.current = null;\n }\n }\n }, [load]);\n\n useEffect(() => {\n if (!enabled) {\n setLoading(false);\n return;\n }\n setLoading(true);\n void run();\n if (pollIntervalMs === undefined || pollIntervalMs <= 0) {\n return () => {\n generation.current += 1;\n requestAbortRef.current?.abort();\n };\n }\n const timer = setInterval(() => void run(), pollIntervalMs);\n return () => {\n clearInterval(timer);\n generation.current += 1;\n requestAbortRef.current?.abort();\n };\n }, [run, enabled, pollIntervalMs]);\n\n const identityMatches = stateIdentity.load === load;\n return {\n data: identityMatches ? data : null,\n loading: identityMatches ? loading : enabled,\n error: identityMatches ? error : null,\n refresh: run,\n };\n}\n\nexport type MutationState = {\n mutating: boolean;\n mutationError: Error | null;\n clearMutationError: () => void;\n};\n\n/**\n * Shared async mutation runner for the write hooks. `run` resolves with the\n * operation's value, or `null` after capturing the error in `mutationError`\n * (callers then roll back optimistic state).\n */\nexport function useMutationRunner(identity: unknown = undefined): MutationState & {\n run: <T>(operation: () => Promise<T>) => Promise<T | null>;\n} {\n const [mutating, setMutating] = useState(false);\n const [mutationError, setMutationError] = useState<Error | null>(null);\n const [stateIdentity, setStateIdentity] = useState<unknown>(() => identity);\n const inFlight = useRef(0);\n const generation = useRef(0);\n const identityRef = useRef(identity);\n useLayoutEffect(() => {\n if (Object.is(identityRef.current, identity)) return;\n identityRef.current = identity;\n generation.current += 1;\n inFlight.current = 0;\n }, [identity]);\n const mounted = useRef(true);\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n useEffect(() => {\n if (!Object.is(stateIdentity, identity)) {\n setStateIdentity(() => identity);\n setMutating(false);\n setMutationError(null);\n }\n }, [identity, stateIdentity]);\n const run = useCallback(\n async <T>(operation: () => Promise<T>): Promise<T | null> => {\n const ownedIdentity = identity;\n const ownedGeneration = generation.current;\n if (!Object.is(identityRef.current, ownedIdentity)) return null;\n inFlight.current += 1;\n if (mounted.current) {\n setMutating(true);\n setMutationError(null);\n }\n try {\n const result = await operation();\n if (\n !mounted.current ||\n generation.current !== ownedGeneration ||\n !Object.is(identityRef.current, ownedIdentity)\n ) {\n return null;\n }\n return result;\n } catch (cause) {\n if (\n mounted.current &&\n generation.current === ownedGeneration &&\n Object.is(identityRef.current, ownedIdentity)\n ) {\n setMutationError(cause instanceof Error ? cause : new Error(String(cause)));\n }\n return null;\n } finally {\n if (\n generation.current === ownedGeneration &&\n Object.is(identityRef.current, ownedIdentity)\n ) {\n inFlight.current -= 1;\n if (mounted.current && inFlight.current === 0) {\n setMutating(false);\n }\n }\n }\n },\n [identity],\n );\n const identityMatches = Object.is(stateIdentity, identity);\n return {\n mutating: identityMatches && mutating,\n mutationError: identityMatches ? mutationError : null,\n clearMutationError: useCallback(() => {\n if (Object.is(identityRef.current, identity)) setMutationError(null);\n }, [identity]),\n run,\n };\n}\n\nexport type SessionEventFeedOptions = {\n /**\n * Share an existing event log (from `useSessionEvents`) instead of opening\n * a second stream. When omitted the hook tails the session's event stream\n * itself, starting at the current `lastSequence`.\n */\n events?: SessionEvent[] | undefined;\n enabled?: boolean | undefined;\n};\n\n/**\n * Invoke `onEvent` for every session event matching `match` — the live-update\n * primitive behind `useTurnQueue` and `useGoal`. Either watches a shared\n * `events` log or tails the stream directly (reconnect handled by the SDK).\n */\nexport function useSessionEventTrigger(\n client: EmbeddedSessionEventClientLike,\n workspaceId: string,\n sessionId: string | null | undefined,\n match: (event: SessionEvent) => boolean,\n onEvent: (event: SessionEvent) => void,\n options: SessionEventFeedOptions = {},\n reconcileBeforeLive?: (() => void | Promise<void>) | undefined,\n): void {\n const enabled = options.enabled ?? true;\n const events = options.events;\n const sharedFeed = events !== undefined;\n const matchRef = useRef(match);\n const onEventRef = useRef(onEvent);\n const reconcileBeforeLiveRef = useRef(reconcileBeforeLive);\n useLayoutEffect(() => {\n matchRef.current = match;\n onEventRef.current = onEvent;\n reconcileBeforeLiveRef.current = reconcileBeforeLive;\n }, [match, onEvent, reconcileBeforeLive]);\n const consumedRef = useRef(0);\n const feedKeyRef = useRef<string | null>(null);\n const sharedFeedHasEventsRef = useRef(false);\n\n // Shared-log mode: scan only the unseen tail on every append.\n useEffect(() => {\n if (!sharedFeed || !enabled || !sessionId) {\n return;\n }\n const feedKey = `${workspaceId}\\u0000${sessionId}`;\n const firstSequence = events[0]?.sequence ?? 0;\n if (feedKeyRef.current !== feedKey) {\n feedKeyRef.current = feedKey;\n sharedFeedHasEventsRef.current = events.length > 0;\n // The caller has already loaded its authoritative initial projection.\n // Seed from the shared log's current tail so mounting a large historical\n // session cannot replay every old event as a new live trigger.\n consumedRef.current = events.at(-1)?.sequence ?? 0;\n return;\n }\n const firstNonEmptyBatch = !sharedFeedHasEventsRef.current && events.length > 0;\n if (firstNonEmptyBatch || firstSequence > consumedRef.current + 1) {\n // The usual shared feed mounts empty, then receives a historical tail.\n // A later discontinuity can likewise replace the retained browser\n // window. In either case, reconcile once from the latest relevant event\n // instead of replaying the whole retained window as live traffic.\n sharedFeedHasEventsRef.current = events.length > 0;\n consumedRef.current = events.at(-1)?.sequence ?? consumedRef.current;\n let latestMatch: SessionEvent | undefined;\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n if (event && matchRef.current(event)) {\n latestMatch = event;\n break;\n }\n }\n if (latestMatch) {\n onEventRef.current(latestMatch);\n }\n return;\n }\n sharedFeedHasEventsRef.current ||= events.length > 0;\n for (const event of events) {\n if (event.sequence <= consumedRef.current) {\n continue;\n }\n consumedRef.current = event.sequence;\n if (matchRef.current(event)) {\n onEventRef.current(event);\n }\n }\n }, [sharedFeed, enabled, events, workspaceId, sessionId]);\n\n // Self-stream mode: tail from the session's current lastSequence.\n useEffect(() => {\n if (sharedFeed || !enabled || !sessionId) {\n return;\n }\n const controller = new AbortController();\n void (async () => {\n try {\n const session = await client.getSession(workspaceId, sessionId);\n if (controller.signal.aborted) {\n return;\n }\n const stream = client.streamEvents(workspaceId, sessionId, {\n after: session.lastSequence,\n signal: controller.signal,\n beforeLive: async () => await reconcileBeforeLiveRef.current?.(),\n });\n for await (const event of stream) {\n if (matchRef.current(event)) {\n onEventRef.current(event);\n }\n }\n } catch {\n // Live updates are best-effort: the read hooks still expose refresh()\n // and the initial load already populated state.\n }\n })();\n return () => {\n controller.abort();\n };\n }, [sharedFeed, enabled, client, workspaceId, sessionId]);\n}\n\n/** Debounce rapid event bursts into one trailing call (default 150ms). */\nexport function useDebouncedCallback(callback: () => void, delayMs = 150): () => void {\n const callbackRef = useRef(callback);\n useLayoutEffect(() => {\n callbackRef.current = callback;\n }, [callback]);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n useEffect(() => {\n return () => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n }\n };\n }, []);\n return useCallback(() => {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current);\n }\n timerRef.current = setTimeout(() => {\n timerRef.current = null;\n callbackRef.current();\n }, delayMs);\n }, [delayMs]);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,141 @@
1
+ // src/session-context.ts
2
+ import { createContext, useContext } from "react";
3
+ var OpenGeniContext = createContext(null);
4
+ var NOOP_REGISTER_RECONCILER = () => () => void 0;
5
+ var NOOP_RECONCILE_SESSION = async () => void 0;
6
+ function useEmbeddedClientRefinement(override, requiredMethods, hookName) {
7
+ const context = useContext(OpenGeniContext);
8
+ const candidate = override.client ?? context?.client;
9
+ const workspaceId = override.workspaceId ?? context?.workspaceId;
10
+ if (!candidate || !workspaceId) {
11
+ throw new Error(
12
+ "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
13
+ );
14
+ }
15
+ const methods = candidate;
16
+ const missing = requiredMethods.filter((method) => typeof methods[method] !== "function");
17
+ if (missing.length > 0) {
18
+ throw new Error(
19
+ `@opengeni/react: ${hookName} requires client method${missing.length === 1 ? "" : "s"} ${missing.join(", ")}.`
20
+ );
21
+ }
22
+ return {
23
+ client: candidate,
24
+ workspaceId,
25
+ workspaceControlEvent: context?.workspaceControlEvent ?? null,
26
+ workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
27
+ registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
28
+ reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION
29
+ };
30
+ }
31
+ function eventClientMethods(override, directMethods) {
32
+ const hasSharedEventFeed = "events" in override && override.events !== void 0;
33
+ return hasSharedEventFeed ? directMethods : ["getSession", "streamEvents", ...directMethods];
34
+ }
35
+ function useOpenGeni(override = {}) {
36
+ const context = useContext(OpenGeniContext);
37
+ const client = override.client ?? context?.client;
38
+ const workspaceId = override.workspaceId ?? context?.workspaceId;
39
+ if (!client || !workspaceId) {
40
+ throw new Error(
41
+ "@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook."
42
+ );
43
+ }
44
+ return {
45
+ client,
46
+ workspaceId,
47
+ workspaceControlEvent: context?.workspaceControlEvent ?? null,
48
+ workspaceControlConnectionState: context?.workspaceControlConnectionState ?? "idle",
49
+ registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,
50
+ reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION
51
+ };
52
+ }
53
+ function useEmbeddedSession(override = {}) {
54
+ return useEmbeddedClientRefinement(
55
+ override,
56
+ [
57
+ "getSession",
58
+ "listEvents",
59
+ "streamEvents",
60
+ "getComposerDraft",
61
+ "saveComposerDraft",
62
+ "sendMessage",
63
+ "steerMessage",
64
+ "getQueue",
65
+ "moveQueueItem",
66
+ "editQueueItem",
67
+ "steerQueueItem",
68
+ "deleteQueueItem",
69
+ "pauseSession",
70
+ "resumeSession",
71
+ "sendApprovalDecision"
72
+ ],
73
+ "session hooks"
74
+ );
75
+ }
76
+ function useEmbeddedSessionRead(override = {}) {
77
+ return useEmbeddedClientRefinement(
78
+ override,
79
+ eventClientMethods(override, ["getSession", "updateSession"]),
80
+ "useSession"
81
+ );
82
+ }
83
+ function useEmbeddedGoal(override = {}) {
84
+ return useEmbeddedClientRefinement(
85
+ override,
86
+ eventClientMethods(override, ["getGoal", "updateGoal", "deleteGoal"]),
87
+ "useGoal"
88
+ );
89
+ }
90
+ function useEmbeddedSessionLineage(override = {}) {
91
+ return useEmbeddedClientRefinement(
92
+ override,
93
+ eventClientMethods(override, ["getSessionLineage"]),
94
+ "useSessionLineage"
95
+ );
96
+ }
97
+ function useEmbeddedFileAttachments(override = {}) {
98
+ return useEmbeddedClientRefinement(override, ["uploadFile"], "useFileAttachments");
99
+ }
100
+ function useEmbeddedHumanInputSession(override = {}) {
101
+ return useEmbeddedClientRefinement(
102
+ override,
103
+ eventClientMethods(override, ["listHumanInputRequests", "submitHumanInputResponse"]),
104
+ "useHumanInputRequests"
105
+ );
106
+ }
107
+ function useEmbeddedSessionMcpApprovalPolicy(override = {}) {
108
+ return useEmbeddedClientRefinement(
109
+ override,
110
+ eventClientMethods(override, ["getSession", "updateSessionMcpApprovalPolicy"]),
111
+ "useSessionMcpApprovalPolicy"
112
+ );
113
+ }
114
+ function useEmbeddedRealtimeSession(override = {}) {
115
+ return useEmbeddedClientRefinement(override, [], "realtime hooks");
116
+ }
117
+ function useOpenGeniClient(override = {}) {
118
+ const context = useContext(OpenGeniContext);
119
+ const client = override.client ?? context?.client;
120
+ if (!client) {
121
+ throw new Error(
122
+ "@opengeni/react: no OpenGeni client available. Wrap the tree in <OpenGeniProvider> or pass { client } to the hook."
123
+ );
124
+ }
125
+ return client;
126
+ }
127
+
128
+ export {
129
+ OpenGeniContext,
130
+ useOpenGeni,
131
+ useEmbeddedSession,
132
+ useEmbeddedSessionRead,
133
+ useEmbeddedGoal,
134
+ useEmbeddedSessionLineage,
135
+ useEmbeddedFileAttachments,
136
+ useEmbeddedHumanInputSession,
137
+ useEmbeddedSessionMcpApprovalPolicy,
138
+ useEmbeddedRealtimeSession,
139
+ useOpenGeniClient
140
+ };
141
+ //# sourceMappingURL=chunk-EKZH6IDG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/session-context.ts"],"sourcesContent":["import type { StreamConnectionState, WorkspaceControlEvent } from \"@opengeni/sdk\";\nimport { createContext, useContext } from \"react\";\nimport type {\n EmbeddedFileAttachmentClientLike,\n EmbeddedGoalClientLike,\n EmbeddedHumanInputSessionClientLike,\n EmbeddedRealtimeSessionClientLike,\n EmbeddedSessionClientLike,\n EmbeddedSessionLineageClientLike,\n EmbeddedSessionMcpApprovalPolicyClientLike,\n EmbeddedSessionReadClientLike,\n SessionClientLike,\n} from \"./client\";\n\nexport type OpenGeniContextValue = {\n client: SessionClientLike;\n workspaceId: string;\n workspaceControlEvent: WorkspaceControlEvent | null;\n workspaceControlConnectionState: StreamConnectionState | \"idle\" | \"error\";\n registerSessionReconciler: (\n sessionId: string,\n key: string,\n reconcile: () => Promise<void>,\n ) => () => void;\n reconcileSession: (sessionId: string) => Promise<void>;\n};\n\nexport const OpenGeniContext = createContext<OpenGeniContextValue | null>(null);\n\nconst NOOP_REGISTER_RECONCILER: OpenGeniContextValue[\"registerSessionReconciler\"] = () => () =>\n undefined;\nconst NOOP_RECONCILE_SESSION: OpenGeniContextValue[\"reconcileSession\"] = async () => undefined;\n\nexport type ClientOverride = {\n client?: SessionClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedSessionClientOverride = {\n client?: EmbeddedSessionClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedHumanInputClientOverride = {\n client?: EmbeddedHumanInputSessionClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedSessionMcpApprovalPolicyClientOverride = {\n client?: EmbeddedSessionMcpApprovalPolicyClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedSessionReadClientOverride = {\n client?: EmbeddedSessionReadClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedRealtimeSessionClientOverride = {\n client?: EmbeddedRealtimeSessionClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedGoalClientOverride = {\n client?: EmbeddedGoalClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedSessionLineageClientOverride = {\n client?: EmbeddedSessionLineageClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedFileAttachmentClientOverride = {\n client?: EmbeddedFileAttachmentClientLike | undefined;\n workspaceId?: string | undefined;\n};\n\nexport type EmbeddedSessionContextValue = Omit<OpenGeniContextValue, \"client\"> & {\n client: EmbeddedSessionClientLike;\n};\n\ntype EmbeddedClientOverride<TClient> = {\n client?: TClient | undefined;\n workspaceId?: string | undefined;\n};\n\ntype EmbeddedClientContextValue<TClient> = Omit<OpenGeniContextValue, \"client\"> & {\n client: TClient;\n};\n\nfunction useEmbeddedClientRefinement<TClient extends object>(\n override: EmbeddedClientOverride<TClient>,\n requiredMethods: readonly string[],\n hookName: string,\n): EmbeddedClientContextValue<TClient> {\n const context = useContext(OpenGeniContext);\n const candidate = override.client ?? context?.client;\n const workspaceId = override.workspaceId ?? context?.workspaceId;\n if (!candidate || !workspaceId) {\n throw new Error(\n \"@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook.\",\n );\n }\n const methods = candidate as Record<string, unknown>;\n const missing = requiredMethods.filter((method) => typeof methods[method] !== \"function\");\n if (missing.length > 0) {\n throw new Error(\n `@opengeni/react: ${hookName} requires client method${missing.length === 1 ? \"\" : \"s\"} ${missing.join(\", \")}.`,\n );\n }\n return {\n client: candidate as TClient,\n workspaceId,\n workspaceControlEvent: context?.workspaceControlEvent ?? null,\n workspaceControlConnectionState: context?.workspaceControlConnectionState ?? \"idle\",\n registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,\n reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION,\n };\n}\n\nfunction eventClientMethods(override: object, directMethods: readonly string[]): readonly string[] {\n const hasSharedEventFeed =\n \"events\" in override && (override as { events?: unknown }).events !== undefined;\n return hasSharedEventFeed ? directMethods : [\"getSession\", \"streamEvents\", ...directMethods];\n}\n\n/** Resolve client + workspace from explicit overrides or the provider. */\nexport function useOpenGeni(override: ClientOverride = {}): OpenGeniContextValue {\n const context = useContext(OpenGeniContext);\n const client = override.client ?? context?.client;\n const workspaceId = override.workspaceId ?? context?.workspaceId;\n if (!client || !workspaceId) {\n throw new Error(\n \"@opengeni/react: no OpenGeni client/workspace available. Wrap the tree in <OpenGeniProvider> or pass { client, workspaceId } to the hook.\",\n );\n }\n return {\n client,\n workspaceId,\n workspaceControlEvent: context?.workspaceControlEvent ?? null,\n workspaceControlConnectionState: context?.workspaceControlConnectionState ?? \"idle\",\n registerSessionReconciler: context?.registerSessionReconciler ?? NOOP_REGISTER_RECONCILER,\n reconcileSession: context?.reconcileSession ?? NOOP_RECONCILE_SESSION,\n };\n}\n\n/**\n * Resolve the narrow client required by the session-only hooks. The full\n * provider client is structurally compatible, while an explicit host proxy\n * only needs to expose session/event/composer/queue/control operations.\n */\nexport function useEmbeddedSession(\n override: EmbeddedSessionClientOverride = {},\n): EmbeddedSessionContextValue {\n return useEmbeddedClientRefinement(\n override,\n [\n \"getSession\",\n \"listEvents\",\n \"streamEvents\",\n \"getComposerDraft\",\n \"saveComposerDraft\",\n \"sendMessage\",\n \"steerMessage\",\n \"getQueue\",\n \"moveQueueItem\",\n \"editQueueItem\",\n \"steerQueueItem\",\n \"deleteQueueItem\",\n \"pauseSession\",\n \"resumeSession\",\n \"sendApprovalDecision\",\n ],\n \"session hooks\",\n );\n}\n\n/** Resolve the exact client surface required by `useSession`. */\nexport function useEmbeddedSessionRead(\n override: EmbeddedSessionReadClientOverride = {},\n): EmbeddedClientContextValue<EmbeddedSessionReadClientLike> {\n return useEmbeddedClientRefinement(\n override,\n eventClientMethods(override, [\"getSession\", \"updateSession\"]),\n \"useSession\",\n );\n}\n\n/** Resolve the exact client surface required by `useGoal`. */\nexport function useEmbeddedGoal(\n override: EmbeddedGoalClientOverride = {},\n): EmbeddedClientContextValue<EmbeddedGoalClientLike> {\n return useEmbeddedClientRefinement(\n override,\n eventClientMethods(override, [\"getGoal\", \"updateGoal\", \"deleteGoal\"]),\n \"useGoal\",\n );\n}\n\n/** Resolve the exact client surface required by `useSessionLineage`. */\nexport function useEmbeddedSessionLineage(\n override: EmbeddedSessionLineageClientOverride = {},\n): EmbeddedClientContextValue<EmbeddedSessionLineageClientLike> {\n return useEmbeddedClientRefinement(\n override,\n eventClientMethods(override, [\"getSessionLineage\"]),\n \"useSessionLineage\",\n );\n}\n\n/** Resolve the exact client surface required by `useFileAttachments`. */\nexport function useEmbeddedFileAttachments(\n override: EmbeddedFileAttachmentClientOverride = {},\n): EmbeddedClientContextValue<EmbeddedFileAttachmentClientLike> {\n return useEmbeddedClientRefinement(override, [\"uploadFile\"], \"useFileAttachments\");\n}\n\n/**\n * Resolve the structured-input refinement without widening the baseline\n * session-only proxy contract.\n */\nexport function useEmbeddedHumanInputSession(override: EmbeddedHumanInputClientOverride = {}): Omit<\n EmbeddedSessionContextValue,\n \"client\"\n> & {\n client: EmbeddedHumanInputSessionClientLike;\n} {\n return useEmbeddedClientRefinement(\n override,\n eventClientMethods(override, [\"listHumanInputRequests\", \"submitHumanInputResponse\"]),\n \"useHumanInputRequests\",\n );\n}\n\n/** Resolve the approval-policy refinement without widening session-only hosts. */\nexport function useEmbeddedSessionMcpApprovalPolicy(\n override: EmbeddedSessionMcpApprovalPolicyClientOverride = {},\n): Omit<EmbeddedSessionContextValue, \"client\"> & {\n client: EmbeddedSessionMcpApprovalPolicyClientLike;\n} {\n return useEmbeddedClientRefinement(\n override,\n eventClientMethods(override, [\"getSession\", \"updateSessionMcpApprovalPolicy\"]),\n \"useSessionMcpApprovalPolicy\",\n );\n}\n\n/** Resolve the exact browser/API surface required by `@opengeni/react/realtime`. */\nexport function useEmbeddedRealtimeSession(\n override: EmbeddedRealtimeSessionClientOverride = {},\n): EmbeddedClientContextValue<EmbeddedRealtimeSessionClientLike> {\n // Preserve the existing lazy capability behavior: proxy/test clients may\n // implement only the methods reached by their selected model and lifecycle.\n // The exported structural type remains the complete embedding contract.\n return useEmbeddedClientRefinement(override, [], \"realtime hooks\");\n}\n\n/**\n * Resolve the client only — for hooks that are not workspace-scoped\n * (`useWorkspaces`, `useBillingUsage`).\n */\nexport function useOpenGeniClient(\n override: Pick<ClientOverride, \"client\"> = {},\n): SessionClientLike {\n const context = useContext(OpenGeniContext);\n const client = override.client ?? context?.client;\n if (!client) {\n throw new Error(\n \"@opengeni/react: no OpenGeni client available. Wrap the tree in <OpenGeniProvider> or pass { client } to the hook.\",\n );\n }\n return client;\n}\n"],"mappings":"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","names":[]}
@@ -5,17 +5,19 @@ import {
5
5
  } from "./chunk-4IJCL7YO.js";
6
6
  import {
7
7
  useDebouncedCallback,
8
+ useMutationRunner,
9
+ usePolledValue,
10
+ useSessionEventTrigger
11
+ } from "./chunk-EB67J347.js";
12
+ import {
8
13
  useEmbeddedFileAttachments,
9
14
  useEmbeddedGoal,
10
15
  useEmbeddedHumanInputSession,
11
16
  useEmbeddedSession,
12
17
  useEmbeddedSessionLineage,
13
18
  useEmbeddedSessionMcpApprovalPolicy,
14
- useEmbeddedSessionRead,
15
- useMutationRunner,
16
- usePolledValue,
17
- useSessionEventTrigger
18
- } from "./chunk-7CJOAW3V.js";
19
+ useEmbeddedSessionRead
20
+ } from "./chunk-EKZH6IDG.js";
19
21
 
20
22
  // src/hooks/use-session.ts
21
23
  import { useCallback, useEffect, useMemo, useState } from "react";
@@ -2100,4 +2102,4 @@ export {
2100
2102
  isHumanInputEvent,
2101
2103
  useHumanInputRequests
2102
2104
  };
2103
- //# sourceMappingURL=chunk-FSPDND3P.js.map
2105
+ //# sourceMappingURL=chunk-JALF5FI3.js.map