@tangle-network/agent-app 0.10.0 → 0.11.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.
@@ -1,5 +1,9 @@
1
+ import {
2
+ stepActivityFlowTrace
3
+ } from "../chunk-AFDROJ64.js";
4
+
1
5
  // src/web-react/index.tsx
2
- import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
6
+ import { useEffect as useEffect3, useMemo, useRef as useRef2, useState as useState3 } from "react";
3
7
 
4
8
  // src/web-react/provider-logo.tsx
5
9
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -206,23 +210,264 @@ async function streamChatTurn(opts) {
206
210
  }
207
211
  }
208
212
 
209
- // src/web-react/index.tsx
213
+ // src/web-react/mission-activity.tsx
214
+ import { useCallback, useEffect as useEffect2, useState as useState2 } from "react";
210
215
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
211
- function ChevronDown({ className }) {
216
+ var LIVE_STATUSES = /* @__PURE__ */ new Set(["pending", "running"]);
217
+ var OK_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "succeeded"]);
218
+ var ERROR_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "cancelled", "aborted"]);
219
+ function activityTone(status) {
220
+ const s = status.toLowerCase();
221
+ if (LIVE_STATUSES.has(s)) return "live";
222
+ if (OK_STATUSES.has(s)) return "ok";
223
+ if (ERROR_STATUSES.has(s)) return "error";
224
+ return "neutral";
225
+ }
226
+ function formatActivityCost(costUsd) {
227
+ if (costUsd === void 0 || !isFinite(costUsd) || costUsd <= 0) return null;
228
+ return costUsd < 0.01 ? `$${costUsd.toFixed(4)}` : `$${costUsd.toFixed(2)}`;
229
+ }
230
+ function formatActivityDuration(durationMs) {
231
+ if (durationMs === void 0 || !isFinite(durationMs) || durationMs < 0) return null;
232
+ const totalSeconds = Math.round(durationMs / 1e3);
233
+ if (totalSeconds < 60) return `${totalSeconds}s`;
234
+ const minutes = Math.floor(totalSeconds / 60);
235
+ const seconds = totalSeconds % 60;
236
+ if (minutes < 60) return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
237
+ return `${Math.floor(minutes / 60)}h ${String(minutes % 60).padStart(2, "0")}m`;
238
+ }
239
+ function mergeActivityPages(existing, incoming) {
240
+ const byTask = /* @__PURE__ */ new Map();
241
+ for (const row of existing) byTask.set(row.taskId, row);
242
+ for (const row of incoming) byTask.set(row.taskId, row);
243
+ return [...byTask.values()].sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt));
244
+ }
245
+ function waterfallLayout(trace) {
246
+ const total = trace.totalMs > 0 ? trace.totalMs : 1;
247
+ return [...trace.spans].sort((a, b) => a.startMs - b.startMs).map((span) => {
248
+ const meta = span.meta ?? {};
249
+ const failed = meta.ok === false || typeof meta.status === "string" && activityTone(meta.status) === "error";
250
+ return {
251
+ name: span.name,
252
+ kind: span.kind,
253
+ offsetPct: Math.max(0, Math.min(100, span.startMs / total * 100)),
254
+ widthPct: Math.max(0.5, Math.min(100, (span.endMs - span.startMs) / total * 100)),
255
+ durationLabel: `${((span.endMs - span.startMs) / 1e3).toFixed(1)}s${span.approx ? "~" : ""}`,
256
+ approx: span.approx === true,
257
+ ok: !failed
258
+ };
259
+ });
260
+ }
261
+ function ChevronGlyph({ className }) {
212
262
  return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" }) });
213
263
  }
264
+ function RefreshGlyph({ className }) {
265
+ return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6" }) });
266
+ }
267
+ function StatusDot({ tone }) {
268
+ return /* @__PURE__ */ jsx2(
269
+ "span",
270
+ {
271
+ className: `h-2 w-2 shrink-0 rounded-full ${tone === "live" ? "animate-pulse bg-yellow-500" : tone === "ok" ? "bg-green-500" : tone === "error" ? "bg-red-500" : "bg-muted-foreground/40"}`
272
+ }
273
+ );
274
+ }
275
+ var BAR_CLASS = {
276
+ pipeline: "bg-muted-foreground/30",
277
+ model: "bg-primary/60",
278
+ tool: "bg-primary"
279
+ };
280
+ function FlowWaterfall({ trace }) {
281
+ const rows = waterfallLayout(trace);
282
+ if (rows.length === 0) return null;
283
+ const cost = formatActivityCost(trace.costUsd);
284
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
285
+ rows.map((row, i) => /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-[minmax(0,2fr)_minmax(0,3fr)_auto] items-center gap-2", children: [
286
+ /* @__PURE__ */ jsx2("span", { className: "truncate font-mono text-[11px] text-muted-foreground", title: row.name, children: row.name }),
287
+ /* @__PURE__ */ jsx2("div", { className: "relative h-2 rounded-sm bg-muted/40", children: /* @__PURE__ */ jsx2(
288
+ "div",
289
+ {
290
+ className: `absolute inset-y-0 rounded-sm ${row.ok ? BAR_CLASS[row.kind] : "bg-red-500/80"} ${row.approx ? "opacity-70" : ""}`,
291
+ style: { left: `${row.offsetPct}%`, width: `${row.widthPct}%` }
292
+ }
293
+ ) }),
294
+ /* @__PURE__ */ jsx2("span", { className: "shrink-0 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: row.durationLabel })
295
+ ] }, i)),
296
+ /* @__PURE__ */ jsxs2("p", { className: "pt-0.5 text-right font-mono text-[10px] tabular-nums text-muted-foreground/60", children: [
297
+ (trace.totalMs / 1e3).toFixed(1),
298
+ "s",
299
+ cost ? ` \xB7 ${cost}` : ""
300
+ ] })
301
+ ] });
302
+ }
303
+ function MissionActivityLane({ activity, startedAt, nowMs }) {
304
+ const [expanded, setExpanded] = useState2(false);
305
+ if (activity.length === 0) return null;
306
+ return /* @__PURE__ */ jsxs2("div", { className: "mt-1 border-l border-border/50 pl-3", children: [
307
+ activity.map((run) => {
308
+ const tone = activityTone(run.status);
309
+ const cost = formatActivityCost(run.costUsd);
310
+ const duration = formatActivityDuration(run.durationMs);
311
+ return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
312
+ /* @__PURE__ */ jsx2(StatusDot, { tone }),
313
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1 truncate", children: [
314
+ /* @__PURE__ */ jsx2("span", { className: "font-medium", children: run.tool }),
315
+ /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground", children: [
316
+ " \u2014 ",
317
+ run.detail
318
+ ] })
319
+ ] }),
320
+ tone === "live" && (run.iteration !== void 0 || run.phase !== void 0) && /* @__PURE__ */ jsx2("span", { className: "shrink-0 rounded-full bg-yellow-500/10 px-1.5 py-0.5 font-mono text-[10px] text-yellow-700 dark:text-yellow-400", children: [run.iteration !== void 0 ? `iter ${run.iteration}` : null, run.phase ?? null].filter(Boolean).join(" \xB7 ") }),
321
+ /* @__PURE__ */ jsxs2("span", { className: "flex shrink-0 items-center gap-1.5 font-mono text-[10px] tabular-nums text-muted-foreground/70", children: [
322
+ tone !== "live" && tone !== "ok" && /* @__PURE__ */ jsx2("span", { children: run.status }),
323
+ cost && /* @__PURE__ */ jsx2("span", { children: cost }),
324
+ duration && /* @__PURE__ */ jsx2("span", { children: duration })
325
+ ] })
326
+ ] }, run.taskId);
327
+ }),
328
+ /* @__PURE__ */ jsxs2(
329
+ "button",
330
+ {
331
+ type: "button",
332
+ onClick: () => setExpanded((v) => !v),
333
+ className: "flex items-center gap-1 py-0.5 text-[10px] font-medium text-muted-foreground/70 transition hover:text-foreground",
334
+ children: [
335
+ /* @__PURE__ */ jsx2(ChevronGlyph, { className: `h-3 w-3 transition-transform ${expanded ? "rotate-180" : ""}` }),
336
+ "timeline"
337
+ ]
338
+ }
339
+ ),
340
+ expanded && /* @__PURE__ */ jsx2("div", { className: "rounded-md border border-border/50 bg-muted/10 p-2", children: /* @__PURE__ */ jsx2(
341
+ FlowWaterfall,
342
+ {
343
+ trace: stepActivityFlowTrace(activity, {
344
+ ...startedAt !== void 0 ? { startedAt } : {},
345
+ ...nowMs !== void 0 ? { nowMs } : {}
346
+ })
347
+ }
348
+ ) })
349
+ ] });
350
+ }
351
+ function ActivityRow({
352
+ record,
353
+ renderMissionRef
354
+ }) {
355
+ const [open, setOpen] = useState2(false);
356
+ const tone = activityTone(record.status);
357
+ const cost = formatActivityCost(record.costUsd);
358
+ const duration = formatActivityDuration(record.durationMs);
359
+ return /* @__PURE__ */ jsxs2("div", { className: "rounded-lg border border-border/60 bg-card", children: [
360
+ /* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => setOpen((v) => !v), className: "flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm", children: [
361
+ /* @__PURE__ */ jsx2(StatusDot, { tone }),
362
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1 truncate", children: [
363
+ /* @__PURE__ */ jsx2("span", { className: "font-medium", children: record.tool }),
364
+ /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground", children: [
365
+ " \u2014 ",
366
+ record.detail
367
+ ] })
368
+ ] }),
369
+ tone === "live" && (record.iteration !== void 0 || record.phase !== void 0) && /* @__PURE__ */ jsx2("span", { className: "shrink-0 rounded-full bg-yellow-500/10 px-2 py-0.5 font-mono text-[10px] text-yellow-700 dark:text-yellow-400", children: [record.iteration !== void 0 ? `iter ${record.iteration}` : null, record.phase ?? null].filter(Boolean).join(" \xB7 ") }),
370
+ /* @__PURE__ */ jsx2(
371
+ "span",
372
+ {
373
+ className: `shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${tone === "ok" ? "bg-green-500/10 text-green-700 dark:text-green-400" : tone === "error" ? "bg-red-500/10 text-red-700 dark:text-red-400" : tone === "live" ? "bg-yellow-500/10 text-yellow-700 dark:text-yellow-400" : "bg-muted/60 text-muted-foreground"}`,
374
+ children: record.status
375
+ }
376
+ ),
377
+ cost && /* @__PURE__ */ jsx2("span", { className: "shrink-0 font-mono text-[11px] tabular-nums text-muted-foreground", children: cost }),
378
+ /* @__PURE__ */ jsx2(ChevronGlyph, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${open ? "rotate-180" : ""}` })
379
+ ] }),
380
+ open && /* @__PURE__ */ jsxs2("div", { className: "space-y-1.5 border-t border-border/40 px-3 py-2.5", children: [
381
+ /* @__PURE__ */ jsxs2("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 font-mono text-[11px]", children: [
382
+ /* @__PURE__ */ jsx2("dt", { className: "text-muted-foreground/60", children: "task" }),
383
+ /* @__PURE__ */ jsx2("dd", { className: "truncate text-muted-foreground", children: record.taskId }),
384
+ /* @__PURE__ */ jsx2("dt", { className: "text-muted-foreground/60", children: "started" }),
385
+ /* @__PURE__ */ jsx2("dd", { className: "text-muted-foreground", children: new Date(record.startedAt).toLocaleString() }),
386
+ duration && /* @__PURE__ */ jsxs2(Fragment, { children: [
387
+ /* @__PURE__ */ jsx2("dt", { className: "text-muted-foreground/60", children: "duration" }),
388
+ /* @__PURE__ */ jsx2("dd", { className: "text-muted-foreground", children: duration })
389
+ ] }),
390
+ record.traceId && /* @__PURE__ */ jsxs2(Fragment, { children: [
391
+ /* @__PURE__ */ jsx2("dt", { className: "text-muted-foreground/60", children: "trace" }),
392
+ /* @__PURE__ */ jsx2("dd", { className: "truncate text-muted-foreground", children: record.traceId })
393
+ ] })
394
+ ] }),
395
+ record.missionRef && renderMissionRef?.(record.missionRef, record)
396
+ ] })
397
+ ] });
398
+ }
399
+ function AgentActivityPanel({ fetchActivity, renderMissionRef, title = "Agent activity", emptyLabel = "No agent runs yet." }) {
400
+ const [rows, setRows] = useState2([]);
401
+ const [cursor, setCursor] = useState2(void 0);
402
+ const [loading, setLoading] = useState2(false);
403
+ const [error, setError] = useState2(null);
404
+ const load = useCallback(
405
+ async (from) => {
406
+ setLoading(true);
407
+ setError(null);
408
+ try {
409
+ const page = await fetchActivity(from);
410
+ setRows((prev) => mergeActivityPages(from === void 0 ? [] : prev, page.items));
411
+ setCursor(page.nextCursor);
412
+ } catch (e) {
413
+ setError(e instanceof Error ? e.message : String(e));
414
+ } finally {
415
+ setLoading(false);
416
+ }
417
+ },
418
+ [fetchActivity]
419
+ );
420
+ useEffect2(() => {
421
+ void load();
422
+ }, [load]);
423
+ return /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
424
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
425
+ /* @__PURE__ */ jsx2("h2", { className: "flex-1 text-sm font-semibold", children: title }),
426
+ /* @__PURE__ */ jsx2(
427
+ "button",
428
+ {
429
+ type: "button",
430
+ onClick: () => void load(),
431
+ disabled: loading,
432
+ "aria-label": "Refresh",
433
+ className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground disabled:opacity-50",
434
+ children: /* @__PURE__ */ jsx2(RefreshGlyph, { className: `h-3.5 w-3.5 ${loading ? "animate-spin" : ""}` })
435
+ }
436
+ )
437
+ ] }),
438
+ error && /* @__PURE__ */ jsx2("p", { className: "rounded-md border border-red-300/60 bg-red-500/5 px-3 py-2 text-xs text-red-600", children: error }),
439
+ !error && rows.length === 0 && !loading && /* @__PURE__ */ jsx2("p", { className: "px-1 text-sm text-muted-foreground", children: emptyLabel }),
440
+ /* @__PURE__ */ jsx2("div", { className: "space-y-1.5", children: rows.map((record) => /* @__PURE__ */ jsx2(ActivityRow, { record, renderMissionRef }, record.taskId)) }),
441
+ cursor && /* @__PURE__ */ jsx2(
442
+ "button",
443
+ {
444
+ type: "button",
445
+ onClick: () => void load(cursor),
446
+ disabled: loading,
447
+ className: "w-full rounded-md border border-border bg-card px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent/30 disabled:opacity-50",
448
+ children: "Older runs"
449
+ }
450
+ )
451
+ ] });
452
+ }
453
+
454
+ // src/web-react/index.tsx
455
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
456
+ function ChevronDown({ className }) {
457
+ return /* @__PURE__ */ jsx3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx3("path", { d: "m6 9 6 6 6-6" }) });
458
+ }
214
459
  function SearchGlyph({ className }) {
215
- return /* @__PURE__ */ jsxs2("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
216
- /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "11", r: "8" }),
217
- /* @__PURE__ */ jsx2("path", { d: "m21 21-4.3-4.3" })
460
+ return /* @__PURE__ */ jsxs3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
461
+ /* @__PURE__ */ jsx3("circle", { cx: "11", cy: "11", r: "8" }),
462
+ /* @__PURE__ */ jsx3("path", { d: "m21 21-4.3-4.3" })
218
463
  ] });
219
464
  }
220
465
  function SparkleGlyph({ className }) {
221
- return /* @__PURE__ */ jsx2("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6 2.1 2.1m0-12.8-2.1 2.1M7.7 16.3l-2.1 2.1" }) });
466
+ return /* @__PURE__ */ jsx3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx3("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3M5.6 5.6l2.1 2.1m8.6 8.6 2.1 2.1m0-12.8-2.1 2.1M7.7 16.3l-2.1 2.1" }) });
222
467
  }
223
468
  function useClickOutside(onOutside) {
224
469
  const ref = useRef2(null);
225
- useEffect2(() => {
470
+ useEffect3(() => {
226
471
  function handler(e) {
227
472
  if (ref.current && !ref.current.contains(e.target)) onOutside();
228
473
  }
@@ -257,7 +502,7 @@ function formatContext(len) {
257
502
  return `${len} ctx`;
258
503
  }
259
504
  function SectionHeader({ children }) {
260
- return /* @__PURE__ */ jsx2("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground/70", children });
505
+ return /* @__PURE__ */ jsx3("div", { className: "px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground/70", children });
261
506
  }
262
507
  function ModelRow({
263
508
  model,
@@ -267,30 +512,30 @@ function ModelRow({
267
512
  }) {
268
513
  const price = formatPrice(model.pricing?.prompt);
269
514
  const ctx = formatContext(model.contextLength);
270
- return /* @__PURE__ */ jsxs2(
515
+ return /* @__PURE__ */ jsxs3(
271
516
  "button",
272
517
  {
273
518
  type: "button",
274
519
  onClick: onSelect,
275
520
  className: `flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${selected ? "bg-primary/10 font-medium" : "hover:bg-accent/30"}`,
276
521
  children: [
277
- renderProviderBadge ? renderProviderBadge(model.provider) : /* @__PURE__ */ jsx2(ProviderLogo, { provider: model.provider, size: 16 }),
278
- /* @__PURE__ */ jsx2("span", { className: "truncate", children: model.name }),
279
- !model.supportsTools && /* @__PURE__ */ jsx2("span", { className: "shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: "no tools" }),
280
- /* @__PURE__ */ jsxs2("span", { className: "ml-auto flex shrink-0 items-center gap-2 text-xs text-muted-foreground", children: [
281
- ctx && /* @__PURE__ */ jsx2("span", { children: ctx }),
282
- price && /* @__PURE__ */ jsx2("span", { children: price })
522
+ renderProviderBadge ? renderProviderBadge(model.provider) : /* @__PURE__ */ jsx3(ProviderLogo, { provider: model.provider, size: 16 }),
523
+ /* @__PURE__ */ jsx3("span", { className: "truncate", children: model.name }),
524
+ !model.supportsTools && /* @__PURE__ */ jsx3("span", { className: "shrink-0 rounded bg-muted/60 px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: "no tools" }),
525
+ /* @__PURE__ */ jsxs3("span", { className: "ml-auto flex shrink-0 items-center gap-2 text-xs text-muted-foreground", children: [
526
+ ctx && /* @__PURE__ */ jsx3("span", { children: ctx }),
527
+ price && /* @__PURE__ */ jsx3("span", { children: price })
283
528
  ] })
284
529
  ]
285
530
  }
286
531
  );
287
532
  }
288
533
  function ModelPicker({ value, onChange, models, loading, renderProviderBadge, recommendedLabel = "Recommended" }) {
289
- const [open, setOpen] = useState2(false);
290
- const [query, setQuery] = useState2("");
534
+ const [open, setOpen] = useState3(false);
535
+ const [query, setQuery] = useState3("");
291
536
  const containerRef = useClickOutside(() => setOpen(false));
292
537
  const inputRef = useRef2(null);
293
- useEffect2(() => {
538
+ useEffect3(() => {
294
539
  if (open) inputRef.current?.focus();
295
540
  }, [open]);
296
541
  const selected = models.find((m) => m.id === value);
@@ -317,24 +562,24 @@ function ModelPicker({ value, onChange, models, loading, renderProviderBadge, re
317
562
  setOpen(false);
318
563
  setQuery("");
319
564
  };
320
- return /* @__PURE__ */ jsxs2("div", { ref: containerRef, className: "relative inline-flex", children: [
321
- /* @__PURE__ */ jsxs2(
565
+ return /* @__PURE__ */ jsxs3("div", { ref: containerRef, className: "relative inline-flex", children: [
566
+ /* @__PURE__ */ jsxs3(
322
567
  "button",
323
568
  {
324
569
  type: "button",
325
570
  onClick: () => setOpen((v) => !v),
326
571
  className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent/30",
327
572
  children: [
328
- selected ? renderProviderBadge ? renderProviderBadge(selected.provider) : /* @__PURE__ */ jsx2(ProviderLogo, { provider: selected.provider, size: 16 }) : /* @__PURE__ */ jsx2(SparkleGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
329
- /* @__PURE__ */ jsx2("span", { className: "max-w-[160px] truncate", children: selected?.name ?? value }),
330
- /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
573
+ selected ? renderProviderBadge ? renderProviderBadge(selected.provider) : /* @__PURE__ */ jsx3(ProviderLogo, { provider: selected.provider, size: 16 }) : /* @__PURE__ */ jsx3(SparkleGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
574
+ /* @__PURE__ */ jsx3("span", { className: "max-w-[160px] truncate", children: selected?.name ?? value }),
575
+ /* @__PURE__ */ jsx3(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
331
576
  ]
332
577
  }
333
578
  ),
334
- open && /* @__PURE__ */ jsxs2("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-[420px] overflow-hidden rounded-xl border border-border bg-card shadow-lg", children: [
335
- /* @__PURE__ */ jsx2("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-background px-3 py-2", children: [
336
- /* @__PURE__ */ jsx2(SearchGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
337
- /* @__PURE__ */ jsx2(
579
+ open && /* @__PURE__ */ jsxs3("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-[420px] overflow-hidden rounded-xl border border-border bg-card shadow-lg", children: [
580
+ /* @__PURE__ */ jsx3("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 rounded-lg border border-border bg-background px-3 py-2", children: [
581
+ /* @__PURE__ */ jsx3(SearchGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
582
+ /* @__PURE__ */ jsx3(
338
583
  "input",
339
584
  {
340
585
  ref: inputRef,
@@ -346,20 +591,20 @@ function ModelPicker({ value, onChange, models, loading, renderProviderBadge, re
346
591
  }
347
592
  )
348
593
  ] }) }),
349
- /* @__PURE__ */ jsxs2("div", { className: "max-h-[400px] overflow-y-auto p-1 pb-2", children: [
350
- loading && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading models..." }),
351
- !loading && filtered && /* @__PURE__ */ jsxs2(Fragment, { children: [
352
- filtered.length === 0 && /* @__PURE__ */ jsx2("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models match your search" }),
353
- filtered.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
594
+ /* @__PURE__ */ jsxs3("div", { className: "max-h-[400px] overflow-y-auto p-1 pb-2", children: [
595
+ loading && /* @__PURE__ */ jsx3("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "Loading models..." }),
596
+ !loading && filtered && /* @__PURE__ */ jsxs3(Fragment2, { children: [
597
+ filtered.length === 0 && /* @__PURE__ */ jsx3("div", { className: "px-3 py-4 text-center text-sm text-muted-foreground", children: "No models match your search" }),
598
+ filtered.map((m) => /* @__PURE__ */ jsx3(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
354
599
  ] }),
355
- !loading && !filtered && /* @__PURE__ */ jsxs2(Fragment, { children: [
356
- sections.recommended.length > 0 && /* @__PURE__ */ jsxs2(Fragment, { children: [
357
- /* @__PURE__ */ jsx2(SectionHeader, { children: recommendedLabel }),
358
- sections.recommended.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
600
+ !loading && !filtered && /* @__PURE__ */ jsxs3(Fragment2, { children: [
601
+ sections.recommended.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
602
+ /* @__PURE__ */ jsx3(SectionHeader, { children: recommendedLabel }),
603
+ sections.recommended.map((m) => /* @__PURE__ */ jsx3(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
359
604
  ] }),
360
- sections.byProvider.map((g) => /* @__PURE__ */ jsxs2("div", { children: [
361
- /* @__PURE__ */ jsx2(SectionHeader, { children: g.provider }),
362
- g.items.map((m) => /* @__PURE__ */ jsx2(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
605
+ sections.byProvider.map((g) => /* @__PURE__ */ jsxs3("div", { children: [
606
+ /* @__PURE__ */ jsx3(SectionHeader, { children: g.provider }),
607
+ g.items.map((m) => /* @__PURE__ */ jsx3(ModelRow, { model: m, selected: m.id === value, onSelect: () => select(m.id), renderProviderBadge }, m.id))
363
608
  ] }, g.provider))
364
609
  ] })
365
610
  ] })
@@ -373,11 +618,11 @@ var EFFORT_LEVELS = [
373
618
  { id: "high", label: "High" }
374
619
  ];
375
620
  function EffortPicker({ value, onChange }) {
376
- const [open, setOpen] = useState2(false);
621
+ const [open, setOpen] = useState3(false);
377
622
  const containerRef = useClickOutside(() => setOpen(false));
378
623
  const selected = EFFORT_LEVELS.find((l) => l.id === value) ?? EFFORT_LEVELS[2];
379
- return /* @__PURE__ */ jsxs2("div", { ref: containerRef, className: "relative inline-flex", children: [
380
- /* @__PURE__ */ jsxs2(
624
+ return /* @__PURE__ */ jsxs3("div", { ref: containerRef, className: "relative inline-flex", children: [
625
+ /* @__PURE__ */ jsxs3(
381
626
  "button",
382
627
  {
383
628
  type: "button",
@@ -385,13 +630,13 @@ function EffortPicker({ value, onChange }) {
385
630
  title: "Reasoning effort",
386
631
  className: "inline-flex items-center gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent/30",
387
632
  children: [
388
- /* @__PURE__ */ jsx2(SparkleGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
389
- /* @__PURE__ */ jsx2("span", { children: selected.label }),
390
- /* @__PURE__ */ jsx2(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
633
+ /* @__PURE__ */ jsx3(SparkleGlyph, { className: "h-3.5 w-3.5 text-muted-foreground" }),
634
+ /* @__PURE__ */ jsx3("span", { children: selected.label }),
635
+ /* @__PURE__ */ jsx3(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })
391
636
  ]
392
637
  }
393
638
  ),
394
- open && /* @__PURE__ */ jsx2("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-36 overflow-hidden rounded-xl border border-border bg-card p-1 shadow-lg", children: EFFORT_LEVELS.map((l) => /* @__PURE__ */ jsx2(
639
+ open && /* @__PURE__ */ jsx3("div", { className: "absolute bottom-full left-0 z-50 mb-2 w-36 overflow-hidden rounded-xl border border-border bg-card p-1 shadow-lg", children: EFFORT_LEVELS.map((l) => /* @__PURE__ */ jsx3(
395
640
  "button",
396
641
  {
397
642
  type: "button",
@@ -407,41 +652,41 @@ function EffortPicker({ value, onChange }) {
407
652
  ] });
408
653
  }
409
654
  function RunDrillIn({ run, onClose }) {
410
- return /* @__PURE__ */ jsxs2("div", { className: "fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-border bg-card shadow-xl", children: [
411
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
412
- /* @__PURE__ */ jsx2(
655
+ return /* @__PURE__ */ jsxs3("div", { className: "fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-border bg-card shadow-xl", children: [
656
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 border-b border-border px-4 py-3", children: [
657
+ /* @__PURE__ */ jsx3(
413
658
  "span",
414
659
  {
415
660
  className: `h-2 w-2 shrink-0 rounded-full ${run.status === "running" ? "bg-yellow-500" : run.status === "error" ? "bg-red-500" : "bg-green-500"}`
416
661
  }
417
662
  ),
418
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
419
- /* @__PURE__ */ jsx2("p", { className: "truncate text-sm font-semibold", children: run.title }),
420
- /* @__PURE__ */ jsx2("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: run.toolName })
663
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
664
+ /* @__PURE__ */ jsx3("p", { className: "truncate text-sm font-semibold", children: run.title }),
665
+ /* @__PURE__ */ jsx3("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: run.toolName })
421
666
  ] }),
422
- /* @__PURE__ */ jsx2(
667
+ /* @__PURE__ */ jsx3(
423
668
  "button",
424
669
  {
425
670
  type: "button",
426
671
  onClick: onClose,
427
672
  "aria-label": "Close",
428
673
  className: "rounded-md p-1.5 text-muted-foreground transition hover:bg-accent/30 hover:text-foreground",
429
- children: /* @__PURE__ */ jsx2("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "M18 6 6 18M6 6l12 12" }) })
674
+ children: /* @__PURE__ */ jsx3("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx3("path", { d: "M18 6 6 18M6 6l12 12" }) })
430
675
  }
431
676
  )
432
677
  ] }),
433
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
434
- run.steps.length === 0 && /* @__PURE__ */ jsx2("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
435
- run.steps.map((step, i) => /* @__PURE__ */ jsxs2("div", { className: "rounded-lg border border-border/60 bg-background", children: [
436
- /* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-2 border-b border-border/40 px-3 py-1.5", children: [
437
- /* @__PURE__ */ jsx2("span", { className: `font-mono text-[11px] ${step.status === "error" ? "text-red-600" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
438
- /* @__PURE__ */ jsx2("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
439
- /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: new Date(step.at).toLocaleTimeString() })
678
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 space-y-3 overflow-y-auto p-4", children: [
679
+ run.steps.length === 0 && /* @__PURE__ */ jsx3("p", { className: "text-sm text-muted-foreground", children: "No steps recorded yet." }),
680
+ run.steps.map((step, i) => /* @__PURE__ */ jsxs3("div", { className: "rounded-lg border border-border/60 bg-background", children: [
681
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-baseline gap-2 border-b border-border/40 px-3 py-1.5", children: [
682
+ /* @__PURE__ */ jsx3("span", { className: `font-mono text-[11px] ${step.status === "error" ? "text-red-600" : "text-muted-foreground"}`, children: step.status === "error" ? "\u2717" : "$" }),
683
+ /* @__PURE__ */ jsx3("code", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: step.label }),
684
+ /* @__PURE__ */ jsx3("span", { className: "shrink-0 text-[10px] text-muted-foreground/60", children: new Date(step.at).toLocaleTimeString() })
440
685
  ] }),
441
- step.detail && /* @__PURE__ */ jsx2("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: step.detail })
686
+ step.detail && /* @__PURE__ */ jsx3("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: step.detail })
442
687
  ] }, i))
443
688
  ] }),
444
- /* @__PURE__ */ jsx2("p", { className: "border-t border-border px-4 py-2 text-[11px] text-muted-foreground/60", children: "Readonly drill-in. Follow up in the main chat." })
689
+ /* @__PURE__ */ jsx3("p", { className: "border-t border-border px-4 py-2 text-[11px] text-muted-foreground/60", children: "Readonly drill-in. Follow up in the main chat." })
445
690
  ] });
446
691
  }
447
692
  function pendingApprovalOf(call) {
@@ -449,6 +694,30 @@ function pendingApprovalOf(call) {
449
694
  if (!outcome?.ok || outcome.result?.status !== "queued_for_approval" || !outcome.result.proposalId) return null;
450
695
  return { proposalId: outcome.result.proposalId };
451
696
  }
697
+ function ToolGlyph({ name, className }) {
698
+ if (name.startsWith("sandbox_")) {
699
+ return /* @__PURE__ */ jsxs3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
700
+ /* @__PURE__ */ jsx3("polyline", { points: "4 17 10 11 4 5" }),
701
+ /* @__PURE__ */ jsx3("line", { x1: "12", y1: "19", x2: "20", y2: "19" })
702
+ ] });
703
+ }
704
+ if (name === "submit_proposal") {
705
+ return /* @__PURE__ */ jsxs3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
706
+ /* @__PURE__ */ jsx3("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
707
+ /* @__PURE__ */ jsx3("path", { d: "M14 2v6h6M9 15l2 2 4-4" })
708
+ ] });
709
+ }
710
+ if (name === "schedule_followup") {
711
+ return /* @__PURE__ */ jsxs3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": true, children: [
712
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "9" }),
713
+ /* @__PURE__ */ jsx3("path", { d: "M12 7v5l3 3" })
714
+ ] });
715
+ }
716
+ return /* @__PURE__ */ jsxs3("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [
717
+ /* @__PURE__ */ jsx3("path", { d: "M12 3v3m0 12v3M3 12h3m12 0h3" }),
718
+ /* @__PURE__ */ jsx3("circle", { cx: "12", cy: "12", r: "4" })
719
+ ] });
720
+ }
452
721
  function toolOutcomeOf(call) {
453
722
  return call.result;
454
723
  }
@@ -480,21 +749,36 @@ function truncate(v, max = 240) {
480
749
  function KvRows({ data }) {
481
750
  const entries = Object.entries(data).filter(([, v]) => v !== void 0 && v !== null && v !== "");
482
751
  if (!entries.length) return null;
483
- return /* @__PURE__ */ jsx2("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs2("div", { className: "contents", children: [
484
- /* @__PURE__ */ jsx2("dt", { className: "font-mono text-[11px] text-muted-foreground/70", children: k }),
485
- /* @__PURE__ */ jsx2("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-[11px] text-muted-foreground", children: truncate(v) })
752
+ return /* @__PURE__ */ jsx3("dl", { className: "grid grid-cols-[auto_1fr] gap-x-3 gap-y-1", children: entries.map(([k, v]) => /* @__PURE__ */ jsxs3("div", { className: "contents", children: [
753
+ /* @__PURE__ */ jsx3("dt", { className: "font-mono text-[11px] text-muted-foreground/70", children: k }),
754
+ /* @__PURE__ */ jsx3("dd", { className: "min-w-0 whitespace-pre-wrap break-words font-mono text-[11px] text-muted-foreground", children: truncate(v) })
486
755
  ] }, k)) });
487
756
  }
757
+ function ShellDetail({ call }) {
758
+ const outcome = toolOutcomeOf(call);
759
+ const r = outcome?.result ?? {};
760
+ return /* @__PURE__ */ jsxs3("div", { className: "overflow-hidden rounded-md bg-zinc-900 font-mono text-[11px] leading-relaxed", children: [
761
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-3 pt-2 text-zinc-400", children: [
762
+ /* @__PURE__ */ jsx3("span", { className: "select-none text-zinc-500", children: "$" }),
763
+ /* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate text-zinc-200", children: String(call.args?.command ?? "") }),
764
+ r.exitCode != null && /* @__PURE__ */ jsxs3("span", { className: r.exitCode === 0 ? "text-emerald-400" : "text-red-400", children: [
765
+ "exit ",
766
+ r.exitCode
767
+ ] })
768
+ ] }),
769
+ /* @__PURE__ */ jsx3("pre", { className: "max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300", children: outcome?.ok === false ? outcome.message ?? "failed" : [r.stdout, r.stderr].filter(Boolean).join("\n") || "(no output)" })
770
+ ] });
771
+ }
488
772
  function DefaultToolDetail({ call }) {
489
773
  const outcome = toolOutcomeOf(call);
490
- return /* @__PURE__ */ jsxs2("div", { className: "space-y-2", children: [
491
- call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
492
- /* @__PURE__ */ jsx2("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/50", children: "Called with" }),
493
- /* @__PURE__ */ jsx2(KvRows, { data: call.args })
774
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
775
+ call.args && Object.keys(call.args).length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
776
+ /* @__PURE__ */ jsx3("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/50", children: "Called with" }),
777
+ /* @__PURE__ */ jsx3(KvRows, { data: call.args })
494
778
  ] }),
495
- outcome && /* @__PURE__ */ jsxs2("div", { children: [
496
- /* @__PURE__ */ jsx2("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/50", children: outcome.ok === false ? "Failed" : "Result" }),
497
- outcome.ok === false ? /* @__PURE__ */ jsx2("p", { className: "text-xs text-red-600", children: outcome.message ?? "Tool failed" }) : outcome.result && typeof outcome.result === "object" ? /* @__PURE__ */ jsx2(KvRows, { data: outcome.result }) : /* @__PURE__ */ jsx2("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(outcome.result) })
779
+ outcome && /* @__PURE__ */ jsxs3("div", { children: [
780
+ /* @__PURE__ */ jsx3("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/50", children: outcome.ok === false ? "Failed" : "Result" }),
781
+ outcome.ok === false ? /* @__PURE__ */ jsx3("p", { className: "text-xs text-red-600", children: outcome.message ?? "Tool failed" }) : outcome.result && typeof outcome.result === "object" ? /* @__PURE__ */ jsx3(KvRows, { data: outcome.result }) : /* @__PURE__ */ jsx3("p", { className: "font-mono text-[11px] text-muted-foreground", children: truncate(outcome.result) })
498
782
  ] })
499
783
  ] });
500
784
  }
@@ -505,31 +789,32 @@ function ToolCallCard({
505
789
  onOpenRun,
506
790
  renderers
507
791
  }) {
508
- const [expanded, setExpanded] = useState2(false);
792
+ const [expanded, setExpanded] = useState3(false);
509
793
  const pending = call.status === "done" ? pendingApprovalOf(call) : null;
510
794
  const failed = call.status === "error" || toolOutcomeOf(call)?.ok === false;
511
795
  const custom = renderers?.[call.name]?.(call, message);
512
- return /* @__PURE__ */ jsxs2(
796
+ return /* @__PURE__ */ jsxs3(
513
797
  "div",
514
798
  {
515
799
  className: `w-fit min-w-[280px] max-w-full rounded-lg border text-xs transition ${pending ? "border-amber-300/60 bg-amber-500/5" : failed ? "border-red-300/60 bg-red-500/5" : "border-border/60 bg-muted/20"}`,
516
800
  children: [
517
- /* @__PURE__ */ jsxs2(
801
+ /* @__PURE__ */ jsxs3(
518
802
  "button",
519
803
  {
520
804
  type: "button",
521
805
  onClick: () => setExpanded((v) => !v),
522
806
  className: "flex w-full items-center gap-2 px-3 py-2 text-left",
523
807
  children: [
524
- /* @__PURE__ */ jsx2(
808
+ /* @__PURE__ */ jsx3(
525
809
  "span",
526
810
  {
527
811
  className: `h-2 w-2 shrink-0 rounded-full ${call.status === "running" ? "animate-pulse bg-yellow-500" : pending ? "bg-amber-500" : failed ? "bg-red-500" : "bg-green-500"}`
528
812
  }
529
813
  ),
530
- /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate font-medium", children: friendlyToolTitle(call) }),
531
- pending && approval && /* @__PURE__ */ jsxs2("span", { className: "flex shrink-0 items-center gap-1", onClick: (e) => e.stopPropagation(), children: [
532
- /* @__PURE__ */ jsx2(
814
+ /* @__PURE__ */ jsx3(ToolGlyph, { name: call.name, className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
815
+ /* @__PURE__ */ jsx3("span", { className: "min-w-0 flex-1 truncate font-medium", children: friendlyToolTitle(call) }),
816
+ pending && approval && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1", onClick: (e) => e.stopPropagation(), children: [
817
+ /* @__PURE__ */ jsx3(
533
818
  "button",
534
819
  {
535
820
  type: "button",
@@ -538,7 +823,7 @@ function ToolCallCard({
538
823
  children: "Approve"
539
824
  }
540
825
  ),
541
- /* @__PURE__ */ jsx2(
826
+ /* @__PURE__ */ jsx3(
542
827
  "button",
543
828
  {
544
829
  type: "button",
@@ -548,14 +833,14 @@ function ToolCallCard({
548
833
  }
549
834
  )
550
835
  ] }),
551
- /* @__PURE__ */ jsx2("span", { className: "shrink-0 text-[11px] text-muted-foreground/70", children: call.status === "running" ? "running\u2026" : pending ? "awaiting approval" : failed ? "failed" : "done" }),
552
- /* @__PURE__ */ jsx2(ChevronDown, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}` })
836
+ /* @__PURE__ */ jsx3("span", { className: "shrink-0 text-[11px] text-muted-foreground/70", children: call.status === "running" ? "running\u2026" : pending ? "awaiting approval" : failed ? "failed" : "done" }),
837
+ /* @__PURE__ */ jsx3(ChevronDown, { className: `h-3 w-3 shrink-0 text-muted-foreground transition-transform ${expanded ? "rotate-180" : ""}` })
553
838
  ]
554
839
  }
555
840
  ),
556
- expanded && /* @__PURE__ */ jsxs2("div", { className: "border-t border-border/40 px-3 py-2.5", children: [
557
- custom ?? /* @__PURE__ */ jsx2(DefaultToolDetail, { call }),
558
- onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */ jsx2(
841
+ expanded && /* @__PURE__ */ jsxs3("div", { className: "border-t border-border/40 px-3 py-2.5", children: [
842
+ custom ?? (call.name === "sandbox_run_command" ? /* @__PURE__ */ jsx3(ShellDetail, { call }) : /* @__PURE__ */ jsx3(DefaultToolDetail, { call })),
843
+ onOpenRun && call.name.startsWith("sandbox_") && /* @__PURE__ */ jsx3(
559
844
  "button",
560
845
  {
561
846
  type: "button",
@@ -583,26 +868,34 @@ function AssistantMessage({
583
868
  const content = useSmoothText(msg.content, streaming);
584
869
  const reasoning = useSmoothText(msg.reasoning ?? "", streaming);
585
870
  const reasoningScrollRef = useRef2(null);
586
- useEffect2(() => {
871
+ const thinkStartRef = useRef2(null);
872
+ const thinkMsRef = useRef2(null);
873
+ if (streaming && reasoning && !content && thinkStartRef.current === null) {
874
+ thinkStartRef.current = performance.now();
875
+ }
876
+ if (content && thinkStartRef.current !== null && thinkMsRef.current === null) {
877
+ thinkMsRef.current = performance.now() - thinkStartRef.current;
878
+ }
879
+ useEffect3(() => {
587
880
  const el = reasoningScrollRef.current;
588
881
  if (el && streaming && !content) el.scrollTop = el.scrollHeight;
589
882
  }, [reasoning, streaming, content]);
590
- return /* @__PURE__ */ jsxs2("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
591
- /* @__PURE__ */ jsxs2("div", { className: "mb-1 flex items-baseline gap-2 text-[11px] tracking-wide text-muted-foreground/60", children: [
592
- /* @__PURE__ */ jsx2("span", { className: "font-semibold uppercase", children: agentLabel }),
593
- msg.modelUsed && /* @__PURE__ */ jsx2("span", { className: "font-mono normal-case", children: msg.modelUsed }),
594
- formatTokensPerSecond(msg) && /* @__PURE__ */ jsx2("span", { children: formatTokensPerSecond(msg) }),
595
- formatModelCost(msg, models) && /* @__PURE__ */ jsx2("span", { children: formatModelCost(msg, models) })
883
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
884
+ /* @__PURE__ */ jsxs3("div", { className: "mb-1 flex items-baseline gap-2 text-[11px] tracking-wide text-muted-foreground/60", children: [
885
+ /* @__PURE__ */ jsx3("span", { className: "font-semibold uppercase", children: agentLabel }),
886
+ msg.modelUsed && /* @__PURE__ */ jsx3("span", { className: "font-mono normal-case", children: msg.modelUsed }),
887
+ formatTokensPerSecond(msg) && /* @__PURE__ */ jsx3("span", { children: formatTokensPerSecond(msg) }),
888
+ formatModelCost(msg, models) && /* @__PURE__ */ jsx3("span", { children: formatModelCost(msg, models) })
596
889
  ] }),
597
- reasoning && /* @__PURE__ */ jsxs2("details", { className: "mb-2 rounded-md border border-border/40 bg-muted/30 px-3 py-2", open: !content, children: [
598
- /* @__PURE__ */ jsxs2("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: [
599
- content ? "Thinking\u2026" : "Thinking",
600
- !content && /* @__PURE__ */ jsx2("span", { className: "ml-1 inline-block animate-pulse", children: "\u25CF" })
601
- ] }),
602
- /* @__PURE__ */ jsx2("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-sm text-muted-foreground/80", children: reasoning })
890
+ reasoning && /* @__PURE__ */ jsxs3("details", { className: "mb-2 rounded-lg border-l-2 border-border/70 bg-muted/20 px-3 py-2", open: !content, children: [
891
+ /* @__PURE__ */ jsx3("summary", { className: "cursor-pointer select-none text-xs font-medium text-muted-foreground", children: !content ? /* @__PURE__ */ jsx3("span", { className: "animate-pulse", children: "Thinking\u2026" }) : thinkMsRef.current != null ? `Thought for ${Math.max(1, Math.round(thinkMsRef.current / 1e3))}s` : "Thought process" }),
892
+ /* @__PURE__ */ jsx3("div", { ref: reasoningScrollRef, className: "mt-2 max-h-48 overflow-y-auto whitespace-pre-wrap text-[13px] leading-relaxed text-muted-foreground/70", children: reasoning })
893
+ ] }),
894
+ /* @__PURE__ */ jsxs3("div", { className: "text-base leading-[1.75]", children: [
895
+ renderBody(content),
896
+ streaming && content && !msg.toolCalls?.length && /* @__PURE__ */ jsx3("span", { className: "ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-pulse rounded-sm bg-foreground/70", "aria-hidden": true })
603
897
  ] }),
604
- /* @__PURE__ */ jsx2("div", { className: "text-base leading-[1.75]", children: renderBody(content) }),
605
- msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx2("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc) => /* @__PURE__ */ jsx2(
898
+ msg.toolCalls && msg.toolCalls.length > 0 && /* @__PURE__ */ jsx3("div", { className: "mt-2 flex flex-col gap-1.5", children: msg.toolCalls.map((tc) => /* @__PURE__ */ jsx3(
606
899
  ToolCallCard,
607
900
  {
608
901
  call: tc,
@@ -617,15 +910,15 @@ function AssistantMessage({
617
910
  ] });
618
911
  }
619
912
  function ThinkingRow({ agentLabel }) {
620
- const [seconds, setSeconds] = useState2(0);
621
- useEffect2(() => {
913
+ const [seconds, setSeconds] = useState3(0);
914
+ useEffect3(() => {
622
915
  const id = setInterval(() => setSeconds((s) => s + 1), 1e3);
623
916
  return () => clearInterval(id);
624
917
  }, []);
625
- return /* @__PURE__ */ jsxs2("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
626
- /* @__PURE__ */ jsx2("p", { className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground/60", children: agentLabel }),
627
- /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-base text-muted-foreground", children: [
628
- /* @__PURE__ */ jsx2("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
918
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: [
919
+ /* @__PURE__ */ jsx3("p", { className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground/60", children: agentLabel }),
920
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-base text-muted-foreground", children: [
921
+ /* @__PURE__ */ jsx3("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": true, children: /* @__PURE__ */ jsx3("path", { d: "M21 12a9 9 0 1 1-6.219-8.56", strokeLinecap: "round" }) }),
629
922
  "Thinking",
630
923
  seconds >= 3 ? ` \xB7 ${seconds}s` : "..."
631
924
  ] })
@@ -643,14 +936,14 @@ function ChatMessages({
643
936
  onToolCallClick,
644
937
  toolRenderers
645
938
  }) {
646
- const renderBody = renderMarkdown ?? ((content) => /* @__PURE__ */ jsx2("p", { className: "whitespace-pre-wrap", children: content }));
939
+ const renderBody = renderMarkdown ?? ((content) => /* @__PURE__ */ jsx3("p", { className: "whitespace-pre-wrap", children: content }));
647
940
  const lastIsUser = messages[messages.length - 1]?.role === "user";
648
- return /* @__PURE__ */ jsxs2(Fragment, { children: [
941
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
649
942
  messages.map(
650
- (msg) => msg.role === "user" ? /* @__PURE__ */ jsx2("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs2("div", { className: "ml-auto w-fit max-w-[85%]", children: [
651
- /* @__PURE__ */ jsx2("p", { className: "mb-1 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground/60", children: userLabel }),
652
- /* @__PURE__ */ jsx2("div", { className: "rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 text-base leading-relaxed", children: /* @__PURE__ */ jsx2("p", { className: "whitespace-pre-wrap", children: msg.content }) })
653
- ] }) }, msg.id) : /* @__PURE__ */ jsx2(
943
+ (msg) => msg.role === "user" ? /* @__PURE__ */ jsx3("div", { className: "mx-auto w-full max-w-3xl px-6 py-3", children: /* @__PURE__ */ jsxs3("div", { className: "ml-auto w-fit max-w-[85%]", children: [
944
+ /* @__PURE__ */ jsx3("p", { className: "mb-1 text-right text-[11px] font-semibold uppercase tracking-wide text-muted-foreground/60", children: userLabel }),
945
+ /* @__PURE__ */ jsx3("div", { className: "rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 text-base leading-relaxed", children: /* @__PURE__ */ jsx3("p", { className: "whitespace-pre-wrap", children: msg.content }) })
946
+ ] }) }, msg.id) : /* @__PURE__ */ jsx3(
654
947
  AssistantMessage,
655
948
  {
656
949
  msg,
@@ -666,22 +959,30 @@ function ChatMessages({
666
959
  msg.id
667
960
  )
668
961
  ),
669
- loading && lastIsUser && /* @__PURE__ */ jsx2(ThinkingRow, { agentLabel })
962
+ loading && lastIsUser && /* @__PURE__ */ jsx3(ThinkingRow, { agentLabel })
670
963
  ] });
671
964
  }
672
965
  export {
966
+ AgentActivityPanel,
673
967
  ChatMessages,
674
968
  EffortPicker,
969
+ FlowWaterfall,
970
+ MissionActivityLane,
675
971
  ModelPicker,
676
972
  ProviderLogo,
677
973
  RunDrillIn,
974
+ activityTone,
678
975
  consumeChatStream,
679
976
  dispatchChatStreamLine,
977
+ formatActivityCost,
978
+ formatActivityDuration,
680
979
  formatModelCost,
681
980
  formatTokensPerSecond,
981
+ mergeActivityPages,
682
982
  nextRevealCount,
683
983
  pendingApprovalOf,
684
984
  streamChatTurn,
685
- useSmoothText
985
+ useSmoothText,
986
+ waterfallLayout
686
987
  };
687
988
  //# sourceMappingURL=index.js.map