@opengeni/react 0.37.0 → 0.40.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/src/realtime.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batteries-included realtime voice controls and hooks.
|
|
3
|
+
*
|
|
4
|
+
* Import this subpath lazily when the composer action slot is rendered. The
|
|
5
|
+
* base `@opengeni/react` entry remains SSR/browser safe and does not eagerly
|
|
6
|
+
* load microphone, WebRTC, Gateway, or realtime UI code.
|
|
7
|
+
*/
|
|
8
|
+
export {
|
|
9
|
+
CodexRealtimeControl,
|
|
10
|
+
NewSessionRealtimeControl,
|
|
11
|
+
RealtimeModelPickerMenu,
|
|
12
|
+
RealtimeVoiceControl,
|
|
13
|
+
SessionCodexRealtimeControl,
|
|
14
|
+
SessionRealtimeControl,
|
|
15
|
+
codexRealtimeAdmissionAllowed,
|
|
16
|
+
codexRealtimeAdmissionBlocker,
|
|
17
|
+
useRealtimeModelSelection,
|
|
18
|
+
useSessionCodexRealtime,
|
|
19
|
+
useSessionRealtime,
|
|
20
|
+
useWorkspaceRealtimeModelSelection,
|
|
21
|
+
} from "./realtime/realtime-control";
|
|
22
|
+
export type {
|
|
23
|
+
RealtimeModelOption,
|
|
24
|
+
SessionRealtimeControllerFactory,
|
|
25
|
+
} from "./realtime/realtime-control";
|
|
26
|
+
export type { EmbeddedRealtimeSessionClientLike } from "./client";
|
|
27
|
+
export type { EmbeddedRealtimeSessionClientOverride } from "./session-context";
|
package/src/session-context.ts
CHANGED
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
EmbeddedFileAttachmentClientLike,
|
|
5
5
|
EmbeddedGoalClientLike,
|
|
6
6
|
EmbeddedHumanInputSessionClientLike,
|
|
7
|
+
EmbeddedRealtimeSessionClientLike,
|
|
7
8
|
EmbeddedSessionClientLike,
|
|
8
9
|
EmbeddedSessionLineageClientLike,
|
|
9
10
|
EmbeddedSessionMcpApprovalPolicyClientLike,
|
|
@@ -55,6 +56,11 @@ export type EmbeddedSessionReadClientOverride = {
|
|
|
55
56
|
workspaceId?: string | undefined;
|
|
56
57
|
};
|
|
57
58
|
|
|
59
|
+
export type EmbeddedRealtimeSessionClientOverride = {
|
|
60
|
+
client?: EmbeddedRealtimeSessionClientLike | undefined;
|
|
61
|
+
workspaceId?: string | undefined;
|
|
62
|
+
};
|
|
63
|
+
|
|
58
64
|
export type EmbeddedGoalClientOverride = {
|
|
59
65
|
client?: EmbeddedGoalClientLike | undefined;
|
|
60
66
|
workspaceId?: string | undefined;
|
|
@@ -240,6 +246,16 @@ export function useEmbeddedSessionMcpApprovalPolicy(
|
|
|
240
246
|
);
|
|
241
247
|
}
|
|
242
248
|
|
|
249
|
+
/** Resolve the exact browser/API surface required by `@opengeni/react/realtime`. */
|
|
250
|
+
export function useEmbeddedRealtimeSession(
|
|
251
|
+
override: EmbeddedRealtimeSessionClientOverride = {},
|
|
252
|
+
): EmbeddedClientContextValue<EmbeddedRealtimeSessionClientLike> {
|
|
253
|
+
// Preserve the existing lazy capability behavior: proxy/test clients may
|
|
254
|
+
// implement only the methods reached by their selected model and lifecycle.
|
|
255
|
+
// The exported structural type remains the complete embedding contract.
|
|
256
|
+
return useEmbeddedClientRefinement(override, [], "realtime hooks");
|
|
257
|
+
}
|
|
258
|
+
|
|
243
259
|
/**
|
|
244
260
|
* Resolve the client only — for hooks that are not workspace-scoped
|
|
245
261
|
* (`useWorkspaces`, `useBillingUsage`).
|
package/src/session-ui.ts
CHANGED
|
@@ -7,6 +7,8 @@ export type {
|
|
|
7
7
|
HumanInputFormMessages,
|
|
8
8
|
HumanInputFormProps,
|
|
9
9
|
} from "./components/human-input-form";
|
|
10
|
+
export { HumanInputSurface } from "./components/human-input-surface";
|
|
11
|
+
export type { HumanInputSurfaceProps } from "./components/human-input-surface";
|
|
10
12
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline";
|
|
11
13
|
export type { MessageTimelineProps } from "./components/message-timeline";
|
|
12
14
|
export { BUILT_IN_TURN_SUMMARY_FACET_IDS } from "./timeline/turn-summary";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
/* ----------------------------------------------------------------------------
|
|
4
|
+
Session compute label
|
|
5
|
+
|
|
6
|
+
Optional host-supplied display name for the session's active compute target
|
|
7
|
+
(e.g. "Cloud sandbox" or a Connected Machine name). Used by exec_command
|
|
8
|
+
collapsed previews. The host owns resolution; this package never calls the
|
|
9
|
+
machines API.
|
|
10
|
+
-------------------------------------------------------------------------- */
|
|
11
|
+
|
|
12
|
+
const TimelineComputeLabelContext = createContext<string | null>(null);
|
|
13
|
+
|
|
14
|
+
export const TimelineComputeLabelProvider = TimelineComputeLabelContext.Provider;
|
|
15
|
+
|
|
16
|
+
export function useTimelineComputeLabel(): string | null {
|
|
17
|
+
return useContext(TimelineComputeLabelContext);
|
|
18
|
+
}
|
package/src/timeline/index.ts
CHANGED
|
@@ -95,6 +95,9 @@ export type { FoldRestingState } from "./fold-memory";
|
|
|
95
95
|
// seen activity ids (live tool enter across rail remounts)
|
|
96
96
|
export { SeenActivityIdsProvider, useSeenActivityIds } from "./seen-activity-ids";
|
|
97
97
|
|
|
98
|
+
// session compute label (host-supplied active machine / cloud sandbox name)
|
|
99
|
+
export { TimelineComputeLabelProvider, useTimelineComputeLabel } from "./compute-label";
|
|
100
|
+
|
|
98
101
|
// turn-collapse summary chip
|
|
99
102
|
export { BUILT_IN_TURN_SUMMARY_FACET_IDS, TurnSummary, useTurnSettleOpen } from "./turn-summary";
|
|
100
103
|
export type {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
KeyboardIcon,
|
|
14
14
|
KeyRoundIcon,
|
|
15
15
|
LockIcon,
|
|
16
|
+
MessageCircleQuestionIcon,
|
|
16
17
|
MessagesSquareIcon,
|
|
17
18
|
MessageSquareIcon,
|
|
18
19
|
MousePointer2Icon,
|
|
@@ -28,6 +29,7 @@ import {
|
|
|
28
29
|
} from "lucide-react";
|
|
29
30
|
import type { ReactNode } from "react";
|
|
30
31
|
import { stringifyPayload, tryParseJson } from "../lib/format";
|
|
32
|
+
import { useTimelineComputeLabel } from "./compute-label";
|
|
31
33
|
import {
|
|
32
34
|
applyPatchOpsFromToolItem,
|
|
33
35
|
controlCaret,
|
|
@@ -94,6 +96,14 @@ function RunningPreview({ children }: { children: ReactNode }) {
|
|
|
94
96
|
);
|
|
95
97
|
}
|
|
96
98
|
|
|
99
|
+
/** Prefix a collapsed preview with the host-supplied active compute label. */
|
|
100
|
+
function withComputePreview(label: string | null, preview: string): string {
|
|
101
|
+
if (!label) {
|
|
102
|
+
return preview;
|
|
103
|
+
}
|
|
104
|
+
return `on ${label} · ${preview}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
97
107
|
/* ---- exec_command ---------------------------------------------------------- */
|
|
98
108
|
|
|
99
109
|
function ExecRenderer({ item }: ToolRendererProps) {
|
|
@@ -103,6 +113,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
103
113
|
const running = item.status === "running";
|
|
104
114
|
const out = item.output;
|
|
105
115
|
const title = `$ ${cmd}`;
|
|
116
|
+
const computeLabel = useTimelineComputeLabel();
|
|
106
117
|
|
|
107
118
|
// No output event ever arrived (item.output stays undefined from creation):
|
|
108
119
|
// the turn failed before the output insert — most likely a NUL byte in the
|
|
@@ -116,7 +127,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
116
127
|
title={title}
|
|
117
128
|
titleMono
|
|
118
129
|
chip={{ tone: "bad", text: "failed" }}
|
|
119
|
-
preview="output lost — NUL byte could not be stored"
|
|
130
|
+
preview={withComputePreview(computeLabel, "output lost — NUL byte could not be stored")}
|
|
120
131
|
>
|
|
121
132
|
<BodyNote tone="error">
|
|
122
133
|
output contained a NUL byte and could not be stored; the turn failed on this tool's
|
|
@@ -137,7 +148,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
137
148
|
title={title}
|
|
138
149
|
titleMono
|
|
139
150
|
chip={{ tone: "bad", text: "failed" }}
|
|
140
|
-
preview="tool call failed"
|
|
151
|
+
preview={withComputePreview(computeLabel, "tool call failed")}
|
|
141
152
|
>
|
|
142
153
|
<BodyNote tone="error">the tool call failed with no output.</BodyNote>
|
|
143
154
|
</ActivityDisclosure>
|
|
@@ -146,6 +157,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
146
157
|
|
|
147
158
|
if (running) {
|
|
148
159
|
const streamed = typeof out === "string" ? stripExecBanner(out) : "";
|
|
160
|
+
const runningPreview = streamed ? `${streamed.split("\n").length} lines` : "running…";
|
|
149
161
|
return (
|
|
150
162
|
<ActivityDisclosure
|
|
151
163
|
icon={<TerminalIcon className={ICON_SIZE} />}
|
|
@@ -154,9 +166,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
154
166
|
titleMono
|
|
155
167
|
running
|
|
156
168
|
preview={
|
|
157
|
-
<RunningPreview>
|
|
158
|
-
{streamed ? `${streamed.split("\n").length} lines` : "running…"}
|
|
159
|
-
</RunningPreview>
|
|
169
|
+
<RunningPreview>{withComputePreview(computeLabel, runningPreview)}</RunningPreview>
|
|
160
170
|
}
|
|
161
171
|
>
|
|
162
172
|
{/* The row title is already `$ ${cmd}`; the TermBlock header drops the
|
|
@@ -184,8 +194,9 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
184
194
|
iconTone = "failed";
|
|
185
195
|
}
|
|
186
196
|
|
|
187
|
-
const
|
|
197
|
+
const peek = binary ? "binary output" : tailPeek(stripped) || "(no output)";
|
|
188
198
|
const truncated = execTruncated(text);
|
|
199
|
+
const preview = withComputePreview(computeLabel, truncated ? `⋯ truncated · ${peek}` : peek);
|
|
189
200
|
// Hand TermBlock the FULL stripped output; it owns the tail/show-more slicing.
|
|
190
201
|
const body = binary ? "(binary output suppressed)" : stripped;
|
|
191
202
|
|
|
@@ -198,7 +209,7 @@ function ExecRenderer({ item }: ToolRendererProps) {
|
|
|
198
209
|
{...(chip ? { chip } : {})}
|
|
199
210
|
failed={item.status === "failed"}
|
|
200
211
|
cancelled={item.status === "cancelled"}
|
|
201
|
-
preview={
|
|
212
|
+
preview={preview}
|
|
202
213
|
>
|
|
203
214
|
<TermBlock
|
|
204
215
|
command={null}
|
|
@@ -1254,6 +1265,179 @@ function MemoryProposeRenderer({ item }: ToolRendererProps) {
|
|
|
1254
1265
|
);
|
|
1255
1266
|
}
|
|
1256
1267
|
|
|
1268
|
+
/* ---- request_human_input --------------------------------------------------- */
|
|
1269
|
+
|
|
1270
|
+
function askToolPreview(args: unknown): string | null {
|
|
1271
|
+
const record = args && typeof args === "object" ? (args as Record<string, unknown>) : null;
|
|
1272
|
+
const questions = Array.isArray(record?.questions) ? record.questions : null;
|
|
1273
|
+
if (!questions || questions.length === 0) {
|
|
1274
|
+
return null;
|
|
1275
|
+
}
|
|
1276
|
+
const first = questions[0];
|
|
1277
|
+
if (!first || typeof first !== "object") {
|
|
1278
|
+
return null;
|
|
1279
|
+
}
|
|
1280
|
+
const q = first as Record<string, unknown>;
|
|
1281
|
+
const text =
|
|
1282
|
+
typeof q.label === "string" && q.label.trim()
|
|
1283
|
+
? q.label.trim()
|
|
1284
|
+
: typeof q.prompt === "string" && q.prompt.trim()
|
|
1285
|
+
? q.prompt.trim()
|
|
1286
|
+
: null;
|
|
1287
|
+
if (!text) {
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
const preview = truncatePreview(text, 90);
|
|
1291
|
+
return questions.length > 1 ? `${preview} · ${questions.length} questions` : preview;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
function AskRenderer({ item }: ToolRendererProps) {
|
|
1295
|
+
const args = redactSecrets(parseToolArgs(item.arguments));
|
|
1296
|
+
const preview = askToolPreview(args);
|
|
1297
|
+
const icon = <MessageCircleQuestionIcon className={ICON_SIZE} />;
|
|
1298
|
+
const title = "Ask";
|
|
1299
|
+
|
|
1300
|
+
if (item.status === "running") {
|
|
1301
|
+
return (
|
|
1302
|
+
<ActivityDisclosure
|
|
1303
|
+
icon={icon}
|
|
1304
|
+
iconTone="running"
|
|
1305
|
+
title={title}
|
|
1306
|
+
running
|
|
1307
|
+
preview={
|
|
1308
|
+
preview ? (
|
|
1309
|
+
<RunningPreview>{preview}</RunningPreview>
|
|
1310
|
+
) : (
|
|
1311
|
+
<RunningPreview>Waiting…</RunningPreview>
|
|
1312
|
+
)
|
|
1313
|
+
}
|
|
1314
|
+
>
|
|
1315
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1316
|
+
</ActivityDisclosure>
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
1321
|
+
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1322
|
+
return (
|
|
1323
|
+
<ActivityDisclosure
|
|
1324
|
+
icon={icon}
|
|
1325
|
+
iconTone="failed"
|
|
1326
|
+
title={title}
|
|
1327
|
+
chip={{ tone: "bad", text: "error" }}
|
|
1328
|
+
preview={truncatePreview(outText, 80) || preview || "Error"}
|
|
1329
|
+
>
|
|
1330
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1331
|
+
<PayloadBlock label="Error" value={outText} failed />
|
|
1332
|
+
</ActivityDisclosure>
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
return (
|
|
1337
|
+
<ActivityDisclosure
|
|
1338
|
+
icon={icon}
|
|
1339
|
+
iconTone="muted"
|
|
1340
|
+
title={title}
|
|
1341
|
+
cancelled={item.status === "cancelled"}
|
|
1342
|
+
preview={item.status === "cancelled" ? undefined : (preview ?? undefined)}
|
|
1343
|
+
>
|
|
1344
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1345
|
+
{outText ? <PayloadBlock label="Result" value={outText} /> : null}
|
|
1346
|
+
</ActivityDisclosure>
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/* ---- run_on ---------------------------------------------------------------- */
|
|
1351
|
+
|
|
1352
|
+
function runOnTargetName(output: unknown): string | null {
|
|
1353
|
+
const { text } = unwrapMcpOutput(output);
|
|
1354
|
+
const parsed = tryParseJson(text);
|
|
1355
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1356
|
+
const name = (parsed as { targetName?: unknown }).targetName;
|
|
1357
|
+
if (typeof name === "string" && name.trim()) {
|
|
1358
|
+
return name.trim();
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
return null;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
function runOnOpPreview(args: Record<string, unknown>): string | null {
|
|
1365
|
+
const op = args.op;
|
|
1366
|
+
if (!op || typeof op !== "object" || Array.isArray(op)) {
|
|
1367
|
+
return null;
|
|
1368
|
+
}
|
|
1369
|
+
const record = op as Record<string, unknown>;
|
|
1370
|
+
if (record.kind === "exec" && typeof record.cmd === "string" && record.cmd.trim()) {
|
|
1371
|
+
return `$ ${record.cmd.trim()}`;
|
|
1372
|
+
}
|
|
1373
|
+
if (
|
|
1374
|
+
(record.kind === "read" || record.kind === "write") &&
|
|
1375
|
+
typeof record.path === "string" &&
|
|
1376
|
+
record.path.trim()
|
|
1377
|
+
) {
|
|
1378
|
+
return truncatePreview(record.path.trim(), 72);
|
|
1379
|
+
}
|
|
1380
|
+
return null;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
function RunOnRenderer({ item }: ToolRendererProps) {
|
|
1384
|
+
const parsedArgs = parseToolArgs(item.arguments);
|
|
1385
|
+
const args = redactSecrets(parsedArgs);
|
|
1386
|
+
const targetName = runOnTargetName(item.output);
|
|
1387
|
+
const title = targetName ? `Run on ${targetName}` : "Run on";
|
|
1388
|
+
const opPreview = runOnOpPreview(parsedArgs);
|
|
1389
|
+
const icon = <ServerIcon className={ICON_SIZE} />;
|
|
1390
|
+
|
|
1391
|
+
if (item.status === "running") {
|
|
1392
|
+
return (
|
|
1393
|
+
<ActivityDisclosure
|
|
1394
|
+
icon={icon}
|
|
1395
|
+
iconTone="running"
|
|
1396
|
+
title={title}
|
|
1397
|
+
running
|
|
1398
|
+
preview={
|
|
1399
|
+
opPreview ? (
|
|
1400
|
+
<RunningPreview>{opPreview}</RunningPreview>
|
|
1401
|
+
) : (
|
|
1402
|
+
<RunningPreview>Running…</RunningPreview>
|
|
1403
|
+
)
|
|
1404
|
+
}
|
|
1405
|
+
>
|
|
1406
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1407
|
+
</ActivityDisclosure>
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
1412
|
+
if ((isError || item.status === "failed") && item.status !== "cancelled") {
|
|
1413
|
+
return (
|
|
1414
|
+
<ActivityDisclosure
|
|
1415
|
+
icon={icon}
|
|
1416
|
+
iconTone="failed"
|
|
1417
|
+
title={title}
|
|
1418
|
+
chip={{ tone: "bad", text: "error" }}
|
|
1419
|
+
preview={truncatePreview(outText, 80) || opPreview || "Error"}
|
|
1420
|
+
>
|
|
1421
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1422
|
+
<PayloadBlock label="Error" value={outText} failed />
|
|
1423
|
+
</ActivityDisclosure>
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
return (
|
|
1428
|
+
<ActivityDisclosure
|
|
1429
|
+
icon={icon}
|
|
1430
|
+
iconTone="muted"
|
|
1431
|
+
title={title}
|
|
1432
|
+
cancelled={item.status === "cancelled"}
|
|
1433
|
+
preview={item.status === "cancelled" ? undefined : (opPreview ?? undefined)}
|
|
1434
|
+
>
|
|
1435
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
1436
|
+
{outText ? <PayloadBlock label="Result" value={outText} /> : null}
|
|
1437
|
+
</ActivityDisclosure>
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1257
1441
|
/* ---- generic fallback (first-party MCP, external MCP, unknown) ------------- */
|
|
1258
1442
|
|
|
1259
1443
|
/**
|
|
@@ -1355,42 +1539,45 @@ function truncatePreview(text: string, max: number): string {
|
|
|
1355
1539
|
/** Explicit first-party leaf/prefix → canonical product icons (match nav/pages). */
|
|
1356
1540
|
function GenericToolIcon({ name }: { name: string }) {
|
|
1357
1541
|
const leaf = mcpToolLeaf(name);
|
|
1358
|
-
const Icon =
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
leaf
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1542
|
+
const Icon =
|
|
1543
|
+
leaf === "request_human_input"
|
|
1544
|
+
? MessageCircleQuestionIcon
|
|
1545
|
+
: leaf.startsWith("goal_")
|
|
1546
|
+
? TargetIcon
|
|
1547
|
+
: leaf.startsWith("memory_") ||
|
|
1548
|
+
leaf === "preference_registry_summary" ||
|
|
1549
|
+
leaf === "preference_registry_get"
|
|
1550
|
+
? BrainCircuitIcon
|
|
1551
|
+
: leaf.startsWith("session_") ||
|
|
1552
|
+
leaf === "sessions_list" ||
|
|
1553
|
+
leaf === "set_session_title" ||
|
|
1554
|
+
leaf === "set_other_session_title"
|
|
1555
|
+
? MessagesSquareIcon
|
|
1556
|
+
: leaf.startsWith("sandbox") || leaf === "sandboxes_list" || leaf === "run_on"
|
|
1557
|
+
? ServerIcon
|
|
1558
|
+
: leaf.startsWith("rig_")
|
|
1559
|
+
? ServerCogIcon
|
|
1560
|
+
: leaf.startsWith("scheduled_")
|
|
1561
|
+
? CalendarClockIcon
|
|
1562
|
+
: leaf.startsWith("artifacts_")
|
|
1563
|
+
? PanelsTopLeftIcon
|
|
1564
|
+
: leaf.startsWith("social_")
|
|
1565
|
+
? Share2Icon
|
|
1566
|
+
: leaf.startsWith("slack_")
|
|
1567
|
+
? MessageSquareIcon
|
|
1568
|
+
: leaf.startsWith("github_")
|
|
1569
|
+
? FolderGitIcon
|
|
1570
|
+
: leaf.startsWith("variable_")
|
|
1571
|
+
? BoxIcon
|
|
1572
|
+
: leaf.startsWith("environment_")
|
|
1573
|
+
? KeyRoundIcon
|
|
1574
|
+
: leaf.includes("document") ||
|
|
1575
|
+
leaf.includes("knowledge") ||
|
|
1576
|
+
leaf === "list_document_bases"
|
|
1577
|
+
? FileSearchIcon
|
|
1578
|
+
: leaf === "tool_search" || leaf === "load_skill"
|
|
1579
|
+
? PlugIcon
|
|
1580
|
+
: WrenchIcon;
|
|
1394
1581
|
return <Icon className={ICON_SIZE} />;
|
|
1395
1582
|
}
|
|
1396
1583
|
|
|
@@ -1404,6 +1591,8 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
1404
1591
|
{ match: "rawType", type: "hosted_tool_call", render: WebSearchRenderer },
|
|
1405
1592
|
// First-party sandbox + MCP tools resolve by name (exact or MCP leaf).
|
|
1406
1593
|
{ match: "name", name: "exec_command", render: ExecRenderer },
|
|
1594
|
+
{ match: "name", name: "request_human_input", render: AskRenderer },
|
|
1595
|
+
{ match: "name", name: "run_on", render: RunOnRenderer },
|
|
1407
1596
|
{ match: "name", name: "write_stdin", render: WriteStdinRenderer },
|
|
1408
1597
|
{ match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
|
|
1409
1598
|
{ match: "name", name: "apply_patch", render: ApplyPatchRenderer },
|
package/styles/index.css
CHANGED
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
@theme inline {
|
|
20
20
|
--font-og-sans: var(--og-font-sans);
|
|
21
21
|
--font-og-mono: var(--og-font-mono);
|
|
22
|
+
/* Compatibility aliases still used by published components and the exact
|
|
23
|
+
dropdown primitives shared with the web app. Keep the public stylesheet
|
|
24
|
+
self-contained instead of requiring consumers to copy the app theme. */
|
|
25
|
+
--font-sans: var(--og-font-sans);
|
|
26
|
+
--font-mono: var(--og-font-mono);
|
|
22
27
|
|
|
23
28
|
/* Type ramp — a real 4-step scale (no half-pixel hand-tuning). Every
|
|
24
29
|
timeline string maps to one of these via `text-og-{xs,sm,base,md}`;
|
|
@@ -31,6 +36,8 @@
|
|
|
31
36
|
--text-og-base--line-height: 1.55;
|
|
32
37
|
--text-og-md: 15px;
|
|
33
38
|
--text-og-md--line-height: 1.6;
|
|
39
|
+
--text-2xs: 0.6875rem;
|
|
40
|
+
--text-2xs--line-height: 1rem;
|
|
34
41
|
|
|
35
42
|
--color-og-bg: var(--og-color-bg);
|
|
36
43
|
--color-og-surface-1: var(--og-color-surface-1);
|
|
@@ -44,6 +51,7 @@
|
|
|
44
51
|
|
|
45
52
|
--color-og-accent: var(--og-color-accent);
|
|
46
53
|
--color-og-accent-strong: var(--og-color-accent-strong);
|
|
54
|
+
--color-og-accent-deep: var(--og-color-accent-deep);
|
|
47
55
|
--color-og-accent-fg: var(--og-color-accent-fg);
|
|
48
56
|
--color-og-accent-soft: var(--og-color-accent-soft);
|
|
49
57
|
|
|
@@ -55,11 +63,64 @@
|
|
|
55
63
|
--color-og-status-cancelled: var(--og-color-status-cancelled);
|
|
56
64
|
--color-og-danger: var(--og-color-danger);
|
|
57
65
|
|
|
66
|
+
--color-bg: var(--og-color-bg);
|
|
67
|
+
--color-surface: var(--og-color-surface-1);
|
|
68
|
+
--color-surface-2: var(--og-color-surface-2);
|
|
69
|
+
--color-surface-3: var(--og-color-surface-3);
|
|
70
|
+
--color-border: var(--og-color-border);
|
|
71
|
+
--color-border-strong: var(--og-color-border-strong);
|
|
72
|
+
--color-fg: var(--og-color-fg);
|
|
73
|
+
--color-fg-muted: var(--og-color-fg-muted);
|
|
74
|
+
--color-fg-subtle: var(--og-color-fg-subtle);
|
|
75
|
+
--color-brand: var(--og-color-accent);
|
|
76
|
+
--color-brand-strong: var(--og-color-accent-deep);
|
|
77
|
+
--color-brand-fg: var(--og-color-accent-fg);
|
|
78
|
+
--color-status-queued: var(--og-color-status-queued);
|
|
79
|
+
--color-status-running: var(--og-color-status-running);
|
|
80
|
+
--color-status-idle: var(--og-color-status-idle);
|
|
81
|
+
--color-status-waiting: var(--og-color-status-waiting);
|
|
82
|
+
--color-status-success: var(--og-color-status-idle);
|
|
83
|
+
--color-status-failed: var(--og-color-status-failed);
|
|
84
|
+
--color-status-cancelled: var(--og-color-status-cancelled);
|
|
85
|
+
--color-danger: var(--og-color-danger);
|
|
86
|
+
|
|
87
|
+
--color-background: var(--color-bg);
|
|
88
|
+
--color-foreground: var(--color-fg);
|
|
89
|
+
--color-card: var(--color-surface);
|
|
90
|
+
--color-card-foreground: var(--color-fg);
|
|
91
|
+
--color-popover: var(--color-surface-3);
|
|
92
|
+
--color-popover-foreground: var(--color-fg);
|
|
93
|
+
--color-primary: var(--color-brand-strong);
|
|
94
|
+
--color-primary-foreground: var(--color-brand-fg);
|
|
95
|
+
--color-secondary: var(--color-surface-2);
|
|
96
|
+
--color-secondary-foreground: var(--color-fg);
|
|
97
|
+
--color-muted: var(--color-surface-2);
|
|
98
|
+
--color-muted-foreground: var(--color-fg-muted);
|
|
99
|
+
--color-accent: var(--color-surface-2);
|
|
100
|
+
--color-accent-foreground: var(--color-fg);
|
|
101
|
+
--color-destructive: var(--og-color-danger);
|
|
102
|
+
--color-destructive-filled: color-mix(in oklch, var(--og-color-danger) 70%, black);
|
|
103
|
+
--color-destructive-foreground: var(--og-color-accent-fg);
|
|
104
|
+
--color-input: var(--color-surface-2);
|
|
105
|
+
--color-ring: var(--color-brand);
|
|
106
|
+
--color-sidebar: var(--color-surface);
|
|
107
|
+
--color-sidebar-foreground: var(--color-fg);
|
|
108
|
+
--color-sidebar-primary: var(--color-brand-strong);
|
|
109
|
+
--color-sidebar-primary-foreground: var(--color-brand-fg);
|
|
110
|
+
--color-sidebar-accent: var(--color-surface-2);
|
|
111
|
+
--color-sidebar-accent-foreground: var(--color-fg);
|
|
112
|
+
--color-sidebar-border: var(--color-border);
|
|
113
|
+
--color-sidebar-ring: var(--color-brand);
|
|
114
|
+
|
|
58
115
|
--radius-og-xs: var(--og-radius-xs);
|
|
59
116
|
--radius-og-sm: var(--og-radius-sm);
|
|
60
117
|
--radius-og-md: var(--og-radius-md);
|
|
61
118
|
--radius-og-lg: var(--og-radius-lg);
|
|
62
119
|
--radius-og-xl: var(--og-radius-xl);
|
|
120
|
+
--radius-sm: var(--og-radius-sm);
|
|
121
|
+
--radius-md: var(--og-radius-md);
|
|
122
|
+
--radius-lg: var(--og-radius-lg);
|
|
123
|
+
--radius-xl: var(--og-radius-xl);
|
|
63
124
|
|
|
64
125
|
--shadow-og-sm: var(--og-shadow-sm);
|
|
65
126
|
--shadow-og-md: var(--og-shadow-md);
|
package/styles/tokens.css
CHANGED
|
@@ -107,6 +107,8 @@
|
|
|
107
107
|
--og-session-chrome-rail-pad: 0.2rem;
|
|
108
108
|
--og-session-chrome-panel-pad-x: 0.5rem;
|
|
109
109
|
--og-session-chrome-panel-pad-y: 0.4rem;
|
|
110
|
+
/* Expanded agents/queue/inbox/goal body — scroll inside, never grow unbound. */
|
|
111
|
+
--og-session-chrome-panel-max-height: min(18rem, 40dvh);
|
|
110
112
|
--og-session-chrome-duration: 220ms;
|
|
111
113
|
--og-session-chrome-crossfade-duration: 180ms;
|
|
112
114
|
--og-session-chrome-ease: var(--og-ease-out);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/session-context.ts","../src/hooks/internal.ts"],"sourcesContent":["import type { StreamConnectionState, WorkspaceControlEvent } from \"@opengeni/sdk\";\nimport { createContext, useContext } from \"react\";\nimport type {\n EmbeddedFileAttachmentClientLike,\n EmbeddedGoalClientLike,\n EmbeddedHumanInputSessionClientLike,\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 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/**\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","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":[]}
|