@opengeni/react 0.25.0 → 0.26.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 (46) hide show
  1. package/dist/{chunk-AVPU5PMC.js → chunk-F4CFWKVI.js} +332 -64
  2. package/dist/chunk-F4CFWKVI.js.map +1 -0
  3. package/dist/chunk-GQN2QIR2.js +3767 -0
  4. package/dist/chunk-GQN2QIR2.js.map +1 -0
  5. package/dist/{chunk-I3BJZIG5.js → chunk-HIWPQYWI.js} +2 -120
  6. package/dist/chunk-HIWPQYWI.js.map +1 -0
  7. package/dist/chunk-M7X4JZOD.js +121 -0
  8. package/dist/chunk-M7X4JZOD.js.map +1 -0
  9. package/dist/{chunk-SHOFILHJ.js → chunk-MP6237JB.js} +570 -984
  10. package/dist/chunk-MP6237JB.js.map +1 -0
  11. package/dist/chunk-OZDLELJQ.js +937 -0
  12. package/dist/chunk-OZDLELJQ.js.map +1 -0
  13. package/dist/{chunk-RDPDU4TA.js → chunk-TMH6HZWF.js} +3 -3
  14. package/dist/{chunk-6XUS5VFM.js → chunk-YU5PGUK7.js} +6 -4
  15. package/dist/{chunk-6XUS5VFM.js.map → chunk-YU5PGUK7.js.map} +1 -1
  16. package/dist/{composer-BXb0Q1HF.d.ts → composer-DoC4veX1.d.ts} +4 -75
  17. package/dist/composer.d.ts +2 -1
  18. package/dist/composer.js +4 -3
  19. package/dist/index.d.ts +14 -259
  20. package/dist/index.js +1016 -5140
  21. package/dist/index.js.map +1 -1
  22. package/dist/machines.js +3 -2
  23. package/dist/session-Du_FsrZ1.d.ts +264 -0
  24. package/dist/session-ui-BqQDH7YV.d.ts +183 -0
  25. package/dist/session-ui.d.ts +7 -0
  26. package/dist/session-ui.js +17 -0
  27. package/dist/session-ui.js.map +1 -0
  28. package/dist/session.d.ts +3 -1
  29. package/dist/session.js +26 -9
  30. package/dist/use-file-attachments-C0kpHrs9.d.ts +76 -0
  31. package/dist/{session-BEvtFWhe.d.ts → use-turn-queue-3rkJwjFv.d.ts} +3 -185
  32. package/package.json +6 -2
  33. package/src/components/message-timeline.tsx +92 -52
  34. package/src/hooks/use-composer.ts +456 -69
  35. package/src/hooks/use-file-attachments.ts +1 -1
  36. package/src/hooks/use-goal.ts +1 -1
  37. package/src/hooks/use-session-events.ts +5 -0
  38. package/src/hooks/use-session-lineage.ts +1 -1
  39. package/src/hooks/use-session.ts +9 -6
  40. package/src/session-ui.ts +13 -0
  41. package/src/session.ts +15 -0
  42. package/src/timeline/activity-rail.tsx +1 -1
  43. package/dist/chunk-AVPU5PMC.js.map +0 -1
  44. package/dist/chunk-I3BJZIG5.js.map +0 -1
  45. package/dist/chunk-SHOFILHJ.js.map +0 -1
  46. /package/dist/{chunk-RDPDU4TA.js.map → chunk-TMH6HZWF.js.map} +0 -0
@@ -0,0 +1,3767 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-TK7G6XLT.js";
4
+ import {
5
+ buildTimeline,
6
+ groupTimeline
7
+ } from "./chunk-OZDLELJQ.js";
8
+ import {
9
+ formatRelativeTime,
10
+ stringifyPayload,
11
+ truncate,
12
+ tryParseJson
13
+ } from "./chunk-M7X4JZOD.js";
14
+ import {
15
+ EmptyQueueStateSurface
16
+ } from "./chunk-YDNWMKXO.js";
17
+
18
+ // src/components/queue-surface.tsx
19
+ import { Loader2Icon } from "lucide-react";
20
+ import { lazy, Suspense } from "react";
21
+ import { jsx, jsxs } from "react/jsx-runtime";
22
+ var loadQueueSurface = () => import("./queue-surface-implementation-3UW3MIIR.js");
23
+ var QueueSurface = createQueueSurface(loadQueueSurface);
24
+ function createQueueSurface(loadImplementation) {
25
+ const LazyQueueSurface = lazy(async () => ({
26
+ default: (await loadImplementation()).QueueSurface
27
+ }));
28
+ return function QueueSurfaceBoundary(props) {
29
+ const { queue } = props;
30
+ if (queue.queue.length === 0) {
31
+ if (!queue.stoppingPreviousAttempt && !queue.error && !queue.mutationError) return null;
32
+ return /* @__PURE__ */ jsx(EmptyQueueStateSurface, { queue });
33
+ }
34
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(QueueSurfaceFallback, { count: queue.queue.length }), children: /* @__PURE__ */ jsx(LazyQueueSurface, { ...props }) });
35
+ };
36
+ }
37
+ function QueueSurfaceFallback({ count }) {
38
+ return /* @__PURE__ */ jsx(
39
+ "div",
40
+ {
41
+ "aria-live": "polite",
42
+ className: "mx-auto mb-2 w-full max-w-3xl shrink-0 px-4 sm:px-6",
43
+ "data-testid": "queue-surface-loading",
44
+ role: "status",
45
+ children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-border bg-surface/80 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-xs text-fg-muted pointer-coarse:min-h-[44px]", children: [
46
+ /* @__PURE__ */ jsx(
47
+ Loader2Icon,
48
+ {
49
+ "aria-hidden": "true",
50
+ className: "size-3.5 shrink-0 animate-spin motion-reduce:animate-none"
51
+ }
52
+ ),
53
+ "Loading ",
54
+ count,
55
+ " queued prompt",
56
+ count === 1 ? "" : "s",
57
+ "\u2026"
58
+ ] }) })
59
+ }
60
+ );
61
+ }
62
+
63
+ // src/components/human-input-form.tsx
64
+ import { useEffect, useId, useRef, useState } from "react";
65
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
66
+ var defaultHumanInputFormMessages = {
67
+ title: "Your input is needed",
68
+ description: "The agent is paused until you answer these questions.",
69
+ submit: "Continue",
70
+ skip: "Skip",
71
+ submitting: "Submitting\u2026",
72
+ other: "Other",
73
+ deadlineLabel: "Respond before",
74
+ formatDeadline,
75
+ required: "This question is required.",
76
+ minLength: (count) => `Enter at least ${count} characters.`,
77
+ maxLength: (count) => `Enter no more than ${count} characters.`,
78
+ otherRequired: "Enter a value for Other.",
79
+ minSelections: (count) => `Choose at least ${count} option${count === 1 ? "" : "s"}.`,
80
+ maxSelections: (count) => `Choose no more than ${count} option${count === 1 ? "" : "s"}.`
81
+ };
82
+ function HumanInputForm({
83
+ request,
84
+ onSubmit,
85
+ submitting = false,
86
+ error,
87
+ title,
88
+ description,
89
+ submitLabel,
90
+ skipLabel,
91
+ messages: messageOverrides,
92
+ autoFocus = true,
93
+ className
94
+ }) {
95
+ const messages = { ...defaultHumanInputFormMessages, ...messageOverrides };
96
+ const resolvedTitle = title === void 0 ? messages.title : title;
97
+ const resolvedDescription = description === void 0 ? messages.description : description;
98
+ const resolvedSubmitLabel = submitLabel ?? messages.submit;
99
+ const resolvedSkipLabel = skipLabel ?? messages.skip;
100
+ const formId = useId();
101
+ const [drafts, setDrafts] = useState(
102
+ () => initialDrafts(request.questions)
103
+ );
104
+ const [validationErrors, setValidationErrors] = useState({});
105
+ const [submissionError, setSubmissionError] = useState(null);
106
+ const [submittingInternally, setSubmittingInternally] = useState(false);
107
+ const submissionInFlight = useRef(false);
108
+ const submissionGeneration = useRef(0);
109
+ const busy = submitting || submittingInternally;
110
+ useEffect(() => {
111
+ submissionGeneration.current += 1;
112
+ submissionInFlight.current = false;
113
+ setDrafts(initialDrafts(request.questions));
114
+ setValidationErrors({});
115
+ setSubmissionError(null);
116
+ setSubmittingInternally(false);
117
+ }, [request.id, request.questions]);
118
+ const update = (questionId, apply) => {
119
+ setDrafts((current) => ({
120
+ ...current,
121
+ [questionId]: apply(current[questionId] ?? emptyDraft())
122
+ }));
123
+ setValidationErrors((current) => {
124
+ if (!(questionId in current)) return current;
125
+ const next = { ...current };
126
+ delete next[questionId];
127
+ return next;
128
+ });
129
+ };
130
+ const submitResponse = async (response) => {
131
+ if (busy || submissionInFlight.current) return;
132
+ const generation = submissionGeneration.current;
133
+ submissionInFlight.current = true;
134
+ setSubmissionError(null);
135
+ setSubmittingInternally(true);
136
+ try {
137
+ await onSubmit(response);
138
+ } catch (cause) {
139
+ if (generation === submissionGeneration.current) {
140
+ setSubmissionError(cause instanceof Error ? cause.message : String(cause));
141
+ }
142
+ } finally {
143
+ if (generation === submissionGeneration.current) {
144
+ submissionInFlight.current = false;
145
+ setSubmittingInternally(false);
146
+ }
147
+ }
148
+ };
149
+ const submit = async (event) => {
150
+ event.preventDefault();
151
+ const result = answersFromDrafts(request.questions, drafts, messages);
152
+ if (Object.keys(result.errors).length > 0) {
153
+ setValidationErrors(result.errors);
154
+ return;
155
+ }
156
+ await submitResponse({ outcome: "answered", answers: result.answers });
157
+ };
158
+ return /* @__PURE__ */ jsxs2(
159
+ "form",
160
+ {
161
+ "data-human-input-request": request.id,
162
+ onSubmit: (event) => void submit(event),
163
+ className: cn(
164
+ "og-root flex w-full flex-col gap-5 rounded-og-lg border border-og-border bg-og-surface-1 p-5 shadow-og-sm",
165
+ className
166
+ ),
167
+ children: [
168
+ /* @__PURE__ */ jsxs2("header", { children: [
169
+ /* @__PURE__ */ jsx2("h2", { className: "text-og-md font-semibold text-og-fg", children: resolvedTitle }),
170
+ resolvedDescription ? /* @__PURE__ */ jsx2("div", { className: "mt-1 text-og-sm text-og-fg-muted", children: resolvedDescription }) : null,
171
+ request.expiresAt ? /* @__PURE__ */ jsxs2("p", { className: "mt-1 text-og-xs text-og-fg-subtle", children: [
172
+ messages.deadlineLabel,
173
+ " ",
174
+ /* @__PURE__ */ jsx2("time", { dateTime: request.expiresAt, children: messages.formatDeadline(request.expiresAt) })
175
+ ] }) : null
176
+ ] }),
177
+ /* @__PURE__ */ jsx2("fieldset", { disabled: busy, className: "contents", children: request.questions.map((question, index) => {
178
+ const draft = drafts[question.id] ?? emptyDraft();
179
+ const fieldId = `${formId}-${index}`;
180
+ const errorId = `${fieldId}-error`;
181
+ const helpId = `${fieldId}-help`;
182
+ const describedBy = [question.helpText ? helpId : null, validationErrors[question.id] ? errorId : null].filter(Boolean).join(" ") || void 0;
183
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-2", children: [
184
+ /* @__PURE__ */ jsxs2(
185
+ "label",
186
+ {
187
+ htmlFor: question.kind === "text" ? fieldId : void 0,
188
+ className: "text-og-base font-medium text-og-fg",
189
+ children: [
190
+ question.label ?? question.prompt,
191
+ question.required ? /* @__PURE__ */ jsx2("span", { "aria-hidden": true, className: "ml-1 text-og-status-failed", children: "*" }) : null
192
+ ]
193
+ }
194
+ ),
195
+ question.label ? /* @__PURE__ */ jsx2("p", { className: "text-og-sm text-og-fg-muted", children: question.prompt }) : null,
196
+ question.helpText ? /* @__PURE__ */ jsx2("p", { id: helpId, className: "text-og-xs text-og-fg-subtle", children: question.helpText }) : null,
197
+ question.kind === "text" ? /* @__PURE__ */ jsx2(
198
+ "textarea",
199
+ {
200
+ id: fieldId,
201
+ value: draft.values[0] ?? "",
202
+ onChange: (event) => update(question.id, (current) => ({
203
+ ...current,
204
+ values: event.target.value ? [event.target.value] : []
205
+ })),
206
+ "aria-invalid": Boolean(validationErrors[question.id]),
207
+ "aria-describedby": describedBy,
208
+ autoFocus: autoFocus && index === 0,
209
+ rows: 3,
210
+ className: "min-h-20 w-full resize-y rounded-og-md border border-og-border bg-og-surface-2 px-3 py-2 text-og-base text-og-fg outline-none placeholder:text-og-fg-subtle focus:border-og-accent"
211
+ }
212
+ ) : /* @__PURE__ */ jsxs2(
213
+ "div",
214
+ {
215
+ role: question.kind === "single_select" ? "radiogroup" : "group",
216
+ "aria-describedby": describedBy,
217
+ className: "flex flex-col gap-2",
218
+ children: [
219
+ question.options.map((option, optionIndex) => {
220
+ const checked = draft.values.includes(option.id);
221
+ return /* @__PURE__ */ jsxs2(
222
+ "label",
223
+ {
224
+ className: "flex cursor-pointer items-start gap-2 rounded-og-md border border-og-border px-3 py-2 hover:bg-og-surface-2",
225
+ children: [
226
+ /* @__PURE__ */ jsx2(
227
+ "input",
228
+ {
229
+ type: question.kind === "single_select" ? "radio" : "checkbox",
230
+ name: question.kind === "single_select" ? fieldId : void 0,
231
+ autoFocus: autoFocus && index === 0 && optionIndex === 0,
232
+ checked,
233
+ onChange: (event) => update(question.id, (current) => ({
234
+ ...current,
235
+ values: question.kind === "single_select" ? event.target.checked ? [option.id] : [] : event.target.checked ? [...current.values, option.id] : current.values.filter((value) => value !== option.id),
236
+ ...question.kind === "single_select" && event.target.checked ? { otherSelected: false } : {}
237
+ })),
238
+ className: "mt-0.5 accent-og-accent"
239
+ }
240
+ ),
241
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
242
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-sm font-medium text-og-fg", children: option.label }),
243
+ option.description ? /* @__PURE__ */ jsx2("span", { className: "block text-og-xs text-og-fg-muted", children: option.description }) : null
244
+ ] })
245
+ ]
246
+ },
247
+ option.id
248
+ );
249
+ }),
250
+ question.allowOther ? /* @__PURE__ */ jsxs2("label", { className: "flex items-start gap-2 rounded-og-md border border-og-border px-3 py-2", children: [
251
+ /* @__PURE__ */ jsx2(
252
+ "input",
253
+ {
254
+ type: question.kind === "single_select" ? "radio" : "checkbox",
255
+ name: question.kind === "single_select" ? fieldId : void 0,
256
+ checked: draft.otherSelected,
257
+ autoFocus: autoFocus && index === 0 && question.options.length === 0,
258
+ onChange: (event) => update(question.id, (current) => ({
259
+ ...current,
260
+ otherSelected: event.target.checked,
261
+ ...question.kind === "single_select" && event.target.checked ? { values: [] } : {}
262
+ })),
263
+ className: "mt-2 accent-og-accent"
264
+ }
265
+ ),
266
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
267
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-sm font-medium text-og-fg", children: messages.other }),
268
+ /* @__PURE__ */ jsx2(
269
+ "input",
270
+ {
271
+ type: "text",
272
+ value: draft.other,
273
+ disabled: !draft.otherSelected || busy,
274
+ onChange: (event) => update(question.id, (current) => ({
275
+ ...current,
276
+ other: event.target.value
277
+ })),
278
+ className: "mt-1 w-full rounded-og-sm border border-og-border bg-og-surface-2 px-2 py-1.5 text-og-sm text-og-fg outline-none focus:border-og-accent disabled:opacity-50"
279
+ }
280
+ )
281
+ ] })
282
+ ] }) : null
283
+ ]
284
+ }
285
+ ),
286
+ validationErrors[question.id] ? /* @__PURE__ */ jsx2("p", { id: errorId, role: "alert", className: "text-og-xs text-og-status-failed", children: validationErrors[question.id] }) : null
287
+ ] }, question.id);
288
+ }) }),
289
+ error ?? submissionError ? /* @__PURE__ */ jsx2("p", { role: "alert", className: "text-og-sm text-og-status-failed", children: error ?? submissionError }) : null,
290
+ /* @__PURE__ */ jsxs2("footer", { className: "flex items-center justify-end gap-2", children: [
291
+ request.allowSkip ? /* @__PURE__ */ jsx2(
292
+ "button",
293
+ {
294
+ type: "button",
295
+ disabled: busy,
296
+ onClick: () => void submitResponse({ outcome: "skipped" }),
297
+ className: "rounded-og-md border border-og-border px-3 py-2 text-og-sm font-medium text-og-fg-muted hover:bg-og-surface-2 disabled:opacity-50",
298
+ children: resolvedSkipLabel
299
+ }
300
+ ) : null,
301
+ /* @__PURE__ */ jsx2(
302
+ "button",
303
+ {
304
+ type: "submit",
305
+ disabled: busy,
306
+ className: "rounded-og-md bg-og-accent px-3 py-2 text-og-sm font-medium text-og-accent-fg hover:bg-og-accent-strong disabled:opacity-50",
307
+ children: busy ? messages.submitting : resolvedSubmitLabel
308
+ }
309
+ )
310
+ ] })
311
+ ]
312
+ }
313
+ );
314
+ }
315
+ function answersFromDrafts(questions, drafts, messageOverrides = {}) {
316
+ const messages = { ...defaultHumanInputFormMessages, ...messageOverrides };
317
+ const answers = [];
318
+ const errors = {};
319
+ for (const question of questions) {
320
+ const draft = drafts[question.id] ?? emptyDraft();
321
+ const values = question.kind === "text" ? draft.values.filter(Boolean) : draft.values;
322
+ const other = draft.otherSelected ? draft.other.trim() : "";
323
+ const supplied = values.length + (other ? 1 : 0);
324
+ if (question.required && supplied === 0) {
325
+ errors[question.id] = messages.required;
326
+ continue;
327
+ }
328
+ if (question.kind === "text") {
329
+ const value = values[0] ?? "";
330
+ if (value && question.validation?.minLength != null && value.length < question.validation.minLength) {
331
+ errors[question.id] = messages.minLength(question.validation.minLength);
332
+ continue;
333
+ }
334
+ if (question.validation?.maxLength != null && value.length > question.validation.maxLength) {
335
+ errors[question.id] = messages.maxLength(question.validation.maxLength);
336
+ continue;
337
+ }
338
+ } else {
339
+ if (draft.otherSelected && !other) {
340
+ errors[question.id] = messages.otherRequired;
341
+ continue;
342
+ }
343
+ const min = question.validation?.minSelections;
344
+ const max = question.kind === "single_select" ? 1 : question.validation?.maxSelections;
345
+ if (min != null && supplied < min) {
346
+ errors[question.id] = messages.minSelections(min);
347
+ continue;
348
+ }
349
+ if (max != null && supplied > max) {
350
+ errors[question.id] = messages.maxSelections(max);
351
+ continue;
352
+ }
353
+ }
354
+ if (supplied > 0) {
355
+ answers.push({
356
+ questionId: question.id,
357
+ values,
358
+ ...other ? { other } : {}
359
+ });
360
+ }
361
+ }
362
+ return { answers, errors };
363
+ }
364
+ function initialDrafts(questions) {
365
+ return Object.fromEntries(questions.map((question) => [question.id, emptyDraft()]));
366
+ }
367
+ function emptyDraft() {
368
+ return { values: [], other: "", otherSelected: false };
369
+ }
370
+ function formatDeadline(value) {
371
+ const date = new Date(value);
372
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
373
+ }
374
+
375
+ // src/timeline/registry.ts
376
+ function rawTypeOf(item) {
377
+ const raw = item.raw;
378
+ if (raw && typeof raw === "object" && typeof raw.type === "string") {
379
+ return raw.type;
380
+ }
381
+ return null;
382
+ }
383
+ function createToolRegistry(baseEntries, baseFallback, options = {}) {
384
+ const entries = [...options.entries ?? [], ...baseEntries];
385
+ const fallback = options.fallback ?? baseFallback;
386
+ const byRawType = /* @__PURE__ */ new Map();
387
+ const byName = /* @__PURE__ */ new Map();
388
+ for (const entry of entries) {
389
+ if (entry.match === "rawType") {
390
+ if (!byRawType.has(entry.type)) {
391
+ byRawType.set(entry.type, entry.render);
392
+ }
393
+ } else if (!byName.has(entry.name)) {
394
+ byName.set(entry.name, entry.render);
395
+ }
396
+ }
397
+ const resolve = (item) => {
398
+ const rawType = rawTypeOf(item);
399
+ if (rawType) {
400
+ const byType = byRawType.get(rawType);
401
+ if (byType) {
402
+ return byType;
403
+ }
404
+ }
405
+ return byName.get(item.name) ?? fallback;
406
+ };
407
+ return { resolve, fallback };
408
+ }
409
+
410
+ // src/timeline/parsers.ts
411
+ function sandboxCommandExitCode(out) {
412
+ const match = String(out ?? "").match(/Process exited with code (-?\d+)/);
413
+ return match ? Number(match[1]) : null;
414
+ }
415
+ function parseExecBannerSessionId(out) {
416
+ const text = String(out ?? "");
417
+ const outputIdx = text.indexOf("\nOutput:\n");
418
+ const banner = outputIdx >= 0 ? text.slice(0, outputIdx) : text.startsWith("Output:\n") ? "" : text;
419
+ const match = banner.match(/Process running with session ID (\d+)/);
420
+ if (!match) {
421
+ return null;
422
+ }
423
+ const n = Number.parseInt(match[1], 10);
424
+ return Number.isFinite(n) ? n : null;
425
+ }
426
+ function stripExecBanner(out) {
427
+ const text = String(out ?? "");
428
+ const marker = text.indexOf("\nOutput:\n");
429
+ if (marker >= 0) {
430
+ return text.slice(marker + "\nOutput:\n".length);
431
+ }
432
+ if (text.startsWith("Output:\n")) {
433
+ return text.slice("Output:\n".length);
434
+ }
435
+ return text;
436
+ }
437
+ function execTruncated(out) {
438
+ return /Total output lines:|\.{3}\d+ tokens truncated\.{3}|\[\.{3}\d+ characters truncated/.test(
439
+ String(out ?? "")
440
+ );
441
+ }
442
+ function isExecSessionLostBanner(out) {
443
+ return /write_stdin failed: session not found: \d+/.test(String(out ?? ""));
444
+ }
445
+ function looksBinary(text) {
446
+ return text.includes("\0") || text.startsWith("\x7FELF");
447
+ }
448
+ function controlCaret(printable) {
449
+ return String(printable).replace(
450
+ /[\u0000-\u001f]/g,
451
+ (c) => `^${String.fromCharCode(c.charCodeAt(0) + 64)}`
452
+ );
453
+ }
454
+ function v4aToGitFileDiff(op) {
455
+ const status = op.type === "create_file" ? "added" : op.type === "delete_file" ? "deleted" : op.moveTo ? "renamed" : "modified";
456
+ const oldPath = op.moveTo ? op.path : null;
457
+ const path = op.moveTo || op.path;
458
+ const hunks = [];
459
+ let additions = 0;
460
+ let deletions = 0;
461
+ let sawHunkAnchor = false;
462
+ if (op.type !== "delete_file") {
463
+ const lines = (op.diff ?? "").split("\n");
464
+ let cur = null;
465
+ let oldNo = 1;
466
+ let newNo = 1;
467
+ for (const raw of lines) {
468
+ if (raw.startsWith("@@")) {
469
+ sawHunkAnchor = true;
470
+ const match = raw.match(/-(\d+)(?:,\d+)?\s+\+(\d+)/);
471
+ oldNo = match ? Number(match[1]) : 1;
472
+ newNo = match ? Number(match[2]) : 1;
473
+ cur = {
474
+ oldStart: oldNo,
475
+ oldLines: 0,
476
+ newStart: newNo,
477
+ newLines: 0,
478
+ header: raw,
479
+ lines: [{ type: "meta", oldNo: null, newNo: null, text: raw }]
480
+ };
481
+ hunks.push(cur);
482
+ } else if (cur || op.type === "create_file") {
483
+ if (!cur) {
484
+ cur = {
485
+ oldStart: 0,
486
+ oldLines: 0,
487
+ newStart: 1,
488
+ newLines: 0,
489
+ header: "",
490
+ lines: []
491
+ };
492
+ hunks.push(cur);
493
+ oldNo = 0;
494
+ newNo = 1;
495
+ }
496
+ if (raw.startsWith("+")) {
497
+ cur.lines.push({
498
+ type: "add",
499
+ oldNo: null,
500
+ newNo: newNo++,
501
+ text: raw.slice(1)
502
+ });
503
+ cur.newLines += 1;
504
+ additions += 1;
505
+ } else if (raw.startsWith("-")) {
506
+ cur.lines.push({
507
+ type: "del",
508
+ oldNo: oldNo++,
509
+ newNo: null,
510
+ text: raw.slice(1)
511
+ });
512
+ cur.oldLines += 1;
513
+ deletions += 1;
514
+ } else {
515
+ cur.lines.push({
516
+ type: "context",
517
+ oldNo: oldNo++,
518
+ newNo: newNo++,
519
+ text: raw.replace(/^ /, "")
520
+ });
521
+ cur.oldLines += 1;
522
+ cur.newLines += 1;
523
+ }
524
+ }
525
+ }
526
+ if (op.type === "update_file" && !sawHunkAnchor && lines.some((l) => l.trim().length > 0)) {
527
+ throw new Error("malformed V4A: no @@ hunk anchor");
528
+ }
529
+ }
530
+ return {
531
+ path,
532
+ oldPath,
533
+ status,
534
+ isBinary: false,
535
+ isImage: false,
536
+ additions,
537
+ deletions,
538
+ hunks,
539
+ truncated: false
540
+ };
541
+ }
542
+ function applyPatchOps(raw) {
543
+ const r = raw ?? {};
544
+ if (Array.isArray(r.operations)) {
545
+ return r.operations;
546
+ }
547
+ return r.operation ? [r.operation] : [];
548
+ }
549
+ function isApplyPatch(item) {
550
+ const type = item.raw && typeof item.raw === "object" ? item.raw.type : void 0;
551
+ return type === "apply_patch_call" || item.name === "apply_patch_call";
552
+ }
553
+ var SECRET_KEY = /^(value|secret|token|password|api[_-]?key|signing[_-]?key)$/i;
554
+ function redactSecrets(value) {
555
+ if (Array.isArray(value)) {
556
+ return value.map(redactSecrets);
557
+ }
558
+ if (value && typeof value === "object") {
559
+ const out = {};
560
+ for (const [k, v] of Object.entries(value)) {
561
+ out[k] = SECRET_KEY.test(k) ? "\u2022\u2022\u2022\u2022" : redactSecrets(v);
562
+ }
563
+ return out;
564
+ }
565
+ return value;
566
+ }
567
+ function parseToolArgs(args) {
568
+ if (args == null) {
569
+ return {};
570
+ }
571
+ if (typeof args === "string") {
572
+ const parsed = tryParseJson(args);
573
+ return parsed && typeof parsed === "object" ? parsed : {};
574
+ }
575
+ return typeof args === "object" ? args : {};
576
+ }
577
+ function tailPeek(text) {
578
+ const trimmed = text.trim();
579
+ if (!trimmed) {
580
+ return "";
581
+ }
582
+ const lines = trimmed.split("\n");
583
+ return lines[lines.length - 1] ?? "";
584
+ }
585
+ function unwrapMcpOutput(output) {
586
+ if (output && typeof output === "object" && output.type === "text" && typeof output.text === "string") {
587
+ const record = output;
588
+ return { text: record.text, isError: Boolean(record.isError) };
589
+ }
590
+ if (output && typeof output === "object" && "content" in output) {
591
+ const record = output;
592
+ const isError = Boolean(record.isError);
593
+ if (Array.isArray(record.content)) {
594
+ const textPart = record.content.find(
595
+ (part) => !!part && typeof part === "object" && part.type === "text"
596
+ );
597
+ return {
598
+ text: textPart ? String(textPart.text) : JSON.stringify(output),
599
+ isError
600
+ };
601
+ }
602
+ return { text: JSON.stringify(output), isError };
603
+ }
604
+ return {
605
+ text: typeof output === "string" ? output : output == null ? "" : JSON.stringify(output),
606
+ isError: false
607
+ };
608
+ }
609
+ function screenshotDataUrl(out) {
610
+ if (typeof out === "string") {
611
+ if (out.startsWith("data:image")) {
612
+ return out;
613
+ }
614
+ if (out.startsWith("{") || out.startsWith("[")) {
615
+ const parsed = tryParseJson(out);
616
+ if (parsed !== void 0 && parsed !== out) {
617
+ return screenshotDataUrl(parsed);
618
+ }
619
+ }
620
+ return null;
621
+ }
622
+ if (Array.isArray(out)) {
623
+ for (const entry of out) {
624
+ const url = screenshotDataUrl(entry);
625
+ if (url) {
626
+ return url;
627
+ }
628
+ }
629
+ return null;
630
+ }
631
+ if (out === null || typeof out !== "object") {
632
+ return null;
633
+ }
634
+ const record = out;
635
+ const imageUrl = record.image_url ?? record.imageUrl;
636
+ if (typeof imageUrl === "string" && imageUrl.startsWith("data:image")) {
637
+ return imageUrl;
638
+ }
639
+ if (imageUrl && typeof imageUrl === "object") {
640
+ const url = imageUrl.url;
641
+ if (typeof url === "string" && url.startsWith("data:image")) {
642
+ return url;
643
+ }
644
+ }
645
+ const image = record.image;
646
+ if (image && typeof image === "object") {
647
+ const mediaType = typeof image.mediaType === "string" ? image.mediaType : "image/png";
648
+ const base64 = bytesToBase64(image.data);
649
+ if (base64) {
650
+ return `data:${mediaType};base64,${base64}`;
651
+ }
652
+ if (typeof image.data === "string" && image.data.length > 0) {
653
+ return `data:${mediaType};base64,${image.data}`;
654
+ }
655
+ }
656
+ return null;
657
+ }
658
+ function mediaPreviewFact(out) {
659
+ if (typeof out === "string" && (out.startsWith("{") || out.startsWith("["))) {
660
+ const parsed = tryParseJson(out);
661
+ if (parsed !== void 0 && parsed !== out) return mediaPreviewFact(parsed);
662
+ }
663
+ if (Array.isArray(out)) {
664
+ for (const entry of out) {
665
+ const preview = mediaPreviewFact(entry);
666
+ if (preview) return preview;
667
+ }
668
+ return null;
669
+ }
670
+ if (!out || typeof out !== "object") return null;
671
+ const record = out;
672
+ if (record.type !== "media_preview" || typeof record.mediaType !== "string" || record.fullOutputAvailable !== false || typeof record.preview !== "string" || record.inlineBytes !== null && typeof record.inlineBytes !== "number") {
673
+ return null;
674
+ }
675
+ return record;
676
+ }
677
+ function bytesToBase64(data) {
678
+ const isByte = (n) => typeof n === "number" && Number.isInteger(n) && n >= 0 && n <= 255;
679
+ let bytes = null;
680
+ if (Array.isArray(data) && data.every(isByte)) {
681
+ bytes = data;
682
+ } else if (data && typeof data === "object") {
683
+ const record = data;
684
+ if (record.type === "Buffer" && Array.isArray(record.data) && record.data.every(isByte)) {
685
+ bytes = record.data;
686
+ } else {
687
+ const keys = Object.keys(record);
688
+ if (keys.length > 0 && keys.every((key) => /^\d+$/.test(key))) {
689
+ const values = keys.sort((a, b) => Number(a) - Number(b)).map((key) => record[key]);
690
+ if (values.every(isByte)) {
691
+ bytes = values;
692
+ }
693
+ }
694
+ }
695
+ }
696
+ if (!bytes || bytes.length === 0) {
697
+ return null;
698
+ }
699
+ try {
700
+ let binary = "";
701
+ const CHUNK = 32768;
702
+ for (let i = 0; i < bytes.length; i += CHUNK) {
703
+ binary += String.fromCharCode(...bytes.slice(i, i + CHUNK));
704
+ }
705
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(binary, "binary").toString("base64");
706
+ } catch {
707
+ return null;
708
+ }
709
+ }
710
+
711
+ // src/timeline/disclosure-context.tsx
712
+ import { createContext, useContext } from "react";
713
+ import { jsx as jsx3 } from "react/jsx-runtime";
714
+ var DisclosureDefaultsContext = createContext(void 0);
715
+ function DisclosureDefaultsProvider({
716
+ defaultOpen,
717
+ children
718
+ }) {
719
+ return /* @__PURE__ */ jsx3(DisclosureDefaultsContext.Provider, { value: defaultOpen, children });
720
+ }
721
+ function useForcedDefaultOpen() {
722
+ return useContext(DisclosureDefaultsContext);
723
+ }
724
+
725
+ // src/timeline/screenshot-lightbox.tsx
726
+ import { XIcon } from "lucide-react";
727
+ import { AnimatePresence, motion } from "motion/react";
728
+ import { createContext as createContext2, useCallback, useContext as useContext2, useMemo, useState as useState2 } from "react";
729
+ import { Dialog } from "radix-ui";
730
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
731
+ var LightboxContext = createContext2(null);
732
+ function useLightbox() {
733
+ return useContext2(LightboxContext) ?? NOOP;
734
+ }
735
+ function useLightboxOptional() {
736
+ return useContext2(LightboxContext);
737
+ }
738
+ var NOOP = { open: () => {
739
+ } };
740
+ function LightboxProvider({ children }) {
741
+ const ancestor = useContext2(LightboxContext);
742
+ if (ancestor) {
743
+ return /* @__PURE__ */ jsx4(Fragment, { children });
744
+ }
745
+ return /* @__PURE__ */ jsx4(LightboxRoot, { children });
746
+ }
747
+ function LightboxRoot({ children }) {
748
+ const [state, setState] = useState2(null);
749
+ const open = useCallback((src, caption) => {
750
+ setState(caption ? { src, caption } : { src });
751
+ }, []);
752
+ const controller = useMemo(() => ({ open }), [open]);
753
+ return /* @__PURE__ */ jsxs3(LightboxContext.Provider, { value: controller, children: [
754
+ children,
755
+ /* @__PURE__ */ jsx4(Dialog.Root, { open: state !== null, onOpenChange: (next) => !next && setState(null), children: /* @__PURE__ */ jsx4(AnimatePresence, { children: state !== null ? /* @__PURE__ */ jsxs3(Dialog.Portal, { forceMount: true, children: [
756
+ /* @__PURE__ */ jsx4(Dialog.Overlay, { asChild: true, forceMount: true, children: /* @__PURE__ */ jsx4(
757
+ motion.div,
758
+ {
759
+ initial: { opacity: 0 },
760
+ animate: { opacity: 1 },
761
+ exit: { opacity: 0 },
762
+ transition: { duration: 0.15 },
763
+ className: "og-root fixed inset-0 z-50 bg-black/90 backdrop-blur-md"
764
+ }
765
+ ) }),
766
+ /* @__PURE__ */ jsx4(
767
+ Dialog.Content,
768
+ {
769
+ asChild: true,
770
+ forceMount: true,
771
+ "aria-label": "Screenshot",
772
+ onOpenAutoFocus: (event) => event.preventDefault(),
773
+ children: /* @__PURE__ */ jsxs3(
774
+ motion.div,
775
+ {
776
+ initial: { opacity: 0, scale: 0.98 },
777
+ animate: { opacity: 1, scale: 1 },
778
+ exit: { opacity: 0, scale: 0.98 },
779
+ transition: { duration: 0.18, ease: [0.16, 1, 0.3, 1] },
780
+ onClick: (event) => {
781
+ if (event.target === event.currentTarget) {
782
+ setState(null);
783
+ }
784
+ },
785
+ className: "og-root fixed inset-0 z-50 flex items-center justify-center p-6 sm:p-12",
786
+ children: [
787
+ /* @__PURE__ */ jsx4(Dialog.Title, { className: "sr-only", children: "Screenshot" }),
788
+ /* @__PURE__ */ jsxs3("figure", { className: "m-0 flex max-h-full max-w-5xl flex-col items-center gap-3", children: [
789
+ /* @__PURE__ */ jsxs3("div", { className: "relative flex min-h-0 w-fit max-w-full", children: [
790
+ /* @__PURE__ */ jsx4(
791
+ "img",
792
+ {
793
+ src: state.src,
794
+ alt: state.caption ?? "Screenshot",
795
+ className: "min-h-0 max-h-[82vh] w-auto max-w-full rounded-og-md border border-white/10 object-contain shadow-og-lg"
796
+ }
797
+ ),
798
+ /* @__PURE__ */ jsx4(
799
+ Dialog.Close,
800
+ {
801
+ className: cn(
802
+ "absolute -right-3 -top-3 inline-flex size-9 items-center justify-center rounded-full",
803
+ "border border-white/15 bg-black/60 text-white/70 backdrop-blur",
804
+ "transition-colors hover:border-white/30 hover:text-white"
805
+ ),
806
+ "aria-label": "Close",
807
+ children: /* @__PURE__ */ jsx4(XIcon, { className: "size-4" })
808
+ }
809
+ )
810
+ ] }),
811
+ state.caption ? /* @__PURE__ */ jsx4("figcaption", { className: "max-w-2xl text-center font-og-mono text-og-xs text-white/55", children: state.caption }) : /* @__PURE__ */ jsx4("figcaption", { className: "font-og-mono text-og-xs uppercase tracking-[0.1em] text-white/35", children: "Esc or click outside to close" })
812
+ ] })
813
+ ]
814
+ }
815
+ )
816
+ }
817
+ )
818
+ ] }) : null }) })
819
+ ] });
820
+ }
821
+
822
+ // src/timeline/shared.tsx
823
+ import { CameraIcon, CameraOffIcon, ChevronRightIcon } from "lucide-react";
824
+ import { useState as useState3 } from "react";
825
+ import { Collapsible } from "radix-ui";
826
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
827
+ var ICON_TONE = {
828
+ accent: "text-og-accent",
829
+ failed: "text-og-status-failed",
830
+ running: "text-og-status-running",
831
+ muted: "text-og-fg-subtle"
832
+ };
833
+ function ActivityDisclosure({
834
+ icon,
835
+ iconTone: iconToneProp = "muted",
836
+ title,
837
+ titleMono,
838
+ running,
839
+ preview,
840
+ media,
841
+ chip: chipProp,
842
+ failed,
843
+ cancelled,
844
+ expandable = true,
845
+ children
846
+ }) {
847
+ const iconTone = failed && iconToneProp === "muted" ? "failed" : iconToneProp;
848
+ const chip = chipProp ?? (failed ? { tone: "bad", text: "failed" } : cancelled ? { tone: "interrupted", text: "interrupted" } : void 0);
849
+ const forcedDefaultOpen = useForcedDefaultOpen();
850
+ const [open, setOpen] = useState3(forcedDefaultOpen ?? false);
851
+ const hasBody = expandable && children != null;
852
+ const previewVisible = preview != null && !open;
853
+ const rowClass = cn(
854
+ "group/disclosure flex w-full min-w-0 items-center gap-2 rounded-og-sm px-1.5 py-1.5 text-left text-og-base",
855
+ "text-og-fg-muted transition-colors duration-150",
856
+ // A tool row is a touch target on coarse pointers: grow its padding so the
857
+ // hit area clears the 40px minimum without loosening the dense desktop rail.
858
+ "pointer-coarse:py-2.5"
859
+ );
860
+ const inner = /* @__PURE__ */ jsxs4(Fragment2, { children: [
861
+ hasBody ? /* @__PURE__ */ jsx5(ChevronRightIcon, { className: "size-3.5 shrink-0 text-og-fg-subtle transition-transform duration-150 ease-og-in-out group-data-[state=open]/disclosure:rotate-90" }) : /* @__PURE__ */ jsx5("span", { className: "size-3.5 shrink-0" }),
862
+ /* @__PURE__ */ jsx5("span", { className: cn("shrink-0", ICON_TONE[iconTone]), children: icon }),
863
+ /* @__PURE__ */ jsx5(
864
+ "span",
865
+ {
866
+ className: cn(
867
+ "min-w-0 shrink truncate text-og-base font-medium",
868
+ titleMono && "font-og-mono text-og-sm font-normal",
869
+ running && "og-shimmer-text"
870
+ ),
871
+ children: title
872
+ }
873
+ ),
874
+ previewVisible && !media ? /* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate text-og-sm text-og-fg-subtle", children: preview }) : /* @__PURE__ */ jsx5("span", { className: "flex-1" }),
875
+ media ? /* @__PURE__ */ jsx5("span", { className: "ml-auto flex shrink-0 items-center gap-2 pl-2", children: media }) : chip && !open ? /* @__PURE__ */ jsx5("span", { className: "ml-auto shrink-0 pl-2", children: /* @__PURE__ */ jsx5(Chip, { chip }) }) : null
876
+ ] });
877
+ const dataStatus = cancelled ? "cancelled" : failed ? "failed" : void 0;
878
+ if (!hasBody) {
879
+ return /* @__PURE__ */ jsx5("div", { className: cn(rowClass, "cursor-default"), "data-status": dataStatus, children: inner });
880
+ }
881
+ return /* @__PURE__ */ jsxs4(Collapsible.Root, { open, onOpenChange: setOpen, children: [
882
+ /* @__PURE__ */ jsx5(Collapsible.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5(
883
+ "div",
884
+ {
885
+ role: "button",
886
+ tabIndex: 0,
887
+ onKeyDown: (event) => {
888
+ if (event.key === "Enter" || event.key === " ") {
889
+ event.preventDefault();
890
+ setOpen((prev) => !prev);
891
+ }
892
+ },
893
+ "data-status": dataStatus,
894
+ className: cn(
895
+ rowClass,
896
+ "cursor-pointer outline-none hover:bg-og-surface-1 hover:text-og-fg",
897
+ "focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-0"
898
+ ),
899
+ children: inner
900
+ }
901
+ ) }),
902
+ /* @__PURE__ */ jsx5(Collapsible.Content, { className: "overflow-hidden data-[state=closed]:animate-og-collapse data-[state=open]:animate-og-expand", children: /* @__PURE__ */ jsx5("div", { className: "mb-2 ml-7 mt-1.5 flex flex-col gap-2", children }) })
903
+ ] });
904
+ }
905
+ function Chip({ chip }) {
906
+ const base = "inline-flex items-center font-og-mono text-og-xs leading-none";
907
+ const withText = chip.text ? "gap-1.5" : "gap-0";
908
+ if (chip.tone === "bad") {
909
+ return /* @__PURE__ */ jsxs4("span", { className: cn(base, withText, "text-og-status-failed"), children: [
910
+ /* @__PURE__ */ jsx5("span", { className: "size-1.5 rounded-full bg-og-status-failed" }),
911
+ chip.text
912
+ ] });
913
+ }
914
+ if (chip.tone === "interrupted") {
915
+ return /* @__PURE__ */ jsx5("span", { className: cn(base, "text-og-fg-subtle og-cancelled-chip"), children: chip.text });
916
+ }
917
+ return /* @__PURE__ */ jsx5("span", { className: cn(base, "text-og-fg-subtle"), children: chip.text });
918
+ }
919
+ function TermBlock({
920
+ command,
921
+ workdir,
922
+ output,
923
+ live,
924
+ tailLines = 12,
925
+ failed
926
+ }) {
927
+ const [full, setFull] = useState3(false);
928
+ const empty = output.trim() === "";
929
+ const lines = output.split("\n");
930
+ const big = lines.length > tailLines + 4;
931
+ const shown = full || !big ? output : lines.slice(-tailLines).join("\n");
932
+ const showMore = big && !full;
933
+ const showHeader = command != null || workdir != null;
934
+ return /* @__PURE__ */ jsxs4(
935
+ "div",
936
+ {
937
+ className: cn(
938
+ "min-w-0 border-l-2 pl-3",
939
+ failed ? "border-og-status-failed/50" : live ? "border-og-status-running/50" : "border-og-border"
940
+ ),
941
+ children: [
942
+ showHeader ? /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 pb-1", children: [
943
+ /* @__PURE__ */ jsx5("span", { className: "select-none text-og-status-idle", children: "$" }),
944
+ command != null ? /* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate font-og-mono text-og-sm text-og-fg-muted", children: command }) : /* @__PURE__ */ jsx5("span", { className: "flex-1" }),
945
+ workdir ? /* @__PURE__ */ jsx5("span", { className: "shrink-0 font-og-mono text-og-xs text-og-fg-subtle", children: workdir }) : null
946
+ ] }) : null,
947
+ empty ? /* @__PURE__ */ jsx5("p", { className: "font-og-mono text-og-xs italic text-og-fg-subtle", children: "(no output)" }) : /* @__PURE__ */ jsxs4("pre", { className: "max-h-72 overflow-auto whitespace-pre-wrap break-all font-og-mono text-og-xs leading-5 text-og-fg-muted", children: [
948
+ shown,
949
+ live ? /* @__PURE__ */ jsx5("span", { className: "ml-px inline-block h-[1em] w-[2px] translate-y-[2px] animate-og-blink bg-og-accent align-middle" }) : null
950
+ ] }),
951
+ showMore ? /* @__PURE__ */ jsxs4(
952
+ "button",
953
+ {
954
+ type: "button",
955
+ onClick: () => setFull(true),
956
+ className: "mt-1 text-left text-og-xs text-og-fg-subtle transition-colors hover:text-og-fg",
957
+ children: [
958
+ "show full output (",
959
+ lines.length,
960
+ " lines)"
961
+ ]
962
+ }
963
+ ) : null
964
+ ]
965
+ }
966
+ );
967
+ }
968
+ function PayloadBlock({
969
+ label,
970
+ value,
971
+ failed
972
+ }) {
973
+ const text = typeof value === "string" ? value : stringifyPayload(value);
974
+ if (!text || text.trim() === "") {
975
+ return null;
976
+ }
977
+ return /* @__PURE__ */ jsxs4(
978
+ "div",
979
+ {
980
+ className: cn(
981
+ "min-w-0 border-l-2 pl-3",
982
+ failed ? "border-og-status-failed/50" : "border-og-border"
983
+ ),
984
+ children: [
985
+ /* @__PURE__ */ jsx5("p", { className: "mb-1 text-og-xs font-medium uppercase tracking-[0.08em] text-og-fg-subtle", children: label }),
986
+ /* @__PURE__ */ jsx5(
987
+ "pre",
988
+ {
989
+ className: cn(
990
+ "max-h-64 overflow-auto whitespace-pre-wrap break-all font-og-mono text-og-xs leading-5",
991
+ failed ? "text-og-status-failed" : "text-og-fg-muted"
992
+ ),
993
+ children: text
994
+ }
995
+ )
996
+ ]
997
+ }
998
+ );
999
+ }
1000
+ function BodyNote({
1001
+ children,
1002
+ tone
1003
+ }) {
1004
+ if (tone === "error") {
1005
+ return /* @__PURE__ */ jsx5("div", { className: "border-l-2 border-og-status-failed/50 pl-3 font-og-mono text-og-xs leading-5 text-og-status-failed", children });
1006
+ }
1007
+ return /* @__PURE__ */ jsx5("p", { className: "px-0.5 text-og-sm italic leading-5 text-og-fg-subtle", children });
1008
+ }
1009
+ var MEDIA_BOX = "h-7 w-[52px] shrink-0 rounded-og-xs border border-og-border";
1010
+ function MediaSkeleton() {
1011
+ return /* @__PURE__ */ jsxs4(
1012
+ "span",
1013
+ {
1014
+ className: cn(
1015
+ MEDIA_BOX,
1016
+ "relative inline-flex items-center justify-center overflow-hidden bg-og-surface-2"
1017
+ ),
1018
+ children: [
1019
+ /* @__PURE__ */ jsx5("span", { className: "absolute inset-0 animate-og-pulse bg-og-surface-3/50" }),
1020
+ /* @__PURE__ */ jsx5(CameraIcon, { className: "relative size-3.5 text-og-fg-subtle" })
1021
+ ]
1022
+ }
1023
+ );
1024
+ }
1025
+ function MediaEmpty() {
1026
+ return /* @__PURE__ */ jsx5("span", { className: cn(MEDIA_BOX, "inline-flex items-center justify-center bg-og-bg"), children: /* @__PURE__ */ jsx5(CameraOffIcon, { className: "size-3.5 text-og-fg-subtle" }) });
1027
+ }
1028
+ function Thumbnail({
1029
+ src,
1030
+ caption,
1031
+ alt = "screenshot"
1032
+ }) {
1033
+ const lightbox = useLightboxOptional();
1034
+ const [failed, setFailed] = useState3(false);
1035
+ if (failed) {
1036
+ return /* @__PURE__ */ jsx5(MediaEmpty, {});
1037
+ }
1038
+ const img = /* @__PURE__ */ jsx5(
1039
+ "img",
1040
+ {
1041
+ src,
1042
+ alt,
1043
+ onError: () => setFailed(true),
1044
+ className: "h-full w-full object-cover transition-opacity group-hover/thumb:opacity-80"
1045
+ }
1046
+ );
1047
+ if (!lightbox) {
1048
+ return /* @__PURE__ */ jsx5("span", { className: cn(MEDIA_BOX, "inline-flex overflow-hidden bg-og-bg"), children: img });
1049
+ }
1050
+ return /* @__PURE__ */ jsx5(
1051
+ "button",
1052
+ {
1053
+ type: "button",
1054
+ onClick: (event) => {
1055
+ event.stopPropagation();
1056
+ lightbox.open(src, caption);
1057
+ },
1058
+ onKeyDown: (event) => {
1059
+ if (event.key === "Enter" || event.key === " ") {
1060
+ event.stopPropagation();
1061
+ }
1062
+ },
1063
+ className: cn(
1064
+ MEDIA_BOX,
1065
+ "group/thumb relative inline-flex overflow-hidden bg-og-bg outline-none",
1066
+ "focus-visible:ring-2 focus-visible:ring-og-accent"
1067
+ ),
1068
+ "aria-label": "Expand screenshot",
1069
+ children: img
1070
+ }
1071
+ );
1072
+ }
1073
+ function ScreenshotFigure({
1074
+ src,
1075
+ caption,
1076
+ alt = "screenshot"
1077
+ }) {
1078
+ const lightbox = useLightboxOptional();
1079
+ const [failed, setFailed] = useState3(false);
1080
+ const surface = "block w-full overflow-hidden rounded-og-md border border-og-border bg-og-bg";
1081
+ const img = /* @__PURE__ */ jsx5(
1082
+ "img",
1083
+ {
1084
+ src,
1085
+ alt,
1086
+ onError: () => setFailed(true),
1087
+ className: "max-h-80 w-full object-contain"
1088
+ }
1089
+ );
1090
+ return /* @__PURE__ */ jsxs4("figure", { className: "m-0 min-w-0", children: [
1091
+ failed ? /* @__PURE__ */ jsx5("div", { className: "rounded-og-md border border-og-border bg-og-bg px-3 py-6 text-center font-og-mono text-og-xs text-og-fg-subtle", children: "image unavailable" }) : lightbox ? /* @__PURE__ */ jsx5(
1092
+ "button",
1093
+ {
1094
+ type: "button",
1095
+ onClick: () => lightbox.open(src, caption),
1096
+ className: surface,
1097
+ "aria-label": "Expand screenshot",
1098
+ children: img
1099
+ }
1100
+ ) : /* @__PURE__ */ jsx5("div", { className: surface, children: img }),
1101
+ caption ? /* @__PURE__ */ jsx5("figcaption", { className: "mt-1.5 font-og-mono text-og-xs text-og-fg-subtle", children: caption }) : null
1102
+ ] });
1103
+ }
1104
+
1105
+ // src/lib/git-patch.ts
1106
+ function gitFileDiffToPatch(file) {
1107
+ const oldPath = file.oldPath ?? file.path;
1108
+ const newPath = file.path;
1109
+ const lines = [];
1110
+ lines.push(`diff --git a/${oldPath} b/${newPath}`);
1111
+ if (file.status === "deleted") {
1112
+ lines.push(`--- a/${oldPath}`);
1113
+ lines.push(`+++ /dev/null`);
1114
+ } else if (file.status === "added" || file.status === "untracked") {
1115
+ lines.push(`--- /dev/null`);
1116
+ lines.push(`+++ b/${newPath}`);
1117
+ } else {
1118
+ lines.push(`--- a/${oldPath}`);
1119
+ lines.push(`+++ b/${newPath}`);
1120
+ }
1121
+ for (const hunk of file.hunks) {
1122
+ const headerIsValid = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/.test(hunk.header ?? "");
1123
+ const header = headerIsValid ? hunk.header : `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;
1124
+ lines.push(header);
1125
+ for (const line of hunk.lines) {
1126
+ if (line.type === "meta") continue;
1127
+ const prefix = line.type === "add" ? "+" : line.type === "del" ? "-" : " ";
1128
+ lines.push(`${prefix}${line.text}`);
1129
+ }
1130
+ }
1131
+ return lines.join("\n") + "\n";
1132
+ }
1133
+
1134
+ // src/components/pierre-diff.tsx
1135
+ import {
1136
+ lazy as lazy2,
1137
+ Suspense as Suspense2,
1138
+ useEffect as useEffect2,
1139
+ useState as useState4
1140
+ } from "react";
1141
+ import { jsx as jsx6 } from "react/jsx-runtime";
1142
+ var LazyPatchDiff = lazy2(async () => {
1143
+ const mod = await import("@pierre/diffs/react");
1144
+ return { default: mod.PatchDiff };
1145
+ });
1146
+ function PierreDiff({
1147
+ diff,
1148
+ layout = "unified",
1149
+ themeType,
1150
+ theme,
1151
+ disableWorkerPool,
1152
+ loading,
1153
+ fallback,
1154
+ plain,
1155
+ overflow = "wrap",
1156
+ className
1157
+ }) {
1158
+ const [failed, setFailed] = useState4(false);
1159
+ const [forcedColors, setForcedColors] = useState4(false);
1160
+ useEffect2(() => {
1161
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1162
+ const query = window.matchMedia("(forced-colors: active)");
1163
+ const update = () => setForcedColors(query.matches);
1164
+ update();
1165
+ query.addEventListener?.("change", update);
1166
+ return () => query.removeEventListener?.("change", update);
1167
+ }, []);
1168
+ useEffect2(() => {
1169
+ if (plain) return;
1170
+ let cancelled = false;
1171
+ void import("@pierre/diffs/react").catch(() => {
1172
+ if (!cancelled) setFailed(true);
1173
+ });
1174
+ return () => {
1175
+ cancelled = true;
1176
+ };
1177
+ }, [plain]);
1178
+ if (plain || failed || forcedColors) {
1179
+ return /* @__PURE__ */ jsx6(
1180
+ "div",
1181
+ {
1182
+ className,
1183
+ "data-opengeni-plain-diff": forcedColors ? "forced-colors" : "fallback",
1184
+ children: fallback ?? /* @__PURE__ */ jsx6(PlainPatch, { diff })
1185
+ }
1186
+ );
1187
+ }
1188
+ const options = {
1189
+ diffStyle: layout,
1190
+ overflow,
1191
+ stickyHeader: true,
1192
+ ...theme ? { theme } : {
1193
+ theme: {
1194
+ dark: "github-dark-high-contrast",
1195
+ light: "github-light-high-contrast"
1196
+ }
1197
+ },
1198
+ themeType: themeType ?? "dark"
1199
+ };
1200
+ const pierreVars = {
1201
+ "--diffs-dark-bg": "var(--og-color-bg)",
1202
+ "--diffs-light-bg": "var(--og-color-bg)",
1203
+ "--diffs-bg-buffer-override": "var(--og-color-surface-1)",
1204
+ "--diffs-bg-separator-override": "var(--og-color-surface-1)",
1205
+ "--diffs-addition-color-override": "var(--og-color-status-idle)",
1206
+ "--diffs-deletion-color-override": "var(--og-color-status-failed)",
1207
+ "--diffs-fg-number-addition-override": "var(--og-color-status-idle)",
1208
+ "--diffs-fg-number-deletion-override": "var(--og-color-status-failed)",
1209
+ "--diffs-bg-addition-override": "color-mix(in oklab, var(--og-color-status-idle) 12%, var(--og-color-bg))",
1210
+ "--diffs-bg-deletion-override": "color-mix(in oklab, var(--og-color-status-failed) 12%, var(--og-color-bg))",
1211
+ "--diffs-bg-addition-emphasis-override": "color-mix(in oklab, var(--og-color-status-idle) 22%, var(--og-color-bg))",
1212
+ "--diffs-bg-deletion-emphasis-override": "color-mix(in oklab, var(--og-color-status-failed) 22%, var(--og-color-bg))",
1213
+ "--diffs-header-font-family": "var(--og-font-sans)",
1214
+ "--diffs-font-family": "var(--og-font-mono)",
1215
+ "--diffs-font-size": "var(--og-code-font-size)",
1216
+ "--diffs-line-height": "var(--og-code-line-height)"
1217
+ };
1218
+ return /* @__PURE__ */ jsx6("div", { className: cn("min-w-0", className), "data-opengeni-pierre-diff": true, style: pierreVars, children: /* @__PURE__ */ jsx6(Suspense2, { fallback: loading ?? /* @__PURE__ */ jsx6(DiffSkeleton, {}), children: diff.map((file) => /* @__PURE__ */ jsx6("div", { className: "mb-2", children: /* @__PURE__ */ jsx6(
1219
+ LazyPatchDiff,
1220
+ {
1221
+ patch: gitFileDiffToPatch(file),
1222
+ options,
1223
+ ...disableWorkerPool !== void 0 ? { disableWorkerPool } : {}
1224
+ }
1225
+ ) }, file.path)) }) });
1226
+ }
1227
+ function DiffSkeleton() {
1228
+ return /* @__PURE__ */ jsx6("div", { className: "p-3 text-og-sm text-og-fg-subtle", children: "Loading diff\u2026" });
1229
+ }
1230
+ function PlainPatch({ diff }) {
1231
+ if (diff.length === 0) {
1232
+ return /* @__PURE__ */ jsx6("div", { className: "p-3 text-og-sm text-og-fg-subtle", children: "No changes" });
1233
+ }
1234
+ return /* @__PURE__ */ jsx6("div", { className: "min-w-0", children: diff.map((file) => /* @__PURE__ */ jsx6(
1235
+ "pre",
1236
+ {
1237
+ "aria-label": `Diff for ${file.path}`,
1238
+ className: "mb-2 overflow-auto whitespace-pre rounded-og-sm border border-og-border bg-og-bg/60 p-2.5 font-og-mono text-og-xs leading-5",
1239
+ role: "region",
1240
+ tabIndex: 0,
1241
+ children: gitFileDiffToPatch(file).split("\n").map((line, index) => /* @__PURE__ */ jsx6(
1242
+ "span",
1243
+ {
1244
+ className: cn(
1245
+ "block",
1246
+ line.startsWith("@@") ? "text-og-accent" : line.startsWith("+") ? "text-og-status-idle" : line.startsWith("-") ? "text-og-status-failed" : "text-og-fg-muted"
1247
+ ),
1248
+ children: line || " "
1249
+ },
1250
+ index
1251
+ ))
1252
+ },
1253
+ file.path
1254
+ )) });
1255
+ }
1256
+
1257
+ // src/timeline/tool-renderers.tsx
1258
+ import {
1259
+ CameraIcon as CameraIcon2,
1260
+ CameraOffIcon as CameraOffIcon2,
1261
+ FileDiffIcon,
1262
+ GlobeIcon,
1263
+ ImageIcon,
1264
+ KeyboardIcon,
1265
+ KeyRoundIcon,
1266
+ LockIcon,
1267
+ MousePointer2Icon,
1268
+ PlugIcon,
1269
+ SearchIcon,
1270
+ TerminalIcon,
1271
+ WrenchIcon
1272
+ } from "lucide-react";
1273
+
1274
+ // src/timeline/tool-diff.tsx
1275
+ import { useState as useState6 } from "react";
1276
+
1277
+ // src/lib/use-theme-type.ts
1278
+ import { useEffect as useEffect3, useState as useState5 } from "react";
1279
+ function useThemeType(forced) {
1280
+ const [detected, setDetected] = useState5("dark");
1281
+ useEffect3(() => {
1282
+ if (forced || typeof document === "undefined") return;
1283
+ const read = () => {
1284
+ const el = document.querySelector("[data-og-theme]");
1285
+ const value = el?.getAttribute("data-og-theme");
1286
+ setDetected(value === "light" ? "light" : "dark");
1287
+ };
1288
+ read();
1289
+ const observer = new MutationObserver(read);
1290
+ observer.observe(document.documentElement, {
1291
+ attributes: true,
1292
+ attributeFilter: ["data-og-theme"],
1293
+ subtree: true
1294
+ });
1295
+ return () => observer.disconnect();
1296
+ }, [forced]);
1297
+ return forced ?? detected;
1298
+ }
1299
+
1300
+ // src/timeline/tool-diff.tsx
1301
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1302
+ function ToolDiff({ files }) {
1303
+ const [layout, setLayout] = useState6("unified");
1304
+ const themeType = useThemeType(void 0);
1305
+ return /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1306
+ /* @__PURE__ */ jsx7("div", { className: "mb-1.5 flex justify-end", children: /* @__PURE__ */ jsx7(LayoutToggle, { layout, onChange: setLayout }) }),
1307
+ /* @__PURE__ */ jsx7(PierreDiff, { diff: files, layout, themeType })
1308
+ ] });
1309
+ }
1310
+ function LayoutToggle({
1311
+ layout,
1312
+ onChange
1313
+ }) {
1314
+ return /* @__PURE__ */ jsx7("div", { className: "inline-flex items-center gap-px rounded-og-xs border border-og-border p-px", children: ["unified", "split"].map((value) => /* @__PURE__ */ jsx7(
1315
+ "button",
1316
+ {
1317
+ type: "button",
1318
+ onClick: () => onChange(value),
1319
+ className: cn(
1320
+ "rounded-og-xs px-2 py-[3px] text-og-xs font-medium capitalize transition-colors",
1321
+ layout === value ? "bg-og-surface-2 text-og-fg" : "text-og-fg-subtle hover:text-og-fg-muted"
1322
+ ),
1323
+ children: value
1324
+ },
1325
+ value
1326
+ )) });
1327
+ }
1328
+ function RawPatch({ diff }) {
1329
+ const occurrences = /* @__PURE__ */ new Map();
1330
+ const lines = diff.split("\n").map((line) => {
1331
+ const occurrence = (occurrences.get(line) ?? 0) + 1;
1332
+ occurrences.set(line, occurrence);
1333
+ return { key: `${line}\0${occurrence}`, line };
1334
+ });
1335
+ return /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1336
+ /* @__PURE__ */ jsx7("p", { className: "mb-1 text-og-xs font-medium uppercase tracking-[0.08em] text-og-fg-subtle", children: "raw patch (could not parse hunks)" }),
1337
+ /* @__PURE__ */ jsx7("pre", { className: "max-h-72 overflow-auto border-l-2 border-og-border pl-3 font-og-mono text-og-xs leading-5", children: lines.map(({ key, line }) => /* @__PURE__ */ jsx7(
1338
+ "span",
1339
+ {
1340
+ className: cn(
1341
+ "block",
1342
+ line.startsWith("@@") ? "text-og-accent" : line.startsWith("+") ? "text-og-status-idle" : line.startsWith("-") ? "text-og-status-failed" : "text-og-fg-muted"
1343
+ ),
1344
+ children: line || " "
1345
+ },
1346
+ key
1347
+ )) })
1348
+ ] });
1349
+ }
1350
+
1351
+ // src/timeline/tool-display-name.ts
1352
+ function toolDisplayName(name) {
1353
+ const boundary = name.indexOf("__");
1354
+ const toolPart = boundary >= 0 ? name.slice(boundary + 2) : name;
1355
+ return toolPart.replace(/[_-]+/g, " ").trim();
1356
+ }
1357
+
1358
+ // src/timeline/tool-renderers.tsx
1359
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1360
+ var ICON_SIZE = "size-3.5";
1361
+ function RunningPreview({ children }) {
1362
+ return /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-1.5", children: [
1363
+ /* @__PURE__ */ jsx8("span", { className: "size-1.5 shrink-0 animate-og-pulse rounded-full bg-og-status-running" }),
1364
+ /* @__PURE__ */ jsx8("span", { className: "min-w-0 truncate", children })
1365
+ ] });
1366
+ }
1367
+ function ExecRenderer({ item }) {
1368
+ const args = parseToolArgs(item.arguments);
1369
+ const cmd = typeof args.cmd === "string" ? args.cmd : "";
1370
+ const workdir = typeof args.workdir === "string" ? args.workdir : null;
1371
+ const running = item.status === "running";
1372
+ const out = item.output;
1373
+ const title = `$ ${cmd}`;
1374
+ if (item.status === "failed" && out === void 0) {
1375
+ return /* @__PURE__ */ jsx8(
1376
+ ActivityDisclosure,
1377
+ {
1378
+ icon: /* @__PURE__ */ jsx8(TerminalIcon, { className: ICON_SIZE }),
1379
+ iconTone: "failed",
1380
+ title,
1381
+ titleMono: true,
1382
+ chip: { tone: "bad", text: "failed" },
1383
+ preview: "output lost \u2014 NUL byte could not be stored",
1384
+ children: /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: "output contained a NUL byte and could not be stored; the turn failed on this tool's output insert \u2014 no output event ever arrived." })
1385
+ }
1386
+ );
1387
+ }
1388
+ if (item.status === "failed" && (out == null || out === "")) {
1389
+ return /* @__PURE__ */ jsx8(
1390
+ ActivityDisclosure,
1391
+ {
1392
+ icon: /* @__PURE__ */ jsx8(TerminalIcon, { className: ICON_SIZE }),
1393
+ iconTone: "failed",
1394
+ title,
1395
+ titleMono: true,
1396
+ chip: { tone: "bad", text: "failed" },
1397
+ preview: "tool call failed",
1398
+ children: /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: "the tool call failed with no output." })
1399
+ }
1400
+ );
1401
+ }
1402
+ if (running) {
1403
+ const streamed = typeof out === "string" ? stripExecBanner(out) : "";
1404
+ return /* @__PURE__ */ jsx8(
1405
+ ActivityDisclosure,
1406
+ {
1407
+ icon: /* @__PURE__ */ jsx8(TerminalIcon, { className: ICON_SIZE }),
1408
+ iconTone: "running",
1409
+ title,
1410
+ titleMono: true,
1411
+ running: true,
1412
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: streamed ? `${streamed.split("\n").length} lines` : "running\u2026" }),
1413
+ children: /* @__PURE__ */ jsx8(TermBlock, { command: null, workdir, output: streamed, live: true })
1414
+ }
1415
+ );
1416
+ }
1417
+ const text = typeof out === "string" ? out : stringifyPayload(out);
1418
+ const stripped = stripExecBanner(text);
1419
+ const bgSession = parseExecBannerSessionId(text);
1420
+ const exitCode = sandboxCommandExitCode(text);
1421
+ const binary = looksBinary(stripped);
1422
+ let chip;
1423
+ let iconTone = "muted";
1424
+ if (bgSession != null) {
1425
+ chip = { tone: "muted", text: `session ${bgSession}` };
1426
+ } else if (exitCode != null && exitCode !== 0) {
1427
+ chip = { tone: "bad", text: `exit ${exitCode}` };
1428
+ iconTone = "failed";
1429
+ }
1430
+ const preview = binary ? "binary output" : tailPeek(stripped) || "(no output)";
1431
+ const truncated = execTruncated(text);
1432
+ const body = binary ? "(binary output suppressed)" : stripped;
1433
+ return /* @__PURE__ */ jsxs6(
1434
+ ActivityDisclosure,
1435
+ {
1436
+ icon: /* @__PURE__ */ jsx8(TerminalIcon, { className: ICON_SIZE }),
1437
+ iconTone,
1438
+ title,
1439
+ titleMono: true,
1440
+ ...chip ? { chip } : {},
1441
+ failed: item.status === "failed",
1442
+ cancelled: item.status === "cancelled",
1443
+ preview: truncated ? `\u22EF truncated \xB7 ${preview}` : preview,
1444
+ children: [
1445
+ /* @__PURE__ */ jsx8(
1446
+ TermBlock,
1447
+ {
1448
+ command: null,
1449
+ workdir,
1450
+ output: body,
1451
+ failed: item.status === "failed" || exitCode != null && exitCode !== 0
1452
+ }
1453
+ ),
1454
+ bgSession != null ? /* @__PURE__ */ jsxs6(BodyNote, { children: [
1455
+ "\u21B3 session ",
1456
+ bgSession,
1457
+ " \u2014 a later write_stdin can target this PTY."
1458
+ ] }) : null
1459
+ ]
1460
+ }
1461
+ );
1462
+ }
1463
+ function WriteStdinRenderer({ item }) {
1464
+ const args = parseToolArgs(item.arguments);
1465
+ const sessionId = typeof args.session_id === "string" || typeof args.session_id === "number" ? args.session_id : void 0;
1466
+ const running = item.status === "running";
1467
+ const text = typeof item.output === "string" ? item.output : stringifyPayload(item.output);
1468
+ const lost = isExecSessionLostBanner(text);
1469
+ const keys = controlCaret(typeof args.chars === "string" ? args.chars : "");
1470
+ const exitCode = sandboxCommandExitCode(text);
1471
+ const stripped = stripExecBanner(text);
1472
+ if (running) {
1473
+ return /* @__PURE__ */ jsx8(
1474
+ ActivityDisclosure,
1475
+ {
1476
+ icon: /* @__PURE__ */ jsx8(KeyboardIcon, { className: ICON_SIZE }),
1477
+ iconTone: "running",
1478
+ title: `session ${sessionId} \u2190 ${keys || "\u2205"}`,
1479
+ titleMono: true,
1480
+ running: true,
1481
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: "sending\u2026" }),
1482
+ children: /* @__PURE__ */ jsxs6(BodyNote, { children: [
1483
+ "sending input to session ",
1484
+ sessionId,
1485
+ "\u2026"
1486
+ ] })
1487
+ }
1488
+ );
1489
+ }
1490
+ let chip;
1491
+ if (lost) {
1492
+ chip = { tone: "bad", text: "lost" };
1493
+ } else if (exitCode != null && exitCode !== 0) {
1494
+ chip = { tone: "bad", text: `exit ${exitCode}` };
1495
+ }
1496
+ return /* @__PURE__ */ jsx8(
1497
+ ActivityDisclosure,
1498
+ {
1499
+ icon: /* @__PURE__ */ jsx8(KeyboardIcon, { className: ICON_SIZE }),
1500
+ iconTone: lost ? "failed" : "muted",
1501
+ title: `session ${sessionId} \u2190 ${keys || "\u2205"}`,
1502
+ titleMono: true,
1503
+ ...chip ? { chip } : {},
1504
+ failed: item.status === "failed",
1505
+ cancelled: item.status === "cancelled",
1506
+ preview: lost ? `session ${sessionId} PTY vanished` : tailPeek(stripped) || "sent",
1507
+ children: lost ? /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: stripped || text }) : /* @__PURE__ */ jsx8(TermBlock, { command: `write_stdin \u2192 session ${sessionId}`, output: stripped })
1508
+ }
1509
+ );
1510
+ }
1511
+ function verbForOp(op) {
1512
+ if (!op) {
1513
+ return "Edited";
1514
+ }
1515
+ return op.type === "create_file" ? "Created" : op.type === "delete_file" ? "Deleted" : op.moveTo ? "Renamed" : "Edited";
1516
+ }
1517
+ function basename(path) {
1518
+ const parts = path.split("/").filter(Boolean);
1519
+ return parts.length ? parts[parts.length - 1] : path;
1520
+ }
1521
+ function dirname(path) {
1522
+ const idx = path.lastIndexOf("/");
1523
+ return idx >= 0 ? path.slice(0, idx + 1) : "";
1524
+ }
1525
+ function PathPreview({
1526
+ path,
1527
+ add,
1528
+ del
1529
+ }) {
1530
+ return /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-2 truncate font-og-mono", children: [
1531
+ /* @__PURE__ */ jsxs6("span", { className: "truncate", children: [
1532
+ /* @__PURE__ */ jsx8("span", { className: "text-og-fg-subtle", children: dirname(path) }),
1533
+ /* @__PURE__ */ jsx8("span", { className: "text-og-fg-muted", children: basename(path) })
1534
+ ] }),
1535
+ add != null || del != null ? /* @__PURE__ */ jsxs6("span", { className: "shrink-0 text-og-fg-subtle", children: [
1536
+ add != null ? `+${add}` : "",
1537
+ add != null && del != null ? " " : "",
1538
+ del != null ? `\u2212${del}` : ""
1539
+ ] }) : null
1540
+ ] });
1541
+ }
1542
+ function ApplyPatchRenderer({ item }) {
1543
+ const ops = applyPatchOps(item.raw);
1544
+ const failed = item.status === "failed";
1545
+ const cancelled = item.status === "cancelled";
1546
+ const running = item.status === "running";
1547
+ const firstOp = ops[0];
1548
+ if (running) {
1549
+ const fileCount = ops.length;
1550
+ const titleVerb = firstOp ? `Applying ${basename(firstOp.path)}` : "Applying patch";
1551
+ return /* @__PURE__ */ jsx8(
1552
+ ActivityDisclosure,
1553
+ {
1554
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1555
+ iconTone: "running",
1556
+ title: fileCount > 1 ? `Applying ${fileCount} files` : titleVerb,
1557
+ running: true,
1558
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: fileCount > 1 ? `${fileCount} files` : firstOp ? firstOp.path : "applying\u2026" }),
1559
+ children: ops.map((op) => {
1560
+ const file2 = safeParseOp(op);
1561
+ return file2 ? /* @__PURE__ */ jsx8(ToolDiff, { files: [file2] }, op.path) : /* @__PURE__ */ jsxs6("div", { children: [
1562
+ /* @__PURE__ */ jsx8("p", { className: "mb-1 font-og-mono text-og-xs text-og-fg-muted", children: op.path }),
1563
+ /* @__PURE__ */ jsx8(RawPatch, { diff: op.diff ?? "" })
1564
+ ] }, op.path);
1565
+ })
1566
+ }
1567
+ );
1568
+ }
1569
+ if (failed) {
1570
+ return /* @__PURE__ */ jsx8(
1571
+ ActivityDisclosure,
1572
+ {
1573
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1574
+ iconTone: "failed",
1575
+ title: firstOp ? `${verbForOp(firstOp)} ${basename(firstOp.path)}` : "apply_patch",
1576
+ chip: { tone: "bad", text: "failed" },
1577
+ preview: typeof item.output === "string" ? item.output : "patch failed",
1578
+ children: /* @__PURE__ */ jsx8(PayloadBlock, { label: "Error", value: item.output, failed: true })
1579
+ }
1580
+ );
1581
+ }
1582
+ if (ops.length > 1) {
1583
+ const parsed = ops.map((op) => safeParseOp(op));
1584
+ const goodFiles = parsed.filter((f) => f !== null);
1585
+ const add = goodFiles.reduce((n, f) => n + f.additions, 0);
1586
+ const del = goodFiles.reduce((n, f) => n + f.deletions, 0);
1587
+ return /* @__PURE__ */ jsx8(
1588
+ ActivityDisclosure,
1589
+ {
1590
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1591
+ iconTone: "accent",
1592
+ title: `Edited ${ops.length} files`,
1593
+ cancelled,
1594
+ preview: /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-2 font-og-mono", children: [
1595
+ /* @__PURE__ */ jsxs6("span", { className: "text-og-fg-muted", children: [
1596
+ ops.length,
1597
+ " files"
1598
+ ] }),
1599
+ /* @__PURE__ */ jsxs6("span", { className: "text-og-fg-subtle", children: [
1600
+ "+",
1601
+ add,
1602
+ " \u2212",
1603
+ del
1604
+ ] })
1605
+ ] }),
1606
+ children: ops.map((op, index) => {
1607
+ const file2 = parsed[index];
1608
+ return file2 ? /* @__PURE__ */ jsx8(ToolDiff, { files: [file2] }, op.path) : /* @__PURE__ */ jsxs6("div", { children: [
1609
+ /* @__PURE__ */ jsx8("p", { className: "mb-1 font-og-mono text-og-xs text-og-fg-muted", children: op.path }),
1610
+ /* @__PURE__ */ jsx8(RawPatch, { diff: op.diff ?? "" })
1611
+ ] }, op.path);
1612
+ })
1613
+ }
1614
+ );
1615
+ }
1616
+ if (!firstOp) {
1617
+ return /* @__PURE__ */ jsx8(GenericRenderer, { item });
1618
+ }
1619
+ if (firstOp.type === "delete_file") {
1620
+ return /* @__PURE__ */ jsx8(
1621
+ ActivityDisclosure,
1622
+ {
1623
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1624
+ iconTone: "failed",
1625
+ title: `Deleted ${basename(firstOp.path)}`,
1626
+ cancelled,
1627
+ preview: /* @__PURE__ */ jsx8(PathPreview, { path: firstOp.path }),
1628
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: "File deleted \u2014 no diff to show." })
1629
+ }
1630
+ );
1631
+ }
1632
+ const file = safeParseOp(firstOp);
1633
+ if (!file) {
1634
+ return /* @__PURE__ */ jsx8(
1635
+ ActivityDisclosure,
1636
+ {
1637
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1638
+ iconTone: "accent",
1639
+ title: `${verbForOp(firstOp)} ${basename(firstOp.path)}`,
1640
+ cancelled,
1641
+ preview: /* @__PURE__ */ jsxs6("span", { className: "inline-flex items-center gap-2 font-og-mono", children: [
1642
+ /* @__PURE__ */ jsx8("span", { className: "text-og-fg-muted", children: basename(firstOp.path) }),
1643
+ /* @__PURE__ */ jsx8("span", { className: "text-og-fg-subtle", children: "malformed V4A" })
1644
+ ] }),
1645
+ children: /* @__PURE__ */ jsx8(RawPatch, { diff: firstOp.diff ?? "" })
1646
+ }
1647
+ );
1648
+ }
1649
+ return /* @__PURE__ */ jsx8(
1650
+ ActivityDisclosure,
1651
+ {
1652
+ icon: /* @__PURE__ */ jsx8(FileDiffIcon, { className: ICON_SIZE }),
1653
+ iconTone: "accent",
1654
+ title: `${verbForOp(firstOp)} ${basename(file.path)}`,
1655
+ cancelled,
1656
+ preview: /* @__PURE__ */ jsx8(PathPreview, { path: file.path, add: file.additions, del: file.deletions }),
1657
+ children: /* @__PURE__ */ jsx8(ToolDiff, { files: [file] })
1658
+ }
1659
+ );
1660
+ }
1661
+ function safeParseOp(op) {
1662
+ try {
1663
+ return v4aToGitFileDiff(op);
1664
+ } catch {
1665
+ return null;
1666
+ }
1667
+ }
1668
+ function computerVerb(action) {
1669
+ if (!action || !action.type) {
1670
+ return "Acted";
1671
+ }
1672
+ switch (action.type) {
1673
+ case "screenshot":
1674
+ return "Screenshot";
1675
+ case "click":
1676
+ return `Clicked (${action.x}, ${action.y})`;
1677
+ case "double_click":
1678
+ return `Double-clicked (${action.x}, ${action.y})`;
1679
+ case "move":
1680
+ return `Moved (${action.x}, ${action.y})`;
1681
+ case "scroll":
1682
+ return "Scrolled";
1683
+ case "type": {
1684
+ const t = action.text ?? "";
1685
+ return `Typed \u201C${t.slice(0, 28)}${t.length > 28 ? "\u2026" : ""}\u201D`;
1686
+ }
1687
+ case "keypress":
1688
+ return `Pressed ${(action.keys ?? []).join("+")}`;
1689
+ case "drag":
1690
+ return "Dragged";
1691
+ case "wait":
1692
+ return "Waited";
1693
+ default:
1694
+ return action.type;
1695
+ }
1696
+ }
1697
+ function asComputerArgs(args) {
1698
+ if (!args) {
1699
+ return {};
1700
+ }
1701
+ const parsed = typeof args === "string" ? tryParseJson(args) : args;
1702
+ if (!parsed || typeof parsed !== "object") {
1703
+ return {};
1704
+ }
1705
+ const record = parsed;
1706
+ return {
1707
+ ...typeof record.x === "number" ? { x: record.x } : {},
1708
+ ...typeof record.y === "number" ? { y: record.y } : {},
1709
+ ...typeof record.text === "string" ? { text: record.text } : {},
1710
+ ...Array.isArray(record.keys) ? { keys: record.keys } : {},
1711
+ ...typeof record.button === "string" ? { button: record.button } : {}
1712
+ };
1713
+ }
1714
+ function ComputerCallRenderer({ item }) {
1715
+ const raw = item.raw ?? {};
1716
+ const functionAction = !raw.action && item.name.startsWith("computer_") && item.name !== "computer_call" ? { type: item.name.slice("computer_".length), ...asComputerArgs(item.arguments) } : void 0;
1717
+ const action = raw.action ?? functionAction;
1718
+ const actions = raw.actions ?? (action ? [action] : []);
1719
+ const verb = computerVerb(action);
1720
+ const out = item.output;
1721
+ const running = item.status === "running";
1722
+ const rejected = raw.providerData?.approvalStatus === "rejected";
1723
+ const readOnly = typeof out === "string" && out.includes("read-only");
1724
+ const shotUrl = screenshotDataUrl(out);
1725
+ const omittedMedia = mediaPreviewFact(out);
1726
+ const empty = out === "" || out == null;
1727
+ const batched = actions.length > 1 ? actions.map((a) => computerVerb(a)).join(" \xB7 ") : null;
1728
+ const countSuffix = actions.length > 1 ? ` \xB7${actions.length}` : "";
1729
+ const isShot = action?.type === "screenshot";
1730
+ if (running) {
1731
+ return /* @__PURE__ */ jsx8(
1732
+ ActivityDisclosure,
1733
+ {
1734
+ icon: isShot ? /* @__PURE__ */ jsx8(CameraIcon2, { className: ICON_SIZE }) : /* @__PURE__ */ jsx8(MousePointer2Icon, { className: ICON_SIZE }),
1735
+ iconTone: "running",
1736
+ title: verb,
1737
+ running: true,
1738
+ media: /* @__PURE__ */ jsx8(MediaSkeleton, {}),
1739
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: "capturing frame\u2026" })
1740
+ }
1741
+ );
1742
+ }
1743
+ if (readOnly) {
1744
+ return /* @__PURE__ */ jsx8(
1745
+ ActivityDisclosure,
1746
+ {
1747
+ icon: /* @__PURE__ */ jsx8(MousePointer2Icon, { className: ICON_SIZE }),
1748
+ iconTone: "failed",
1749
+ title: verb,
1750
+ chip: { tone: "bad", text: "read-only" },
1751
+ preview: "write actions disabled",
1752
+ children: /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: "computer-use is read-only \u2014 write actions are disabled." })
1753
+ }
1754
+ );
1755
+ }
1756
+ if (rejected) {
1757
+ return /* @__PURE__ */ jsx8(
1758
+ ActivityDisclosure,
1759
+ {
1760
+ icon: /* @__PURE__ */ jsx8(LockIcon, { className: ICON_SIZE }),
1761
+ iconTone: "muted",
1762
+ title: verb,
1763
+ preview: "approval rejected \u2014 this action did not run",
1764
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: "approval rejected \u2014 this action did not run." })
1765
+ }
1766
+ );
1767
+ }
1768
+ const isFailed = item.status === "failed";
1769
+ const isCancelled = item.status === "cancelled";
1770
+ if (shotUrl) {
1771
+ const caption = `${verb}${actions.length > 1 ? ` (+${actions.length - 1} more)` : ""}`;
1772
+ return /* @__PURE__ */ jsxs6(
1773
+ ActivityDisclosure,
1774
+ {
1775
+ icon: isShot ? /* @__PURE__ */ jsx8(CameraIcon2, { className: ICON_SIZE }) : /* @__PURE__ */ jsx8(MousePointer2Icon, { className: ICON_SIZE }),
1776
+ iconTone: isFailed ? "failed" : "accent",
1777
+ title: `${verb}${countSuffix}`,
1778
+ failed: isFailed,
1779
+ cancelled: isCancelled,
1780
+ media: /* @__PURE__ */ jsx8(Thumbnail, { src: shotUrl, caption }),
1781
+ children: [
1782
+ /* @__PURE__ */ jsx8(ScreenshotFigure, { src: shotUrl, caption }),
1783
+ batched ? /* @__PURE__ */ jsxs6(BodyNote, { children: [
1784
+ "batched: ",
1785
+ batched
1786
+ ] }) : null
1787
+ ]
1788
+ }
1789
+ );
1790
+ }
1791
+ if (omittedMedia) {
1792
+ return /* @__PURE__ */ jsxs6(
1793
+ ActivityDisclosure,
1794
+ {
1795
+ icon: /* @__PURE__ */ jsx8(CameraOffIcon2, { className: ICON_SIZE }),
1796
+ iconTone: isFailed ? "failed" : "muted",
1797
+ title: `${verb}${countSuffix} \xB7 image omitted \xB7 not retained`,
1798
+ failed: isFailed,
1799
+ cancelled: isCancelled,
1800
+ preview: "inline image omitted \xB7 not retained",
1801
+ media: /* @__PURE__ */ jsx8(MediaEmpty, {}),
1802
+ children: [
1803
+ /* @__PURE__ */ jsxs6(BodyNote, { children: [
1804
+ "The inline ",
1805
+ omittedMedia.mediaType,
1806
+ " output was omitted from the audit timeline and its source bytes were not retained."
1807
+ ] }),
1808
+ batched ? /* @__PURE__ */ jsxs6(BodyNote, { children: [
1809
+ "batched: ",
1810
+ batched
1811
+ ] }) : null
1812
+ ]
1813
+ }
1814
+ );
1815
+ }
1816
+ if (empty) {
1817
+ return /* @__PURE__ */ jsx8(
1818
+ ActivityDisclosure,
1819
+ {
1820
+ icon: /* @__PURE__ */ jsx8(CameraOffIcon2, { className: ICON_SIZE }),
1821
+ iconTone: isFailed ? "failed" : "muted",
1822
+ title: verb,
1823
+ failed: isFailed,
1824
+ cancelled: isCancelled,
1825
+ media: /* @__PURE__ */ jsx8(MediaEmpty, {}),
1826
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: isFailed ? "computer_call failed \u2014 no image returned." : isCancelled ? "computer_call interrupted \u2014 no image returned." : "(no image) \u2014 the session returned an empty screenshot." })
1827
+ }
1828
+ );
1829
+ }
1830
+ return /* @__PURE__ */ jsx8(
1831
+ ActivityDisclosure,
1832
+ {
1833
+ icon: /* @__PURE__ */ jsx8(MousePointer2Icon, { className: ICON_SIZE }),
1834
+ iconTone: isFailed ? "failed" : "accent",
1835
+ title: verb,
1836
+ failed: isFailed,
1837
+ cancelled: isCancelled,
1838
+ preview: batched ?? void 0,
1839
+ expandable: batched != null,
1840
+ children: batched ? /* @__PURE__ */ jsx8(BodyNote, { children: batched }) : null
1841
+ }
1842
+ );
1843
+ }
1844
+ function WebSearchRenderer({ item }) {
1845
+ const raw = item.raw ?? {};
1846
+ const action = raw.providerData?.action ?? {};
1847
+ const query = action.query ?? "(query unavailable)";
1848
+ const queries = action.queries ?? [];
1849
+ const variants = queries.length > 1 ? ` +${queries.length - 1} variants` : "";
1850
+ const running = item.status === "running";
1851
+ const rawResults = item.output?.results;
1852
+ const results = Array.isArray(rawResults) ? rawResults.filter((r) => !!r && typeof r === "object") : void 0;
1853
+ const resultOccurrences = /* @__PURE__ */ new Map();
1854
+ const keyedResults = results?.map((result) => {
1855
+ const contentKey = `${result.domain}\0${result.title}\0${result.snippet}`;
1856
+ const occurrence = (resultOccurrences.get(contentKey) ?? 0) + 1;
1857
+ resultOccurrences.set(contentKey, occurrence);
1858
+ return { key: `${contentKey}\0${occurrence}`, result };
1859
+ });
1860
+ if (running) {
1861
+ return /* @__PURE__ */ jsx8(
1862
+ ActivityDisclosure,
1863
+ {
1864
+ icon: /* @__PURE__ */ jsx8(SearchIcon, { className: ICON_SIZE }),
1865
+ iconTone: "running",
1866
+ title: "Searching the web",
1867
+ running: true,
1868
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: `${query}${variants}` }),
1869
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: "searching\u2026 results fold into the model context (no output event)." })
1870
+ }
1871
+ );
1872
+ }
1873
+ return /* @__PURE__ */ jsx8(
1874
+ ActivityDisclosure,
1875
+ {
1876
+ icon: /* @__PURE__ */ jsx8(SearchIcon, { className: ICON_SIZE }),
1877
+ iconTone: "muted",
1878
+ title: "Searched the web",
1879
+ preview: `${query}${variants}`,
1880
+ failed: item.status === "failed",
1881
+ cancelled: item.status === "cancelled",
1882
+ children: keyedResults && keyedResults.length ? /* @__PURE__ */ jsx8("ul", { className: "flex flex-col gap-2", children: keyedResults.map(({ key, result }) => /* @__PURE__ */ jsxs6("li", { className: "flex gap-2.5", children: [
1883
+ /* @__PURE__ */ jsx8(GlobeIcon, { className: "mt-0.5 size-3.5 shrink-0 text-og-fg-subtle" }),
1884
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
1885
+ /* @__PURE__ */ jsxs6("p", { className: "truncate text-og-base text-og-fg", children: [
1886
+ result.title,
1887
+ " ",
1888
+ /* @__PURE__ */ jsx8("span", { className: "text-og-fg-subtle", children: result.domain })
1889
+ ] }),
1890
+ /* @__PURE__ */ jsx8("p", { className: "text-og-sm leading-5 text-og-fg-muted", children: result.snippet })
1891
+ ] })
1892
+ ] }, key)) }) : /* @__PURE__ */ jsx8(BodyNote, { children: "results folded into model context \u2014 no list available." })
1893
+ }
1894
+ );
1895
+ }
1896
+ var VIEW_IMAGE_ERRORS = [
1897
+ "was not found",
1898
+ "is not a file",
1899
+ "exceeded the allowed size",
1900
+ "is not a supported image",
1901
+ "unable to read image"
1902
+ ];
1903
+ function ViewImageRenderer({ item }) {
1904
+ const args = parseToolArgs(item.arguments);
1905
+ const path = typeof args.path === "string" ? args.path : "";
1906
+ const out = item.output;
1907
+ const text = typeof out === "string" ? out : "";
1908
+ const omittedMedia = mediaPreviewFact(out);
1909
+ if (item.status === "running") {
1910
+ return /* @__PURE__ */ jsx8(
1911
+ ActivityDisclosure,
1912
+ {
1913
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1914
+ iconTone: "running",
1915
+ title: `View ${basename(path)}`,
1916
+ running: true,
1917
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: "reading\u2026" }),
1918
+ media: /* @__PURE__ */ jsx8(MediaSkeleton, {}),
1919
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: "reading image\u2026" })
1920
+ }
1921
+ );
1922
+ }
1923
+ const viewFailed = item.status === "failed";
1924
+ const viewCancelled = item.status === "cancelled";
1925
+ const errMatch = VIEW_IMAGE_ERRORS.find((p) => text.includes(p));
1926
+ if (errMatch) {
1927
+ const tooBig = text.includes("exceeded the allowed size");
1928
+ return /* @__PURE__ */ jsx8(
1929
+ ActivityDisclosure,
1930
+ {
1931
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1932
+ iconTone: "failed",
1933
+ title: `View ${basename(path)}`,
1934
+ chip: { tone: "bad", text: tooBig ? "too large" : "error" },
1935
+ preview: text,
1936
+ children: /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: text })
1937
+ }
1938
+ );
1939
+ }
1940
+ if (text.startsWith("OpenAI file reference:")) {
1941
+ return /* @__PURE__ */ jsx8(
1942
+ ActivityDisclosure,
1943
+ {
1944
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1945
+ iconTone: viewFailed ? "failed" : "muted",
1946
+ title: `Viewed ${basename(path)}`,
1947
+ failed: viewFailed,
1948
+ cancelled: viewCancelled,
1949
+ preview: path,
1950
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: text })
1951
+ }
1952
+ );
1953
+ }
1954
+ if (omittedMedia) {
1955
+ return /* @__PURE__ */ jsx8(
1956
+ ActivityDisclosure,
1957
+ {
1958
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1959
+ iconTone: viewFailed ? "failed" : "muted",
1960
+ title: `Viewed ${basename(path)} \xB7 image omitted \xB7 not retained`,
1961
+ failed: viewFailed,
1962
+ cancelled: viewCancelled,
1963
+ preview: "inline image omitted \xB7 not retained",
1964
+ media: /* @__PURE__ */ jsx8(MediaEmpty, {}),
1965
+ children: /* @__PURE__ */ jsxs6(BodyNote, { children: [
1966
+ "The inline ",
1967
+ omittedMedia.mediaType,
1968
+ " output was omitted from the audit timeline and its source bytes were not retained."
1969
+ ] })
1970
+ }
1971
+ );
1972
+ }
1973
+ if (text.includes("No image data")) {
1974
+ return /* @__PURE__ */ jsx8(
1975
+ ActivityDisclosure,
1976
+ {
1977
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1978
+ iconTone: viewFailed ? "failed" : "muted",
1979
+ title: `Viewed ${basename(path)}`,
1980
+ failed: viewFailed,
1981
+ cancelled: viewCancelled,
1982
+ preview: "(no image)",
1983
+ children: /* @__PURE__ */ jsx8(BodyNote, { children: viewFailed ? "view_image failed \u2014 no image data returned." : viewCancelled ? "view_image interrupted." : "(no image) \u2014 the sandbox session returned no image data." })
1984
+ }
1985
+ );
1986
+ }
1987
+ if (text.startsWith("data:")) {
1988
+ return /* @__PURE__ */ jsx8(
1989
+ ActivityDisclosure,
1990
+ {
1991
+ icon: /* @__PURE__ */ jsx8(ImageIcon, { className: ICON_SIZE }),
1992
+ iconTone: viewFailed ? "failed" : "accent",
1993
+ title: `Viewed ${basename(path)}`,
1994
+ failed: viewFailed,
1995
+ cancelled: viewCancelled,
1996
+ media: /* @__PURE__ */ jsx8(Thumbnail, { src: text, caption: path, alt: path }),
1997
+ children: /* @__PURE__ */ jsx8(ScreenshotFigure, { src: text, caption: path, alt: path })
1998
+ }
1999
+ );
2000
+ }
2001
+ return /* @__PURE__ */ jsx8(GenericRenderer, { item });
2002
+ }
2003
+ function SecretSetRenderer({ item }) {
2004
+ const args = parseToolArgs(item.arguments);
2005
+ const name = typeof args.name === "string" ? args.name : "variable";
2006
+ if (item.status === "running") {
2007
+ return /* @__PURE__ */ jsx8(
2008
+ ActivityDisclosure,
2009
+ {
2010
+ icon: /* @__PURE__ */ jsx8(KeyRoundIcon, { className: ICON_SIZE }),
2011
+ iconTone: "running",
2012
+ title: `Set ${name}`,
2013
+ running: true,
2014
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: "setting\u2026" }),
2015
+ children: /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: redactSecrets(args) })
2016
+ }
2017
+ );
2018
+ }
2019
+ if (item.status === "failed") {
2020
+ const errorText = typeof item.output === "string" ? item.output : null;
2021
+ return /* @__PURE__ */ jsxs6(
2022
+ ActivityDisclosure,
2023
+ {
2024
+ icon: /* @__PURE__ */ jsx8(KeyRoundIcon, { className: ICON_SIZE }),
2025
+ iconTone: "failed",
2026
+ title: `Set ${name}`,
2027
+ failed: true,
2028
+ preview: errorText ?? "variable write failed",
2029
+ children: [
2030
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: redactSecrets(args) }),
2031
+ errorText ? /* @__PURE__ */ jsx8(PayloadBlock, { label: "Error", value: errorText, failed: true }) : /* @__PURE__ */ jsx8(BodyNote, { tone: "error", children: "the tool call failed with no output." })
2032
+ ]
2033
+ }
2034
+ );
2035
+ }
2036
+ return /* @__PURE__ */ jsxs6(
2037
+ ActivityDisclosure,
2038
+ {
2039
+ icon: /* @__PURE__ */ jsx8(KeyRoundIcon, { className: ICON_SIZE }),
2040
+ iconTone: "muted",
2041
+ title: `Set ${name}`,
2042
+ cancelled: item.status === "cancelled",
2043
+ preview: "value write-only \xB7 never returned",
2044
+ children: [
2045
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: redactSecrets(args) }),
2046
+ /* @__PURE__ */ jsx8(BodyNote, { children: "the value is a secret \u2014 redacted in every view; the API never returns it." })
2047
+ ]
2048
+ }
2049
+ );
2050
+ }
2051
+ function GenericRenderer({ item }) {
2052
+ const running = item.status === "running";
2053
+ const args = redactSecrets(parseToolArgs(item.arguments));
2054
+ const display = toolDisplayName(item.name);
2055
+ if (running) {
2056
+ return /* @__PURE__ */ jsx8(
2057
+ ActivityDisclosure,
2058
+ {
2059
+ icon: /* @__PURE__ */ jsx8(PlugIcon, { className: ICON_SIZE }),
2060
+ iconTone: "running",
2061
+ title: display,
2062
+ running: true,
2063
+ preview: /* @__PURE__ */ jsx8(RunningPreview, { children: compactArgs(args) || "running\u2026" }),
2064
+ children: /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: args })
2065
+ }
2066
+ );
2067
+ }
2068
+ const { text: outText, isError } = unwrapMcpOutput(item.output);
2069
+ if ((isError || item.status === "failed") && item.status !== "cancelled") {
2070
+ return /* @__PURE__ */ jsxs6(
2071
+ ActivityDisclosure,
2072
+ {
2073
+ icon: /* @__PURE__ */ jsx8(WrenchIcon, { className: ICON_SIZE }),
2074
+ iconTone: "failed",
2075
+ title: display,
2076
+ chip: { tone: "bad", text: "error" },
2077
+ preview: outText.slice(0, 80),
2078
+ children: [
2079
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: args }),
2080
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Error", value: outText, failed: true })
2081
+ ]
2082
+ }
2083
+ );
2084
+ }
2085
+ return /* @__PURE__ */ jsxs6(
2086
+ ActivityDisclosure,
2087
+ {
2088
+ icon: /* @__PURE__ */ jsx8(WrenchIcon, { className: ICON_SIZE }),
2089
+ iconTone: "muted",
2090
+ title: display,
2091
+ cancelled: item.status === "cancelled",
2092
+ preview: compactArgs(args),
2093
+ children: [
2094
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Arguments", value: args }),
2095
+ /* @__PURE__ */ jsx8(PayloadBlock, { label: "Result", value: outText })
2096
+ ]
2097
+ }
2098
+ );
2099
+ }
2100
+ function compactArgs(args) {
2101
+ const text = stringifyPayload(args).replace(/\s+/g, " ").trim();
2102
+ return text === "{}" ? "" : text.length > 90 ? `${text.slice(0, 89)}\u2026` : text;
2103
+ }
2104
+ var BASE_ENTRIES = [
2105
+ // Provider-native items carry `raw.type` on the wire — this is their source of
2106
+ // truth and is consulted first by the registry.
2107
+ { match: "rawType", type: "apply_patch_call", render: ApplyPatchRenderer },
2108
+ { match: "rawType", type: "computer_call", render: ComputerCallRenderer },
2109
+ { match: "rawType", type: "hosted_tool_call", render: WebSearchRenderer },
2110
+ // First-party sandbox + MCP tools resolve by name. `apply_patch_call` /
2111
+ // `computer_call` are intentionally repeated by name as a fallback only for
2112
+ // first-party replays that omit `raw` (the rawType entries above win whenever
2113
+ // `raw.type` is present, which is the live-wire case).
2114
+ { match: "name", name: "exec_command", render: ExecRenderer },
2115
+ { match: "name", name: "write_stdin", render: WriteStdinRenderer },
2116
+ { match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
2117
+ { match: "name", name: "computer_call", render: ComputerCallRenderer },
2118
+ // Function-mode computer tools (codex / chat-wire transports).
2119
+ { match: "name", name: "computer_screenshot", render: ComputerCallRenderer },
2120
+ { match: "name", name: "computer_click", render: ComputerCallRenderer },
2121
+ { match: "name", name: "computer_double_click", render: ComputerCallRenderer },
2122
+ { match: "name", name: "computer_move", render: ComputerCallRenderer },
2123
+ { match: "name", name: "computer_scroll", render: ComputerCallRenderer },
2124
+ { match: "name", name: "computer_type", render: ComputerCallRenderer },
2125
+ { match: "name", name: "computer_keypress", render: ComputerCallRenderer },
2126
+ { match: "name", name: "computer_drag", render: ComputerCallRenderer },
2127
+ { match: "name", name: "web_search_call", render: WebSearchRenderer },
2128
+ { match: "name", name: "view_image", render: ViewImageRenderer },
2129
+ { match: "name", name: "environment_set_variable", render: SecretSetRenderer }
2130
+ ];
2131
+ var defaultToolRegistry = createToolRegistry(BASE_ENTRIES, GenericRenderer);
2132
+ function createDefaultToolRegistry(options = {}) {
2133
+ return createToolRegistry(BASE_ENTRIES, GenericRenderer, options);
2134
+ }
2135
+
2136
+ // src/timeline/activity-rail.tsx
2137
+ import {
2138
+ ArrowRightIcon,
2139
+ BotIcon,
2140
+ BrainCircuitIcon,
2141
+ BrainIcon,
2142
+ SquareTerminalIcon
2143
+ } from "lucide-react";
2144
+ import { lazy as lazy3, Suspense as Suspense3 } from "react";
2145
+ import { jsx as rowJsx, jsxs as rowJsxs } from "react/jsx-runtime";
2146
+
2147
+ // src/timeline/entrance.tsx
2148
+ import { createContext as createContext3, useContext as useContext3, useRef as useRef2 } from "react";
2149
+ var EntranceAnimationContext = createContext3(true);
2150
+ var EntranceAnimationProvider = EntranceAnimationContext.Provider;
2151
+ function useEntranceAnimation() {
2152
+ const enabled = useContext3(EntranceAnimationContext);
2153
+ const captured = useRef2(enabled);
2154
+ return captured.current;
2155
+ }
2156
+
2157
+ // src/timeline/activity-rail.tsx
2158
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
2159
+ var LazyFleetDecisionRow = lazy3(() => import("./fleet-decision-row-GGCAT4E4.js"));
2160
+ function familyOf(item) {
2161
+ if (item.kind === "tool-call") {
2162
+ return item.name === "exec_command" || item.name === "write_stdin" ? "terminal" : item.name;
2163
+ }
2164
+ return item.kind;
2165
+ }
2166
+ function ActivityRail({
2167
+ items,
2168
+ toolRegistry = defaultToolRegistry,
2169
+ onOpenSession,
2170
+ onMemoryClick,
2171
+ bare,
2172
+ className
2173
+ }) {
2174
+ const enter = useEntranceAnimation();
2175
+ return /* @__PURE__ */ jsx9(
2176
+ "div",
2177
+ {
2178
+ className: cn(
2179
+ // Rows sit TIGHT by default (gap-0.5) so a same-family run reads as one
2180
+ // calm cluster; a family change opens real breathing room (mt-3) below,
2181
+ // so a long rail reads as a few clusters, not a metronome of rows.
2182
+ "flex flex-col gap-0.5",
2183
+ !bare && "border-l-2 border-og-border pl-3 sm:pl-4",
2184
+ !bare && enter && "animate-og-enter",
2185
+ className
2186
+ ),
2187
+ children: items.map((item, index) => {
2188
+ const newFamily = index > 0 && familyOf(item) !== familyOf(items[index - 1]);
2189
+ const row = renderActivity(item, toolRegistry, onOpenSession, onMemoryClick);
2190
+ return /* @__PURE__ */ jsx9("div", { "data-og-timeline-row-anchor": "", className: cn(newFamily && "mt-3"), children: row }, item.id);
2191
+ })
2192
+ }
2193
+ );
2194
+ }
2195
+ function assertNever(item) {
2196
+ throw new Error(`ActivityRail: unhandled activity item ${JSON.stringify(item)}`);
2197
+ }
2198
+ function renderActivity(item, toolRegistry, onOpenSession, onMemoryClick) {
2199
+ switch (item.kind) {
2200
+ case "reasoning":
2201
+ return /* @__PURE__ */ jsx9(ReasoningRow, { item });
2202
+ case "tool-call": {
2203
+ const Renderer = toolRegistry.resolve(item);
2204
+ return /* @__PURE__ */ jsx9(Renderer, { item });
2205
+ }
2206
+ case "worker":
2207
+ return /* @__PURE__ */ jsx9(WorkerRow, { item, onOpenSession });
2208
+ case "sandbox":
2209
+ return /* @__PURE__ */ jsx9(SandboxRow, { item });
2210
+ case "memory":
2211
+ return /* @__PURE__ */ jsx9(MemoryRow, { item, onMemoryClick });
2212
+ case "fleet-decision":
2213
+ return /* @__PURE__ */ jsx9(Suspense3, { fallback: null, children: /* @__PURE__ */ jsx9(
2214
+ LazyFleetDecisionRow,
2215
+ {
2216
+ item,
2217
+ d: ActivityDisclosure,
2218
+ b: BodyNote,
2219
+ j: rowJsx,
2220
+ s: rowJsxs
2221
+ }
2222
+ ) });
2223
+ default:
2224
+ return assertNever(item);
2225
+ }
2226
+ }
2227
+ function ReasoningRow({ item }) {
2228
+ return /* @__PURE__ */ jsx9(
2229
+ ActivityDisclosure,
2230
+ {
2231
+ icon: /* @__PURE__ */ jsx9(BrainIcon, { className: "size-3.5" }),
2232
+ iconTone: "muted",
2233
+ title: item.streaming ? "Thinking" : /* @__PURE__ */ jsx9("span", { className: "font-normal italic text-og-fg-subtle", children: "Thought" }),
2234
+ running: item.streaming,
2235
+ preview: truncate(item.text, 110),
2236
+ children: /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap text-og-base leading-6 text-og-fg-muted", children: item.text })
2237
+ }
2238
+ );
2239
+ }
2240
+ var MEMORY_KIND_LABEL = {
2241
+ preference: "Preference",
2242
+ semantic: "Fact",
2243
+ procedural: "Procedure",
2244
+ decision: "Decision",
2245
+ episodic: "History"
2246
+ };
2247
+ function MemoryRow({
2248
+ item,
2249
+ onMemoryClick
2250
+ }) {
2251
+ const corrected = item.variant === "corrected";
2252
+ const kindLabel = MEMORY_KIND_LABEL[item.memoryKind];
2253
+ const superseded = corrected && Boolean(item.replacementPreview);
2254
+ const targetId = corrected ? item.replacementMemoryId ?? item.memoryId : item.memoryId;
2255
+ const deepLink = Boolean(onMemoryClick);
2256
+ return /* @__PURE__ */ jsxs7(
2257
+ ActivityDisclosure,
2258
+ {
2259
+ icon: /* @__PURE__ */ jsx9(BrainCircuitIcon, { className: "size-3.5" }),
2260
+ iconTone: "muted",
2261
+ title: /* @__PURE__ */ jsxs7("span", { className: "inline-flex min-w-0 items-center gap-2", children: [
2262
+ /* @__PURE__ */ jsx9("span", { className: "shrink-0", children: corrected ? "Updated memory" : "Saved to memory" }),
2263
+ kindLabel ? /* @__PURE__ */ jsx9("span", { className: "shrink-0 rounded-og-xs bg-og-surface-2 px-1.5 py-px text-og-xs font-normal leading-tight text-og-fg-subtle", children: kindLabel }) : null
2264
+ ] }),
2265
+ preview: superseded ? item.replacementPreview : item.preview,
2266
+ children: [
2267
+ superseded ? (
2268
+ // The correction as a before → after: the old memory struck through and
2269
+ // dimmed, the new text in the ordinary body weight below it.
2270
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1.5", children: [
2271
+ /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap text-og-sm leading-6 text-og-fg-subtle line-through", children: item.preview }),
2272
+ /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap text-og-base leading-6 text-og-fg-muted", children: item.replacementPreview })
2273
+ ] })
2274
+ ) : corrected && item.action === "updated" ? (
2275
+ // Edited in place, no replacement record: the memory is still live, so
2276
+ // show its current text — NOT the archived treatment.
2277
+ /* @__PURE__ */ jsxs7(Fragment3, { children: [
2278
+ /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap text-og-base leading-6 text-og-fg-muted", children: item.preview }),
2279
+ /* @__PURE__ */ jsx9(BodyNote, { tone: "muted", children: "Updated in place." })
2280
+ ] })
2281
+ ) : corrected ? (
2282
+ // A correction with no replacement (and not an in-place update) archived the record.
2283
+ /* @__PURE__ */ jsx9(BodyNote, { tone: "muted", children: "Archived." })
2284
+ ) : /* @__PURE__ */ jsx9("p", { className: "whitespace-pre-wrap text-og-base leading-6 text-og-fg-muted", children: item.preview }),
2285
+ item.deduped ? /* @__PURE__ */ jsx9(BodyNote, { tone: "muted", children: "Merged into an existing memory." }) : null,
2286
+ deepLink ? /* @__PURE__ */ jsxs7(
2287
+ "button",
2288
+ {
2289
+ type: "button",
2290
+ onClick: () => onMemoryClick?.(targetId),
2291
+ className: cn(
2292
+ "group/memlink -mx-1 inline-flex w-fit items-center gap-1 rounded-og-sm px-1 py-0.5 text-left text-og-sm text-og-fg-subtle",
2293
+ "outline-none transition-colors duration-150 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent"
2294
+ ),
2295
+ children: [
2296
+ "View in memory",
2297
+ /* @__PURE__ */ jsx9(ArrowRightIcon, { className: "size-3.5 transition-transform duration-150 group-hover/memlink:translate-x-0.5" })
2298
+ ]
2299
+ }
2300
+ ) : null
2301
+ ]
2302
+ }
2303
+ );
2304
+ }
2305
+ function sandboxRowTitle(item) {
2306
+ if (item.name === "sandbox.provision") {
2307
+ if (item.status === "failed") return "Sandbox didn\u2019t start";
2308
+ if (item.status === "running") return "Starting sandbox";
2309
+ if (item.origin === "resumed") return "Sandbox reattached";
2310
+ if (item.origin === "restored") return "Sandbox restored";
2311
+ if (item.origin === "created") return "Sandbox created";
2312
+ return "Sandbox ready";
2313
+ }
2314
+ return toolDisplayName(item.name);
2315
+ }
2316
+ function SandboxRow({ item }) {
2317
+ return /* @__PURE__ */ jsxs7(
2318
+ ActivityDisclosure,
2319
+ {
2320
+ icon: /* @__PURE__ */ jsx9(SquareTerminalIcon, { className: "size-3.5" }),
2321
+ iconTone: item.status === "failed" ? "failed" : item.status === "running" ? "running" : "muted",
2322
+ title: sandboxRowTitle(item),
2323
+ running: item.status === "running",
2324
+ failed: item.status === "failed",
2325
+ cancelled: item.status === "cancelled",
2326
+ preview: item.command ?? void 0,
2327
+ children: [
2328
+ item.command ? /* @__PURE__ */ jsx9(PayloadBlock, { label: "Command", value: item.command }) : null,
2329
+ item.output ? /* @__PURE__ */ jsx9(PayloadBlock, { label: "Output", value: item.output }) : null
2330
+ ]
2331
+ }
2332
+ );
2333
+ }
2334
+ function WorkerRow({
2335
+ item,
2336
+ onOpenSession
2337
+ }) {
2338
+ const running = item.status === "running";
2339
+ const failed = item.status === "failed";
2340
+ const cancelled = item.status === "cancelled";
2341
+ const title = item.action === "spawn" ? running ? "Spawning worker" : failed ? "Worker spawn failed" : cancelled ? "Worker interrupted" : "Worker spawned" : running ? "Messaging worker" : failed ? "Worker message failed" : cancelled ? "Worker interrupted" : "Worker messaged";
2342
+ const sessionId = item.workerSessionId;
2343
+ const deepLink = Boolean(sessionId) && Boolean(onOpenSession) && !failed && !cancelled;
2344
+ const inner = /* @__PURE__ */ jsxs7(Fragment3, { children: [
2345
+ /* @__PURE__ */ jsx9("span", { className: "size-3.5 shrink-0", "aria-hidden": true }),
2346
+ /* @__PURE__ */ jsx9("span", { className: cn("mt-px shrink-0", failed ? "text-og-status-failed" : "text-og-accent"), children: /* @__PURE__ */ jsx9(BotIcon, { className: "size-3.5" }) }),
2347
+ /* @__PURE__ */ jsxs7("div", { className: "min-w-0 flex-1", children: [
2348
+ /* @__PURE__ */ jsx9(
2349
+ "span",
2350
+ {
2351
+ className: cn(
2352
+ "text-og-base font-medium",
2353
+ running ? "og-shimmer-text" : failed ? "text-og-status-failed" : "text-og-fg"
2354
+ ),
2355
+ children: title
2356
+ }
2357
+ ),
2358
+ item.prompt ? /* @__PURE__ */ jsx9("p", { className: "mt-0.5 truncate text-og-sm text-og-fg-muted", children: truncate(item.prompt, 140) }) : null
2359
+ ] }),
2360
+ failed ? /* @__PURE__ */ jsxs7("span", { className: "inline-flex shrink-0 self-center items-center gap-1.5 font-og-mono text-og-xs leading-none text-og-status-failed", children: [
2361
+ /* @__PURE__ */ jsx9("span", { className: "size-1.5 rounded-full bg-og-status-failed" }),
2362
+ "failed"
2363
+ ] }) : cancelled ? /* @__PURE__ */ jsx9("span", { className: "og-cancelled-chip shrink-0 self-center font-og-mono text-og-xs leading-none text-og-fg-subtle", children: "interrupted" }) : deepLink ? /* @__PURE__ */ jsx9(
2364
+ ArrowRightIcon,
2365
+ {
2366
+ "aria-hidden": true,
2367
+ className: "mt-0.5 size-3.5 shrink-0 text-og-fg-subtle transition-[transform,color] duration-150 group-hover/worker:translate-x-0.5 group-hover/worker:text-og-fg"
2368
+ }
2369
+ ) : null
2370
+ ] });
2371
+ if (deepLink && sessionId && onOpenSession) {
2372
+ return /* @__PURE__ */ jsx9(
2373
+ "button",
2374
+ {
2375
+ type: "button",
2376
+ onClick: () => onOpenSession(sessionId),
2377
+ className: cn(
2378
+ "group/worker -mx-1.5 flex w-full items-start gap-2 rounded-og-sm px-1.5 py-1.5 text-left",
2379
+ "outline-none transition-colors duration-150 hover:bg-og-surface-1 focus-visible:ring-2 focus-visible:ring-og-accent",
2380
+ "pointer-coarse:py-2.5"
2381
+ ),
2382
+ children: inner
2383
+ }
2384
+ );
2385
+ }
2386
+ return /* @__PURE__ */ jsx9("div", { className: "flex items-start gap-2 px-1.5 py-1.5", children: inner });
2387
+ }
2388
+
2389
+ // src/timeline/turn-summary.tsx
2390
+ import { CheckIcon, ChevronRightIcon as ChevronRightIcon2, CircleSlashIcon, TriangleAlertIcon } from "lucide-react";
2391
+ import { useState as useState7 } from "react";
2392
+ import { Collapsible as Collapsible2 } from "radix-ui";
2393
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
2394
+ function TurnSummary({
2395
+ items,
2396
+ outcome,
2397
+ failureText,
2398
+ durationMs,
2399
+ defaultOpen,
2400
+ bare,
2401
+ children
2402
+ }) {
2403
+ const forcedDefaultOpen = useForcedDefaultOpen();
2404
+ const [open, setOpen] = useState7(defaultOpen ?? forcedDefaultOpen ?? false);
2405
+ const enter = useEntranceAnimation();
2406
+ const facets = summarizeTurn(items, durationMs);
2407
+ return /* @__PURE__ */ jsxs8(
2408
+ Collapsible2.Root,
2409
+ {
2410
+ open,
2411
+ onOpenChange: setOpen,
2412
+ className: enter && !bare ? "animate-og-enter" : void 0,
2413
+ children: [
2414
+ /* @__PURE__ */ jsxs8(
2415
+ Collapsible2.Trigger,
2416
+ {
2417
+ className: cn(
2418
+ // Both the top-level turn fold and a nested cluster fold render as a
2419
+ // FLAT rail row — chevron + glyph + facets on the page background, no
2420
+ // border, no fill. Only a hover tint hints the row is expandable, so a
2421
+ // collapsed turn never reads as a boxed card. The top-level row is a
2422
+ // touch larger (base text, size-5 glyph, wider gap) so it still reads
2423
+ // as a turn landmark above the nested cluster rows it groups.
2424
+ "group flex w-full items-center rounded-og-sm text-left transition-colors",
2425
+ // A folded turn is a touch target on coarse pointers: grow the row so it
2426
+ // clears the 40px minimum without disturbing the calm desktop rhythm.
2427
+ "pointer-coarse:py-2.5",
2428
+ bare ? "gap-2 px-1.5 py-1.5 text-og-sm text-og-fg-muted" : "-mx-2 gap-2.5 px-2 py-1.5 text-og-base text-og-fg-muted",
2429
+ // A failed fold keeps its red accent (glyph + inline reason below) and a
2430
+ // faint red hover wash so attention still lands there; every other
2431
+ // outcome gets the neutral surface hover.
2432
+ outcome === "failed" ? "hover:bg-og-status-failed/[0.06] hover:text-og-fg" : "hover:bg-og-surface-1 hover:text-og-fg"
2433
+ ),
2434
+ children: [
2435
+ /* @__PURE__ */ jsx10(ChevronRightIcon2, { className: "size-3.5 shrink-0 text-og-fg-subtle transition-transform duration-150 group-data-[state=open]:rotate-90" }),
2436
+ /* @__PURE__ */ jsx10(
2437
+ "span",
2438
+ {
2439
+ className: cn(
2440
+ "inline-flex shrink-0 items-center justify-center",
2441
+ bare ? "size-3.5" : "size-5",
2442
+ outcome === "failed" ? "text-og-status-failed" : "text-og-fg-subtle"
2443
+ ),
2444
+ children: outcome === "failed" ? /* @__PURE__ */ jsx10(TriangleAlertIcon, { className: "size-3" }) : outcome === "cancelled" ? /* @__PURE__ */ jsx10(CircleSlashIcon, { className: "size-3" }) : outcome === "complete" ? /* @__PURE__ */ jsx10(CheckIcon, { className: bare ? "size-3" : "size-3.5" }) : /* @__PURE__ */ jsx10("span", { className: "size-1.5 animate-og-pulse rounded-full bg-og-fg-subtle" })
2445
+ }
2446
+ ),
2447
+ /* @__PURE__ */ jsxs8("span", { className: cn("min-w-0 flex-1 truncate", bare ? "text-og-sm" : "text-og-fg-muted"), children: [
2448
+ facets,
2449
+ outcome === "failed" && failureText ? /* @__PURE__ */ jsxs8("span", { className: "text-og-status-failed", children: [
2450
+ " \xB7 ",
2451
+ failureText
2452
+ ] }) : null,
2453
+ outcome === "cancelled" ? /* @__PURE__ */ jsx10("span", { className: "text-og-fg-subtle", children: " \xB7 interrupted" }) : null
2454
+ ] }),
2455
+ /* @__PURE__ */ jsx10(
2456
+ "span",
2457
+ {
2458
+ "aria-hidden": true,
2459
+ className: cn(
2460
+ "ml-auto shrink-0 pl-2 text-og-xs text-og-fg-subtle transition-opacity duration-150",
2461
+ "opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100",
2462
+ "pointer-coarse:opacity-100"
2463
+ ),
2464
+ children: open ? "hide steps" : "show steps"
2465
+ }
2466
+ )
2467
+ ]
2468
+ }
2469
+ ),
2470
+ /* @__PURE__ */ jsx10(Collapsible2.Content, { className: "overflow-hidden data-[state=closed]:animate-og-collapse data-[state=open]:animate-og-expand", children: /* @__PURE__ */ jsx10("div", { className: bare ? "pt-1 pl-5" : "pt-2", children }) })
2471
+ ]
2472
+ }
2473
+ );
2474
+ }
2475
+ function summarizeTurn(items, durationMs) {
2476
+ let files = 0;
2477
+ let commands = 0;
2478
+ let screenshots = 0;
2479
+ let memoriesSaved = 0;
2480
+ let memoriesUpdated = 0;
2481
+ for (const item of items) {
2482
+ if (item.kind === "memory") {
2483
+ if (item.variant === "corrected") {
2484
+ memoriesUpdated += 1;
2485
+ } else {
2486
+ memoriesSaved += 1;
2487
+ }
2488
+ continue;
2489
+ }
2490
+ if (item.kind !== "tool-call") {
2491
+ continue;
2492
+ }
2493
+ if (isApplyPatch(item)) {
2494
+ files += applyPatchOps(item.raw).length;
2495
+ } else if (item.name === "exec_command") {
2496
+ commands += 1;
2497
+ } else if (rawTypeOf(item) === "computer_call" || item.name === "computer_call" || item.name === "computer_screenshot") {
2498
+ if (screenshotDataUrl(item.output) !== null || mediaPreviewFact(item.output) !== null) {
2499
+ screenshots += 1;
2500
+ }
2501
+ }
2502
+ }
2503
+ const parts = [`${items.length} ${items.length === 1 ? "step" : "steps"}`];
2504
+ if (files) {
2505
+ parts.push(`${files} ${files === 1 ? "file" : "files"} edited`);
2506
+ }
2507
+ if (commands) {
2508
+ parts.push(`${commands} ${commands === 1 ? "command" : "commands"}`);
2509
+ }
2510
+ if (screenshots) {
2511
+ parts.push(`${screenshots} ${screenshots === 1 ? "screenshot" : "screenshots"}`);
2512
+ }
2513
+ if (memoriesSaved) {
2514
+ parts.push(`${memoriesSaved} ${memoriesSaved === 1 ? "memory" : "memories"} saved`);
2515
+ }
2516
+ if (memoriesUpdated) {
2517
+ parts.push(`${memoriesUpdated} ${memoriesUpdated === 1 ? "memory" : "memories"} updated`);
2518
+ }
2519
+ const duration = formatDurationFacet(durationMs);
2520
+ if (duration) {
2521
+ parts.push(duration);
2522
+ }
2523
+ return parts.join(" \xB7 ");
2524
+ }
2525
+ function formatDurationFacet(durationMs) {
2526
+ if (durationMs === void 0 || !Number.isFinite(durationMs) || durationMs < 1e3) {
2527
+ return null;
2528
+ }
2529
+ const totalSeconds = Math.floor(durationMs / 1e3);
2530
+ if (totalSeconds < 60) {
2531
+ return `${totalSeconds}s`;
2532
+ }
2533
+ const totalMinutes = Math.floor(totalSeconds / 60);
2534
+ if (totalMinutes < 60) {
2535
+ return `${totalMinutes}m`;
2536
+ }
2537
+ const hours = Math.floor(totalMinutes / 60);
2538
+ const minutes = totalMinutes % 60;
2539
+ return `${hours}h ${String(minutes).padStart(2, "0")}m`;
2540
+ }
2541
+
2542
+ // src/components/markdown.tsx
2543
+ import { memo } from "react";
2544
+ import ReactMarkdown from "react-markdown";
2545
+ import remarkGfm from "remark-gfm";
2546
+ import { jsx as jsx11 } from "react/jsx-runtime";
2547
+ var components = {
2548
+ h1: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2549
+ "h1",
2550
+ {
2551
+ className: "mt-5 mb-2.5 text-xl font-semibold tracking-tight text-og-fg first:mt-0",
2552
+ ...props,
2553
+ children
2554
+ }
2555
+ ),
2556
+ h2: ({ children, ...props }) => /* @__PURE__ */ jsx11("h2", { className: "mt-5 mb-2 text-lg font-semibold tracking-tight text-og-fg first:mt-0", ...props, children }),
2557
+ h3: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2558
+ "h3",
2559
+ {
2560
+ className: "mt-4 mb-1.5 text-og-md font-semibold tracking-tight text-og-fg first:mt-0",
2561
+ ...props,
2562
+ children
2563
+ }
2564
+ ),
2565
+ h4: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2566
+ "h4",
2567
+ {
2568
+ className: "mt-4 mb-1.5 text-og-sm font-semibold uppercase tracking-[0.04em] text-og-fg-muted first:mt-0",
2569
+ ...props,
2570
+ children
2571
+ }
2572
+ ),
2573
+ p: ({ children, ...props }) => /* @__PURE__ */ jsx11("p", { className: "my-2.5 leading-7 first:mt-0 last:mb-0", ...props, children }),
2574
+ strong: ({ children, ...props }) => /* @__PURE__ */ jsx11("strong", { className: "font-semibold text-og-fg", ...props, children }),
2575
+ em: ({ children, ...props }) => /* @__PURE__ */ jsx11("em", { className: "italic", ...props, children }),
2576
+ a: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2577
+ "a",
2578
+ {
2579
+ className: "break-words font-medium text-og-accent underline-offset-2 hover:underline",
2580
+ target: "_blank",
2581
+ rel: "noreferrer noopener",
2582
+ ...props,
2583
+ children
2584
+ }
2585
+ ),
2586
+ ul: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2587
+ "ul",
2588
+ {
2589
+ className: "my-2.5 ml-5 flex list-disc flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0",
2590
+ ...props,
2591
+ children
2592
+ }
2593
+ ),
2594
+ ol: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2595
+ "ol",
2596
+ {
2597
+ className: "my-2.5 ml-5 flex list-decimal flex-col gap-1 marker:text-og-fg-subtle first:mt-0 last:mb-0",
2598
+ ...props,
2599
+ children
2600
+ }
2601
+ ),
2602
+ // GFM task-list items carry a leading checkbox <input>; `list-none` + a
2603
+ // negative margin pull the checkbox back to the bullet column so it aligns
2604
+ // with the text.
2605
+ li: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2606
+ "li",
2607
+ {
2608
+ className: "leading-7 marker:text-og-fg-subtle [&>ul]:my-1 [&>ol]:my-1 [&:has(>input)]:list-none [&:has(>input)]:-ml-5",
2609
+ ...props,
2610
+ children
2611
+ }
2612
+ ),
2613
+ input: ({ type, ...props }) => type === "checkbox" ? /* @__PURE__ */ jsx11(
2614
+ "input",
2615
+ {
2616
+ ...props,
2617
+ type: "checkbox",
2618
+ disabled: true,
2619
+ className: "mr-2 size-3.5 translate-y-[2px] cursor-default accent-og-accent align-baseline"
2620
+ }
2621
+ ) : /* @__PURE__ */ jsx11("input", { type, ...props }),
2622
+ blockquote: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2623
+ "blockquote",
2624
+ {
2625
+ className: "my-3 border-l-2 border-og-border-strong pl-3.5 text-og-fg-muted [&>p]:my-1.5 first:mt-0 last:mb-0",
2626
+ ...props,
2627
+ children
2628
+ }
2629
+ ),
2630
+ hr: (props) => /* @__PURE__ */ jsx11("hr", { className: "my-4 border-0 border-t border-og-border", ...props }),
2631
+ // Inline `code` vs fenced code blocks. react-markdown v10 no longer passes an
2632
+ // `inline` flag; a fenced block is a <code> whose parent is <pre> (styled by
2633
+ // the `pre` renderer), so a `code` reaching here is treated as inline.
2634
+ code: ({ children, className: _className, ...props }) => /* @__PURE__ */ jsx11(
2635
+ "code",
2636
+ {
2637
+ className: "rounded-og-xs border border-og-border bg-og-surface-1 px-1 py-0.5 font-og-mono text-og-sm text-og-fg",
2638
+ ...props,
2639
+ children
2640
+ }
2641
+ ),
2642
+ // Fenced code blocks — mirror the timeline's PayloadBlock <pre> styling for
2643
+ // visual consistency (bordered, scrollable, mono, surface background).
2644
+ pre: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2645
+ "pre",
2646
+ {
2647
+ className: "my-3 max-h-96 overflow-auto rounded-og-md border border-og-border bg-og-bg/60 p-3 font-og-mono text-og-sm text-og-fg-muted [&>code]:border-0 [&>code]:bg-transparent [&>code]:p-0 [&>code]:text-inherit first:mt-0 last:mb-0",
2648
+ ...props,
2649
+ children
2650
+ }
2651
+ ),
2652
+ table: ({ children, ...props }) => /* @__PURE__ */ jsx11("div", { className: "my-3 max-w-full overflow-x-auto rounded-og-md border border-og-border first:mt-0 last:mb-0", children: /* @__PURE__ */ jsx11("table", { className: "w-full border-collapse text-og-base", ...props, children }) }),
2653
+ thead: ({ children, ...props }) => /* @__PURE__ */ jsx11("thead", { className: "bg-og-surface-1", ...props, children }),
2654
+ th: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2655
+ "th",
2656
+ {
2657
+ className: "border-b border-og-border px-3 py-1.5 text-left font-medium text-og-fg",
2658
+ ...props,
2659
+ children
2660
+ }
2661
+ ),
2662
+ td: ({ children, ...props }) => /* @__PURE__ */ jsx11(
2663
+ "td",
2664
+ {
2665
+ className: "border-b border-og-border px-3 py-1.5 align-top text-og-fg-muted [tr:last-child>&]:border-b-0",
2666
+ ...props,
2667
+ children
2668
+ }
2669
+ ),
2670
+ img: ({ alt, ...props }) => /* @__PURE__ */ jsx11(
2671
+ "img",
2672
+ {
2673
+ alt: alt ?? "",
2674
+ className: "my-3 max-w-full rounded-og-md border border-og-border",
2675
+ ...props
2676
+ }
2677
+ )
2678
+ };
2679
+ function MarkdownImpl({ children, className }) {
2680
+ return (
2681
+ // `min-w-0` lets the prose shrink inside flex parents (message bubbles) so
2682
+ // long links and code blocks wrap/scroll instead of forcing overflow.
2683
+ /* @__PURE__ */ jsx11("div", { className: cn("min-w-0 break-words", className), children: /* @__PURE__ */ jsx11(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children }) })
2684
+ );
2685
+ }
2686
+ var Markdown = memo(MarkdownImpl);
2687
+
2688
+ // src/components/session-status.tsx
2689
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
2690
+ var SESSION_STATUS_META = {
2691
+ queued: {
2692
+ label: "Queued",
2693
+ dotClassName: "bg-og-status-queued",
2694
+ badgeClassName: "text-og-fg-muted border-og-border bg-og-status-queued/10",
2695
+ pulse: true
2696
+ },
2697
+ running: {
2698
+ label: "Running",
2699
+ dotClassName: "bg-og-status-running",
2700
+ badgeClassName: "text-og-status-running border-og-status-running/30 bg-og-status-running/10",
2701
+ pulse: true
2702
+ },
2703
+ recovering: {
2704
+ label: "Recovering",
2705
+ dotClassName: "bg-og-status-running",
2706
+ badgeClassName: "text-og-status-running border-og-status-running/30 bg-og-status-running/10",
2707
+ pulse: true
2708
+ },
2709
+ waiting_capacity: {
2710
+ label: "Waiting for capacity",
2711
+ dotClassName: "bg-og-status-waiting",
2712
+ badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10",
2713
+ pulse: true
2714
+ },
2715
+ idle: {
2716
+ label: "Idle",
2717
+ dotClassName: "bg-og-status-idle",
2718
+ badgeClassName: "text-og-status-idle border-og-status-idle/30 bg-og-status-idle/10",
2719
+ pulse: false
2720
+ },
2721
+ requires_action: {
2722
+ label: "Waiting on you",
2723
+ dotClassName: "bg-og-status-waiting",
2724
+ badgeClassName: "text-og-status-waiting border-og-status-waiting/35 bg-og-status-waiting/10",
2725
+ pulse: true
2726
+ },
2727
+ failed: {
2728
+ label: "Failed",
2729
+ dotClassName: "bg-og-status-failed",
2730
+ badgeClassName: "text-og-status-failed border-og-status-failed/35 bg-og-status-failed/10",
2731
+ pulse: false
2732
+ },
2733
+ cancelled: {
2734
+ label: "Cancelled",
2735
+ dotClassName: "bg-og-status-cancelled",
2736
+ badgeClassName: "text-og-fg-subtle border-og-border bg-og-status-cancelled/10",
2737
+ pulse: false
2738
+ }
2739
+ };
2740
+ function SessionStatus({ status, label, size = "md", className }) {
2741
+ const meta = SESSION_STATUS_META[status];
2742
+ return /* @__PURE__ */ jsxs9(
2743
+ "span",
2744
+ {
2745
+ "data-status": status,
2746
+ className: cn(
2747
+ "og-root inline-flex shrink-0 items-center rounded-full border font-medium",
2748
+ size === "sm" ? "gap-1 px-1.5 py-px text-og-xs" : "gap-1.5 px-2 py-0.5 text-xs",
2749
+ meta.badgeClassName,
2750
+ className
2751
+ ),
2752
+ children: [
2753
+ /* @__PURE__ */ jsx12(StatusDot, { status, className: size === "sm" ? "size-1" : "size-1.5" }),
2754
+ label ?? meta.label
2755
+ ]
2756
+ }
2757
+ );
2758
+ }
2759
+ function StatusDot({ status, className }) {
2760
+ const meta = SESSION_STATUS_META[status];
2761
+ return /* @__PURE__ */ jsx12(
2762
+ "span",
2763
+ {
2764
+ className: cn(
2765
+ "relative inline-flex size-1.5 shrink-0 rounded-full",
2766
+ meta.dotClassName,
2767
+ className
2768
+ ),
2769
+ children: meta.pulse ? /* @__PURE__ */ jsx12("span", { className: cn("absolute inset-0 animate-og-pulse rounded-full", meta.dotClassName) }) : null
2770
+ }
2771
+ );
2772
+ }
2773
+
2774
+ // src/components/message-timeline.tsx
2775
+ import {
2776
+ ArrowDownIcon,
2777
+ ArrowRightIcon as ArrowRightIcon2,
2778
+ BotIcon as BotIcon2,
2779
+ CheckCircle2Icon,
2780
+ CheckIcon as CheckIcon2,
2781
+ ChevronRightIcon as ChevronRightIcon3,
2782
+ PauseCircleIcon,
2783
+ PauseIcon,
2784
+ PencilLineIcon,
2785
+ PlayIcon,
2786
+ RefreshCwIcon,
2787
+ TargetIcon,
2788
+ Trash2Icon,
2789
+ TriangleAlertIcon as TriangleAlertIcon2,
2790
+ XCircleIcon
2791
+ } from "lucide-react";
2792
+ import { AnimatePresence as AnimatePresence2, motion as motion2 } from "motion/react";
2793
+ import { Collapsible as Collapsible3 } from "radix-ui";
2794
+ import {
2795
+ memo as memo2,
2796
+ useCallback as useCallback2,
2797
+ useEffect as useEffect4,
2798
+ useLayoutEffect,
2799
+ useMemo as useMemo2,
2800
+ useRef as useRef3,
2801
+ useState as useState8
2802
+ } from "react";
2803
+ import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
2804
+ var INITIAL_MOUNTED_GROUPS = 1;
2805
+ function MessageTimeline({
2806
+ events,
2807
+ items,
2808
+ status,
2809
+ renderMessageText,
2810
+ onOpenSession,
2811
+ onMemoryClick,
2812
+ onReconnect,
2813
+ resolveProviderLogo,
2814
+ toolRegistry = defaultToolRegistry,
2815
+ autoFollow = true,
2816
+ hasOlder = false,
2817
+ loadingOlder = false,
2818
+ onLoadOlder,
2819
+ emptyState,
2820
+ className
2821
+ }) {
2822
+ const resolvedItems = useMemo2(() => items ?? buildTimeline(events ?? []), [items, events]);
2823
+ const allGroups = useMemo2(() => groupTimeline(resolvedItems), [resolvedItems]);
2824
+ const keyedGroups = useStableTimelineGroupKeys(allGroups);
2825
+ const { mountedGroups: groups, mountingOlderGroups } = useProgressivelyMountedGroups(keyedGroups);
2826
+ const firstGroupKey = allGroups[0] ? timelineGroupKey(allGroups[0]) : null;
2827
+ const scrollRef = useRef3(null);
2828
+ const topSentinelRef = useRef3(null);
2829
+ const previousBulkFirstKeyRef = useRef3(void 0);
2830
+ const [pinned, setPinned] = useState8(true);
2831
+ const [bulkActive, setBulkActive] = useState8(true);
2832
+ const [revealed, setRevealed] = useState8(false);
2833
+ const programmaticScrollTargetsRef = useRef3([]);
2834
+ const assignScrollTop = useCallback2((node, value) => {
2835
+ const previous = node.scrollTop;
2836
+ node.scrollTop = value;
2837
+ if (node.scrollTop === previous) {
2838
+ return;
2839
+ }
2840
+ const pending = programmaticScrollTargetsRef.current;
2841
+ pending.push(node.scrollTop);
2842
+ if (pending.length > 32) {
2843
+ pending.splice(0, pending.length - 32);
2844
+ }
2845
+ }, []);
2846
+ const pinnedRef = useRef3(true);
2847
+ const anchorRef = useRef3(null);
2848
+ const lastItem = resolvedItems[resolvedItems.length - 1];
2849
+ const streaming = lastItem !== void 0 && (lastItem.kind === "agent-message" || lastItem.kind === "reasoning") && lastItem.streaming;
2850
+ const working = status === "running" && !streaming;
2851
+ const firstKeyChangedForBulk = previousBulkFirstKeyRef.current !== void 0 && previousBulkFirstKeyRef.current !== firstGroupKey;
2852
+ const bulkRender = allGroups.length > 0 && (bulkActive || firstKeyChangedForBulk || mountingOlderGroups);
2853
+ const captureAnchor = useCallback2(() => {
2854
+ const node = scrollRef.current;
2855
+ const inner = node?.firstElementChild;
2856
+ if (!node || !inner) {
2857
+ anchorRef.current = null;
2858
+ return;
2859
+ }
2860
+ const containerTop = node.getBoundingClientRect().top;
2861
+ const findVisible = (selector) => {
2862
+ for (const candidate of inner.querySelectorAll(selector)) {
2863
+ if (candidate instanceof HTMLElement && candidate.dataset.ogTimelineChrome !== void 0) {
2864
+ continue;
2865
+ }
2866
+ const rect = candidate.getBoundingClientRect();
2867
+ if (rect.bottom > containerTop + 1 && rect.height > 0) {
2868
+ return candidate;
2869
+ }
2870
+ }
2871
+ return null;
2872
+ };
2873
+ const anchor = findVisible("[data-og-timeline-row-anchor]") ?? findVisible("[data-og-timeline-group-anchor]");
2874
+ if (anchor) {
2875
+ const rect = anchor.getBoundingClientRect();
2876
+ anchorRef.current = { el: anchor, top: rect.top - containerTop };
2877
+ return;
2878
+ }
2879
+ anchorRef.current = null;
2880
+ }, []);
2881
+ const restoreAnchor = useCallback2(
2882
+ (node) => {
2883
+ if (autoFollow && pinnedRef.current) {
2884
+ assignScrollTop(node, node.scrollHeight);
2885
+ return;
2886
+ }
2887
+ const anchor = anchorRef.current;
2888
+ if (!anchor || !anchor.el.isConnected) {
2889
+ return;
2890
+ }
2891
+ const containerTop = node.getBoundingClientRect().top;
2892
+ const now = anchor.el.getBoundingClientRect().top - containerTop;
2893
+ const diff = now - anchor.top;
2894
+ if (diff !== 0) {
2895
+ assignScrollTop(node, node.scrollTop + diff);
2896
+ }
2897
+ },
2898
+ [assignScrollTop, autoFollow]
2899
+ );
2900
+ useLayoutEffect(() => {
2901
+ const node = scrollRef.current;
2902
+ if (node) {
2903
+ restoreAnchor(node);
2904
+ }
2905
+ if (!revealed && groups.length > 0) {
2906
+ setRevealed(true);
2907
+ }
2908
+ }, [resolvedItems, working, revealed, groups.length, restoreAnchor]);
2909
+ useLayoutEffect(() => {
2910
+ if (allGroups.length === 0 && revealed) {
2911
+ setRevealed(false);
2912
+ }
2913
+ }, [allGroups.length, revealed]);
2914
+ useLayoutEffect(() => {
2915
+ previousBulkFirstKeyRef.current = firstGroupKey;
2916
+ if (!bulkRender) {
2917
+ return;
2918
+ }
2919
+ setBulkActive(true);
2920
+ if (mountingOlderGroups) {
2921
+ return;
2922
+ }
2923
+ const frame = requestFrame(() => setBulkActive(false));
2924
+ return () => cancelFrame(frame);
2925
+ }, [bulkRender, firstGroupKey, mountingOlderGroups]);
2926
+ useEffect4(() => {
2927
+ const root = scrollRef.current;
2928
+ const target = topSentinelRef.current;
2929
+ if (!root || !target || mountingOlderGroups || !hasOlder || loadingOlder || !onLoadOlder || typeof IntersectionObserver === "undefined") {
2930
+ return;
2931
+ }
2932
+ const observer = new IntersectionObserver(
2933
+ (entries) => {
2934
+ if (entries.some((entry) => entry.isIntersecting)) {
2935
+ onLoadOlder();
2936
+ }
2937
+ },
2938
+ { root, rootMargin: "1600px 0px 0px 0px" }
2939
+ );
2940
+ observer.observe(target);
2941
+ return () => observer.disconnect();
2942
+ }, [hasOlder, loadingOlder, onLoadOlder, firstGroupKey, mountingOlderGroups]);
2943
+ useEffect4(() => {
2944
+ const node = scrollRef.current;
2945
+ const inner = node?.firstElementChild;
2946
+ if (!node || !inner || typeof ResizeObserver === "undefined") {
2947
+ return;
2948
+ }
2949
+ const observer = new ResizeObserver(() => {
2950
+ const current = scrollRef.current;
2951
+ if (!current) {
2952
+ return;
2953
+ }
2954
+ restoreAnchor(current);
2955
+ });
2956
+ observer.observe(inner);
2957
+ return () => observer.disconnect();
2958
+ }, [restoreAnchor]);
2959
+ const onScroll = () => {
2960
+ const node = scrollRef.current;
2961
+ if (!node) {
2962
+ return;
2963
+ }
2964
+ const pending = programmaticScrollTargetsRef.current;
2965
+ const programmaticIndex = pending.findIndex((target) => Math.abs(node.scrollTop - target) <= 1);
2966
+ const programmatic = programmaticIndex >= 0;
2967
+ if (programmatic) {
2968
+ pending.splice(0, programmaticIndex + 1);
2969
+ } else {
2970
+ pending.length = 0;
2971
+ }
2972
+ const nextPinned = node.scrollHeight - node.scrollTop - node.clientHeight < 48;
2973
+ if (nextPinned || !programmatic) {
2974
+ pinnedRef.current = nextPinned;
2975
+ setPinned(nextPinned);
2976
+ }
2977
+ if (!programmatic) {
2978
+ captureAnchor();
2979
+ }
2980
+ };
2981
+ return /* @__PURE__ */ jsx13(LightboxProvider, { children: /* @__PURE__ */ jsx13(EntranceAnimationProvider, { value: !bulkRender, children: /* @__PURE__ */ jsxs10("div", { className: cn("og-root relative flex min-h-0 flex-col", className), children: [
2982
+ /* @__PURE__ */ jsx13(
2983
+ "div",
2984
+ {
2985
+ ref: scrollRef,
2986
+ onScroll,
2987
+ style: groups.length > 0 && !revealed ? { visibility: "hidden" } : void 0,
2988
+ className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-6 [overflow-anchor:none] sm:px-6",
2989
+ children: /* @__PURE__ */ jsxs10("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-5", children: [
2990
+ groups.length === 0 && !working ? emptyState ?? /* @__PURE__ */ jsx13("p", { className: "py-10 text-center text-sm text-og-fg-subtle", children: "No activity yet." }) : null,
2991
+ hasOlder && !mountingOlderGroups ? /* @__PURE__ */ jsx13(
2992
+ "div",
2993
+ {
2994
+ ref: topSentinelRef,
2995
+ "data-og-top-sentinel": "",
2996
+ "data-og-timeline-chrome": "",
2997
+ "aria-hidden": "true",
2998
+ className: "h-px w-full shrink-0"
2999
+ }
3000
+ ) : null,
3001
+ loadingOlder ? /* @__PURE__ */ jsx13("div", { "data-og-timeline-chrome": "", className: "flex items-center gap-2 text-sm", children: /* @__PURE__ */ jsx13("span", { className: "og-shimmer-text font-medium", children: "Loading earlier activity\u2026" }) }) : null,
3002
+ groups.map(({ group, key }, index) => /* @__PURE__ */ jsx13("div", { "data-og-timeline-group-anchor": "", children: /* @__PURE__ */ jsx13(
3003
+ TimelineGroupView,
3004
+ {
3005
+ group,
3006
+ renderMessageText,
3007
+ onOpenSession,
3008
+ onMemoryClick,
3009
+ onReconnect,
3010
+ resolveProviderLogo,
3011
+ toolRegistry,
3012
+ foldLiveCluster: isAgentProgress(groups[index + 1]?.group)
3013
+ }
3014
+ ) }, key)),
3015
+ working ? /* @__PURE__ */ jsx13("div", { className: "flex items-center gap-2 text-sm", children: /* @__PURE__ */ jsx13("span", { className: "og-shimmer-text font-medium", children: "Working\u2026" }) }) : null
3016
+ ] })
3017
+ }
3018
+ ),
3019
+ /* @__PURE__ */ jsx13(AnimatePresence2, { children: !pinned && autoFollow ? /* @__PURE__ */ jsxs10(
3020
+ motion2.button,
3021
+ {
3022
+ type: "button",
3023
+ initial: { opacity: 0, y: 8 },
3024
+ animate: { opacity: 1, y: 0 },
3025
+ exit: { opacity: 0, y: 8 },
3026
+ transition: { duration: 0.15, ease: "easeOut" },
3027
+ onClick: () => {
3028
+ const node = scrollRef.current;
3029
+ if (node) {
3030
+ node.scrollTo({ top: node.scrollHeight, behavior: "smooth" });
3031
+ }
3032
+ pinnedRef.current = true;
3033
+ setPinned(true);
3034
+ },
3035
+ className: cn(
3036
+ "absolute bottom-4 left-1/2 -translate-x-1/2",
3037
+ "inline-flex items-center gap-1.5 rounded-full border border-og-border bg-og-surface-3/90 px-3 py-1.5",
3038
+ "text-xs font-medium text-og-fg shadow-og-md backdrop-blur",
3039
+ "hover:border-og-border-strong"
3040
+ ),
3041
+ children: [
3042
+ /* @__PURE__ */ jsx13(ArrowDownIcon, { className: "size-3.5" }),
3043
+ "Jump to latest"
3044
+ ]
3045
+ }
3046
+ ) : null })
3047
+ ] }) }) });
3048
+ }
3049
+ function useStableTimelineGroupKeys(allGroups) {
3050
+ const previousRef = useRef3([]);
3051
+ const keyedGroups = useMemo2(() => {
3052
+ const previousByItemId = /* @__PURE__ */ new Map();
3053
+ for (const previous of previousRef.current) {
3054
+ for (const itemId of timelineGroupItemIds(previous.group)) {
3055
+ previousByItemId.set(itemId, previous);
3056
+ }
3057
+ }
3058
+ const usedKeys = /* @__PURE__ */ new Set();
3059
+ return allGroups.map((group, index) => {
3060
+ const itemIds = timelineGroupItemIds(group);
3061
+ let retainedKey;
3062
+ for (const itemId of itemIds) {
3063
+ const previous = previousByItemId.get(itemId);
3064
+ if (previous && !usedKeys.has(previous.key)) {
3065
+ retainedKey = previous.key;
3066
+ break;
3067
+ }
3068
+ }
3069
+ const canonicalKey = timelineGroupKey(group);
3070
+ let key = retainedKey ?? canonicalKey;
3071
+ let collision = 0;
3072
+ while (usedKeys.has(key)) {
3073
+ key = `${canonicalKey}:${index}:${collision}`;
3074
+ collision += 1;
3075
+ }
3076
+ usedKeys.add(key);
3077
+ return { group, key };
3078
+ });
3079
+ }, [allGroups]);
3080
+ useLayoutEffect(() => {
3081
+ previousRef.current = keyedGroups;
3082
+ }, [keyedGroups]);
3083
+ return keyedGroups;
3084
+ }
3085
+ function useProgressivelyMountedGroups(allGroups) {
3086
+ const previousGroupKeysRef = useRef3([]);
3087
+ const groupKeys = useMemo2(() => {
3088
+ const nextKeys = allGroups.map((group) => group.key);
3089
+ const previousKeys = previousGroupKeysRef.current;
3090
+ return nextKeys.length === previousKeys.length && nextKeys.every((key, index) => key === previousKeys[index]) ? previousKeys : nextKeys;
3091
+ }, [allGroups]);
3092
+ useLayoutEffect(() => {
3093
+ previousGroupKeysRef.current = groupKeys;
3094
+ }, [groupKeys]);
3095
+ const [window2, setWindow] = useState8(() => ({
3096
+ groupKeys,
3097
+ visibleStart: Math.max(0, allGroups.length - INITIAL_MOUNTED_GROUPS)
3098
+ }));
3099
+ const lastPossibleStart = Math.max(0, allGroups.length - INITIAL_MOUNTED_GROUPS);
3100
+ let visibleStart = 0;
3101
+ if (allGroups.length > 0) {
3102
+ const currentIndexByKey = new Map(groupKeys.map((key, index) => [key, index]));
3103
+ let retainedStart;
3104
+ for (const key of window2.groupKeys.slice(window2.visibleStart)) {
3105
+ const index = currentIndexByKey.get(key);
3106
+ if (index !== void 0) {
3107
+ retainedStart = index;
3108
+ break;
3109
+ }
3110
+ }
3111
+ visibleStart = retainedStart === void 0 ? lastPossibleStart : Math.min(retainedStart, lastPossibleStart);
3112
+ }
3113
+ useEffect4(() => {
3114
+ if (window2.groupKeys !== groupKeys || window2.visibleStart !== visibleStart) {
3115
+ setWindow({ groupKeys, visibleStart });
3116
+ return;
3117
+ }
3118
+ if (visibleStart === 0) {
3119
+ return;
3120
+ }
3121
+ const frame = requestFrame(() => {
3122
+ setWindow(
3123
+ (current) => current.groupKeys === groupKeys && current.visibleStart > 0 ? { ...current, visibleStart: current.visibleStart - 1 } : current
3124
+ );
3125
+ });
3126
+ return () => cancelFrame(frame);
3127
+ }, [groupKeys, visibleStart, window2.groupKeys, window2.visibleStart]);
3128
+ return {
3129
+ mountedGroups: allGroups.slice(visibleStart),
3130
+ mountingOlderGroups: visibleStart > 0
3131
+ };
3132
+ }
3133
+ function requestFrame(callback) {
3134
+ if (typeof requestAnimationFrame === "function") {
3135
+ return requestAnimationFrame(callback);
3136
+ }
3137
+ return window.setTimeout(() => callback(performance.now()), 16);
3138
+ }
3139
+ function cancelFrame(id) {
3140
+ if (typeof cancelAnimationFrame === "function") {
3141
+ cancelAnimationFrame(id);
3142
+ return;
3143
+ }
3144
+ window.clearTimeout(id);
3145
+ }
3146
+ var TimelineGroupView = memo2(function TimelineGroupView2({
3147
+ group,
3148
+ renderMessageText,
3149
+ onOpenSession,
3150
+ onMemoryClick,
3151
+ onReconnect,
3152
+ resolveProviderLogo,
3153
+ toolRegistry,
3154
+ insideTurn = false,
3155
+ foldLiveCluster = false
3156
+ }) {
3157
+ switch (group.kind) {
3158
+ case "activity":
3159
+ return group.outcome || foldLiveCluster && clusterIsSettled(group) ? /* @__PURE__ */ jsx13(
3160
+ TurnSummary,
3161
+ {
3162
+ items: group.items,
3163
+ outcome: group.outcome,
3164
+ failureText: insideTurn ? void 0 : group.failureText,
3165
+ defaultOpen: !insideTurn && group.outcome === "failed" ? true : void 0,
3166
+ bare: insideTurn,
3167
+ children: /* @__PURE__ */ jsx13(
3168
+ ActivityRail,
3169
+ {
3170
+ items: group.items,
3171
+ onOpenSession,
3172
+ onMemoryClick,
3173
+ toolRegistry,
3174
+ bare: insideTurn
3175
+ }
3176
+ )
3177
+ }
3178
+ ) : (
3179
+ // A nested live cluster hangs on the turn's rail (bare); a top-level one
3180
+ // owns its own rail.
3181
+ /* @__PURE__ */ jsx13(
3182
+ ActivityRail,
3183
+ {
3184
+ items: group.items,
3185
+ onOpenSession,
3186
+ onMemoryClick,
3187
+ toolRegistry,
3188
+ bare: insideTurn
3189
+ }
3190
+ )
3191
+ );
3192
+ case "turn": {
3193
+ const activityItems = flattenActivityItems(group.groups);
3194
+ const body = group.groups.map((child) => /* @__PURE__ */ jsx13(
3195
+ TimelineGroupView2,
3196
+ {
3197
+ group: child,
3198
+ renderMessageText,
3199
+ onOpenSession,
3200
+ onMemoryClick,
3201
+ onReconnect,
3202
+ resolveProviderLogo,
3203
+ toolRegistry,
3204
+ insideTurn: true
3205
+ },
3206
+ timelineGroupKey(child)
3207
+ ));
3208
+ return /* @__PURE__ */ jsx13(
3209
+ TurnSummary,
3210
+ {
3211
+ items: activityItems,
3212
+ outcome: group.outcome,
3213
+ failureText: insideTurn ? void 0 : group.failureText,
3214
+ durationMs: durationBetween(group.startedAt, group.endedAt),
3215
+ defaultOpen: !insideTurn && group.outcome === "failed" ? true : void 0,
3216
+ bare: insideTurn,
3217
+ children: insideTurn ? (
3218
+ // A nested turn is already on an ancestor rail — its body just stacks
3219
+ // flush (the bare-node body already indents it), so no second rule.
3220
+ /* @__PURE__ */ jsx13("div", { className: "flex flex-col gap-4", children: body })
3221
+ ) : (
3222
+ // The top-level turn draws THE rail: one thin continuous rule that
3223
+ // every nested node and step hangs off of.
3224
+ /* @__PURE__ */ jsx13("div", { className: "flex flex-col gap-4 border-l-2 border-og-border pl-3 sm:pl-4", children: body })
3225
+ )
3226
+ }
3227
+ );
3228
+ }
3229
+ case "item":
3230
+ return /* @__PURE__ */ jsx13(
3231
+ TimelineRow,
3232
+ {
3233
+ item: group.item,
3234
+ renderMessageText,
3235
+ onReconnect,
3236
+ resolveProviderLogo,
3237
+ onOpenSession
3238
+ }
3239
+ );
3240
+ }
3241
+ });
3242
+ function timelineGroupKey(group) {
3243
+ switch (group.kind) {
3244
+ case "item":
3245
+ return group.item.id;
3246
+ case "activity":
3247
+ return group.id;
3248
+ case "turn":
3249
+ return group.id;
3250
+ }
3251
+ }
3252
+ function timelineGroupItemIds(group) {
3253
+ switch (group.kind) {
3254
+ case "item":
3255
+ return [group.item.id];
3256
+ case "activity":
3257
+ return group.items.map((item) => item.id);
3258
+ case "turn":
3259
+ return group.groups.flatMap(timelineGroupItemIds);
3260
+ }
3261
+ }
3262
+ function isAgentProgress(next) {
3263
+ if (!next) {
3264
+ return false;
3265
+ }
3266
+ return next.kind === "activity" || next.kind === "turn" || next.kind === "item" && next.item.kind === "agent-message";
3267
+ }
3268
+ function clusterIsSettled(group) {
3269
+ return group.items.every((item) => {
3270
+ if (item.kind === "reasoning") {
3271
+ return !item.streaming;
3272
+ }
3273
+ if (item.kind === "memory" || item.kind === "fleet-decision") {
3274
+ return true;
3275
+ }
3276
+ return item.status !== "running";
3277
+ });
3278
+ }
3279
+ function flattenActivityItems(groups) {
3280
+ const items = [];
3281
+ for (const group of groups) {
3282
+ if (group.kind === "activity") {
3283
+ items.push(...group.items);
3284
+ } else if (group.kind === "turn") {
3285
+ items.push(...flattenActivityItems(group.groups));
3286
+ }
3287
+ }
3288
+ return items;
3289
+ }
3290
+ function durationBetween(startedAt, endedAt) {
3291
+ const started = Date.parse(startedAt);
3292
+ const ended = Date.parse(endedAt);
3293
+ if (!Number.isFinite(started) || !Number.isFinite(ended) || ended < started) {
3294
+ return void 0;
3295
+ }
3296
+ return ended - started;
3297
+ }
3298
+ function TimelineRow({
3299
+ item,
3300
+ renderMessageText,
3301
+ onReconnect,
3302
+ resolveProviderLogo,
3303
+ onOpenSession
3304
+ }) {
3305
+ switch (item.kind) {
3306
+ case "user-message":
3307
+ return /* @__PURE__ */ jsx13(UserMessageRow, { item, renderMessageText });
3308
+ case "agent-message":
3309
+ return /* @__PURE__ */ jsx13(AgentMessageRow, { item, renderMessageText });
3310
+ case "worker-completion":
3311
+ return /* @__PURE__ */ jsx13(WorkerCompletionRow, { item, onOpenSession });
3312
+ case "session-status":
3313
+ return /* @__PURE__ */ jsx13(SessionStatusRow, { item });
3314
+ case "goal":
3315
+ return /* @__PURE__ */ jsx13(GoalRow, { item });
3316
+ case "notice":
3317
+ return /* @__PURE__ */ jsx13(NoticeRow, { item });
3318
+ case "auth-needed":
3319
+ return /* @__PURE__ */ jsx13(
3320
+ AuthNeededRow,
3321
+ {
3322
+ item,
3323
+ onReconnect,
3324
+ resolveProviderLogo
3325
+ }
3326
+ );
3327
+ default:
3328
+ return null;
3329
+ }
3330
+ }
3331
+ function UserMessageRow({
3332
+ item,
3333
+ renderMessageText
3334
+ }) {
3335
+ const enter = useEntranceAnimation();
3336
+ return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "flex justify-end"), children: /* @__PURE__ */ jsx13("div", { className: "flex max-w-[85%] min-w-0 flex-col items-end gap-1", children: /* @__PURE__ */ jsx13("div", { className: "w-fit max-w-full min-w-0 rounded-og-lg rounded-br-og-xs border border-og-border bg-og-surface-2 px-4 py-2.5 text-og-md leading-6 text-og-fg", children: renderMessageText ? renderMessageText(item.text, item) : /* @__PURE__ */ jsx13(Markdown, { children: item.text }) }) }) });
3337
+ }
3338
+ function AgentMessageRow({
3339
+ item,
3340
+ renderMessageText
3341
+ }) {
3342
+ const enter = useEntranceAnimation();
3343
+ const caret = item.streaming ? /* @__PURE__ */ jsx13(
3344
+ "span",
3345
+ {
3346
+ className: "ml-0.5 inline-block h-[1.1em] w-[2px] translate-y-[3px] animate-og-blink rounded-full bg-og-accent",
3347
+ "aria-hidden": true
3348
+ }
3349
+ ) : null;
3350
+ return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "min-w-0 text-og-md leading-7 text-og-fg"), children: renderMessageText ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
3351
+ renderMessageText(item.text, item),
3352
+ caret
3353
+ ] }) : (
3354
+ // While streaming, let the caret ride the end of the last rendered line:
3355
+ // the trailing block (usually a <p>) flows inline so the caret sits on
3356
+ // its baseline instead of dropping to a new line.
3357
+ /* @__PURE__ */ jsxs10("div", { className: item.streaming ? "[&_>div>:last-child]:inline" : void 0, children: [
3358
+ /* @__PURE__ */ jsx13(Markdown, { children: item.text }),
3359
+ caret
3360
+ ] })
3361
+ ) });
3362
+ }
3363
+ function workerCompletionMeta(item) {
3364
+ if (item.childStatus === "failed") {
3365
+ return {
3366
+ label: "Worker failed",
3367
+ icon: XCircleIcon,
3368
+ iconClass: "text-og-status-failed",
3369
+ accentClass: "border-og-status-failed/60"
3370
+ };
3371
+ }
3372
+ if (item.goalStatus === "paused") {
3373
+ return {
3374
+ label: "Worker paused",
3375
+ icon: PauseCircleIcon,
3376
+ iconClass: "text-og-status-waiting",
3377
+ accentClass: "border-og-status-waiting/50"
3378
+ };
3379
+ }
3380
+ if (item.goalStatus === "completed") {
3381
+ return {
3382
+ label: "Worker completed",
3383
+ icon: CheckCircle2Icon,
3384
+ iconClass: "text-og-status-idle",
3385
+ accentClass: "border-og-status-idle/45"
3386
+ };
3387
+ }
3388
+ return {
3389
+ label: "Worker reported back",
3390
+ icon: BotIcon2,
3391
+ iconClass: "text-og-accent",
3392
+ accentClass: "border-og-border-strong"
3393
+ };
3394
+ }
3395
+ function WorkerCompletionRow({
3396
+ item,
3397
+ onOpenSession
3398
+ }) {
3399
+ const enter = useEntranceAnimation();
3400
+ const [open, setOpen] = useState8(false);
3401
+ const meta = workerCompletionMeta(item);
3402
+ const Icon = meta.icon;
3403
+ const showPausedReason = item.childStatus !== "failed" && item.goalStatus === "paused" && Boolean(item.pausedReason?.trim());
3404
+ const details = [
3405
+ ...item.text.trim() ? [{ label: "Report", value: item.text.trim() }] : [],
3406
+ ...item.evidence?.trim() ? [{ label: "Evidence", value: item.evidence.trim(), muted: true }] : [],
3407
+ ...showPausedReason ? [{ label: "Paused because", value: item.pausedReason.trim(), muted: true }] : []
3408
+ ];
3409
+ const hasDetails = details.length > 0;
3410
+ return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "min-w-0"), children: /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col gap-2 border-l-2 pl-3", meta.accentClass), children: [
3411
+ /* @__PURE__ */ jsxs10("div", { className: "flex items-start gap-2.5", children: [
3412
+ /* @__PURE__ */ jsx13("span", { className: cn("mt-0.5 shrink-0", meta.iconClass), children: /* @__PURE__ */ jsx13(Icon, { className: "size-4" }) }),
3413
+ /* @__PURE__ */ jsx13("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxs10("p", { className: "text-og-base leading-5 text-og-fg", children: [
3414
+ /* @__PURE__ */ jsx13("span", { className: "font-medium", children: meta.label }),
3415
+ item.goalText ? /* @__PURE__ */ jsxs10("span", { className: "text-og-fg-muted", children: [
3416
+ " \xB7 ",
3417
+ truncate(item.goalText, 90)
3418
+ ] }) : null
3419
+ ] }) }),
3420
+ item.childSessionId && onOpenSession ? /* @__PURE__ */ jsxs10(
3421
+ "button",
3422
+ {
3423
+ type: "button",
3424
+ onClick: () => onOpenSession(item.childSessionId),
3425
+ className: cn(
3426
+ "-my-0.5 -mr-1 inline-flex shrink-0 items-center gap-1 rounded-og-sm px-2 py-1 text-og-sm font-medium text-og-fg-muted pointer-coarse:py-2",
3427
+ "outline-none transition-colors duration-150 hover:bg-og-surface-2 hover:text-og-fg",
3428
+ "focus-visible:ring-2 focus-visible:ring-og-accent"
3429
+ ),
3430
+ children: [
3431
+ "View session",
3432
+ /* @__PURE__ */ jsx13(ArrowRightIcon2, { className: "size-3.5" })
3433
+ ]
3434
+ }
3435
+ ) : null
3436
+ ] }),
3437
+ hasDetails ? /* @__PURE__ */ jsxs10(Collapsible3.Root, { open, onOpenChange: setOpen, children: [
3438
+ /* @__PURE__ */ jsx13(Collapsible3.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs10(
3439
+ "button",
3440
+ {
3441
+ type: "button",
3442
+ className: cn(
3443
+ "group/wc -mx-1 inline-flex w-fit items-center gap-1 rounded-og-sm px-1 py-0.5 text-og-xs font-medium text-og-fg-subtle",
3444
+ "outline-none transition-colors duration-150 hover:text-og-fg-muted focus-visible:ring-2 focus-visible:ring-og-accent"
3445
+ ),
3446
+ children: [
3447
+ /* @__PURE__ */ jsx13(ChevronRightIcon3, { className: "size-3 transition-transform duration-150 ease-og-in-out group-data-[state=open]/wc:rotate-90" }),
3448
+ open ? "Hide details" : "Show details"
3449
+ ]
3450
+ }
3451
+ ) }),
3452
+ /* @__PURE__ */ jsx13(Collapsible3.Content, { className: "overflow-hidden data-[state=closed]:animate-og-collapse data-[state=open]:animate-og-expand", children: /* @__PURE__ */ jsx13("div", { className: "ml-1 mt-1.5 flex flex-col gap-2.5", children: details.map((detail) => /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
3453
+ /* @__PURE__ */ jsx13("p", { className: "mb-1 text-og-xs font-medium uppercase tracking-[0.08em] text-og-fg-subtle", children: detail.label }),
3454
+ /* @__PURE__ */ jsx13(
3455
+ "p",
3456
+ {
3457
+ className: cn(
3458
+ "whitespace-pre-wrap break-words text-og-sm leading-6",
3459
+ detail.muted ? "text-og-fg-subtle" : "text-og-fg-muted"
3460
+ ),
3461
+ children: detail.value
3462
+ }
3463
+ )
3464
+ ] }, detail.label)) }) })
3465
+ ] }) : null
3466
+ ] }) });
3467
+ }
3468
+ function SessionStatusRow({ item }) {
3469
+ const enter = useEntranceAnimation();
3470
+ const meta = SESSION_STATUS_META[item.status];
3471
+ return /* @__PURE__ */ jsxs10(
3472
+ "div",
3473
+ {
3474
+ className: cn(
3475
+ enter && "animate-og-enter",
3476
+ "flex items-center gap-3 text-og-xs text-og-fg-subtle"
3477
+ ),
3478
+ role: "status",
3479
+ children: [
3480
+ /* @__PURE__ */ jsx13("span", { className: "h-px flex-1 bg-og-border" }),
3481
+ /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
3482
+ /* @__PURE__ */ jsx13(StatusDot, { status: item.status, className: "size-1" }),
3483
+ meta.label.toLowerCase(),
3484
+ " \xB7 ",
3485
+ formatRelativeTime(item.occurredAt)
3486
+ ] }),
3487
+ /* @__PURE__ */ jsx13("span", { className: "h-px flex-1 bg-og-border" })
3488
+ ]
3489
+ }
3490
+ );
3491
+ }
3492
+ var NEUTRAL_PILL = "border-og-border bg-og-surface-1 text-og-fg-muted";
3493
+ var GOAL_META = {
3494
+ set: {
3495
+ label: "Goal set",
3496
+ pill: "border-og-accent/30 bg-og-accent/10 text-og-accent",
3497
+ icon: TargetIcon
3498
+ },
3499
+ updated: { label: "Goal updated", pill: NEUTRAL_PILL, icon: PencilLineIcon },
3500
+ completed: {
3501
+ label: "Goal completed",
3502
+ pill: "border-og-status-idle/30 bg-og-status-idle/10 text-og-status-idle",
3503
+ icon: CheckIcon2
3504
+ },
3505
+ paused: {
3506
+ label: "Goal paused",
3507
+ pill: "border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting",
3508
+ icon: PauseIcon
3509
+ },
3510
+ resumed: { label: "Goal resumed", pill: NEUTRAL_PILL, icon: PlayIcon },
3511
+ cleared: { label: "Goal cleared", pill: NEUTRAL_PILL, icon: Trash2Icon },
3512
+ continuation: { label: "Continuing toward the goal", pill: NEUTRAL_PILL, icon: ArrowRightIcon2 }
3513
+ };
3514
+ function GoalRow({ item }) {
3515
+ const enter = useEntranceAnimation();
3516
+ const { label, pill, icon: Icon } = GOAL_META[item.action];
3517
+ return /* @__PURE__ */ jsx13("div", { className: cn(enter && "animate-og-enter", "flex justify-center"), children: /* @__PURE__ */ jsxs10(
3518
+ "span",
3519
+ {
3520
+ className: cn(
3521
+ "inline-flex max-w-full items-center gap-1.5 rounded-full border px-3 py-1 text-og-sm",
3522
+ pill
3523
+ ),
3524
+ children: [
3525
+ /* @__PURE__ */ jsx13(Icon, { className: "size-3.5 shrink-0" }),
3526
+ /* @__PURE__ */ jsxs10("span", { className: "truncate", children: [
3527
+ label,
3528
+ item.text ? `: ${truncate(item.text, 90)}` : ""
3529
+ ] })
3530
+ ]
3531
+ }
3532
+ ) });
3533
+ }
3534
+ function NoticeRow({ item }) {
3535
+ const enter = useEntranceAnimation();
3536
+ const tone = item.tone === "failed" ? "border-og-status-failed/35 bg-og-status-failed/10 text-og-status-failed" : item.tone === "waiting" ? "border-og-status-waiting/35 bg-og-status-waiting/10 text-og-status-waiting" : "border-og-border bg-og-surface-1 text-og-fg-muted";
3537
+ return /* @__PURE__ */ jsxs10(
3538
+ "div",
3539
+ {
3540
+ className: cn(
3541
+ enter && "animate-og-enter",
3542
+ "flex items-start gap-2.5 rounded-og-md border px-3.5 py-2.5 text-sm",
3543
+ tone
3544
+ ),
3545
+ role: "status",
3546
+ children: [
3547
+ /* @__PURE__ */ jsx13(
3548
+ TriangleAlertIcon2,
3549
+ {
3550
+ className: cn("mt-0.5 size-4 shrink-0", item.tone === "cancelled" && "opacity-60")
3551
+ }
3552
+ ),
3553
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0 flex-1", children: [
3554
+ /* @__PURE__ */ jsx13("span", { className: "whitespace-pre-wrap break-words", children: item.text }),
3555
+ item.details ? /* @__PURE__ */ jsxs10("details", { className: "mt-2 text-xs", children: [
3556
+ /* @__PURE__ */ jsx13("summary", { className: "cursor-pointer font-medium", children: item.details.label }),
3557
+ /* @__PURE__ */ jsx13("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-all rounded-og-sm bg-black/5 p-2 font-mono dark:bg-white/5", children: JSON.stringify(item.details.value, null, 2) })
3558
+ ] }) : null
3559
+ ] }),
3560
+ item.action ? /* @__PURE__ */ jsx13(
3561
+ "a",
3562
+ {
3563
+ className: "shrink-0 rounded-og-sm border border-current/25 px-2 py-1 text-xs font-medium hover:bg-current/10",
3564
+ href: item.action.url,
3565
+ rel: "noreferrer",
3566
+ target: "_blank",
3567
+ children: item.action.label
3568
+ }
3569
+ ) : null
3570
+ ]
3571
+ }
3572
+ );
3573
+ }
3574
+ function AuthNeededRow({
3575
+ item,
3576
+ onReconnect,
3577
+ resolveProviderLogo
3578
+ }) {
3579
+ const enter = useEntranceAnimation();
3580
+ const [busy, setBusy] = useState8(false);
3581
+ const [failed, setFailed] = useState8(false);
3582
+ const provider = providerLabel(item.providerDomain);
3583
+ const unavailable = item.reason === "unsupported_auth" || item.reason === "resource_scope_unavailable";
3584
+ const missing = item.reason === "missing_connection";
3585
+ const actionLabel = missing ? "Connect" : "Reconnect";
3586
+ const start = async () => {
3587
+ if (!onReconnect || busy) {
3588
+ return;
3589
+ }
3590
+ setBusy(true);
3591
+ setFailed(false);
3592
+ try {
3593
+ await onReconnect(item);
3594
+ setBusy(false);
3595
+ } catch {
3596
+ setFailed(true);
3597
+ setBusy(false);
3598
+ }
3599
+ };
3600
+ return /* @__PURE__ */ jsxs10("div", { className: cn(enter && "animate-og-enter", "flex flex-col gap-2"), role: "status", children: [
3601
+ /* @__PURE__ */ jsxs10("div", { className: "flex flex-col gap-3 rounded-og-lg border border-og-border bg-og-surface-1 px-3.5 py-3 sm:flex-row sm:items-center", children: [
3602
+ /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [
3603
+ /* @__PURE__ */ jsx13(
3604
+ AuthProviderLogo,
3605
+ {
3606
+ src: resolveProviderLogo?.(item.providerDomain) ?? null,
3607
+ label: provider
3608
+ }
3609
+ ),
3610
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
3611
+ /* @__PURE__ */ jsx13("p", { className: "truncate text-og-md font-medium text-og-fg", children: unavailable ? `${provider} tools unavailable` : `${actionLabel} ${provider}` }),
3612
+ /* @__PURE__ */ jsx13("p", { className: "truncate text-og-sm text-og-fg-subtle", children: authReasonLine(item.reason) })
3613
+ ] })
3614
+ ] }),
3615
+ !unavailable && onReconnect ? /* @__PURE__ */ jsxs10(
3616
+ "button",
3617
+ {
3618
+ type: "button",
3619
+ onClick: () => void start(),
3620
+ disabled: busy,
3621
+ className: cn(
3622
+ "inline-flex w-full shrink-0 items-center justify-center gap-1.5 rounded-og-md bg-og-accent px-3 py-1.5 text-sm font-medium text-og-accent-fg sm:w-auto",
3623
+ "transition-colors hover:bg-og-accent-strong disabled:opacity-70 pointer-coarse:min-h-9"
3624
+ ),
3625
+ children: [
3626
+ /* @__PURE__ */ jsx13(RefreshCwIcon, { className: cn("size-3.5", busy && "animate-og-spin"), "aria-hidden": true }),
3627
+ busy ? "Opening\u2026" : actionLabel
3628
+ ]
3629
+ }
3630
+ ) : !unavailable && item.authorizationUrl ? /* @__PURE__ */ jsxs10(
3631
+ "a",
3632
+ {
3633
+ href: item.authorizationUrl,
3634
+ rel: "noreferrer",
3635
+ target: "_blank",
3636
+ className: cn(
3637
+ "inline-flex w-full shrink-0 items-center justify-center gap-1.5 rounded-og-md bg-og-accent px-3 py-1.5 text-sm font-medium text-og-accent-fg sm:w-auto",
3638
+ "transition-colors hover:bg-og-accent-strong pointer-coarse:min-h-9"
3639
+ ),
3640
+ children: [
3641
+ /* @__PURE__ */ jsx13(RefreshCwIcon, { className: "size-3.5", "aria-hidden": true }),
3642
+ actionLabel
3643
+ ]
3644
+ }
3645
+ ) : null
3646
+ ] }),
3647
+ !unavailable ? /* @__PURE__ */ jsxs10("p", { className: "px-1 text-og-xs text-og-fg-subtle", children: [
3648
+ "This tool call wasn't replayed. After ",
3649
+ missing ? "connecting" : "reconnecting",
3650
+ ", send a new message to try again."
3651
+ ] }) : null,
3652
+ failed ? /* @__PURE__ */ jsxs10("p", { className: "px-1 text-og-xs text-og-status-failed", children: [
3653
+ "Couldn't start ",
3654
+ missing ? "connecting" : "reconnecting",
3655
+ " ",
3656
+ provider,
3657
+ ". Try again."
3658
+ ] }) : null
3659
+ ] });
3660
+ }
3661
+ function AuthProviderLogo({ src, label }) {
3662
+ const [failed, setFailed] = useState8(false);
3663
+ useEffect4(() => setFailed(false), [src]);
3664
+ const showImage = src && !failed;
3665
+ return /* @__PURE__ */ jsx13(
3666
+ "span",
3667
+ {
3668
+ className: "relative flex size-10 shrink-0 items-center justify-center overflow-hidden rounded-og-md border border-og-border bg-og-surface-2 text-sm font-semibold text-og-fg-muted",
3669
+ "aria-hidden": true,
3670
+ children: showImage ? /* @__PURE__ */ jsx13(
3671
+ "img",
3672
+ {
3673
+ src,
3674
+ alt: "",
3675
+ loading: "lazy",
3676
+ decoding: "async",
3677
+ className: "size-full object-contain p-1.5",
3678
+ onError: () => setFailed(true)
3679
+ }
3680
+ ) : /* @__PURE__ */ jsx13("span", { children: monogram(label) })
3681
+ }
3682
+ );
3683
+ }
3684
+ function monogram(label) {
3685
+ const words = label.trim().split(/\s+/).filter(Boolean);
3686
+ if (words.length === 0) {
3687
+ return "?";
3688
+ }
3689
+ if (words.length === 1) {
3690
+ return words[0].slice(0, 2).toUpperCase();
3691
+ }
3692
+ return (words[0][0] + words[1][0]).toUpperCase();
3693
+ }
3694
+ function providerLabel(domain) {
3695
+ const host = domain.trim().replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? "";
3696
+ const first = host.split(".")[0] ?? host;
3697
+ if (!first) {
3698
+ return "this service";
3699
+ }
3700
+ return first.charAt(0).toUpperCase() + first.slice(1);
3701
+ }
3702
+ function authReasonLine(reason) {
3703
+ switch (reason) {
3704
+ case "insufficient_scope":
3705
+ return "It needs additional access to continue.";
3706
+ case "missing_connection":
3707
+ return "It isn't connected yet.";
3708
+ case "expired":
3709
+ case "refresh_failed":
3710
+ return "Its access expired.";
3711
+ case "unsupported_auth":
3712
+ return "This connection cannot authenticate the configured tool endpoint.";
3713
+ case "resource_scope_unavailable":
3714
+ return "This tool endpoint cannot enforce the selected repository access.";
3715
+ default:
3716
+ return "Its connection needs attention.";
3717
+ }
3718
+ }
3719
+
3720
+ export {
3721
+ QueueSurface,
3722
+ defaultHumanInputFormMessages,
3723
+ HumanInputForm,
3724
+ answersFromDrafts,
3725
+ rawTypeOf,
3726
+ createToolRegistry,
3727
+ sandboxCommandExitCode,
3728
+ parseExecBannerSessionId,
3729
+ stripExecBanner,
3730
+ execTruncated,
3731
+ isExecSessionLostBanner,
3732
+ looksBinary,
3733
+ controlCaret,
3734
+ v4aToGitFileDiff,
3735
+ applyPatchOps,
3736
+ isApplyPatch,
3737
+ redactSecrets,
3738
+ parseToolArgs,
3739
+ tailPeek,
3740
+ unwrapMcpOutput,
3741
+ DisclosureDefaultsProvider,
3742
+ useLightbox,
3743
+ useLightboxOptional,
3744
+ LightboxProvider,
3745
+ ActivityDisclosure,
3746
+ TermBlock,
3747
+ PayloadBlock,
3748
+ BodyNote,
3749
+ MediaSkeleton,
3750
+ MediaEmpty,
3751
+ Thumbnail,
3752
+ ScreenshotFigure,
3753
+ useThemeType,
3754
+ gitFileDiffToPatch,
3755
+ PierreDiff,
3756
+ defaultToolRegistry,
3757
+ createDefaultToolRegistry,
3758
+ ActivityRail,
3759
+ TurnSummary,
3760
+ Markdown,
3761
+ SESSION_STATUS_META,
3762
+ SessionStatus,
3763
+ StatusDot,
3764
+ MessageTimeline,
3765
+ TimelineRow
3766
+ };
3767
+ //# sourceMappingURL=chunk-GQN2QIR2.js.map