@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.
- package/README.md +88 -2
- package/dist/{chunk-7CJOAW3V.js → chunk-EB67J347.js} +1 -135
- package/dist/chunk-EB67J347.js.map +1 -0
- package/dist/chunk-EKZH6IDG.js +141 -0
- package/dist/chunk-EKZH6IDG.js.map +1 -0
- package/dist/{chunk-FSPDND3P.js → chunk-JALF5FI3.js} +8 -6
- package/dist/{chunk-FSPDND3P.js.map → chunk-JALF5FI3.js.map} +1 -1
- package/dist/{chunk-UCZPNXV3.js → chunk-KR2SK5GJ.js} +1292 -711
- package/dist/chunk-KR2SK5GJ.js.map +1 -0
- package/dist/{chunk-IP22SLO6.js → chunk-OMCFRWHL.js} +28 -441
- package/dist/chunk-OMCFRWHL.js.map +1 -0
- package/dist/{chunk-HJ4OQVGW.js → chunk-Q2NCKWTK.js} +10 -5
- package/dist/chunk-Q2NCKWTK.js.map +1 -0
- package/dist/{chunk-ALA3UGWB.js → chunk-SXIQK54Z.js} +8 -6
- package/dist/{chunk-ALA3UGWB.js.map → chunk-SXIQK54Z.js.map} +1 -1
- package/dist/chunk-WZT5G5OR.js +433 -0
- package/dist/chunk-WZT5G5OR.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/components/chat-composer.d.ts +5 -1
- package/dist/components/human-input-form.d.ts +16 -6
- package/dist/components/human-input-surface.d.ts +19 -0
- package/dist/components/machine-input-display.d.ts +12 -0
- package/dist/components/message-timeline.d.ts +6 -1
- package/dist/components/session-chrome.d.ts +1 -0
- package/dist/components/tip-follow.d.ts +30 -0
- package/dist/composer.js +14 -11
- package/dist/hooks/use-composer.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2246 -2236
- package/dist/index.js.map +1 -1
- package/dist/machines.js +4 -3
- package/dist/realtime/dropdown-menu.d.ts +16 -0
- package/dist/realtime/realtime-control.d.ts +126 -0
- package/dist/realtime.d.ts +11 -0
- package/dist/realtime.js +1023 -0
- package/dist/realtime.js.map +1 -0
- package/dist/session-context.d.ts +7 -1
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +5 -3
- package/dist/session.js +4 -3
- package/dist/timeline/compute-label.d.ts +2 -0
- package/dist/timeline/index.d.ts +1 -0
- package/package.json +7 -2
- package/src/client.ts +13 -0
- package/src/components/chat-composer.tsx +11 -2
- package/src/components/composer-transcription-control.tsx +2 -2
- package/src/components/composer.tsx +14 -5
- package/src/components/human-input-form.tsx +506 -186
- package/src/components/human-input-surface.tsx +80 -0
- package/src/components/machine-input-display.ts +83 -0
- package/src/components/message-timeline.tsx +348 -308
- package/src/components/model-policy-picker.tsx +8 -3
- package/src/components/sandbox-files.tsx +1 -1
- package/src/components/session-chrome.tsx +3 -1
- package/src/components/tip-follow.ts +127 -6
- package/src/hooks/use-composer.ts +16 -13
- package/src/index.ts +4 -0
- package/src/realtime/dropdown-menu.tsx +123 -0
- package/src/realtime/realtime-control.tsx +1178 -0
- package/src/realtime.ts +27 -0
- package/src/session-context.ts +16 -0
- package/src/session-ui.ts +2 -0
- package/src/timeline/compute-label.tsx +18 -0
- package/src/timeline/index.ts +3 -0
- package/src/timeline/tool-renderers.tsx +232 -43
- package/styles/index.css +61 -0
- package/styles/tokens.css +2 -0
- package/dist/chunk-7CJOAW3V.js.map +0 -1
- package/dist/chunk-HJ4OQVGW.js.map +0 -1
- package/dist/chunk-IP22SLO6.js.map +0 -1
- 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
|
|
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. `
|
|
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-
|
|
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":";AACA,SAAS,eAAe,kBAAkB;AA0BnC,IAAM,kBAAkB,cAA2C,IAAI;AAE9E,IAAM,2BAA8E,MAAM,MACxF;AACF,IAAM,yBAAmE,YAAY;AA4DrF,SAAS,4BACP,UACA,iBACA,UACqC;AACrC,QAAM,UAAU,WAAW,eAAe;AAC1C,QAAM,YAAY,SAAS,UAAU,SAAS;AAC9C,QAAM,cAAc,SAAS,eAAe,SAAS;AACrD,MAAI,CAAC,aAAa,CAAC,aAAa;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU;AAChB,QAAM,UAAU,gBAAgB,OAAO,CAAC,WAAW,OAAO,QAAQ,MAAM,MAAM,UAAU;AACxF,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,oBAAoB,QAAQ,0BAA0B,QAAQ,WAAW,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,IAAI,CAAC;AAAA,IAC7G;AAAA,EACF;AACA,SAAO;AAAA,IACL,QAAQ;AAAA,IACR;AAAA,IACA,uBAAuB,SAAS,yBAAyB;AAAA,IACzD,iCAAiC,SAAS,mCAAmC;AAAA,IAC7E,2BAA2B,SAAS,6BAA6B;AAAA,IACjE,kBAAkB,SAAS,oBAAoB;AAAA,EACjD;AACF;AAEA,SAAS,mBAAmB,UAAkB,eAAqD;AACjG,QAAM,qBACJ,YAAY,YAAa,SAAkC,WAAW;AACxE,SAAO,qBAAqB,gBAAgB,CAAC,cAAc,gBAAgB,GAAG,aAAa;AAC7F;AAGO,SAAS,YAAY,WAA2B,CAAC,GAAyB;AAC/E,QAAM,UAAU,WAAW,eAAe;AAC1C,QAAM,SAAS,SAAS,UAAU,SAAS;AAC3C,QAAM,cAAc,SAAS,eAAe,SAAS;AACrD,MAAI,CAAC,UAAU,CAAC,aAAa;AAC3B,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,uBAAuB,SAAS,yBAAyB;AAAA,IACzD,iCAAiC,SAAS,mCAAmC;AAAA,IAC7E,2BAA2B,SAAS,6BAA6B;AAAA,IACjE,kBAAkB,SAAS,oBAAoB;AAAA,EACjD;AACF;AAOO,SAAS,mBACd,WAA0C,CAAC,GACd;AAC7B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAGO,SAAS,uBACd,WAA8C,CAAC,GACY;AAC3D,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,UAAU,CAAC,cAAc,eAAe,CAAC;AAAA,IAC5D;AAAA,EACF;AACF;AAGO,SAAS,gBACd,WAAuC,CAAC,GACY;AACpD,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,UAAU,CAAC,WAAW,cAAc,YAAY,CAAC;AAAA,IACpE;AAAA,EACF;AACF;AAGO,SAAS,0BACd,WAAiD,CAAC,GACY;AAC9D,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,UAAU,CAAC,mBAAmB,CAAC;AAAA,IAClD;AAAA,EACF;AACF;AAGO,SAAS,2BACd,WAAiD,CAAC,GACY;AAC9D,SAAO,4BAA4B,UAAU,CAAC,YAAY,GAAG,oBAAoB;AACnF;AAMO,SAAS,6BAA6B,WAA6C,CAAC,GAKzF;AACA,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,UAAU,CAAC,0BAA0B,0BAA0B,CAAC;AAAA,IACnF;AAAA,EACF;AACF;AAGO,SAAS,oCACd,WAA2D,CAAC,GAG5D;AACA,SAAO;AAAA,IACL;AAAA,IACA,mBAAmB,UAAU,CAAC,cAAc,gCAAgC,CAAC;AAAA,IAC7E;AAAA,EACF;AACF;AAGO,SAAS,2BACd,WAAkD,CAAC,GACY;AAI/D,SAAO,4BAA4B,UAAU,CAAC,GAAG,gBAAgB;AACnE;AAMO,SAAS,kBACd,WAA2C,CAAC,GACzB;AACnB,QAAM,UAAU,WAAW,eAAe;AAC1C,QAAM,SAAS,SAAS,UAAU,SAAS;AAC3C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","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
|
-
|
|
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-
|
|
2105
|
+
//# sourceMappingURL=chunk-JALF5FI3.js.map
|