@opengeni/react 0.15.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +110 -10
  2. package/dist/{chunk-TOJR776I.js → chunk-6BXBGJ3B.js} +45 -369
  3. package/dist/chunk-6BXBGJ3B.js.map +1 -0
  4. package/dist/chunk-6UT5OC3P.js +2133 -0
  5. package/dist/chunk-6UT5OC3P.js.map +1 -0
  6. package/dist/chunk-EBTT3MYN.js +92 -0
  7. package/dist/chunk-EBTT3MYN.js.map +1 -0
  8. package/dist/chunk-HHFA4AFX.js +425 -0
  9. package/dist/chunk-HHFA4AFX.js.map +1 -0
  10. package/dist/chunk-LAZ2A743.js +2393 -0
  11. package/dist/chunk-LAZ2A743.js.map +1 -0
  12. package/dist/chunk-MRSECXRP.js +563 -0
  13. package/dist/chunk-MRSECXRP.js.map +1 -0
  14. package/dist/chunk-TK7G6XLT.js +18 -0
  15. package/dist/chunk-TK7G6XLT.js.map +1 -0
  16. package/dist/composer-DvUaa5ki.d.ts +585 -0
  17. package/dist/composer.d.ts +6 -0
  18. package/dist/composer.js +56 -0
  19. package/dist/composer.js.map +1 -0
  20. package/dist/index.d.ts +1288 -1785
  21. package/dist/index.js +7015 -8806
  22. package/dist/index.js.map +1 -1
  23. package/dist/machines.d.ts +412 -3
  24. package/dist/machines.js +3 -1
  25. package/dist/noto-sans-arabic-loader-IA2T4X2A.js +21 -0
  26. package/dist/noto-sans-arabic-loader-IA2T4X2A.js.map +1 -0
  27. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js +292 -0
  28. package/dist/noto-sans-jp-loader.generated-HFGLYBR3.js.map +1 -0
  29. package/dist/queue-surface-implementation-NQMV2ML3.js +890 -0
  30. package/dist/queue-surface-implementation-NQMV2ML3.js.map +1 -0
  31. package/dist/session-Gd4iVPYw.d.ts +425 -0
  32. package/dist/session-context-DIFFlXKc.d.ts +52 -0
  33. package/dist/session.d.ts +4 -0
  34. package/dist/session.js +50 -0
  35. package/dist/session.js.map +1 -0
  36. package/dist/use-composer-BCfm4mzX.d.ts +112 -0
  37. package/package.json +20 -4
  38. package/src/approvals.ts +5 -3
  39. package/src/client.ts +40 -0
  40. package/src/components/approval-surface.tsx +198 -0
  41. package/src/components/chat-composer.tsx +94 -993
  42. package/src/components/code-editor.tsx +104 -26
  43. package/src/components/command-palette.tsx +8 -2
  44. package/src/components/composer-transcription-control.tsx +211 -0
  45. package/src/components/composer.tsx +1536 -0
  46. package/src/components/desktop-viewer.tsx +3 -3
  47. package/src/components/enrollment-consent.tsx +2 -2
  48. package/src/components/file-browser.tsx +367 -29
  49. package/src/components/human-input-form.tsx +417 -0
  50. package/src/components/machine-card.tsx +1 -1
  51. package/src/components/machines-dashboard.tsx +3 -3
  52. package/src/components/message-timeline.tsx +15 -7
  53. package/src/components/model-picker.tsx +12 -3
  54. package/src/components/pierre-diff.tsx +32 -6
  55. package/src/components/queue-surface-implementation.tsx +1041 -0
  56. package/src/components/queue-surface-state.tsx +94 -0
  57. package/src/components/queue-surface.tsx +41 -559
  58. package/src/components/sandbox-files.tsx +138 -14
  59. package/src/components/sandbox-terminal.tsx +8 -2
  60. package/src/components/sandbox-workspace.tsx +339 -119
  61. package/src/components/workbench-changes.tsx +84 -33
  62. package/src/components/workspace-dock.tsx +188 -18
  63. package/src/composer.ts +60 -0
  64. package/src/hooks/internal.ts +115 -34
  65. package/src/hooks/use-composer.ts +335 -104
  66. package/src/hooks/use-human-input.ts +131 -0
  67. package/src/hooks/use-machine-chip.ts +2 -2
  68. package/src/hooks/use-machines.ts +27 -11
  69. package/src/hooks/use-sandbox-files.ts +310 -62
  70. package/src/hooks/use-sandbox-git.ts +154 -40
  71. package/src/hooks/use-sandbox-terminal.ts +28 -6
  72. package/src/hooks/use-session-capabilities.ts +38 -9
  73. package/src/hooks/use-session-control.ts +37 -8
  74. package/src/hooks/use-session-events.ts +522 -48
  75. package/src/hooks/use-slash-commands.ts +38 -38
  76. package/src/hooks/use-transcription.ts +757 -0
  77. package/src/hooks/use-turn-queue.ts +134 -66
  78. package/src/hooks/use-windowed-sections.ts +2 -2
  79. package/src/hooks/use-workspace-capture.ts +146 -40
  80. package/src/hooks/use-workspace-edit.ts +50 -14
  81. package/src/hooks/use-workspace-sessions.ts +3 -0
  82. package/src/human-input.ts +72 -0
  83. package/src/index.ts +76 -6
  84. package/src/lib/noto-sans-arabic-loader.ts +21 -0
  85. package/src/lib/noto-sans-jp-loader.generated.ts +295 -0
  86. package/src/lib/use-portal-token-style.ts +51 -0
  87. package/src/lib/use-unicode-fonts.ts +57 -0
  88. package/src/provider.tsx +4 -69
  89. package/src/session-context.ts +135 -0
  90. package/src/session.ts +79 -0
  91. package/src/timeline/parsers.ts +35 -0
  92. package/src/timeline/projection.ts +12 -4
  93. package/src/timeline/tool-renderers.tsx +41 -0
  94. package/src/timeline/turn-summary.tsx +2 -2
  95. package/src/timeline/types.ts +16 -8
  96. package/styles/index.css +56 -0
  97. package/styles/index.d.ts +2 -0
  98. package/styles/tokens.css +11 -6
  99. package/styles/tokens.d.ts +2 -0
  100. package/dist/chunk-TOJR776I.js.map +0 -1
  101. package/dist/machines-BpdwuQcD.d.ts +0 -449
@@ -0,0 +1,2133 @@
1
+ import {
2
+ CREDIT_EXHAUSTION_MESSAGE,
3
+ humanizeFailureReason,
4
+ isCreditExhaustion,
5
+ tryParseJson,
6
+ useDebouncedCallback,
7
+ useEmbeddedHumanInputSession,
8
+ useEmbeddedSession,
9
+ useMutationRunner,
10
+ usePolledValue,
11
+ useSessionEventTrigger
12
+ } from "./chunk-HHFA4AFX.js";
13
+
14
+ // src/timeline/projection.ts
15
+ var WORKER_SPAWN_TOOL = "session_create";
16
+ var WORKER_MESSAGE_TOOL = "session_send_message";
17
+ function buildTimeline(events) {
18
+ const items = [];
19
+ const prescan = prescanTurnAnchors(events);
20
+ const ordered = orderTimelineEvents(events, prescan);
21
+ const last = () => items[items.length - 1];
22
+ const closeStreamingTail = () => {
23
+ const open = last();
24
+ if ((open?.kind === "agent-message" || open?.kind === "reasoning") && open.streaming) {
25
+ open.streaming = false;
26
+ }
27
+ };
28
+ const finalizeOpen = (turnId, disposition = "complete") => {
29
+ for (const item of items) {
30
+ if (turnId !== void 0 && "turnId" in item && item.turnId && turnId && item.turnId !== turnId) {
31
+ continue;
32
+ }
33
+ if ((item.kind === "agent-message" || item.kind === "reasoning") && item.streaming) {
34
+ item.streaming = false;
35
+ }
36
+ if ((item.kind === "tool-call" || item.kind === "worker") && item.status === "running") {
37
+ item.status = disposition;
38
+ }
39
+ if (item.kind === "sandbox" && item.status === "running") {
40
+ item.status = disposition;
41
+ }
42
+ }
43
+ };
44
+ for (const event of ordered) {
45
+ const payload = asRecord(event.payload);
46
+ const turnId = event.turnId ?? null;
47
+ switch (event.type) {
48
+ case "user.message": {
49
+ closeStreamingTail();
50
+ const childCompletion = workerCompletionPayload(payload.childCompletion);
51
+ if (childCompletion) {
52
+ items.push({
53
+ kind: "worker-completion",
54
+ id: event.id,
55
+ turnId,
56
+ occurredAt: event.occurredAt,
57
+ childSessionId: childCompletion.childSessionId,
58
+ childStatus: childCompletion.childStatus,
59
+ goalStatus: childCompletion.goalStatus,
60
+ goalText: childCompletion.goalText,
61
+ evidence: childCompletion.evidence,
62
+ pausedReason: childCompletion.pausedReason,
63
+ text: typeof payload.text === "string" ? payload.text : ""
64
+ });
65
+ break;
66
+ }
67
+ items.push({
68
+ kind: "user-message",
69
+ id: event.id,
70
+ text: typeof payload.text === "string" ? payload.text : "",
71
+ resources: resourceRefs(payload.resources),
72
+ tools: toolRefs(payload.tools),
73
+ occurredAt: event.occurredAt
74
+ });
75
+ break;
76
+ }
77
+ case "agent.message.delta": {
78
+ const text = typeof payload.text === "string" ? payload.text : "";
79
+ if (!text) {
80
+ break;
81
+ }
82
+ const open = last();
83
+ if (open?.kind === "agent-message" && open.streaming && open.turnId === turnId) {
84
+ open.text += text;
85
+ break;
86
+ }
87
+ closeStreamingTail();
88
+ items.push({
89
+ kind: "agent-message",
90
+ id: event.id,
91
+ turnId,
92
+ text,
93
+ streaming: true,
94
+ occurredAt: event.occurredAt
95
+ });
96
+ break;
97
+ }
98
+ case "agent.message.completed": {
99
+ const text = typeof payload.text === "string" ? payload.text : "";
100
+ const open = [...items].reverse().find(
101
+ (item) => item.kind === "agent-message" && item.turnId === turnId
102
+ );
103
+ if (open && (open.streaming || !open.text || text === open.text || text.startsWith(open.text))) {
104
+ if (!open.text || text && text.startsWith(open.text)) {
105
+ open.text = text || open.text;
106
+ }
107
+ open.streaming = false;
108
+ break;
109
+ }
110
+ if (text) {
111
+ items.push({
112
+ kind: "agent-message",
113
+ id: event.id,
114
+ turnId,
115
+ text,
116
+ streaming: false,
117
+ occurredAt: event.occurredAt
118
+ });
119
+ }
120
+ break;
121
+ }
122
+ case "agent.reasoning.delta": {
123
+ const text = reasoningText(event.payload);
124
+ if (!text) {
125
+ break;
126
+ }
127
+ const open = last();
128
+ if (open?.kind === "reasoning" && open.streaming && open.turnId === turnId) {
129
+ open.text += text;
130
+ break;
131
+ }
132
+ closeStreamingTail();
133
+ items.push({
134
+ kind: "reasoning",
135
+ id: event.id,
136
+ turnId,
137
+ text,
138
+ streaming: true,
139
+ occurredAt: event.occurredAt
140
+ });
141
+ break;
142
+ }
143
+ case "agent.toolCall.created": {
144
+ const name = typeof payload.name === "string" ? payload.name : "tool";
145
+ const callId = typeof payload.id === "string" ? payload.id : null;
146
+ const args = payload.arguments ?? null;
147
+ closeStreamingTail();
148
+ if (name === WORKER_SPAWN_TOOL || name === WORKER_MESSAGE_TOOL) {
149
+ items.push({
150
+ kind: "worker",
151
+ id: event.id,
152
+ turnId,
153
+ callId,
154
+ action: name === WORKER_SPAWN_TOOL ? "spawn" : "message",
155
+ prompt: workerPrompt(args),
156
+ workerSessionId: extractSessionRef(args),
157
+ status: "running",
158
+ occurredAt: event.occurredAt
159
+ });
160
+ break;
161
+ }
162
+ items.push({
163
+ kind: "tool-call",
164
+ id: event.id,
165
+ turnId,
166
+ callId,
167
+ name,
168
+ arguments: args,
169
+ output: void 0,
170
+ // The provider-native item drives the per-tool renderers (apply_patch
171
+ // operation, computer_call action, web_search providerData, …).
172
+ raw: payload.raw,
173
+ status: "running",
174
+ occurredAt: event.occurredAt
175
+ });
176
+ break;
177
+ }
178
+ case "agent.toolCall.output": {
179
+ const callId = typeof payload.id === "string" ? payload.id : null;
180
+ const target = findOpenCall(items, callId);
181
+ if (!target) {
182
+ break;
183
+ }
184
+ if (target.kind === "worker") {
185
+ target.status = isErrorOutput(payload) ? "failed" : "complete";
186
+ target.workerSessionId = target.workerSessionId ?? extractSessionRef(payload.output);
187
+ break;
188
+ }
189
+ target.status = isErrorOutput(payload) ? "failed" : "complete";
190
+ target.output = payload.output;
191
+ break;
192
+ }
193
+ case "sandbox.operation.started":
194
+ case "sandbox.operation.completed":
195
+ case "sandbox.operation.failed": {
196
+ const name = typeof payload.name === "string" ? payload.name : "sandbox";
197
+ const status = event.type.endsWith(".failed") ? "failed" : event.type.endsWith(".completed") ? "complete" : "running";
198
+ if ((name === "repository-clone" || name === "file-resource-download") && status !== "failed") {
199
+ break;
200
+ }
201
+ const existing = findOpenSandbox(items, name);
202
+ if (existing && status !== "running") {
203
+ existing.status = status;
204
+ if (payload.origin === "created" || payload.origin === "restored" || payload.origin === "resumed") {
205
+ existing.origin = payload.origin;
206
+ }
207
+ const message = failureMessage(payload);
208
+ if (message) {
209
+ existing.output = existing.output ? `${existing.output}
210
+ ${message}` : message;
211
+ }
212
+ break;
213
+ }
214
+ if (!existing) {
215
+ closeStreamingTail();
216
+ items.push({
217
+ kind: "sandbox",
218
+ id: event.id,
219
+ turnId,
220
+ name,
221
+ command: typeof payload.command === "string" ? payload.command : null,
222
+ output: failureMessage(payload) ?? "",
223
+ origin: payload.origin === "created" || payload.origin === "restored" || payload.origin === "resumed" ? payload.origin : null,
224
+ status,
225
+ occurredAt: event.occurredAt
226
+ });
227
+ }
228
+ break;
229
+ }
230
+ case "sandbox.command.output.delta": {
231
+ const text = typeof payload.chunk === "string" ? payload.chunk : typeof payload.text === "string" ? payload.text : typeof payload.output === "string" ? payload.output : "";
232
+ if (!text) {
233
+ break;
234
+ }
235
+ const open = (typeof payload.name === "string" ? findOpenSandbox(items, payload.name) : void 0) ?? [...items].reverse().find(
236
+ (item) => item.kind === "sandbox" && item.status === "running"
237
+ );
238
+ if (open) {
239
+ open.output += text;
240
+ }
241
+ break;
242
+ }
243
+ case "session.status.changed": {
244
+ const status = payload.status;
245
+ if (!isSessionStatus(status)) {
246
+ break;
247
+ }
248
+ if (!ATTENTION_STATUSES.has(status)) {
249
+ break;
250
+ }
251
+ const previous = [...items].reverse().find((item) => item.kind === "session-status");
252
+ if (previous?.status === status) {
253
+ break;
254
+ }
255
+ items.push({ kind: "session-status", id: event.id, status, occurredAt: event.occurredAt });
256
+ break;
257
+ }
258
+ case "session.requiresAction": {
259
+ finalizeOpen(turnId);
260
+ items.push({
261
+ kind: "notice",
262
+ id: event.id,
263
+ tone: "waiting",
264
+ text: "Approval needed \u2014 the turn is paused until someone decides.",
265
+ occurredAt: event.occurredAt
266
+ });
267
+ break;
268
+ }
269
+ case "session.context.compacted": {
270
+ closeStreamingTail();
271
+ const before = typeof payload.estimatedTokensBefore === "number" ? Math.round(payload.estimatedTokensBefore).toLocaleString("en-US") : null;
272
+ const after = typeof payload.estimatedTokensAfter === "number" ? Math.round(payload.estimatedTokensAfter).toLocaleString("en-US") : null;
273
+ items.push({
274
+ kind: "notice",
275
+ id: event.id,
276
+ tone: "waiting",
277
+ text: before && after ? `Active conversation history compacted from approximately ${before} to ${after} tokens.` : "Context compacted so the turn could continue.",
278
+ occurredAt: event.occurredAt
279
+ });
280
+ break;
281
+ }
282
+ case "session.context.compaction.skipped": {
283
+ closeStreamingTail();
284
+ const reason = typeof payload.reason === "string" ? payload.reason : null;
285
+ items.push({
286
+ kind: "notice",
287
+ id: event.id,
288
+ tone: reason === "summarization_failed" ? "failed" : "waiting",
289
+ text: reason === "no_history" ? "Context compaction skipped because there is no active history to compact." : reason === "replacement_not_smaller" ? "Context compaction skipped because the generated checkpoint would not reduce the context." : reason === "replacement_unchanged" ? "Context compaction stopped because it reproduced the same checkpoint without making progress." : reason === "summarization_failed" ? "Context compaction failed without replacing the active conversation history. Request it again to retry." : "Context compaction was not needed.",
290
+ occurredAt: event.occurredAt
291
+ });
292
+ break;
293
+ }
294
+ case "turn.recovery.requested": {
295
+ break;
296
+ }
297
+ case "turn.event.rejected_late": {
298
+ break;
299
+ }
300
+ case "tool.auth_needed":
301
+ case "credential.auth_needed": {
302
+ closeStreamingTail();
303
+ items.push({
304
+ kind: "auth-needed",
305
+ id: event.id,
306
+ turnId,
307
+ providerDomain: typeof payload.providerDomain === "string" ? payload.providerDomain : "",
308
+ connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
309
+ reason: authNeededReason(payload.reason),
310
+ scopes: stringList(payload.scopes),
311
+ resource: typeof payload.resource === "string" ? payload.resource : null,
312
+ toolName: typeof payload.toolName === "string" ? payload.toolName : null,
313
+ authorizationUrl: typeof payload.authorizationUrl === "string" ? payload.authorizationUrl : null,
314
+ occurredAt: event.occurredAt
315
+ });
316
+ break;
317
+ }
318
+ case "turn.completed": {
319
+ if (payload.maintenance === "context_compaction") {
320
+ finalizeOpen(turnId);
321
+ break;
322
+ }
323
+ if (isCreditExhaustionPayload(payload)) {
324
+ finalizeOpen(turnId);
325
+ items.push(turnEndItem(event, "failed", CREDIT_EXHAUSTION_MESSAGE));
326
+ items.push({
327
+ kind: "notice",
328
+ id: event.id,
329
+ tone: "failed",
330
+ text: CREDIT_EXHAUSTION_MESSAGE,
331
+ occurredAt: event.occurredAt
332
+ });
333
+ break;
334
+ }
335
+ finalizeOpen(turnId);
336
+ items.push(turnEndItem(event, "complete", null));
337
+ break;
338
+ }
339
+ case "turn.failed": {
340
+ const hadActivity = hasTurnActivity(items, turnId);
341
+ const failureText = isCreditExhaustionPayload(payload) ? CREDIT_EXHAUSTION_MESSAGE : failureMessage(payload);
342
+ finalizeOpen(turnId, "cancelled");
343
+ items.push(turnEndItem(event, "failed", failureText));
344
+ if (!hadActivity) {
345
+ items.push({
346
+ kind: "notice",
347
+ id: event.id,
348
+ tone: "failed",
349
+ text: failureText ?? "The turn failed.",
350
+ occurredAt: event.occurredAt
351
+ });
352
+ }
353
+ break;
354
+ }
355
+ case "turn.cancelled": {
356
+ if (turnId && !prescan.startedTurnIds.has(turnId)) {
357
+ break;
358
+ }
359
+ const hadActivity = hasTurnActivity(items, turnId);
360
+ finalizeOpen(turnId, "cancelled");
361
+ items.push(turnEndItem(event, "cancelled", null));
362
+ if (!hadActivity) {
363
+ items.push({
364
+ kind: "notice",
365
+ id: event.id,
366
+ tone: "cancelled",
367
+ text: "Interrupted.",
368
+ occurredAt: event.occurredAt
369
+ });
370
+ }
371
+ break;
372
+ }
373
+ case "memory.saved":
374
+ case "memory.corrected": {
375
+ const memory = memoryItem(event.id, event.type, turnId, payload, event.occurredAt);
376
+ if (memory) {
377
+ closeStreamingTail();
378
+ items.push(memory);
379
+ }
380
+ break;
381
+ }
382
+ case "goal.set":
383
+ case "goal.updated":
384
+ case "goal.completed":
385
+ case "goal.paused":
386
+ case "goal.resumed":
387
+ case "goal.cleared":
388
+ case "goal.continuation": {
389
+ items.push({
390
+ kind: "goal",
391
+ id: event.id,
392
+ action: event.type.slice("goal.".length),
393
+ text: goalText(payload),
394
+ occurredAt: event.occurredAt
395
+ });
396
+ break;
397
+ }
398
+ default:
399
+ break;
400
+ }
401
+ }
402
+ return items;
403
+ }
404
+ function isCreditExhaustionPayload(payload) {
405
+ return isCreditExhaustion({
406
+ error: typeof payload.error === "string" ? payload.error : null,
407
+ detail: typeof payload.detail === "string" ? payload.detail : null,
408
+ segmentLimit: typeof payload.segmentLimit === "string" ? payload.segmentLimit : null
409
+ });
410
+ }
411
+ function creditExhaustedFromEvents(events) {
412
+ const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
413
+ for (let index = ordered.length - 1; index >= 0; index -= 1) {
414
+ const event = ordered[index];
415
+ if (event?.type !== "turn.completed" && event?.type !== "turn.failed" && event?.type !== "turn.cancelled") {
416
+ continue;
417
+ }
418
+ return isCreditExhaustionPayload(asRecord(event.payload));
419
+ }
420
+ return false;
421
+ }
422
+ function sessionStatusFromEvents(events) {
423
+ for (let index = events.length - 1; index >= 0; index -= 1) {
424
+ const event = events[index];
425
+ if (event?.type !== "session.status.changed") {
426
+ continue;
427
+ }
428
+ const status = asRecord(event.payload).status;
429
+ if (isSessionStatus(status)) {
430
+ return status;
431
+ }
432
+ }
433
+ return null;
434
+ }
435
+ function isActivityItem(item) {
436
+ switch (item.kind) {
437
+ case "reasoning":
438
+ case "tool-call":
439
+ case "worker":
440
+ case "sandbox":
441
+ case "memory":
442
+ return true;
443
+ default:
444
+ return false;
445
+ }
446
+ }
447
+ function groupTimeline(items) {
448
+ const groups = [];
449
+ for (const item of items) {
450
+ if (isActivityItem(item)) {
451
+ const open = groups[groups.length - 1];
452
+ if (open?.kind === "activity" && open.outcome === void 0) {
453
+ open.items.push(item);
454
+ } else {
455
+ groups.push({ kind: "activity", id: `activity-${item.id}`, items: [item] });
456
+ }
457
+ continue;
458
+ }
459
+ if (item.kind === "turn-end") {
460
+ stampTurnOutcome(groups, item);
461
+ foldSettledTurn(groups, item);
462
+ continue;
463
+ }
464
+ groups.push({ kind: "item", item });
465
+ }
466
+ return groups;
467
+ }
468
+ function prescanTurnAnchors(events) {
469
+ const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
470
+ const queuedTurnByTrigger = /* @__PURE__ */ new Map();
471
+ const startSeqByTrigger = /* @__PURE__ */ new Map();
472
+ const cancelledTurnIds = /* @__PURE__ */ new Set();
473
+ const fallbackSeqByTurn = /* @__PURE__ */ new Map();
474
+ const startedTurnIds = /* @__PURE__ */ new Set();
475
+ for (const event of ordered) {
476
+ const payload = asRecord(event.payload);
477
+ const turnId = event.turnId ?? null;
478
+ if (event.type === "turn.queued") {
479
+ const triggerEventId = typeof payload.triggerEventId === "string" ? payload.triggerEventId : null;
480
+ const queuedTurnId = typeof payload.turnId === "string" ? payload.turnId : turnId;
481
+ if (triggerEventId && queuedTurnId) {
482
+ queuedTurnByTrigger.set(triggerEventId, queuedTurnId);
483
+ }
484
+ continue;
485
+ }
486
+ if (event.type === "turn.started") {
487
+ const triggerEventId = typeof payload.triggerEventId === "string" ? payload.triggerEventId : null;
488
+ if (triggerEventId && !startSeqByTrigger.has(triggerEventId)) {
489
+ startSeqByTrigger.set(triggerEventId, event.sequence);
490
+ }
491
+ if (turnId) {
492
+ startedTurnIds.add(turnId);
493
+ }
494
+ } else if (event.type === "turn.cancelled" && turnId) {
495
+ cancelledTurnIds.add(turnId);
496
+ }
497
+ if (turnId && isTurnExecutionEvidence(event.type)) {
498
+ const previous = fallbackSeqByTurn.get(turnId);
499
+ if (previous === void 0 || event.sequence < previous) {
500
+ fallbackSeqByTurn.set(turnId, event.sequence);
501
+ }
502
+ }
503
+ if (turnId && isTurnExecutionEvidence(event.type)) {
504
+ startedTurnIds.add(turnId);
505
+ }
506
+ }
507
+ for (const [triggerEventId, turnId] of queuedTurnByTrigger) {
508
+ if (!startSeqByTrigger.has(triggerEventId)) {
509
+ const fallbackSeq = fallbackSeqByTurn.get(turnId);
510
+ if (fallbackSeq !== void 0) {
511
+ startSeqByTrigger.set(triggerEventId, fallbackSeq);
512
+ }
513
+ }
514
+ }
515
+ const cancelledBeforeStartTriggers = /* @__PURE__ */ new Set();
516
+ for (const [triggerEventId, turnId] of queuedTurnByTrigger) {
517
+ if (cancelledTurnIds.has(turnId) && !startSeqByTrigger.has(triggerEventId) && !fallbackSeqByTurn.has(turnId)) {
518
+ cancelledBeforeStartTriggers.add(triggerEventId);
519
+ }
520
+ }
521
+ return { queuedTurnByTrigger, startSeqByTrigger, cancelledBeforeStartTriggers, startedTurnIds };
522
+ }
523
+ function orderTimelineEvents(events, prescan) {
524
+ const ordered = [...events].sort((a, b) => a.sequence - b.sequence);
525
+ const insertions = /* @__PURE__ */ new Map();
526
+ for (const event of ordered) {
527
+ if (event.type !== "user.message") {
528
+ continue;
529
+ }
530
+ const queuedTurnId = prescan.queuedTurnByTrigger.get(event.id);
531
+ if (!queuedTurnId) {
532
+ pushInsertion(insertions, event.sequence, event);
533
+ continue;
534
+ }
535
+ if (prescan.cancelledBeforeStartTriggers.has(event.id)) {
536
+ continue;
537
+ }
538
+ const startSeq = prescan.startSeqByTrigger.get(event.id);
539
+ if (startSeq !== void 0) {
540
+ pushInsertion(insertions, startSeq, event);
541
+ continue;
542
+ }
543
+ }
544
+ const projected = [];
545
+ for (const event of ordered) {
546
+ const before = insertions.get(event.sequence);
547
+ if (before) {
548
+ projected.push(...before);
549
+ }
550
+ if (event.type !== "user.message") {
551
+ projected.push(event);
552
+ }
553
+ }
554
+ return projected;
555
+ }
556
+ function pushInsertion(insertions, sequence, event) {
557
+ const bucket = insertions.get(sequence);
558
+ if (bucket) {
559
+ bucket.push(event);
560
+ } else {
561
+ insertions.set(sequence, [event]);
562
+ }
563
+ }
564
+ function isAgentActivityEvent(type) {
565
+ return type.startsWith("agent.") || type.startsWith("sandbox.");
566
+ }
567
+ function isTurnExecutionEvidence(type) {
568
+ return isAgentActivityEvent(type) || type === "turn.completed" || type === "turn.failed" || type === "turn.recovery.requested" || type === "turn.capacity_waiting" || type === "session.requiresAction" || type === "tool.auth_needed" || type === "credential.auth_needed" || type.startsWith("rig.setup.") || type === "codex.capacity.waiting" || type === "codex.capacity.resumed";
569
+ }
570
+ function turnEndItem(event, outcome, failureText) {
571
+ return {
572
+ kind: "turn-end",
573
+ id: `${event.id}-turn-end`,
574
+ turnId: event.turnId ?? null,
575
+ outcome,
576
+ failureText,
577
+ occurredAt: event.occurredAt
578
+ };
579
+ }
580
+ function hasTurnActivity(items, turnId) {
581
+ if (turnId) {
582
+ return items.some((item) => isActivityItem(item) && item.turnId === turnId);
583
+ }
584
+ for (let index = items.length - 1; index >= 0; index -= 1) {
585
+ const item = items[index];
586
+ if (!item || item.kind === "turn-end" || item.kind === "user-message") {
587
+ return false;
588
+ }
589
+ if (isActivityItem(item)) {
590
+ return true;
591
+ }
592
+ }
593
+ return false;
594
+ }
595
+ function stampTurnOutcome(groups, turnEnd) {
596
+ if (turnEnd.turnId === null) {
597
+ const trailing = groups[groups.length - 1];
598
+ if (trailing?.kind === "activity" && trailing.outcome === void 0) {
599
+ applyTurnOutcome(trailing, turnEnd);
600
+ }
601
+ return;
602
+ }
603
+ for (const group of groups) {
604
+ if (group.kind !== "activity" || group.outcome !== void 0) {
605
+ continue;
606
+ }
607
+ if (group.items.some((activity) => activity.turnId === turnEnd.turnId)) {
608
+ applyTurnOutcome(group, turnEnd);
609
+ }
610
+ }
611
+ }
612
+ function applyTurnOutcome(group, turnEnd) {
613
+ if (turnEnd.outcome === "complete") {
614
+ group.outcome = "complete";
615
+ return;
616
+ }
617
+ const hasFailed = group.items.some((item) => "status" in item && item.status === "failed");
618
+ const hasInterrupted = group.items.some(
619
+ (item) => "status" in item && item.status === "cancelled"
620
+ );
621
+ group.outcome = hasFailed ? "failed" : hasInterrupted ? "cancelled" : "complete";
622
+ if (turnEnd.failureText && hasFailed) {
623
+ group.failureText = turnEnd.failureText;
624
+ }
625
+ }
626
+ function foldSettledTurn(groups, turnEnd) {
627
+ let startIndex = groups.length;
628
+ let stoppedAtForeignTurn = false;
629
+ while (startIndex > 0) {
630
+ const previous = groups[startIndex - 1];
631
+ if (isTurnBoundary(previous)) {
632
+ break;
633
+ }
634
+ if (belongsToDifferentTurn(previous, turnEnd.turnId)) {
635
+ stoppedAtForeignTurn = true;
636
+ break;
637
+ }
638
+ startIndex -= 1;
639
+ }
640
+ if (stoppedAtForeignTurn) {
641
+ while (startIndex < groups.length && isBetweenTurnDivider(groups[startIndex])) {
642
+ startIndex += 1;
643
+ }
644
+ }
645
+ const collected = groups.slice(startIndex);
646
+ if (collected.length === 0) {
647
+ return;
648
+ }
649
+ const finalMessage = extractFinalAgentMessage(collected, turnEnd);
650
+ const body = finalMessage ? collected.slice(0, -1) : collected;
651
+ if (body.length === 0) {
652
+ return;
653
+ }
654
+ const firstOccurredAt = groupStartedAt(body[0]) ?? turnEnd.occurredAt;
655
+ const turnGroup = {
656
+ kind: "turn",
657
+ id: `turn-${turnEnd.turnId ?? turnEnd.id}`,
658
+ outcome: turnEnd.outcome,
659
+ startedAt: firstOccurredAt,
660
+ endedAt: turnEnd.occurredAt,
661
+ groups: body
662
+ };
663
+ if (turnEnd.failureText) {
664
+ turnGroup.failureText = turnEnd.failureText;
665
+ }
666
+ groups.splice(
667
+ startIndex,
668
+ collected.length,
669
+ ...finalMessage ? [turnGroup, finalMessage] : [turnGroup]
670
+ );
671
+ }
672
+ function isTurnBoundary(group) {
673
+ return group?.kind === "turn" || group?.kind === "item" && group.item.kind === "user-message";
674
+ }
675
+ function belongsToDifferentTurn(group, turnId) {
676
+ if (!group || !turnId) {
677
+ return false;
678
+ }
679
+ if (group.kind === "activity") {
680
+ return group.items.length > 0 && group.items.every((item) => item.turnId !== null && item.turnId !== turnId);
681
+ }
682
+ return group.kind === "item" && group.item.kind === "agent-message" && group.item.turnId !== null && group.item.turnId !== turnId;
683
+ }
684
+ function isBetweenTurnDivider(group) {
685
+ return group?.kind === "item" && group.item.kind === "session-status" && group.item.status !== "running";
686
+ }
687
+ function extractFinalAgentMessage(groups, turnEnd) {
688
+ const tail = groups[groups.length - 1];
689
+ if (tail?.kind !== "item" || tail.item.kind !== "agent-message" || tail.item.streaming) {
690
+ return null;
691
+ }
692
+ if (tail.item.turnId && turnEnd.turnId && tail.item.turnId !== turnEnd.turnId) {
693
+ return null;
694
+ }
695
+ return tail;
696
+ }
697
+ function groupStartedAt(group) {
698
+ if (!group) {
699
+ return void 0;
700
+ }
701
+ switch (group.kind) {
702
+ case "item":
703
+ return group.item.occurredAt;
704
+ case "activity":
705
+ return group.items[0]?.occurredAt;
706
+ case "turn":
707
+ return group.startedAt;
708
+ }
709
+ }
710
+ function asRecord(value) {
711
+ return value !== null && typeof value === "object" ? value : {};
712
+ }
713
+ var SESSION_STATUSES = [
714
+ "queued",
715
+ "running",
716
+ "idle",
717
+ "requires_action",
718
+ "failed",
719
+ "cancelled"
720
+ ];
721
+ var ATTENTION_STATUSES = /* @__PURE__ */ new Set([
722
+ "requires_action",
723
+ "failed",
724
+ "cancelled"
725
+ ]);
726
+ function resourceRefs(value) {
727
+ if (!Array.isArray(value)) {
728
+ return [];
729
+ }
730
+ return value.filter((entry) => {
731
+ const record = asRecord(entry);
732
+ if (record.kind === "repository") {
733
+ return typeof record.uri === "string" && typeof record.ref === "string";
734
+ }
735
+ return record.kind === "file" && typeof record.fileId === "string";
736
+ });
737
+ }
738
+ function toolRefs(value) {
739
+ if (!Array.isArray(value)) {
740
+ return [];
741
+ }
742
+ return value.filter((entry) => {
743
+ const record = asRecord(entry);
744
+ return record.kind === "mcp" && typeof record.id === "string";
745
+ });
746
+ }
747
+ function workerCompletionPayload(value) {
748
+ const payload = asRecord(value);
749
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
750
+ if (typeof payload.childSessionId !== "string" || !uuidPattern.test(payload.childSessionId) || typeof payload.status !== "string" || payload.status.trim() === "") {
751
+ return null;
752
+ }
753
+ const goal = asRecord(payload.goal);
754
+ return {
755
+ childSessionId: payload.childSessionId,
756
+ childStatus: payload.status,
757
+ goalStatus: typeof goal.status === "string" ? goal.status : null,
758
+ goalText: typeof goal.text === "string" ? goal.text : null,
759
+ evidence: typeof goal.evidence === "string" ? goal.evidence : null,
760
+ // Console pauses and several server paths put the human explanation on
761
+ // `rationale`, not `pausedReason` — same fallback the goal pill uses, so a
762
+ // paused worker card never shows "Worker paused" with the reason missing.
763
+ pausedReason: typeof goal.pausedReason === "string" ? goal.pausedReason : typeof goal.rationale === "string" ? goal.rationale : null
764
+ };
765
+ }
766
+ function isSessionStatus(value) {
767
+ return typeof value === "string" && SESSION_STATUSES.includes(value);
768
+ }
769
+ function isErrorOutput(payload) {
770
+ if (payload.error === true || payload.failed === true) {
771
+ return true;
772
+ }
773
+ const output = payload.output;
774
+ return !!output && typeof output === "object" && output.isError === true;
775
+ }
776
+ function findOpenCall(items, callId) {
777
+ const reversed = [...items].reverse();
778
+ const isCall = (item) => item.kind === "tool-call" || item.kind === "worker";
779
+ if (callId) {
780
+ const byId = reversed.find((item) => isCall(item) && item.callId === callId);
781
+ if (byId) {
782
+ return byId;
783
+ }
784
+ }
785
+ return reversed.find(
786
+ (item) => isCall(item) && item.status === "running"
787
+ );
788
+ }
789
+ function findOpenSandbox(items, name) {
790
+ return [...items].reverse().find(
791
+ (item) => item.kind === "sandbox" && item.name === name && item.status === "running"
792
+ );
793
+ }
794
+ function failureMessage(payload) {
795
+ for (const key of ["error", "message"]) {
796
+ const value = payload[key];
797
+ if (typeof value === "string" && value.trim().length > 0) {
798
+ return humanizeFailureReason(value);
799
+ }
800
+ }
801
+ return null;
802
+ }
803
+ function goalText(payload) {
804
+ if (typeof payload.text === "string" && payload.text) {
805
+ return payload.text;
806
+ }
807
+ const goal = asRecord(payload.goal);
808
+ if (typeof goal.text === "string" && goal.text) {
809
+ return goal.text;
810
+ }
811
+ if (typeof payload.prompt === "string" && payload.prompt) {
812
+ return payload.prompt;
813
+ }
814
+ return null;
815
+ }
816
+ function memoryItem(id, type, turnId, payload, occurredAt) {
817
+ const memoryId = typeof payload.memoryId === "string" && payload.memoryId ? payload.memoryId : null;
818
+ if (!memoryId) {
819
+ return null;
820
+ }
821
+ const replacementPreview = typeof payload.replacementPreview === "string" ? payload.replacementPreview : void 0;
822
+ const replacementMemoryId = typeof payload.replacementMemoryId === "string" ? payload.replacementMemoryId : void 0;
823
+ const action = typeof payload.action === "string" ? payload.action : void 0;
824
+ return {
825
+ kind: "memory",
826
+ id,
827
+ turnId,
828
+ variant: type === "memory.corrected" ? "corrected" : "saved",
829
+ memoryKind: typeof payload.kind === "string" ? payload.kind : "",
830
+ preview: typeof payload.preview === "string" ? payload.preview : "",
831
+ ...payload.deduped === true ? { deduped: true } : {},
832
+ ...replacementPreview ? { replacementPreview } : {},
833
+ ...action ? { action } : {},
834
+ memoryId,
835
+ ...replacementMemoryId ? { replacementMemoryId } : {},
836
+ occurredAt
837
+ };
838
+ }
839
+ var AUTH_NEEDED_REASONS = /* @__PURE__ */ new Set([
840
+ "missing_connection",
841
+ "expired",
842
+ "insufficient_scope",
843
+ "refresh_failed",
844
+ "unsupported_auth",
845
+ "resource_scope_unavailable"
846
+ ]);
847
+ function authNeededReason(value) {
848
+ return typeof value === "string" && AUTH_NEEDED_REASONS.has(value) ? value : null;
849
+ }
850
+ function stringList(value) {
851
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [];
852
+ }
853
+ function reasoningText(payload) {
854
+ const record = asRecord(payload);
855
+ if (typeof record.text === "string") {
856
+ return record.text;
857
+ }
858
+ const content = asRecord(asRecord(record.item).rawItem).content;
859
+ if (!Array.isArray(content)) {
860
+ return "";
861
+ }
862
+ return content.map((part) => {
863
+ const text = asRecord(part).text;
864
+ return typeof text === "string" ? text : "";
865
+ }).join("");
866
+ }
867
+ function workerPrompt(args) {
868
+ const record = asRecord(typeof args === "string" ? tryParseJson(args) : args);
869
+ for (const key of ["initialMessage", "message", "text", "prompt"]) {
870
+ const value = record[key];
871
+ if (typeof value === "string" && value.trim().length > 0) {
872
+ return value;
873
+ }
874
+ }
875
+ return null;
876
+ }
877
+ function extractSessionRef(value, depth = 0) {
878
+ if (depth > 6 || value === null || value === void 0) {
879
+ return null;
880
+ }
881
+ if (typeof value === "string") {
882
+ return extractSessionRef(tryParseJson(value), depth + 1);
883
+ }
884
+ if (Array.isArray(value)) {
885
+ for (const entry of value) {
886
+ const found = extractSessionRef(entry, depth + 1);
887
+ if (found) {
888
+ return found;
889
+ }
890
+ }
891
+ return null;
892
+ }
893
+ if (typeof value !== "object") {
894
+ return null;
895
+ }
896
+ const record = value;
897
+ if (typeof record.sessionId === "string" && looksLikeId(record.sessionId)) {
898
+ return record.sessionId;
899
+ }
900
+ if (typeof record.id === "string" && looksLikeId(record.id) && ("status" in record || "workspaceId" in record || "initialMessage" in record)) {
901
+ return record.id;
902
+ }
903
+ for (const key of ["structuredContent", "session", "result", "content"]) {
904
+ if (key in record) {
905
+ const found = extractSessionRef(record[key], depth + 1);
906
+ if (found) {
907
+ return found;
908
+ }
909
+ }
910
+ }
911
+ if (typeof record.text === "string") {
912
+ return extractSessionRef(tryParseJson(record.text), depth + 1);
913
+ }
914
+ return null;
915
+ }
916
+ function looksLikeId(value) {
917
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
918
+ }
919
+ function toolDisplayName(name) {
920
+ const boundary = name.indexOf("__");
921
+ const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
922
+ return toolPart.replace(/[_-]+/g, " ").trim();
923
+ }
924
+
925
+ // src/hooks/use-session-events.ts
926
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
927
+ var INITIAL_TAIL_PAGE_SIZE = 1e3;
928
+ var OLDER_PAGE_SIZE = 5e3;
929
+ var INITIAL_FETCH_CAP = 1;
930
+ var OLDER_GROUP_TARGET = 32;
931
+ var OLDER_FETCH_CAP = 2;
932
+ var BOUNDARY_PAGE_CAP = 4;
933
+ var EMPTY_EVENTS = [];
934
+ var encoder = new TextEncoder();
935
+ var decoder = new TextDecoder();
936
+ var BROWSER_EVENT_TYPE_MAX_BYTES = 256;
937
+ var BROWSER_EVENT_ID_MAX_BYTES = 256;
938
+ var BROWSER_EVENT_CLIENT_ID_MAX_BYTES = 4 * 1024;
939
+ var BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES = 4 * 1024;
940
+ var BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES = 48 * 1024;
941
+ var BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS = [
942
+ "id",
943
+ "callId",
944
+ "call_id",
945
+ "name",
946
+ "toolName",
947
+ "status",
948
+ "code",
949
+ "isError",
950
+ "stream",
951
+ "commandId",
952
+ "sequence",
953
+ "coalescedUntil",
954
+ "coalescedCount",
955
+ "firstSequence",
956
+ "lastSequence"
957
+ ];
958
+ var SESSION_EVENT_BROWSER_MAX_BYTES = 8 * 1024 * 1024;
959
+ var SESSION_EVENT_BROWSER_MAX_COUNT = 1e4;
960
+ var SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES = 96 * 1024;
961
+ var SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
962
+ var SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
963
+ var EMPTY_EVENT_WINDOW = {
964
+ events: EMPTY_EVENTS,
965
+ bytes: 2,
966
+ truncated: false
967
+ };
968
+ function useSessionEvents(sessionId, options = {}) {
969
+ const { client, workspaceId, reconcileSession } = useEmbeddedSession(options);
970
+ const enabled = options.enabled ?? true;
971
+ const after = options.after ?? 0;
972
+ const replay = options.replay ?? "windowed";
973
+ const fullReplay = replay === "full" || after !== 0;
974
+ const streamKey = `${workspaceId}\0${sessionId ?? ""}\0${after}\0${fullReplay ? "full" : "windowed"}`;
975
+ const [eventWindow, setEventWindow] = useState(EMPTY_EVENT_WINDOW);
976
+ const [connectionState, setConnectionState] = useState("idle");
977
+ const [error, setError] = useState(null);
978
+ const [hasOlder, setHasOlder] = useState(false);
979
+ const [sessionStatusProjection, setSessionStatusProjection] = useState(
980
+ null
981
+ );
982
+ const [initialLoading, setInitialLoading] = useState(true);
983
+ const [loadingOlder, setLoadingOlder] = useState(false);
984
+ const [streamEpoch, setStreamEpoch] = useState(0);
985
+ const lastSequenceRef = useRef(after);
986
+ const streamResumeSequenceRef = useRef(after);
987
+ const oldestSequenceRef = useRef(null);
988
+ const hasOlderRef = useRef(false);
989
+ const loadingOlderRef = useRef(false);
990
+ const initialWindowLoadedRef = useRef(false);
991
+ const streamAbortRef = useRef(null);
992
+ const streamKeyRef = useRef(null);
993
+ const generationRef = useRef(0);
994
+ const eventWindowRef = useRef(EMPTY_EVENT_WINDOW);
995
+ const sessionStatusRef = useRef({
996
+ sequence: after,
997
+ status: null
998
+ });
999
+ const [stateStreamKey, setStateStreamKey] = useState(streamKey);
1000
+ useEffect(() => {
1001
+ if (streamKeyRef.current !== streamKey) {
1002
+ streamKeyRef.current = streamKey;
1003
+ setStateStreamKey(streamKey);
1004
+ eventWindowRef.current = EMPTY_EVENT_WINDOW;
1005
+ setEventWindow(EMPTY_EVENT_WINDOW);
1006
+ setError(null);
1007
+ setHasOlder(false);
1008
+ sessionStatusRef.current = { sequence: after, status: null };
1009
+ setSessionStatusProjection(null);
1010
+ setLoadingOlder(false);
1011
+ setInitialLoading(true);
1012
+ lastSequenceRef.current = after;
1013
+ streamResumeSequenceRef.current = after;
1014
+ oldestSequenceRef.current = null;
1015
+ hasOlderRef.current = false;
1016
+ loadingOlderRef.current = false;
1017
+ initialWindowLoadedRef.current = false;
1018
+ }
1019
+ const generation = generationRef.current + 1;
1020
+ generationRef.current = generation;
1021
+ if (loadingOlderRef.current) {
1022
+ loadingOlderRef.current = false;
1023
+ setLoadingOlder(false);
1024
+ }
1025
+ if (!sessionId || !enabled) {
1026
+ setConnectionState("idle");
1027
+ return;
1028
+ }
1029
+ const controller = new AbortController();
1030
+ const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
1031
+ streamAbortRef.current = controller;
1032
+ let pending = [];
1033
+ let pendingBytes = 2;
1034
+ let flushTimer = null;
1035
+ const flush = () => {
1036
+ if (flushTimer !== null) {
1037
+ clearTimeout(flushTimer);
1038
+ flushTimer = null;
1039
+ }
1040
+ if (!isCurrent()) {
1041
+ pending = [];
1042
+ pendingBytes = 2;
1043
+ return;
1044
+ }
1045
+ if (pending.length === 0) {
1046
+ return;
1047
+ }
1048
+ const batch = pending;
1049
+ pending = [];
1050
+ pendingBytes = 2;
1051
+ const lastInBatch = batch[batch.length - 1];
1052
+ if (lastInBatch) {
1053
+ const batchResumeSequence = Math.max(...batch.map(eventResumeSequence));
1054
+ lastSequenceRef.current = Math.max(lastSequenceRef.current, batchResumeSequence);
1055
+ streamResumeSequenceRef.current = Math.max(
1056
+ streamResumeSequenceRef.current,
1057
+ batchResumeSequence
1058
+ );
1059
+ }
1060
+ observeSessionStatus(batch, sessionStatusRef, setSessionStatusProjection);
1061
+ const current = eventWindowRef.current;
1062
+ const next = boundBrowserSessionEventWindow([...current.events, ...batch]);
1063
+ const retained = {
1064
+ ...next,
1065
+ truncated: current.truncated || next.truncated
1066
+ };
1067
+ eventWindowRef.current = retained;
1068
+ setEventWindow(retained);
1069
+ oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
1070
+ if (retained.truncated) {
1071
+ hasOlderRef.current = true;
1072
+ setHasOlder(true);
1073
+ }
1074
+ };
1075
+ const scheduleFlush = () => {
1076
+ if (!isCurrent()) return;
1077
+ flushTimer ??= setTimeout(flush, 16);
1078
+ };
1079
+ void (async () => {
1080
+ try {
1081
+ if (!fullReplay && !initialWindowLoadedRef.current) {
1082
+ setConnectionState("connecting");
1083
+ const window = await loadEventWindow(client, workspaceId, sessionId, {
1084
+ before: Number.MAX_SAFE_INTEGER,
1085
+ pageSize: INITIAL_TAIL_PAGE_SIZE,
1086
+ targetGroups: Number.POSITIVE_INFINITY,
1087
+ maxFetches: INITIAL_FETCH_CAP,
1088
+ signal: controller.signal
1089
+ });
1090
+ if (!isCurrent()) {
1091
+ return;
1092
+ }
1093
+ observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
1094
+ const retained = boundBrowserSessionEventWindow(window.events);
1095
+ eventWindowRef.current = retained;
1096
+ setEventWindow(retained);
1097
+ oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
1098
+ hasOlderRef.current = window.hasOlder || retained.truncated;
1099
+ lastSequenceRef.current = window.newestSequence;
1100
+ streamResumeSequenceRef.current = window.newestSequence;
1101
+ initialWindowLoadedRef.current = true;
1102
+ setHasOlder(window.hasOlder || retained.truncated);
1103
+ setInitialLoading(false);
1104
+ }
1105
+ if (fullReplay) {
1106
+ setInitialLoading(false);
1107
+ }
1108
+ const stream = client.streamEvents(workspaceId, sessionId, {
1109
+ after: streamResumeSequenceRef.current,
1110
+ signal: controller.signal,
1111
+ beforeLive: async () => await reconcileSession(sessionId),
1112
+ onStateChange: (state) => {
1113
+ if (isCurrent()) {
1114
+ setConnectionState(state);
1115
+ }
1116
+ }
1117
+ });
1118
+ for await (const event of stream) {
1119
+ if (!isCurrent()) break;
1120
+ const boundedEvent = boundBrowserLegacyEvent(event);
1121
+ const boundedEventBytes = browserJsonBytes(boundedEvent);
1122
+ const separatorBytes = pending.length === 0 ? 0 : 1;
1123
+ if (pending.length > 0 && (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes + separatorBytes + boundedEventBytes > SESSION_EVENT_BROWSER_PENDING_MAX_BYTES)) {
1124
+ flush();
1125
+ }
1126
+ pending.push(boundedEvent);
1127
+ pendingBytes += (pending.length === 1 ? 0 : 1) + boundedEventBytes;
1128
+ if (pending.length >= SESSION_EVENT_BROWSER_PENDING_MAX_COUNT || pendingBytes >= SESSION_EVENT_BROWSER_PENDING_MAX_BYTES) {
1129
+ flush();
1130
+ } else {
1131
+ scheduleFlush();
1132
+ }
1133
+ }
1134
+ if (isCurrent()) {
1135
+ flush();
1136
+ setConnectionState("ended");
1137
+ }
1138
+ } catch (cause) {
1139
+ if (isCurrent()) {
1140
+ flush();
1141
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
1142
+ setConnectionState("error");
1143
+ }
1144
+ }
1145
+ })();
1146
+ return () => {
1147
+ controller.abort();
1148
+ if (generationRef.current === generation) {
1149
+ generationRef.current = generation + 1;
1150
+ }
1151
+ if (streamAbortRef.current === controller) {
1152
+ streamAbortRef.current = null;
1153
+ }
1154
+ if (flushTimer !== null) {
1155
+ clearTimeout(flushTimer);
1156
+ }
1157
+ };
1158
+ }, [
1159
+ client,
1160
+ workspaceId,
1161
+ sessionId,
1162
+ after,
1163
+ enabled,
1164
+ fullReplay,
1165
+ streamKey,
1166
+ streamEpoch,
1167
+ reconcileSession
1168
+ ]);
1169
+ const loadOlder = useCallback(async () => {
1170
+ if (!sessionId || loadingOlderRef.current || !hasOlderRef.current) {
1171
+ return false;
1172
+ }
1173
+ const before = oldestSequenceRef.current;
1174
+ if (before === null) {
1175
+ hasOlderRef.current = false;
1176
+ setHasOlder(false);
1177
+ return false;
1178
+ }
1179
+ const generation = generationRef.current;
1180
+ loadingOlderRef.current = true;
1181
+ setLoadingOlder(true);
1182
+ try {
1183
+ const window = await loadEventWindow(client, workspaceId, sessionId, {
1184
+ before,
1185
+ pageSize: OLDER_PAGE_SIZE,
1186
+ targetGroups: OLDER_GROUP_TARGET,
1187
+ maxFetches: OLDER_FETCH_CAP
1188
+ });
1189
+ if (generationRef.current !== generation) {
1190
+ return false;
1191
+ }
1192
+ if (window.events.length === 0) {
1193
+ oldestSequenceRef.current = null;
1194
+ hasOlderRef.current = false;
1195
+ setHasOlder(false);
1196
+ return false;
1197
+ }
1198
+ const current = eventWindowRef.current;
1199
+ assertPrependOrder(current.events, window.events);
1200
+ streamAbortRef.current?.abort();
1201
+ observeSessionStatus(window.events, sessionStatusRef, setSessionStatusProjection);
1202
+ const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
1203
+ direction: "oldest"
1204
+ });
1205
+ const retained = {
1206
+ ...next,
1207
+ truncated: current.truncated || next.truncated
1208
+ };
1209
+ const retainedOldest = retained.events[0]?.sequence ?? null;
1210
+ if (retainedOldest === null || retainedOldest >= before) {
1211
+ throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
1212
+ }
1213
+ eventWindowRef.current = retained;
1214
+ setEventWindow(retained);
1215
+ oldestSequenceRef.current = retainedOldest;
1216
+ streamResumeSequenceRef.current = maxResumeSequence(retained.events);
1217
+ const olderStillAvailable = window.hasOlder;
1218
+ hasOlderRef.current = olderStillAvailable;
1219
+ setHasOlder(olderStillAvailable);
1220
+ setStreamEpoch((epoch) => epoch + 1);
1221
+ return olderStillAvailable;
1222
+ } finally {
1223
+ if (generationRef.current === generation) {
1224
+ loadingOlderRef.current = false;
1225
+ setLoadingOlder(false);
1226
+ }
1227
+ }
1228
+ }, [client, workspaceId, sessionId]);
1229
+ const identityMatches = stateStreamKey === streamKey;
1230
+ const visibleEvents = identityMatches ? eventWindow.events : EMPTY_EVENTS;
1231
+ const timeline = useMemo(() => buildTimeline(visibleEvents), [visibleEvents]);
1232
+ return {
1233
+ events: visibleEvents,
1234
+ timeline,
1235
+ sessionStatus: identityMatches ? sessionStatusProjection : null,
1236
+ connectionState: identityMatches ? connectionState : "idle",
1237
+ lastSequence: identityMatches ? lastSequenceRef.current : after,
1238
+ windowBytes: identityMatches ? eventWindow.bytes : 2,
1239
+ windowTruncated: identityMatches ? eventWindow.truncated : false,
1240
+ initialLoading: fullReplay ? false : identityMatches ? initialLoading : true,
1241
+ hasOlder: !identityMatches ? false : hasOlder,
1242
+ loadingOlder: !identityMatches ? false : loadingOlder,
1243
+ loadOlder,
1244
+ error: identityMatches ? error : null
1245
+ };
1246
+ }
1247
+ function boundBrowserSessionEventWindow(events, options = {}) {
1248
+ const maxBytes = Math.max(1024, options.maxBytes ?? SESSION_EVENT_BROWSER_MAX_BYTES);
1249
+ const maxCount = Math.max(1, Math.floor(options.maxCount ?? SESSION_EVENT_BROWSER_MAX_COUNT));
1250
+ const safe = events.map(boundBrowserLegacyEvent);
1251
+ const selected = [];
1252
+ let bytes = 2;
1253
+ const direction = options.direction ?? "newest";
1254
+ const start = direction === "newest" ? safe.length - 1 : 0;
1255
+ const end = direction === "newest" ? Math.max(-1, safe.length - maxCount - 1) : safe.length;
1256
+ const step = direction === "newest" ? -1 : 1;
1257
+ for (let index = start; index !== end && selected.length < maxCount; index += step) {
1258
+ const event = safe[index];
1259
+ const eventBytes = browserJsonBytes(event);
1260
+ const separator = selected.length === 0 ? 0 : 1;
1261
+ if (bytes + separator + eventBytes > maxBytes) break;
1262
+ selected.push(event);
1263
+ bytes += separator + eventBytes;
1264
+ }
1265
+ if (direction === "newest") selected.reverse();
1266
+ return {
1267
+ events: selected,
1268
+ bytes,
1269
+ truncated: selected.length < safe.length
1270
+ };
1271
+ }
1272
+ function boundBrowserLegacyEvent(event) {
1273
+ const serialized = browserSerialize(event);
1274
+ const originalBytes = serialized.serializable ? encoder.encode(serialized.value).byteLength : null;
1275
+ const typeIsSafe = browserUtf8Bytes(event.type) <= BROWSER_EVENT_TYPE_MAX_BYTES && !event.type.includes("\n") && !event.type.includes("\r");
1276
+ const clientEventId = boundBrowserOptionalText(
1277
+ event.clientEventId,
1278
+ BROWSER_EVENT_CLIENT_ID_MAX_BYTES
1279
+ );
1280
+ const duplicateReason = boundBrowserOptionalText(
1281
+ event.duplicateReason,
1282
+ BROWSER_EVENT_DUPLICATE_REASON_MAX_BYTES
1283
+ );
1284
+ if (serialized.serializable && originalBytes !== null && originalBytes <= SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES && typeIsSafe && clientEventId === event.clientEventId && duplicateReason === event.duplicateReason) {
1285
+ return event;
1286
+ }
1287
+ const envelopeProjection = [
1288
+ !typeIsSafe ? browserEnvelopeFieldProjection("type", event.type, "session.event.envelope_omitted") : null,
1289
+ clientEventId !== event.clientEventId ? browserEnvelopeFieldProjection("clientEventId", event.clientEventId, clientEventId) : null,
1290
+ duplicateReason !== event.duplicateReason ? browserEnvelopeFieldProjection("duplicateReason", event.duplicateReason, duplicateReason) : null
1291
+ ].filter((field) => field !== null);
1292
+ const payloadSerialization = browserSerialize(event.payload);
1293
+ const payloadBytes = payloadSerialization.serializable ? encoder.encode(payloadSerialization.value).byteLength : null;
1294
+ const preview = truncateBrowserUtf8Middle(
1295
+ payloadSerialization.value,
1296
+ BROWSER_EVENT_PAYLOAD_PREVIEW_MAX_BYTES
1297
+ );
1298
+ const truncation = {
1299
+ truncated: true,
1300
+ surface: "browser_legacy_guard",
1301
+ reason: serialized.serializable ? "event_envelope_bytes_exceeded" : "event_not_serializable",
1302
+ originalBytes,
1303
+ deliveredBytes: 0,
1304
+ omittedBytes: originalBytes,
1305
+ estimatedOriginalTokens: originalBytes === null ? null : Math.ceil(originalBytes / 4),
1306
+ estimatedDeliveredTokens: 0,
1307
+ fullEvidence: { available: false, reason: "not_retained" },
1308
+ details: [
1309
+ {
1310
+ path: "$.payload",
1311
+ kind: "payload_preview",
1312
+ originalBytes: payloadBytes,
1313
+ deliveredBytes: browserUtf8Bytes(preview)
1314
+ }
1315
+ ]
1316
+ };
1317
+ const payload = {
1318
+ ...browserPayloadIdentity(event.payload),
1319
+ preview,
1320
+ ...envelopeProjection.length > 0 ? {
1321
+ originalType: boundBrowserText(event.type, BROWSER_EVENT_TYPE_MAX_BYTES),
1322
+ envelopeProjection: {
1323
+ truncated: true,
1324
+ surface: "browser_legacy_guard",
1325
+ fields: envelopeProjection
1326
+ }
1327
+ } : {},
1328
+ truncation
1329
+ };
1330
+ const bounded = {
1331
+ id: boundBrowserText(event.id, BROWSER_EVENT_ID_MAX_BYTES),
1332
+ workspaceId: boundBrowserText(event.workspaceId, BROWSER_EVENT_ID_MAX_BYTES),
1333
+ sessionId: boundBrowserText(event.sessionId, BROWSER_EVENT_ID_MAX_BYTES),
1334
+ sequence: event.sequence,
1335
+ type: typeIsSafe ? event.type : "session.event.envelope_omitted",
1336
+ payload,
1337
+ occurredAt: boundBrowserText(event.occurredAt, BROWSER_EVENT_ID_MAX_BYTES),
1338
+ clientEventId,
1339
+ turnId: boundBrowserOptionalText(event.turnId, BROWSER_EVENT_ID_MAX_BYTES),
1340
+ turnGeneration: event.turnGeneration,
1341
+ turnAttemptId: boundBrowserOptionalText(event.turnAttemptId, BROWSER_EVENT_ID_MAX_BYTES),
1342
+ turnAssociation: event.turnAssociation,
1343
+ duplicateOfEventId: boundBrowserOptionalText(
1344
+ event.duplicateOfEventId,
1345
+ BROWSER_EVENT_ID_MAX_BYTES
1346
+ ),
1347
+ duplicateReason
1348
+ };
1349
+ settleBrowserEventTruncation(bounded, truncation);
1350
+ if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
1351
+ payload.preview = truncateBrowserUtf8Middle(String(payload.preview), 4 * 1024);
1352
+ truncation.details = truncation.details.slice(0, 1);
1353
+ settleBrowserEventTruncation(bounded, truncation);
1354
+ }
1355
+ if (browserJsonBytes(bounded) > SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES) {
1356
+ bounded.clientEventId = null;
1357
+ bounded.duplicateReason = null;
1358
+ payload.preview = "[legacy event omitted at the browser byte boundary]";
1359
+ settleBrowserEventTruncation(bounded, truncation);
1360
+ }
1361
+ return bounded;
1362
+ }
1363
+ function browserJsonBytes(value) {
1364
+ return encoder.encode(browserSerialize(value).value).byteLength;
1365
+ }
1366
+ function browserSerialize(value) {
1367
+ try {
1368
+ const serialized = JSON.stringify(value);
1369
+ return { value: serialized === void 0 ? "null" : serialized, serializable: true };
1370
+ } catch {
1371
+ return {
1372
+ value: '"[unserializable event payload omitted at browser boundary]"',
1373
+ serializable: false
1374
+ };
1375
+ }
1376
+ }
1377
+ function browserPayloadIdentity(payload) {
1378
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) return {};
1379
+ const record = payload;
1380
+ const identity = {};
1381
+ for (const field of BROWSER_EVENT_PAYLOAD_IDENTITY_FIELDS) {
1382
+ const value = record[field];
1383
+ if (typeof value === "string") {
1384
+ identity[field] = boundBrowserText(value, BROWSER_EVENT_ID_MAX_BYTES);
1385
+ } else if (typeof value === "number" || typeof value === "boolean" || value === null) {
1386
+ identity[field] = value;
1387
+ }
1388
+ }
1389
+ return identity;
1390
+ }
1391
+ function browserEnvelopeFieldProjection(field, original, delivered) {
1392
+ return {
1393
+ field,
1394
+ originalBytes: typeof original === "string" ? browserUtf8Bytes(original) : 0,
1395
+ deliveredBytes: typeof delivered === "string" ? browserUtf8Bytes(delivered) : 0
1396
+ };
1397
+ }
1398
+ function boundBrowserOptionalText(value, maxBytes) {
1399
+ return typeof value === "string" ? boundBrowserText(value, maxBytes) : value;
1400
+ }
1401
+ function boundBrowserText(value, maxBytes) {
1402
+ const bytes = encoder.encode(value);
1403
+ if (bytes.byteLength <= maxBytes) return value;
1404
+ const marker = "\u2026[truncated]";
1405
+ const prefixBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
1406
+ let prefixEnd = Math.min(prefixBudget, bytes.byteLength);
1407
+ while (prefixEnd > 0 && prefixEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[prefixEnd])) {
1408
+ prefixEnd -= 1;
1409
+ }
1410
+ return `${decoder.decode(bytes.subarray(0, prefixEnd))}${marker}`;
1411
+ }
1412
+ function truncateBrowserUtf8Middle(value, maxBytes) {
1413
+ const bytes = encoder.encode(value);
1414
+ if (bytes.byteLength <= maxBytes) return value;
1415
+ const marker = `\u2026[${bytes.byteLength - maxBytes} bytes omitted]\u2026`;
1416
+ const contentBudget = Math.max(0, maxBytes - browserUtf8Bytes(marker));
1417
+ const leftBudget = Math.floor(contentBudget / 2);
1418
+ const rightBudget = contentBudget - leftBudget;
1419
+ let leftEnd = Math.min(leftBudget, bytes.byteLength);
1420
+ while (leftEnd > 0 && leftEnd < bytes.byteLength && isBrowserUtf8Continuation(bytes[leftEnd])) {
1421
+ leftEnd -= 1;
1422
+ }
1423
+ let rightStart = Math.max(0, bytes.byteLength - rightBudget);
1424
+ while (rightStart < bytes.byteLength && isBrowserUtf8Continuation(bytes[rightStart])) {
1425
+ rightStart += 1;
1426
+ }
1427
+ return `${decoder.decode(bytes.subarray(0, leftEnd))}${marker}${decoder.decode(bytes.subarray(rightStart))}`;
1428
+ }
1429
+ function settleBrowserEventTruncation(event, truncation) {
1430
+ for (let attempt = 0; attempt < 16; attempt += 1) {
1431
+ const deliveredBytes = browserJsonBytes(event);
1432
+ const omittedBytes = truncation.originalBytes === null ? null : Math.max(0, truncation.originalBytes - deliveredBytes);
1433
+ const estimatedDeliveredTokens = Math.ceil(deliveredBytes / 4);
1434
+ if (truncation.deliveredBytes === deliveredBytes && truncation.omittedBytes === omittedBytes && truncation.estimatedDeliveredTokens === estimatedDeliveredTokens) {
1435
+ return;
1436
+ }
1437
+ truncation.deliveredBytes = deliveredBytes;
1438
+ truncation.omittedBytes = omittedBytes;
1439
+ truncation.estimatedDeliveredTokens = estimatedDeliveredTokens;
1440
+ }
1441
+ throw new RangeError("Browser event byte accounting did not converge");
1442
+ }
1443
+ function browserUtf8Bytes(value) {
1444
+ return encoder.encode(value).byteLength;
1445
+ }
1446
+ function isBrowserUtf8Continuation(value) {
1447
+ return (value & 192) === 128;
1448
+ }
1449
+ function observeSessionStatus(events, ref, setStatus) {
1450
+ let latest = null;
1451
+ for (const event of events) {
1452
+ const status = sessionStatusFromEvents([event]);
1453
+ if (status && (!latest || event.sequence >= latest.sequence)) {
1454
+ latest = { sequence: event.sequence, status };
1455
+ }
1456
+ }
1457
+ if (!latest || latest.sequence < ref.current.sequence) return;
1458
+ ref.current = latest;
1459
+ setStatus(latest.status);
1460
+ }
1461
+ async function loadEventWindow(client, workspaceId, sessionId, options) {
1462
+ let cursor = options.before;
1463
+ let buffer = [];
1464
+ let reachedStart = false;
1465
+ let fetches = 0;
1466
+ while (fetches < options.maxFetches) {
1467
+ if (buffer.length > 0 && groupCount(buffer) >= options.targetGroups) {
1468
+ break;
1469
+ }
1470
+ const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
1471
+ pageSize: options.pageSize,
1472
+ ...options.signal ? { signal: options.signal } : {}
1473
+ });
1474
+ fetches += 1;
1475
+ if (page.length === 0) {
1476
+ reachedStart = true;
1477
+ break;
1478
+ }
1479
+ assertAscending(page);
1480
+ buffer = [...page, ...buffer];
1481
+ cursor = page[0].sequence;
1482
+ if (isLogStart(page[0])) {
1483
+ reachedStart = true;
1484
+ break;
1485
+ }
1486
+ }
1487
+ let snapPages = 0;
1488
+ while (!reachedStart && findBoundaryIndex(buffer) === -1 && snapPages < BOUNDARY_PAGE_CAP && fetches < options.maxFetches) {
1489
+ const page = await loadPreviousPage(client, workspaceId, sessionId, cursor, {
1490
+ pageSize: options.pageSize,
1491
+ ...options.signal ? { signal: options.signal } : {}
1492
+ });
1493
+ fetches += 1;
1494
+ snapPages += 1;
1495
+ if (page.length === 0) {
1496
+ reachedStart = true;
1497
+ break;
1498
+ }
1499
+ assertAscending(page);
1500
+ buffer = [...page, ...buffer];
1501
+ cursor = page[0].sequence;
1502
+ if (isLogStart(page[0])) {
1503
+ reachedStart = true;
1504
+ break;
1505
+ }
1506
+ }
1507
+ if (!reachedStart) {
1508
+ const boundary = findBoundaryIndex(buffer);
1509
+ if (boundary > 0) {
1510
+ buffer = buffer.slice(boundary);
1511
+ }
1512
+ }
1513
+ const oldest = buffer[0] ?? null;
1514
+ const newest = buffer[buffer.length - 1] ?? null;
1515
+ return {
1516
+ events: buffer,
1517
+ oldestSequence: oldest?.sequence ?? null,
1518
+ newestSequence: newest ? maxResumeSequence(buffer) : 0,
1519
+ hasOlder: buffer.length > 0 && !reachedStart && oldest?.type !== "session.created"
1520
+ };
1521
+ }
1522
+ function findBoundaryIndex(events) {
1523
+ for (let index = 0; index < events.length; index += 1) {
1524
+ const type = events[index].type;
1525
+ if (type === "session.created" || type === "user.message") {
1526
+ return index;
1527
+ }
1528
+ }
1529
+ return -1;
1530
+ }
1531
+ async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
1532
+ if (options.signal?.aborted) {
1533
+ throw abortError();
1534
+ }
1535
+ const requested = options.pageSize;
1536
+ if (requested === 0) {
1537
+ return Object.assign([], { requested });
1538
+ }
1539
+ const page = await client.listEvents(workspaceId, sessionId, {
1540
+ before,
1541
+ limit: requested,
1542
+ compact: true
1543
+ });
1544
+ if (options.signal?.aborted) {
1545
+ throw abortError();
1546
+ }
1547
+ return Object.assign(page, { requested });
1548
+ }
1549
+ function groupCount(events) {
1550
+ if (events.length === 0) {
1551
+ return 0;
1552
+ }
1553
+ return groupTimeline(buildTimeline(events)).length;
1554
+ }
1555
+ function isLogStart(event) {
1556
+ return event.type === "session.created" || event.sequence <= 1;
1557
+ }
1558
+ function maxResumeSequence(events) {
1559
+ return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
1560
+ }
1561
+ function eventResumeSequence(event) {
1562
+ const payload = asRecord2(event.payload);
1563
+ const coalescedUntil = Number(payload.coalescedUntil);
1564
+ return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
1565
+ }
1566
+ function asRecord2(value) {
1567
+ return value && typeof value === "object" ? value : {};
1568
+ }
1569
+ function assertAscending(events) {
1570
+ for (let index = 1; index < events.length; index += 1) {
1571
+ if (events[index - 1].sequence >= events[index].sequence) {
1572
+ throw new Error("@opengeni/react: session events must be ordered by ascending sequence");
1573
+ }
1574
+ }
1575
+ }
1576
+ function assertPrependOrder(existing, older) {
1577
+ if (!shouldAssertDevelopment() || existing.length === 0 || older.length === 0) {
1578
+ return;
1579
+ }
1580
+ if (older[older.length - 1].sequence >= existing[0].sequence) {
1581
+ throw new Error("@opengeni/react: loadOlder returned overlapping session events");
1582
+ }
1583
+ }
1584
+ function shouldAssertDevelopment() {
1585
+ const processEnv = globalThis.process?.env;
1586
+ return processEnv !== void 0 && processEnv.NODE_ENV !== "production";
1587
+ }
1588
+ function abortError() {
1589
+ const error = new Error("The operation was aborted.");
1590
+ error.name = "AbortError";
1591
+ return error;
1592
+ }
1593
+
1594
+ // src/hooks/use-turn-queue.ts
1595
+ import { useCallback as useCallback2, useEffect as useEffect2, useLayoutEffect, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
1596
+ function isTurnQueueEvent(event) {
1597
+ return event.type.startsWith("turn.") || event.type.startsWith("session.queue.") || event.type.startsWith("session.control.") || event.type.startsWith("workspace.inference.");
1598
+ }
1599
+ function queueSnapshotCovers(candidate, observed) {
1600
+ return Boolean(
1601
+ candidate && candidate.version >= observed.version && candidate.effectiveControl.controlVersion >= observed.effectiveControl.controlVersion
1602
+ );
1603
+ }
1604
+ var EMPTY_PENDING_BY_TURN = {};
1605
+ function useTurnQueue(sessionId, options = {}) {
1606
+ const { client, workspaceId, workspaceControlEvent, registerSessionReconciler } = useEmbeddedSession(options);
1607
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
1608
+ const targetKey = `${workspaceId}\0${sessionId ?? ""}`;
1609
+ const [snapshot, setSnapshot] = useState2(null);
1610
+ const [stateTargetKey, setStateTargetKey] = useState2(targetKey);
1611
+ const [loading, setLoading] = useState2(enabled);
1612
+ const [error, setError] = useState2(null);
1613
+ const [mutationError, setMutationError] = useState2(null);
1614
+ const [pendingByTurn, setPendingByTurn] = useState2({});
1615
+ const pendingRef = useRef2({});
1616
+ const readGeneration = useRef2(0);
1617
+ const targetKeyRef = useRef2(targetKey);
1618
+ const snapshotRef = useRef2(null);
1619
+ useLayoutEffect(() => {
1620
+ if (targetKeyRef.current === targetKey) return;
1621
+ targetKeyRef.current = targetKey;
1622
+ readGeneration.current += 1;
1623
+ snapshotRef.current = null;
1624
+ pendingRef.current = {};
1625
+ setStateTargetKey(targetKey);
1626
+ setSnapshot(null);
1627
+ setLoading(enabled);
1628
+ setError(null);
1629
+ setMutationError(null);
1630
+ setPendingByTurn({});
1631
+ }, [enabled, targetKey]);
1632
+ const acceptSnapshot = useCallback2(
1633
+ (ownedTargetKey, next) => {
1634
+ if (targetKeyRef.current !== ownedTargetKey) return false;
1635
+ const current = snapshotRef.current;
1636
+ if (next && current && (next.version < current.version || next.effectiveControl.controlVersion < current.effectiveControl.controlVersion)) {
1637
+ return false;
1638
+ }
1639
+ snapshotRef.current = next;
1640
+ setStateTargetKey(ownedTargetKey);
1641
+ setSnapshot(next);
1642
+ return true;
1643
+ },
1644
+ []
1645
+ );
1646
+ const load = useCallback2(
1647
+ async (rejectOnFailure = false) => {
1648
+ if (!sessionId) return;
1649
+ const ownedTargetKey = `${workspaceId}\0${sessionId}`;
1650
+ const ticket = ++readGeneration.current;
1651
+ try {
1652
+ const fetched = await client.getQueue(workspaceId, sessionId);
1653
+ if (targetKeyRef.current !== ownedTargetKey) return;
1654
+ const ownsLatestRead = ticket === readGeneration.current;
1655
+ if (rejectOnFailure) {
1656
+ const committed = acceptSnapshot(ownedTargetKey, fetched);
1657
+ if (!committed && !queueSnapshotCovers(snapshotRef.current, fetched)) {
1658
+ throw new TypeError("Queue reconciliation did not commit authoritative state");
1659
+ }
1660
+ setError(null);
1661
+ if (ownsLatestRead) setLoading(false);
1662
+ } else if (ownsLatestRead) {
1663
+ acceptSnapshot(ownedTargetKey, fetched);
1664
+ setError(null);
1665
+ setLoading(false);
1666
+ }
1667
+ } catch (cause) {
1668
+ if (targetKeyRef.current === ownedTargetKey && (ticket === readGeneration.current || rejectOnFailure)) {
1669
+ setError(asError(cause));
1670
+ if (ticket === readGeneration.current) setLoading(false);
1671
+ }
1672
+ if (rejectOnFailure) throw cause;
1673
+ }
1674
+ },
1675
+ [client, workspaceId, sessionId, acceptSnapshot]
1676
+ );
1677
+ useEffect2(() => {
1678
+ if (!enabled) {
1679
+ setLoading(false);
1680
+ return;
1681
+ }
1682
+ setLoading(true);
1683
+ void load();
1684
+ const pollIntervalMs = options.pollIntervalMs;
1685
+ if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
1686
+ return () => {
1687
+ readGeneration.current += 1;
1688
+ };
1689
+ }
1690
+ const timer = setInterval(() => void load(), pollIntervalMs);
1691
+ return () => {
1692
+ clearInterval(timer);
1693
+ readGeneration.current += 1;
1694
+ };
1695
+ }, [load, enabled, options.pollIntervalMs]);
1696
+ useEffect2(() => {
1697
+ if (enabled && workspaceControlEvent) void load();
1698
+ }, [enabled, load, workspaceControlEvent]);
1699
+ useEffect2(() => {
1700
+ if (!sessionId || !enabled) return;
1701
+ return registerSessionReconciler(sessionId, "queue", load);
1702
+ }, [enabled, load, registerSessionReconciler, sessionId]);
1703
+ const scheduleRefresh = useDebouncedCallback(() => void load());
1704
+ useSessionEventTrigger(
1705
+ client,
1706
+ workspaceId,
1707
+ sessionId,
1708
+ isTurnQueueEvent,
1709
+ scheduleRefresh,
1710
+ {
1711
+ enabled,
1712
+ ...options.events !== void 0 ? { events: options.events } : {}
1713
+ },
1714
+ async () => await load(true)
1715
+ );
1716
+ const mutate = useCallback2(
1717
+ async (turnId, kind, command) => {
1718
+ const ownedTargetKey = targetKey;
1719
+ if (!sessionId || targetKeyRef.current !== ownedTargetKey || pendingRef.current[turnId]) {
1720
+ return null;
1721
+ }
1722
+ const current = snapshotRef.current;
1723
+ const turn = current?.items.find((candidate) => candidate.id === turnId);
1724
+ if (!current || !turn) return null;
1725
+ pendingRef.current = { ...pendingRef.current, [turnId]: kind };
1726
+ setStateTargetKey(ownedTargetKey);
1727
+ setPendingByTurn(pendingRef.current);
1728
+ setMutationError(null);
1729
+ try {
1730
+ const result = await command(current, turn);
1731
+ if (targetKeyRef.current !== ownedTargetKey) return null;
1732
+ acceptSnapshot(ownedTargetKey, result.snapshot);
1733
+ return result;
1734
+ } catch (cause) {
1735
+ if (targetKeyRef.current === ownedTargetKey) {
1736
+ setMutationError(asError(cause));
1737
+ await load();
1738
+ }
1739
+ return null;
1740
+ } finally {
1741
+ if (targetKeyRef.current === ownedTargetKey && turnId in pendingRef.current) {
1742
+ const next = { ...pendingRef.current };
1743
+ delete next[turnId];
1744
+ pendingRef.current = next;
1745
+ setPendingByTurn(next);
1746
+ }
1747
+ }
1748
+ },
1749
+ [acceptSnapshot, load, sessionId, targetKey]
1750
+ );
1751
+ const moveTurn = useCallback2(
1752
+ async (turnId, beforeTurnId) => {
1753
+ const result = await mutate(
1754
+ turnId,
1755
+ "move",
1756
+ (current) => client.moveQueueItem(workspaceId, sessionId, turnId, {
1757
+ clientEventId: operationKey(),
1758
+ expectedQueueVersion: current.version,
1759
+ beforeTurnId
1760
+ })
1761
+ );
1762
+ return result !== null;
1763
+ },
1764
+ [client, mutate, sessionId, workspaceId]
1765
+ );
1766
+ const editTurn = useCallback2(
1767
+ async (turnId, edit) => {
1768
+ const result = await mutate(
1769
+ turnId,
1770
+ "edit",
1771
+ (_current, turn) => client.editQueueItem(workspaceId, sessionId, turnId, {
1772
+ clientEventId: operationKey(),
1773
+ expectedTurnVersion: turn.version,
1774
+ expectedDraftRevision: edit.expectedDraftRevision,
1775
+ replaceDraft: edit.replaceDraft
1776
+ })
1777
+ );
1778
+ return result?.draft ?? null;
1779
+ },
1780
+ [client, mutate, sessionId, workspaceId]
1781
+ );
1782
+ const steerTurn = useCallback2(
1783
+ async (turnId) => {
1784
+ const result = await mutate(
1785
+ turnId,
1786
+ "steer",
1787
+ (current, turn) => client.steerQueueItem(workspaceId, sessionId, turnId, {
1788
+ clientEventId: operationKey(),
1789
+ expectedTurnVersion: turn.version,
1790
+ controlEtag: current.effectiveControl.controlEtag
1791
+ })
1792
+ );
1793
+ return result !== null;
1794
+ },
1795
+ [client, mutate, sessionId, workspaceId]
1796
+ );
1797
+ const removeTurn = useCallback2(
1798
+ async (turnId) => {
1799
+ const result = await mutate(
1800
+ turnId,
1801
+ "delete",
1802
+ (_current, turn) => client.deleteQueueItem(workspaceId, sessionId, turnId, {
1803
+ clientEventId: operationKey(),
1804
+ expectedTurnVersion: turn.version,
1805
+ reason: "Deleted from the prompt queue"
1806
+ })
1807
+ );
1808
+ return result !== null;
1809
+ },
1810
+ [client, mutate, sessionId, workspaceId]
1811
+ );
1812
+ const identityMatches = stateTargetKey === targetKey;
1813
+ const visibleSnapshot = identityMatches ? snapshot : null;
1814
+ const visiblePendingByTurn = identityMatches ? pendingByTurn : EMPTY_PENDING_BY_TURN;
1815
+ const mutating = useMemo2(
1816
+ () => Object.keys(visiblePendingByTurn).length > 0,
1817
+ [visiblePendingByTurn]
1818
+ );
1819
+ const mutationFor = useCallback2(
1820
+ (turnId) => visiblePendingByTurn[turnId] ?? null,
1821
+ [visiblePendingByTurn]
1822
+ );
1823
+ return {
1824
+ snapshot: visibleSnapshot,
1825
+ queue: visibleSnapshot?.items ?? [],
1826
+ effectiveControl: visibleSnapshot?.effectiveControl ?? null,
1827
+ stoppingPreviousAttempt: visibleSnapshot?.stoppingPreviousAttempt ?? false,
1828
+ loading: identityMatches ? loading : enabled,
1829
+ error: identityMatches ? error : null,
1830
+ refresh: load,
1831
+ moveTurn,
1832
+ editTurn,
1833
+ steerTurn,
1834
+ removeTurn,
1835
+ pendingByTurn: visiblePendingByTurn,
1836
+ mutationFor,
1837
+ mutating,
1838
+ mutationError: identityMatches ? mutationError : null,
1839
+ clearMutationError: useCallback2(() => {
1840
+ if (targetKeyRef.current === targetKey) setMutationError(null);
1841
+ }, [targetKey])
1842
+ };
1843
+ }
1844
+ function operationKey() {
1845
+ return globalThis.crypto.randomUUID();
1846
+ }
1847
+ function asError(cause) {
1848
+ return cause instanceof Error ? cause : new Error(String(cause));
1849
+ }
1850
+
1851
+ // src/hooks/use-session-control.ts
1852
+ import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useRef as useRef3 } from "react";
1853
+ function useSessionControl(sessionId, options = {}) {
1854
+ const { client, workspaceId } = useEmbeddedSession(options);
1855
+ const approvalTargetKey = `${workspaceId}\0${sessionId ?? ""}`;
1856
+ const pendingApproval = useRef3(null);
1857
+ useLayoutEffect2(() => {
1858
+ if (pendingApproval.current?.targetKey !== approvalTargetKey) {
1859
+ pendingApproval.current = null;
1860
+ }
1861
+ }, [approvalTargetKey]);
1862
+ const {
1863
+ run: runControl,
1864
+ mutating: controlling,
1865
+ mutationError: controlError,
1866
+ clearMutationError: clearControlError
1867
+ } = useMutationRunner(approvalTargetKey);
1868
+ const {
1869
+ run: runApproval,
1870
+ mutating: responding,
1871
+ mutationError: approvalError,
1872
+ clearMutationError: clearApprovalError
1873
+ } = useMutationRunner(approvalTargetKey);
1874
+ const pause = useCallback3(
1875
+ async (reason) => {
1876
+ if (!sessionId) {
1877
+ return null;
1878
+ }
1879
+ return await runControl(
1880
+ () => client.pauseSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
1881
+ );
1882
+ },
1883
+ [client, workspaceId, sessionId, runControl]
1884
+ );
1885
+ const resume = useCallback3(
1886
+ async (reason) => {
1887
+ if (!sessionId) return null;
1888
+ return await runControl(
1889
+ () => client.resumeSession(workspaceId, sessionId, reason !== void 0 ? { reason } : {})
1890
+ );
1891
+ },
1892
+ [client, workspaceId, sessionId, runControl]
1893
+ );
1894
+ const decide = useCallback3(
1895
+ async (approvalId, decision, message) => {
1896
+ if (!sessionId) {
1897
+ return null;
1898
+ }
1899
+ const decisionKey = JSON.stringify([approvalId, decision, message ?? null]);
1900
+ if (pendingApproval.current?.decisionKey !== decisionKey) {
1901
+ pendingApproval.current = {
1902
+ targetKey: approvalTargetKey,
1903
+ decisionKey,
1904
+ clientEventId: crypto.randomUUID()
1905
+ };
1906
+ }
1907
+ const clientEventId = pendingApproval.current.clientEventId;
1908
+ const accepted = await runApproval(
1909
+ () => client.sendApprovalDecision(workspaceId, sessionId, {
1910
+ approvalId,
1911
+ decision,
1912
+ ...message !== void 0 ? { message } : {},
1913
+ clientEventId
1914
+ })
1915
+ );
1916
+ if (accepted !== null && pendingApproval.current?.targetKey === approvalTargetKey && pendingApproval.current.clientEventId === clientEventId) {
1917
+ pendingApproval.current = null;
1918
+ }
1919
+ return accepted;
1920
+ },
1921
+ [approvalTargetKey, client, workspaceId, sessionId, runApproval]
1922
+ );
1923
+ const approve = useCallback3(
1924
+ async (approvalId, message) => await decide(approvalId, "approve", message),
1925
+ [decide]
1926
+ );
1927
+ const reject = useCallback3(
1928
+ async (approvalId, message) => await decide(approvalId, "reject", message),
1929
+ [decide]
1930
+ );
1931
+ const error = approvalError ?? controlError;
1932
+ const clearError = useCallback3(() => {
1933
+ clearControlError();
1934
+ clearApprovalError();
1935
+ }, [clearControlError, clearApprovalError]);
1936
+ return {
1937
+ pause,
1938
+ resume,
1939
+ controlling,
1940
+ approve,
1941
+ reject,
1942
+ responding,
1943
+ error,
1944
+ clearError
1945
+ };
1946
+ }
1947
+
1948
+ // src/approvals.ts
1949
+ function approvalsFromRequiresAction(payload) {
1950
+ const approvals = payload && typeof payload === "object" ? payload.approvals : void 0;
1951
+ if (!Array.isArray(approvals)) {
1952
+ return [];
1953
+ }
1954
+ return approvals.map((approval, index) => {
1955
+ const raw = approval && typeof approval === "object" ? approval : {};
1956
+ const rawItem = raw.rawItem && typeof raw.rawItem === "object" ? raw.rawItem : {};
1957
+ return {
1958
+ // Mirror the runtime's canonical interruption identity before retaining
1959
+ // the legacy top-level callId fallback for already-normalized payloads.
1960
+ id: String(rawItem.callId ?? rawItem.id ?? raw.id ?? raw.callId ?? raw.name ?? index),
1961
+ name: String(raw.name ?? raw.toolName ?? rawItem.name ?? "approval"),
1962
+ arguments: raw.arguments ?? rawItem.arguments,
1963
+ raw: approval
1964
+ };
1965
+ });
1966
+ }
1967
+ function projectPendingApprovals(events) {
1968
+ let pending = [];
1969
+ let owningTurnId = null;
1970
+ for (const event of events) {
1971
+ switch (event.type) {
1972
+ case "session.requiresAction": {
1973
+ pending = approvalsFromRequiresAction(event.payload);
1974
+ owningTurnId = event.turnId ?? null;
1975
+ break;
1976
+ }
1977
+ case "user.approvalDecision": {
1978
+ const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
1979
+ if (typeof payload.approvalId === "string") {
1980
+ pending = pending.filter((approval) => approval.id !== payload.approvalId);
1981
+ }
1982
+ break;
1983
+ }
1984
+ case "turn.completed":
1985
+ case "turn.failed":
1986
+ case "turn.cancelled": {
1987
+ if (owningTurnId === null || event.turnId == null || event.turnId === owningTurnId) {
1988
+ pending = [];
1989
+ owningTurnId = null;
1990
+ }
1991
+ break;
1992
+ }
1993
+ default:
1994
+ break;
1995
+ }
1996
+ }
1997
+ return pending;
1998
+ }
1999
+
2000
+ // src/human-input.ts
2001
+ function humanInputRequestFromEvent(event) {
2002
+ if (event.type !== "session.humanInput.requested" || !isRecord(event.payload)) return null;
2003
+ const request = event.payload.request;
2004
+ if (!isRecord(request) || typeof request.id !== "string" || !Array.isArray(request.questions)) {
2005
+ return null;
2006
+ }
2007
+ return {
2008
+ id: request.id,
2009
+ turnId: event.turnId ?? null,
2010
+ questions: request.questions,
2011
+ allowSkip: request.allowSkip === true,
2012
+ expiresAt: typeof request.expiresAt === "string" ? request.expiresAt : null
2013
+ };
2014
+ }
2015
+ function projectPendingHumanInputRequests(events) {
2016
+ const pending = /* @__PURE__ */ new Map();
2017
+ for (const event of events) {
2018
+ const requested = humanInputRequestFromEvent(event);
2019
+ if (requested) {
2020
+ pending.set(requested.id, requested);
2021
+ continue;
2022
+ }
2023
+ if (event.type === "user.humanInputResponse" && isRecord(event.payload)) {
2024
+ if (typeof event.payload.requestId === "string") pending.delete(event.payload.requestId);
2025
+ continue;
2026
+ }
2027
+ if (event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled") {
2028
+ for (const [id, request] of pending) {
2029
+ if (request.turnId === null || event.turnId === null || event.turnId === void 0 || request.turnId === event.turnId) {
2030
+ pending.delete(id);
2031
+ }
2032
+ }
2033
+ }
2034
+ }
2035
+ return [...pending.values()];
2036
+ }
2037
+ function isRecord(value) {
2038
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2039
+ }
2040
+
2041
+ // src/hooks/use-human-input.ts
2042
+ import { useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4, useState as useState3 } from "react";
2043
+ function isHumanInputEvent(event) {
2044
+ return event.type === "session.humanInput.requested" || event.type === "user.humanInputResponse" || event.type === "turn.completed" || event.type === "turn.failed" || event.type === "turn.cancelled";
2045
+ }
2046
+ function useHumanInputRequests(sessionId, options = {}) {
2047
+ const { client, workspaceId, registerSessionReconciler } = useEmbeddedHumanInputSession(options);
2048
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
2049
+ const load = useCallback4(
2050
+ async () => sessionId ? await client.listHumanInputRequests(workspaceId, sessionId, { status: "pending" }) : [],
2051
+ [client, sessionId, workspaceId]
2052
+ );
2053
+ const state = usePolledValue(load, {
2054
+ enabled,
2055
+ pollIntervalMs: options.pollIntervalMs
2056
+ });
2057
+ const mutation = useMutationRunner(`${workspaceId}\0${sessionId ?? ""}`);
2058
+ const [respondingRequestId, setRespondingRequestId] = useState3(null);
2059
+ const respondingRequestRef = useRef4(null);
2060
+ useEffect3(() => {
2061
+ respondingRequestRef.current = null;
2062
+ setRespondingRequestId(null);
2063
+ }, [sessionId, workspaceId]);
2064
+ useEffect3(() => {
2065
+ if (!sessionId || !enabled) return;
2066
+ return registerSessionReconciler(sessionId, "human-input", state.refresh);
2067
+ }, [enabled, registerSessionReconciler, sessionId, state.refresh]);
2068
+ const scheduleRefresh = useDebouncedCallback(() => void state.refresh());
2069
+ useSessionEventTrigger(client, workspaceId, sessionId, isHumanInputEvent, scheduleRefresh, {
2070
+ enabled,
2071
+ ...options.events !== void 0 ? { events: options.events } : {}
2072
+ });
2073
+ const respond = useCallback4(
2074
+ async (requestId, response) => {
2075
+ if (!sessionId || respondingRequestRef.current !== null) return null;
2076
+ respondingRequestRef.current = requestId;
2077
+ setRespondingRequestId(requestId);
2078
+ try {
2079
+ const event = await mutation.run(
2080
+ () => client.submitHumanInputResponse(workspaceId, sessionId, requestId, response, {
2081
+ clientEventId: globalThis.crypto.randomUUID()
2082
+ })
2083
+ );
2084
+ if (event) await state.refresh();
2085
+ return event;
2086
+ } finally {
2087
+ if (respondingRequestRef.current === requestId) {
2088
+ respondingRequestRef.current = null;
2089
+ setRespondingRequestId(null);
2090
+ }
2091
+ }
2092
+ },
2093
+ [client, mutation, sessionId, state, workspaceId]
2094
+ );
2095
+ return {
2096
+ requests: [...state.data ?? []].sort(
2097
+ (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
2098
+ ),
2099
+ loading: state.loading,
2100
+ error: state.error,
2101
+ refresh: state.refresh,
2102
+ respond,
2103
+ respondingRequestId,
2104
+ mutationError: mutation.mutationError,
2105
+ clearMutationError: mutation.clearMutationError
2106
+ };
2107
+ }
2108
+
2109
+ export {
2110
+ buildTimeline,
2111
+ creditExhaustedFromEvents,
2112
+ sessionStatusFromEvents,
2113
+ groupTimeline,
2114
+ extractSessionRef,
2115
+ toolDisplayName,
2116
+ SESSION_EVENT_BROWSER_MAX_BYTES,
2117
+ SESSION_EVENT_BROWSER_MAX_COUNT,
2118
+ SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
2119
+ SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
2120
+ SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
2121
+ useSessionEvents,
2122
+ boundBrowserSessionEventWindow,
2123
+ isTurnQueueEvent,
2124
+ useTurnQueue,
2125
+ useSessionControl,
2126
+ approvalsFromRequiresAction,
2127
+ projectPendingApprovals,
2128
+ humanInputRequestFromEvent,
2129
+ projectPendingHumanInputRequests,
2130
+ isHumanInputEvent,
2131
+ useHumanInputRequests
2132
+ };
2133
+ //# sourceMappingURL=chunk-6UT5OC3P.js.map