@tangle-network/sandbox-ui 0.96.0 → 0.97.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.
@@ -191,18 +191,38 @@ import {
191
191
  reasoningEffortsFor
192
192
  } from "@tangle-network/agent-interface";
193
193
  import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
194
- import { ChevronDown as ChevronDown3, Lock, Plus as Plus2, SlidersHorizontal } from "lucide-react";
194
+ import { ChevronDown as ChevronDown3, SlidersHorizontal } from "lucide-react";
195
+ import * as React5 from "react";
196
+
197
+ // src/chat/harness-model-compat.ts
198
+ import {
199
+ harnessSupportsModel,
200
+ snapModelToHarness as snapModelIdToHarness
201
+ } from "@tangle-network/agent-interface";
202
+ import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
203
+ function isModelCompatibleWithHarness(harness, modelId) {
204
+ return harnessSupportsModel(harness, modelId);
205
+ }
206
+ function snapModelToHarness(harness, modelId, models) {
207
+ return snapModelIdToHarness(harness, modelId, models.map(canonicalModelId));
208
+ }
209
+
210
+ // src/chat/informative-lock.tsx
211
+ import { Lock, Plus } from "lucide-react";
195
212
  import * as React3 from "react";
196
213
 
197
214
  // src/lib/use-click-outside.ts
198
215
  import * as React from "react";
199
- function useClickOutside(onOutside) {
216
+ function useClickOutside(onOutside, extras) {
200
217
  const ref = React.useRef(null);
218
+ const extrasRef = React.useRef(extras);
219
+ extrasRef.current = extras;
201
220
  React.useEffect(() => {
202
221
  function handler(event) {
203
- if (ref.current && !ref.current.contains(event.target)) {
204
- onOutside();
205
- }
222
+ const target = event.target;
223
+ if (!ref.current || ref.current.contains(target)) return;
224
+ if (extrasRef.current?.some((extra) => extra.current?.contains(target))) return;
225
+ onOutside();
206
226
  }
207
227
  document.addEventListener("mousedown", handler);
208
228
  return () => document.removeEventListener("mousedown", handler);
@@ -210,23 +230,178 @@ function useClickOutside(onOutside) {
210
230
  return ref;
211
231
  }
212
232
 
213
- // src/chat/harness-model-compat.ts
214
- import {
215
- harnessSupportsModel,
216
- snapModelToHarness as snapModelIdToHarness
217
- } from "@tangle-network/agent-interface";
218
- import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
219
- function isModelCompatibleWithHarness(harness, modelId) {
220
- return harnessSupportsModel(harness, modelId);
221
- }
222
- function snapModelToHarness(harness, modelId, models) {
223
- return snapModelIdToHarness(harness, modelId, models.map(canonicalModelId));
233
+ // src/chat/anchored-popover.tsx
234
+ import * as React2 from "react";
235
+ import { createPortal } from "react-dom";
236
+ import { jsx as jsx2 } from "react/jsx-runtime";
237
+ var AnchoredPopover = React2.forwardRef(
238
+ function AnchoredPopover2({ anchorRef, side, children, className, style, ...rest }, forwardedRef) {
239
+ const innerRef = React2.useRef(null);
240
+ const setRefs = (node) => {
241
+ innerRef.current = node;
242
+ if (typeof forwardedRef === "function") forwardedRef(node);
243
+ else if (forwardedRef) forwardedRef.current = node;
244
+ };
245
+ const [position, setPosition] = React2.useState(null);
246
+ const place = React2.useCallback(() => {
247
+ const anchor = anchorRef.current;
248
+ if (!anchor) return;
249
+ const rect = anchor.getBoundingClientRect();
250
+ const width = innerRef.current?.offsetWidth ?? 0;
251
+ const margin = 8;
252
+ const left = Math.max(
253
+ margin,
254
+ Math.min(rect.left, window.innerWidth - width - margin)
255
+ );
256
+ setPosition(
257
+ side === "top" ? { left, bottom: window.innerHeight - rect.top + margin } : { left, top: rect.bottom + margin }
258
+ );
259
+ }, [anchorRef, side]);
260
+ React2.useLayoutEffect(() => {
261
+ place();
262
+ window.addEventListener("resize", place);
263
+ window.addEventListener("scroll", place, true);
264
+ return () => {
265
+ window.removeEventListener("resize", place);
266
+ window.removeEventListener("scroll", place, true);
267
+ };
268
+ }, [place]);
269
+ return createPortal(
270
+ /* @__PURE__ */ jsx2(
271
+ "div",
272
+ {
273
+ ...rest,
274
+ ref: setRefs,
275
+ className: cn("fixed z-50", className),
276
+ style: {
277
+ ...style,
278
+ pointerEvents: "auto",
279
+ ...position ?? { visibility: "hidden", left: 0, top: 0 }
280
+ },
281
+ children
282
+ }
283
+ ),
284
+ document.body
285
+ );
286
+ }
287
+ );
288
+
289
+ // src/chat/informative-lock.tsx
290
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
291
+ function InformativeLock({
292
+ children,
293
+ ariaLabel,
294
+ lockTitle,
295
+ lockBody,
296
+ newChatLabel,
297
+ onNewChat,
298
+ className,
299
+ triggerClassName,
300
+ popoverClassName,
301
+ side = "top"
302
+ }) {
303
+ const [open, setOpen] = React3.useState(false);
304
+ const closeTimer = React3.useRef(null);
305
+ const popoverRef = React3.useRef(null);
306
+ const ref = useClickOutside(() => setOpen(false), [popoverRef]);
307
+ const cancelClose = React3.useCallback(() => {
308
+ if (closeTimer.current) {
309
+ clearTimeout(closeTimer.current);
310
+ closeTimer.current = null;
311
+ }
312
+ }, []);
313
+ const scheduleClose = React3.useCallback(() => {
314
+ cancelClose();
315
+ closeTimer.current = setTimeout(() => setOpen(false), 150);
316
+ }, [cancelClose]);
317
+ React3.useEffect(() => cancelClose, [cancelClose]);
318
+ return /* @__PURE__ */ jsxs2(
319
+ "div",
320
+ {
321
+ ref,
322
+ className: cn("relative inline-flex", className),
323
+ onMouseEnter: () => {
324
+ cancelClose();
325
+ setOpen(true);
326
+ },
327
+ onMouseLeave: scheduleClose,
328
+ onKeyDown: (event) => {
329
+ if (event.key === "Escape") setOpen(false);
330
+ },
331
+ children: [
332
+ /* @__PURE__ */ jsx3(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ onClick: () => setOpen(true),
337
+ "data-state": open ? "open" : "closed",
338
+ "aria-label": ariaLabel,
339
+ "aria-haspopup": "dialog",
340
+ "aria-expanded": open,
341
+ className: cn(
342
+ "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5 opacity-60",
343
+ "text-xs font-medium text-foreground shadow-sm transition-colors",
344
+ "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
345
+ "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
346
+ triggerClassName
347
+ ),
348
+ children
349
+ }
350
+ ),
351
+ open && /* @__PURE__ */ jsxs2(
352
+ AnchoredPopover,
353
+ {
354
+ ref: popoverRef,
355
+ anchorRef: ref,
356
+ side,
357
+ role: "dialog",
358
+ onMouseEnter: () => {
359
+ cancelClose();
360
+ setOpen(true);
361
+ },
362
+ onMouseLeave: scheduleClose,
363
+ className: cn(
364
+ "w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
365
+ "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
366
+ popoverClassName
367
+ ),
368
+ children: [
369
+ /* @__PURE__ */ jsxs2("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
370
+ /* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
371
+ /* @__PURE__ */ jsx3("span", { children: lockTitle })
372
+ ] }),
373
+ /* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
374
+ /* @__PURE__ */ jsxs2(
375
+ "button",
376
+ {
377
+ type: "button",
378
+ onClick: () => {
379
+ setOpen(false);
380
+ onNewChat();
381
+ },
382
+ className: cn(
383
+ "mt-3 flex w-full items-center justify-center gap-1.5 rounded-md px-2.5 py-2 text-sm font-medium text-foreground",
384
+ "bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
385
+ "hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
386
+ ),
387
+ children: [
388
+ /* @__PURE__ */ jsx3(Plus, { className: "h-4 w-4" }),
389
+ newChatLabel
390
+ ]
391
+ }
392
+ )
393
+ ]
394
+ }
395
+ )
396
+ ]
397
+ }
398
+ );
224
399
  }
225
400
 
226
401
  // src/chat/agent-profile-picker.tsx
227
- import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus, Trash2 } from "lucide-react";
228
- import * as React2 from "react";
229
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
402
+ import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
403
+ import * as React4 from "react";
404
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
230
405
  function profileSubtitle(profile) {
231
406
  if (profile.description) return profile.description;
232
407
  const count = profile.capabilities?.length ?? 0;
@@ -241,21 +416,33 @@ function AgentProfilePicker({
241
416
  onUpdate,
242
417
  onDelete,
243
418
  disabled,
419
+ locked,
420
+ lockReason,
421
+ onNewChat,
244
422
  className,
423
+ triggerClassName,
424
+ popoverClassName,
245
425
  side = "top"
246
426
  }) {
247
- const [open, setOpen] = React2.useState(false);
248
- const [composing, setComposing] = React2.useState(
427
+ const [open, setOpen] = React4.useState(false);
428
+ const [composing, setComposing] = React4.useState(
249
429
  null
250
430
  );
431
+ const popoverRef = React4.useRef(null);
251
432
  const ref = useClickOutside(() => {
252
433
  setOpen(false);
253
434
  setComposing(null);
254
- });
435
+ }, [popoverRef]);
255
436
  const canAuthor = Boolean(capabilities && (onCreate || onUpdate));
256
437
  const builtins = profiles.filter((profile) => profile.builtin);
257
438
  const custom = profiles.filter((profile) => !profile.builtin);
258
439
  const selected = profiles.find((profile) => profile.id === value);
440
+ const selectedName = selected?.name ?? value;
441
+ React4.useEffect(() => {
442
+ if (!locked) return;
443
+ setOpen(false);
444
+ setComposing(null);
445
+ }, [locked]);
259
446
  function select(id) {
260
447
  onChange(id);
261
448
  setOpen(false);
@@ -265,38 +452,64 @@ function AgentProfilePicker({
265
452
  else await onCreate?.(draft);
266
453
  setComposing(null);
267
454
  }
268
- return /* @__PURE__ */ jsxs2("div", { ref, className: cn("relative inline-flex", className), children: [
269
- /* @__PURE__ */ jsxs2(
455
+ if (locked && onNewChat) {
456
+ return /* @__PURE__ */ jsxs3(
457
+ InformativeLock,
458
+ {
459
+ ariaLabel: "Agent profile (locked)",
460
+ lockTitle: `Profile fixed to ${selectedName} for this conversation.`,
461
+ lockBody: "Conversations stay on one profile.",
462
+ newChatLabel: "New chat to switch profile",
463
+ onNewChat,
464
+ className,
465
+ triggerClassName,
466
+ popoverClassName,
467
+ side,
468
+ children: [
469
+ /* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
470
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName })
471
+ ]
472
+ }
473
+ );
474
+ }
475
+ return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-flex", className), children: [
476
+ /* @__PURE__ */ jsxs3(
270
477
  "button",
271
478
  {
272
479
  type: "button",
273
- disabled,
480
+ disabled: disabled || locked,
481
+ title: locked ? lockReason : void 0,
274
482
  onClick: () => setOpen((v) => !v),
275
483
  className: cn(
276
484
  "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
277
485
  "text-xs font-medium text-foreground shadow-sm transition-colors",
278
486
  "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
279
487
  "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
280
- "disabled:cursor-not-allowed disabled:opacity-60"
488
+ "disabled:cursor-not-allowed disabled:opacity-60",
489
+ triggerClassName
281
490
  ),
282
491
  "data-state": open ? "open" : "closed",
283
492
  "aria-label": "Agent profile",
284
493
  children: [
285
- /* @__PURE__ */ jsx2(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
286
- /* @__PURE__ */ jsx2("span", { className: "max-w-[140px] truncate", children: selected?.name ?? value }),
287
- /* @__PURE__ */ jsx2(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
494
+ /* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
495
+ /* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName }),
496
+ !locked && /* @__PURE__ */ jsx4(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
288
497
  ]
289
498
  }
290
499
  ),
291
- open && /* @__PURE__ */ jsx2(
292
- "div",
500
+ open && !locked && /* @__PURE__ */ jsx4(
501
+ AnchoredPopover,
293
502
  {
503
+ ref: popoverRef,
504
+ anchorRef: ref,
505
+ side,
294
506
  className: cn(
295
- "absolute left-0 z-50 w-[320px] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
507
+ "w-[min(320px,calc(100vw-2rem))] overflow-hidden rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-1",
296
508
  "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]",
297
- side === "top" ? "bottom-full mb-2" : "top-full mt-2"
509
+ popoverClassName
298
510
  ),
299
- children: composing ? /* @__PURE__ */ jsx2(
511
+ "data-testid": "agent-profile-popover",
512
+ children: composing ? /* @__PURE__ */ jsx4(
300
513
  ProfileForm,
301
514
  {
302
515
  capabilities: capabilities ?? [],
@@ -304,9 +517,9 @@ function AgentProfilePicker({
304
517
  onCancel: () => setComposing(null),
305
518
  onSave: save
306
519
  }
307
- ) : /* @__PURE__ */ jsxs2("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
308
- /* @__PURE__ */ jsx2(SectionHeader, { children: "Modes" }),
309
- builtins.map((profile) => /* @__PURE__ */ jsx2(
520
+ ) : /* @__PURE__ */ jsxs3("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
521
+ /* @__PURE__ */ jsx4(SectionHeader, { children: "Modes" }),
522
+ builtins.map((profile) => /* @__PURE__ */ jsx4(
310
523
  ProfileRow,
311
524
  {
312
525
  active: profile.id === value,
@@ -316,8 +529,8 @@ function AgentProfilePicker({
316
529
  },
317
530
  profile.id
318
531
  )),
319
- custom.length > 0 && /* @__PURE__ */ jsx2(SectionHeader, { children: "Your agents" }),
320
- custom.map((profile) => /* @__PURE__ */ jsx2(
532
+ custom.length > 0 && /* @__PURE__ */ jsx4(SectionHeader, { children: "Your agents" }),
533
+ custom.map((profile) => /* @__PURE__ */ jsx4(
321
534
  ProfileRow,
322
535
  {
323
536
  active: profile.id === value,
@@ -329,14 +542,14 @@ function AgentProfilePicker({
329
542
  },
330
543
  profile.id
331
544
  )),
332
- canAuthor && /* @__PURE__ */ jsxs2(
545
+ canAuthor && /* @__PURE__ */ jsxs3(
333
546
  "button",
334
547
  {
335
548
  type: "button",
336
549
  onClick: () => setComposing("new"),
337
550
  className: "mt-1 flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm text-[var(--accent-text)] transition-colors hover:bg-accent/40",
338
551
  children: [
339
- /* @__PURE__ */ jsx2(Plus, { className: "h-4 w-4" }),
552
+ /* @__PURE__ */ jsx4(Plus2, { className: "h-4 w-4" }),
340
553
  " New agent\u2026"
341
554
  ]
342
555
  }
@@ -347,7 +560,7 @@ function AgentProfilePicker({
347
560
  ] });
348
561
  }
349
562
  function SectionHeader({ children }) {
350
- return /* @__PURE__ */ jsx2("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
563
+ return /* @__PURE__ */ jsx4("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
351
564
  }
352
565
  function ProfileRow({
353
566
  active,
@@ -357,7 +570,7 @@ function ProfileRow({
357
570
  onEdit,
358
571
  onDelete
359
572
  }) {
360
- return /* @__PURE__ */ jsxs2(
573
+ return /* @__PURE__ */ jsxs3(
361
574
  "div",
362
575
  {
363
576
  className: cn(
@@ -365,40 +578,40 @@ function ProfileRow({
365
578
  active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
366
579
  ),
367
580
  children: [
368
- /* @__PURE__ */ jsxs2(
581
+ /* @__PURE__ */ jsxs3(
369
582
  "button",
370
583
  {
371
584
  type: "button",
372
585
  onClick: onSelect,
373
586
  className: "flex min-w-0 flex-1 items-center gap-2 text-left",
374
587
  children: [
375
- /* @__PURE__ */ jsx2("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx2(Check, { className: "h-3.5 w-3.5" }) }),
376
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
377
- /* @__PURE__ */ jsx2("span", { className: "block truncate font-medium", children: title }),
378
- /* @__PURE__ */ jsx2("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
588
+ /* @__PURE__ */ jsx4("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx4(Check, { className: "h-3.5 w-3.5" }) }),
589
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
590
+ /* @__PURE__ */ jsx4("span", { className: "block truncate font-medium", children: title }),
591
+ /* @__PURE__ */ jsx4("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
379
592
  ] })
380
593
  ]
381
594
  }
382
595
  ),
383
- (onEdit || onDelete) && /* @__PURE__ */ jsxs2("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
384
- onEdit && /* @__PURE__ */ jsx2(
596
+ (onEdit || onDelete) && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
597
+ onEdit && /* @__PURE__ */ jsx4(
385
598
  "button",
386
599
  {
387
600
  type: "button",
388
601
  onClick: onEdit,
389
602
  title: "Edit",
390
603
  className: "rounded p-1 text-muted-foreground hover:text-foreground",
391
- children: /* @__PURE__ */ jsx2(Pencil, { className: "h-3 w-3" })
604
+ children: /* @__PURE__ */ jsx4(Pencil, { className: "h-3 w-3" })
392
605
  }
393
606
  ),
394
- onDelete && /* @__PURE__ */ jsx2(
607
+ onDelete && /* @__PURE__ */ jsx4(
395
608
  "button",
396
609
  {
397
610
  type: "button",
398
611
  onClick: onDelete,
399
612
  title: "Delete",
400
613
  className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
401
- children: /* @__PURE__ */ jsx2(Trash2, { className: "h-3 w-3" })
614
+ children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3 w-3" })
402
615
  }
403
616
  )
404
617
  ] })
@@ -412,16 +625,16 @@ function ProfileForm({
412
625
  onCancel,
413
626
  onSave
414
627
  }) {
415
- const [name, setName] = React2.useState(initial?.name ?? "");
416
- const [caps, setCaps] = React2.useState(
628
+ const [name, setName] = React4.useState(initial?.name ?? "");
629
+ const [caps, setCaps] = React4.useState(
417
630
  initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
418
631
  );
419
- const [instructions, setInstructions] = React2.useState(initial?.instructions ?? "");
632
+ const [instructions, setInstructions] = React4.useState(initial?.instructions ?? "");
420
633
  const toggle = (id) => setCaps(
421
634
  (prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
422
635
  );
423
- return /* @__PURE__ */ jsxs2("div", { className: "space-y-3 p-2.5", children: [
424
- /* @__PURE__ */ jsx2(
636
+ return /* @__PURE__ */ jsxs3("div", { className: "space-y-3 p-2.5", children: [
637
+ /* @__PURE__ */ jsx4(
425
638
  "input",
426
639
  {
427
640
  autoFocus: true,
@@ -431,14 +644,14 @@ function ProfileForm({
431
644
  className: "w-full rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
432
645
  }
433
646
  ),
434
- capabilities.length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
435
- /* @__PURE__ */ jsx2("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
436
- /* @__PURE__ */ jsx2("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs2(
647
+ capabilities.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
648
+ /* @__PURE__ */ jsx4("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
649
+ /* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs3(
437
650
  "label",
438
651
  {
439
652
  className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
440
653
  children: [
441
- /* @__PURE__ */ jsx2(
654
+ /* @__PURE__ */ jsx4(
442
655
  "input",
443
656
  {
444
657
  type: "checkbox",
@@ -447,16 +660,16 @@ function ProfileForm({
447
660
  className: "mt-0.5"
448
661
  }
449
662
  ),
450
- /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
451
- /* @__PURE__ */ jsx2("span", { className: "block text-sm font-medium", children: cap.label }),
452
- cap.description && /* @__PURE__ */ jsx2("span", { className: "block text-xs text-muted-foreground", children: cap.description })
663
+ /* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
664
+ /* @__PURE__ */ jsx4("span", { className: "block text-sm font-medium", children: cap.label }),
665
+ cap.description && /* @__PURE__ */ jsx4("span", { className: "block text-xs text-muted-foreground", children: cap.description })
453
666
  ] })
454
667
  ]
455
668
  },
456
669
  cap.id
457
670
  )) })
458
671
  ] }),
459
- /* @__PURE__ */ jsx2(
672
+ /* @__PURE__ */ jsx4(
460
673
  "textarea",
461
674
  {
462
675
  value: instructions,
@@ -466,8 +679,8 @@ function ProfileForm({
466
679
  className: "w-full resize-none rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
467
680
  }
468
681
  ),
469
- /* @__PURE__ */ jsxs2("div", { className: "flex justify-end gap-2", children: [
470
- /* @__PURE__ */ jsx2(
682
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
683
+ /* @__PURE__ */ jsx4(
471
684
  "button",
472
685
  {
473
686
  type: "button",
@@ -476,7 +689,7 @@ function ProfileForm({
476
689
  children: "Cancel"
477
690
  }
478
691
  ),
479
- /* @__PURE__ */ jsx2(
692
+ /* @__PURE__ */ jsx4(
480
693
  "button",
481
694
  {
482
695
  type: "button",
@@ -496,7 +709,7 @@ function ProfileForm({
496
709
  }
497
710
 
498
711
  // src/chat/agent-session-controls.tsx
499
- import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
712
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
500
713
  function harnessAutonomyNote(harness) {
501
714
  const ownModel = !harnessHonorsModel(harness);
502
715
  const ownEffort = !harnessHonorsEffort(harness);
@@ -520,108 +733,6 @@ function harnessGroup(option) {
520
733
  return harnessAutonomyNote(option.type) === null ? "steerable" : "fixed";
521
734
  }
522
735
  var HARNESS_SEARCH_THRESHOLD = 7;
523
- function InformativeLock({
524
- type,
525
- label,
526
- lockTitle,
527
- lockBody,
528
- newChatLabel,
529
- onNewChat,
530
- triggerClassName
531
- }) {
532
- const [open, setOpen] = React3.useState(false);
533
- const closeTimer = React3.useRef(null);
534
- const ref = useClickOutside(() => setOpen(false));
535
- const cancelClose = React3.useCallback(() => {
536
- if (closeTimer.current) {
537
- clearTimeout(closeTimer.current);
538
- closeTimer.current = null;
539
- }
540
- }, []);
541
- const scheduleClose = React3.useCallback(() => {
542
- cancelClose();
543
- closeTimer.current = setTimeout(() => setOpen(false), 150);
544
- }, [cancelClose]);
545
- React3.useEffect(() => cancelClose, [cancelClose]);
546
- return /* @__PURE__ */ jsxs3(
547
- "div",
548
- {
549
- ref,
550
- className: "relative inline-flex",
551
- onMouseEnter: () => {
552
- cancelClose();
553
- setOpen(true);
554
- },
555
- onMouseLeave: scheduleClose,
556
- onKeyDown: (event) => {
557
- if (event.key === "Escape") setOpen(false);
558
- },
559
- children: [
560
- /* @__PURE__ */ jsxs3(
561
- "button",
562
- {
563
- type: "button",
564
- onClick: () => setOpen(true),
565
- "data-state": open ? "open" : "closed",
566
- "aria-label": "Agent harness (locked)",
567
- "aria-haspopup": "dialog",
568
- "aria-expanded": open,
569
- className: cn(
570
- // Dimmed to read as locked (the harness is bound to this session),
571
- // matching the disabled model picker — the logo stays so the chip is
572
- // still recognizably this harness; the popover explains the lock.
573
- "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5 opacity-60",
574
- "text-xs font-medium text-foreground shadow-sm transition-colors",
575
- "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
576
- "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
577
- triggerClassName
578
- ),
579
- children: [
580
- /* @__PURE__ */ jsx3(HarnessLogo, { type, size: 16 }),
581
- /* @__PURE__ */ jsx3("span", { children: label })
582
- ]
583
- }
584
- ),
585
- open && /* @__PURE__ */ jsxs3(
586
- "div",
587
- {
588
- role: "dialog",
589
- className: cn(
590
- "absolute bottom-full left-0 z-50 mb-2 w-72 rounded-[var(--radius-md)] border border-[var(--md3-outline-variant)] bg-surface-container-highest p-3",
591
- "shadow-[0_8px_30px_rgba(0,0,0,0.45)] ring-1 ring-[#ffffff14]"
592
- ),
593
- children: [
594
- /* @__PURE__ */ jsxs3("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
595
- /* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
596
- /* @__PURE__ */ jsx3("span", { children: lockTitle ?? `Agent fixed to ${label} for this conversation.` })
597
- ] }),
598
- /* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody ?? "Conversations stay on one agent." }),
599
- /* @__PURE__ */ jsxs3(
600
- "button",
601
- {
602
- type: "button",
603
- onClick: () => {
604
- setOpen(false);
605
- onNewChat();
606
- },
607
- className: cn(
608
- "mt-3 flex w-full items-center justify-center gap-1.5 rounded-md px-2.5 py-2 text-sm font-medium text-foreground",
609
- "bg-primary/10 ring-1 ring-inset ring-primary/25 transition-colors",
610
- "hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
611
- ),
612
- children: [
613
- /* @__PURE__ */ jsx3(Plus2, { className: "h-4 w-4" }),
614
- newChatLabel ?? "New chat to switch agent"
615
- ]
616
- }
617
- )
618
- ]
619
- }
620
- )
621
- ]
622
- }
623
- );
624
- }
625
736
  function HarnessDropdown({
626
737
  value,
627
738
  onChange,
@@ -643,16 +754,16 @@ function HarnessDropdown({
643
754
  );
644
755
  const options = HARNESS_OPTIONS.filter((h) => allowed.has(h.type));
645
756
  const selected = options.find((option) => option.type === value);
646
- const [query, setQuery] = React3.useState("");
757
+ const [query, setQuery] = React5.useState("");
647
758
  const showSearch = options.length > HARNESS_SEARCH_THRESHOLD;
648
- const filtered = React3.useMemo(() => {
759
+ const filtered = React5.useMemo(() => {
649
760
  const q = query.trim().toLowerCase();
650
761
  if (!q) return options;
651
762
  return options.filter(
652
763
  (o) => o.label.toLowerCase().includes(q) || o.type.toLowerCase().includes(q) || (o.description?.toLowerCase() ?? "").includes(q)
653
764
  );
654
765
  }, [options, query]);
655
- const sections = React3.useMemo(() => {
766
+ const sections = React5.useMemo(() => {
656
767
  const byGroup = /* @__PURE__ */ new Map();
657
768
  for (const option of filtered) {
658
769
  const key = harnessGroup(option);
@@ -667,27 +778,31 @@ function HarnessDropdown({
667
778
  })).filter((section) => section.items.length > 0);
668
779
  }, [filtered]);
669
780
  if (locked && onNewChat) {
670
- return /* @__PURE__ */ jsx3(
781
+ const label = selected?.label ?? value;
782
+ return /* @__PURE__ */ jsxs4(
671
783
  InformativeLock,
672
784
  {
673
- type: value,
674
- label: selected?.label ?? value,
675
- lockTitle,
676
- lockBody,
677
- newChatLabel,
785
+ ariaLabel: "Agent harness (locked)",
786
+ lockTitle: lockTitle ?? `Agent fixed to ${label} for this conversation.`,
787
+ lockBody: lockBody ?? "Conversations stay on one agent.",
788
+ newChatLabel: newChatLabel ?? "New chat to switch agent",
678
789
  onNewChat,
679
- triggerClassName
790
+ triggerClassName,
791
+ children: [
792
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16 }),
793
+ /* @__PURE__ */ jsx5("span", { children: label })
794
+ ]
680
795
  }
681
796
  );
682
797
  }
683
- return /* @__PURE__ */ jsxs3(
798
+ return /* @__PURE__ */ jsxs4(
684
799
  DropdownMenu2.Root,
685
800
  {
686
801
  onOpenChange: (nextOpen) => {
687
802
  if (!nextOpen) setQuery("");
688
803
  },
689
804
  children: [
690
- /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
805
+ /* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs4(
691
806
  "button",
692
807
  {
693
808
  type: "button",
@@ -703,13 +818,13 @@ function HarnessDropdown({
703
818
  ),
704
819
  "aria-label": "Agent harness",
705
820
  children: [
706
- /* @__PURE__ */ jsx3(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
707
- /* @__PURE__ */ jsx3("span", { className: "truncate", children: selected?.label ?? value }),
708
- !locked && /* @__PURE__ */ jsx3(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
821
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
822
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: selected?.label ?? value }),
823
+ !locked && /* @__PURE__ */ jsx5(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
709
824
  ]
710
825
  }
711
826
  ) }),
712
- /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
827
+ /* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
713
828
  DropdownMenu2.Content,
714
829
  {
715
830
  side,
@@ -719,7 +834,7 @@ function HarnessDropdown({
719
834
  sideOffset: 6,
720
835
  className: pickerMenuContentClass,
721
836
  children: [
722
- showSearch && /* @__PURE__ */ jsx3(
837
+ showSearch && /* @__PURE__ */ jsx5(
723
838
  PickerMenuSearch,
724
839
  {
725
840
  value: query,
@@ -727,7 +842,7 @@ function HarnessDropdown({
727
842
  placeholder: "Search agents..."
728
843
  }
729
844
  ),
730
- /* @__PURE__ */ jsx3("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx3(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs3(
845
+ /* @__PURE__ */ jsx5("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx5(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs4(
731
846
  DropdownMenu2.Item,
732
847
  {
733
848
  onSelect: (event) => {
@@ -738,7 +853,7 @@ function HarnessDropdown({
738
853
  active: option.type === value
739
854
  }),
740
855
  children: [
741
- /* @__PURE__ */ jsx3(
856
+ /* @__PURE__ */ jsx5(
742
857
  HarnessLogo,
743
858
  {
744
859
  type: option.type,
@@ -746,18 +861,18 @@ function HarnessDropdown({
746
861
  className: "mt-0.5"
747
862
  }
748
863
  ),
749
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
750
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5", children: [
751
- /* @__PURE__ */ jsx3("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
752
- harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx3(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
864
+ /* @__PURE__ */ jsxs4("div", { className: "min-w-0 flex-1", children: [
865
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5", children: [
866
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
867
+ harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx5(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
753
868
  ] }),
754
- /* @__PURE__ */ jsx3(PickerMenuMeta, { parts: [option.description] })
869
+ /* @__PURE__ */ jsx5(PickerMenuMeta, { parts: [option.description] })
755
870
  ] })
756
871
  ]
757
872
  },
758
873
  option.type
759
874
  )) }, section.key)) }),
760
- showSearch && /* @__PURE__ */ jsxs3(PickerMenuFooter, { children: [
875
+ showSearch && /* @__PURE__ */ jsxs4(PickerMenuFooter, { children: [
761
876
  filtered.length,
762
877
  " of ",
763
878
  options.length,
@@ -850,7 +965,7 @@ function AgentSessionControls({
850
965
  const modelTriggerClass = collapsed ? cn(collapsedTriggerClass, "h-8 rounded-lg px-2.5 text-xs") : void 0;
851
966
  const inlineForceDown = menuPlacement === "down" && layout === "inline";
852
967
  const avoidCollisions = inlineForceDown ? false : void 0;
853
- const harnessNode = harness && /* @__PURE__ */ jsx3(
968
+ const harnessNode = harness && /* @__PURE__ */ jsx5(
854
969
  HarnessDropdown,
855
970
  {
856
971
  ...harness,
@@ -862,7 +977,7 @@ function AgentSessionControls({
862
977
  triggerClassName: secondaryTriggerClass
863
978
  }
864
979
  );
865
- const profileNode = profile && /* @__PURE__ */ jsx3(
980
+ const profileNode = profile && /* @__PURE__ */ jsx5(
866
981
  AgentProfilePicker,
867
982
  {
868
983
  value: profile.value,
@@ -873,10 +988,15 @@ function AgentSessionControls({
873
988
  onUpdate: profile.onUpdate,
874
989
  onDelete: profile.onDelete,
875
990
  disabled: profile.disabled,
876
- side: inlineForceDown || collapsed ? "bottom" : "top"
991
+ locked: profile.locked,
992
+ lockReason: profile.lockReason,
993
+ onNewChat: profile.onNewChat,
994
+ side: inlineForceDown || collapsed ? "bottom" : "top",
995
+ triggerClassName: secondaryTriggerClass,
996
+ popoverClassName: collapsed ? "w-full" : void 0
877
997
  }
878
998
  );
879
- const modelNode = model && /* @__PURE__ */ jsx3(
999
+ const modelNode = model && /* @__PURE__ */ jsx5(
880
1000
  ModelPicker,
881
1001
  {
882
1002
  variant: "pill",
@@ -897,7 +1017,7 @@ function AgentSessionControls({
897
1017
  );
898
1018
  const modelReasoning = modelReasoningCapability(model?.models, model?.value);
899
1019
  const reasoningOptions = (harness && HARNESS_REASONING_OPTIONS[harness.value]) ?? reasoning?.options;
900
- const reasoningNode = reasoning && /* @__PURE__ */ jsx3(
1020
+ const reasoningNode = reasoning && /* @__PURE__ */ jsx5(
901
1021
  ReasoningLevelPicker,
902
1022
  {
903
1023
  value: reasoning.value,
@@ -911,7 +1031,7 @@ function AgentSessionControls({
911
1031
  }
912
1032
  );
913
1033
  if (layout === "gear") {
914
- return /* @__PURE__ */ jsx3(
1034
+ return /* @__PURE__ */ jsx5(
915
1035
  GearControls,
916
1036
  {
917
1037
  className,
@@ -930,8 +1050,8 @@ function AgentSessionControls({
930
1050
  segments.push({
931
1051
  key: "harness",
932
1052
  label: harnessLabel,
933
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
934
- /* @__PURE__ */ jsx3(HarnessLogo, { type: harness.value, size: 16, className: "shrink-0" }),
1053
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1054
+ /* @__PURE__ */ jsx5(HarnessLogo, { type: harness.value, size: 16, className: "shrink-0" }),
935
1055
  harnessLabel
936
1056
  ] })
937
1057
  });
@@ -948,8 +1068,8 @@ function AgentSessionControls({
948
1068
  segments.push({
949
1069
  key: "model",
950
1070
  label: modelLabel,
951
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
952
- identity && /* @__PURE__ */ jsx3(ModelBrandStack, { identity, size: "sm" }),
1071
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1072
+ identity && /* @__PURE__ */ jsx5(ModelBrandStack, { identity, size: "sm" }),
953
1073
  modelLabel
954
1074
  ] })
955
1075
  });
@@ -961,14 +1081,14 @@ function AgentSessionControls({
961
1081
  segments.push({
962
1082
  key: "effort",
963
1083
  label: effortLabel,
964
- node: /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
965
- /* @__PURE__ */ jsx3("span", { className: "flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx3(ReasoningGlyph, { level: reasoning.value }) }),
1084
+ node: /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-1.5", children: [
1085
+ /* @__PURE__ */ jsx5("span", { className: "flex w-3.5 shrink-0 justify-center", children: /* @__PURE__ */ jsx5(ReasoningGlyph, { level: reasoning.value }) }),
966
1086
  effortLabel
967
1087
  ] })
968
1088
  });
969
1089
  }
970
- const summary = /* @__PURE__ */ jsx3("span", { className: "flex items-center gap-1.5", children: segments.map((segment, index) => /* @__PURE__ */ jsxs3(React3.Fragment, { children: [
971
- index > 0 && /* @__PURE__ */ jsx3(
1090
+ const summary = /* @__PURE__ */ jsx5("span", { className: "flex items-center gap-1.5", children: segments.map((segment, index) => /* @__PURE__ */ jsxs4(React5.Fragment, { children: [
1091
+ index > 0 && /* @__PURE__ */ jsx5(
972
1092
  "span",
973
1093
  {
974
1094
  className: "text-base leading-none text-muted-foreground/60",
@@ -978,7 +1098,7 @@ function AgentSessionControls({
978
1098
  ),
979
1099
  segment.node
980
1100
  ] }, segment.key)) });
981
- return /* @__PURE__ */ jsx3(
1101
+ return /* @__PURE__ */ jsx5(
982
1102
  SummaryControls,
983
1103
  {
984
1104
  className,
@@ -993,7 +1113,7 @@ function AgentSessionControls({
993
1113
  }
994
1114
  );
995
1115
  }
996
- return /* @__PURE__ */ jsxs3(
1116
+ return /* @__PURE__ */ jsxs4(
997
1117
  "div",
998
1118
  {
999
1119
  className: cn("flex flex-wrap items-center gap-2", className),
@@ -1003,7 +1123,7 @@ function AgentSessionControls({
1003
1123
  harnessNode,
1004
1124
  modelNode,
1005
1125
  reasoningNode,
1006
- trailing && /* @__PURE__ */ jsx3("div", { className: "ml-auto flex items-center gap-2", children: trailing })
1126
+ trailing && /* @__PURE__ */ jsx5("div", { className: "ml-auto flex items-center gap-2", children: trailing })
1007
1127
  ]
1008
1128
  }
1009
1129
  );
@@ -1016,11 +1136,11 @@ function GearControls({
1016
1136
  reasoningNode,
1017
1137
  trailing
1018
1138
  }) {
1019
- return /* @__PURE__ */ jsx3(
1139
+ return /* @__PURE__ */ jsx5(
1020
1140
  CollapsibleControls,
1021
1141
  {
1022
1142
  className,
1023
- trigger: /* @__PURE__ */ jsx3(SlidersHorizontal, { className: "h-4 w-4 text-muted-foreground" }),
1143
+ trigger: /* @__PURE__ */ jsx5(SlidersHorizontal, { className: "h-4 w-4 text-muted-foreground" }),
1024
1144
  triggerClassName: cn(
1025
1145
  "inline-flex h-8 w-8 items-center justify-center rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container",
1026
1146
  "text-foreground shadow-sm transition-colors",
@@ -1044,8 +1164,8 @@ function GearSection({
1044
1164
  label,
1045
1165
  children
1046
1166
  }) {
1047
- return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-1", children: [
1048
- /* @__PURE__ */ jsx3("span", { className: "px-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70", children: label }),
1167
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
1168
+ /* @__PURE__ */ jsx5("span", { className: "px-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70", children: label }),
1049
1169
  children
1050
1170
  ] });
1051
1171
  }
@@ -1060,13 +1180,13 @@ function SummaryControls({
1060
1180
  reasoningNode,
1061
1181
  trailing
1062
1182
  }) {
1063
- return /* @__PURE__ */ jsx3(
1183
+ return /* @__PURE__ */ jsx5(
1064
1184
  CollapsibleControls,
1065
1185
  {
1066
1186
  className,
1067
- trigger: /* @__PURE__ */ jsxs3(Fragment2, { children: [
1187
+ trigger: /* @__PURE__ */ jsxs4(Fragment2, { children: [
1068
1188
  summary,
1069
- /* @__PURE__ */ jsx3(ChevronDown3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
1189
+ /* @__PURE__ */ jsx5(ChevronDown3, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
1070
1190
  ] }),
1071
1191
  triggerTitle: title,
1072
1192
  triggerClassName: cn(
@@ -1101,15 +1221,15 @@ function CollapsibleControls({
1101
1221
  reasoningNode,
1102
1222
  trailing
1103
1223
  }) {
1104
- return /* @__PURE__ */ jsxs3(
1224
+ return /* @__PURE__ */ jsxs4(
1105
1225
  "div",
1106
1226
  {
1107
1227
  className: cn("flex items-center gap-2", className),
1108
1228
  "data-testid": "agent-session-controls",
1109
1229
  children: [
1110
- trailing && /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-2", children: trailing }),
1111
- /* @__PURE__ */ jsxs3(DropdownMenu2.Root, { children: [
1112
- /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx3(
1230
+ trailing && /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2", children: trailing }),
1231
+ /* @__PURE__ */ jsxs4(DropdownMenu2.Root, { children: [
1232
+ /* @__PURE__ */ jsx5(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsx5(
1113
1233
  "button",
1114
1234
  {
1115
1235
  type: "button",
@@ -1119,7 +1239,7 @@ function CollapsibleControls({
1119
1239
  children: trigger
1120
1240
  }
1121
1241
  ) }),
1122
- /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
1242
+ /* @__PURE__ */ jsx5(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs4(
1123
1243
  DropdownMenu2.Content,
1124
1244
  {
1125
1245
  side: contentSide,
@@ -1141,10 +1261,10 @@ function CollapsibleControls({
1141
1261
  contentClassName
1142
1262
  ),
1143
1263
  children: [
1144
- profileNode && /* @__PURE__ */ jsx3(GearSection, { label: "Agent", children: profileNode }),
1145
- harnessNode && /* @__PURE__ */ jsx3(GearSection, { label: "Harness", children: harnessNode }),
1146
- modelNode && /* @__PURE__ */ jsx3(GearSection, { label: "Model", children: modelNode }),
1147
- reasoningNode && /* @__PURE__ */ jsx3(GearSection, { label: "Effort", children: reasoningNode })
1264
+ profileNode && /* @__PURE__ */ jsx5(GearSection, { label: "Agent", children: profileNode }),
1265
+ harnessNode && /* @__PURE__ */ jsx5(GearSection, { label: "Harness", children: harnessNode }),
1266
+ modelNode && /* @__PURE__ */ jsx5(GearSection, { label: "Model", children: modelNode }),
1267
+ reasoningNode && /* @__PURE__ */ jsx5(GearSection, { label: "Effort", children: reasoningNode })
1148
1268
  ]
1149
1269
  }
1150
1270
  ) })
@@ -1165,7 +1285,7 @@ import {
1165
1285
  Upload,
1166
1286
  X
1167
1287
  } from "lucide-react";
1168
- import * as React4 from "react";
1288
+ import * as React6 from "react";
1169
1289
 
1170
1290
  // src/chat/attachment-validation.ts
1171
1291
  function isAcceptedType(file, accept) {
@@ -1219,7 +1339,7 @@ function validateComposerFiles(files, config) {
1219
1339
  }
1220
1340
 
1221
1341
  // src/chat/agent-composer.tsx
1222
- import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1342
+ import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1223
1343
  var IMAGE_EXTENSION_BY_MIME = {
1224
1344
  "image/png": "png",
1225
1345
  "image/jpeg": "jpg",
@@ -1238,7 +1358,7 @@ function imageExtension(file) {
1238
1358
  const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
1239
1359
  return fromName?.toLowerCase() ?? "png";
1240
1360
  }
1241
- var MentionEditor = React4.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
1361
+ var MentionEditor = React6.lazy(() => import("./mention-editor-3Q6MYOIU.js"));
1242
1362
  function AgentComposer({
1243
1363
  value,
1244
1364
  onChange,
@@ -1273,24 +1393,24 @@ function AgentComposer({
1273
1393
  sendLabel = "Send",
1274
1394
  autoFocus
1275
1395
  }) {
1276
- const textareaRef = React4.useRef(null);
1277
- const richFocusRef = React4.useRef(null);
1278
- const fileInputRef = React4.useRef(null);
1279
- const folderInputRef = React4.useRef(null);
1280
- const [dragOver, setDragOver] = React4.useState(false);
1281
- const dragDepth = React4.useRef(0);
1282
- const pastedImageCount = React4.useRef(0);
1283
- const resize = React4.useCallback(
1396
+ const textareaRef = React6.useRef(null);
1397
+ const richFocusRef = React6.useRef(null);
1398
+ const fileInputRef = React6.useRef(null);
1399
+ const folderInputRef = React6.useRef(null);
1400
+ const [dragOver, setDragOver] = React6.useState(false);
1401
+ const dragDepth = React6.useRef(0);
1402
+ const pastedImageCount = React6.useRef(0);
1403
+ const resize = React6.useCallback(
1284
1404
  (el) => {
1285
1405
  el.style.height = "auto";
1286
1406
  el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
1287
1407
  },
1288
1408
  [maxHeight]
1289
1409
  );
1290
- React4.useEffect(() => {
1410
+ React6.useEffect(() => {
1291
1411
  if (textareaRef.current) resize(textareaRef.current);
1292
1412
  }, [value, resize]);
1293
- React4.useEffect(() => {
1413
+ React6.useEffect(() => {
1294
1414
  if (!focusShortcut || disabled) return;
1295
1415
  function onKeyDown(event) {
1296
1416
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
@@ -1307,7 +1427,7 @@ function AgentComposer({
1307
1427
  if (!canSend) return;
1308
1428
  onSubmit();
1309
1429
  };
1310
- const registerRichFocus = React4.useCallback((focus) => {
1430
+ const registerRichFocus = React6.useCallback((focus) => {
1311
1431
  richFocusRef.current = focus;
1312
1432
  }, []);
1313
1433
  const dropEnabled = Boolean(onAttach);
@@ -1382,13 +1502,13 @@ function AgentComposer({
1382
1502
  "hover:bg-surface-container-highest hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1383
1503
  "disabled:cursor-not-allowed disabled:opacity-50"
1384
1504
  );
1385
- return /* @__PURE__ */ jsxs4("div", { className: cn("relative", className), children: [
1386
- dragOver && /* @__PURE__ */ jsx4("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
1387
- /* @__PURE__ */ jsx4("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx4(Upload, { className: "size-5" }) }),
1388
- /* @__PURE__ */ jsx4("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
1389
- /* @__PURE__ */ jsx4("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
1505
+ return /* @__PURE__ */ jsxs5("div", { className: cn("relative", className), children: [
1506
+ dragOver && /* @__PURE__ */ jsx6("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs5("div", { className: "text-center", children: [
1507
+ /* @__PURE__ */ jsx6("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx6(Upload, { className: "size-5" }) }),
1508
+ /* @__PURE__ */ jsx6("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
1509
+ /* @__PURE__ */ jsx6("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
1390
1510
  ] }) }),
1391
- /* @__PURE__ */ jsxs4(
1511
+ /* @__PURE__ */ jsxs5(
1392
1512
  "div",
1393
1513
  {
1394
1514
  "data-testid": "agent-composer",
@@ -1402,10 +1522,10 @@ function AgentComposer({
1402
1522
  disabled && "opacity-60"
1403
1523
  ),
1404
1524
  children: [
1405
- attachments.length > 0 && /* @__PURE__ */ jsx4("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
1525
+ attachments.length > 0 && /* @__PURE__ */ jsx6("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
1406
1526
  const isError = file.status === "error";
1407
1527
  const isPending = file.status === "pending";
1408
- return /* @__PURE__ */ jsxs4(
1528
+ return /* @__PURE__ */ jsxs5(
1409
1529
  "span",
1410
1530
  {
1411
1531
  title: isError ? file.errorMessage : void 0,
@@ -1415,40 +1535,40 @@ function AgentComposer({
1415
1535
  isPending && "opacity-60"
1416
1536
  ),
1417
1537
  children: [
1418
- file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx4(
1538
+ file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx6(
1419
1539
  "img",
1420
1540
  {
1421
1541
  src: file.previewUrl,
1422
1542
  alt: "",
1423
1543
  className: "size-8 rounded object-cover"
1424
1544
  }
1425
- ) : file.kind === "folder" ? /* @__PURE__ */ jsx4(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx4(Paperclip, { className: "size-3 shrink-0" }),
1426
- /* @__PURE__ */ jsx4("span", { className: "max-w-[150px] truncate", children: file.name }),
1427
- file.fileCount !== void 0 && /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
1545
+ ) : file.kind === "folder" ? /* @__PURE__ */ jsx6(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx6(Paperclip, { className: "size-3 shrink-0" }),
1546
+ /* @__PURE__ */ jsx6("span", { className: "max-w-[150px] truncate", children: file.name }),
1547
+ file.fileCount !== void 0 && /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground", children: [
1428
1548
  "(",
1429
1549
  file.fileCount,
1430
1550
  ")"
1431
1551
  ] }),
1432
- file.status === "uploading" && /* @__PURE__ */ jsx4(Loader2, { className: "size-3 animate-spin" }),
1433
- isError && file.errorMessage && /* @__PURE__ */ jsx4("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
1434
- isError && onRetryFile && /* @__PURE__ */ jsx4(
1552
+ file.status === "uploading" && /* @__PURE__ */ jsx6(Loader2, { className: "size-3 animate-spin" }),
1553
+ isError && file.errorMessage && /* @__PURE__ */ jsx6("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
1554
+ isError && onRetryFile && /* @__PURE__ */ jsx6(
1435
1555
  "button",
1436
1556
  {
1437
1557
  type: "button",
1438
1558
  "aria-label": `Retry upload ${file.name}`,
1439
1559
  onClick: () => onRetryFile(file.id),
1440
1560
  className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1441
- children: /* @__PURE__ */ jsx4(RotateCcw, { className: "size-3" })
1561
+ children: /* @__PURE__ */ jsx6(RotateCcw, { className: "size-3" })
1442
1562
  }
1443
1563
  ),
1444
- onRemoveFile && /* @__PURE__ */ jsx4(
1564
+ onRemoveFile && /* @__PURE__ */ jsx6(
1445
1565
  "button",
1446
1566
  {
1447
1567
  type: "button",
1448
1568
  "aria-label": `Remove ${file.name}`,
1449
1569
  onClick: () => onRemoveFile(file.id),
1450
1570
  className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
1451
- children: /* @__PURE__ */ jsx4(X, { className: "size-3" })
1571
+ children: /* @__PURE__ */ jsx6(X, { className: "size-3" })
1452
1572
  }
1453
1573
  )
1454
1574
  ]
@@ -1456,10 +1576,10 @@ function AgentComposer({
1456
1576
  file.id
1457
1577
  );
1458
1578
  }) }),
1459
- mention ? /* @__PURE__ */ jsx4(
1460
- React4.Suspense,
1579
+ mention ? /* @__PURE__ */ jsx6(
1580
+ React6.Suspense,
1461
1581
  {
1462
- fallback: /* @__PURE__ */ jsx4(
1582
+ fallback: /* @__PURE__ */ jsx6(
1463
1583
  "textarea",
1464
1584
  {
1465
1585
  rows: minRows,
@@ -1473,7 +1593,7 @@ function AgentComposer({
1473
1593
  )
1474
1594
  }
1475
1595
  ),
1476
- children: /* @__PURE__ */ jsx4(
1596
+ children: /* @__PURE__ */ jsx6(
1477
1597
  MentionEditor,
1478
1598
  {
1479
1599
  value,
@@ -1490,7 +1610,7 @@ function AgentComposer({
1490
1610
  }
1491
1611
  )
1492
1612
  }
1493
- ) : /* @__PURE__ */ jsx4(
1613
+ ) : /* @__PURE__ */ jsx6(
1494
1614
  "textarea",
1495
1615
  {
1496
1616
  ref: textareaRef,
@@ -1515,9 +1635,9 @@ function AgentComposer({
1515
1635
  )
1516
1636
  }
1517
1637
  ),
1518
- /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
1519
- onAttach && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1520
- /* @__PURE__ */ jsx4(
1638
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
1639
+ onAttach && /* @__PURE__ */ jsxs5(Fragment3, { children: [
1640
+ /* @__PURE__ */ jsx6(
1521
1641
  "button",
1522
1642
  {
1523
1643
  type: "button",
@@ -1526,10 +1646,10 @@ function AgentComposer({
1526
1646
  "aria-label": "Attach files",
1527
1647
  title: "Attach files",
1528
1648
  className: attachButtonClass,
1529
- children: /* @__PURE__ */ jsx4(Paperclip, { className: "size-4" })
1649
+ children: /* @__PURE__ */ jsx6(Paperclip, { className: "size-4" })
1530
1650
  }
1531
1651
  ),
1532
- /* @__PURE__ */ jsx4(
1652
+ /* @__PURE__ */ jsx6(
1533
1653
  "input",
1534
1654
  {
1535
1655
  ref: fileInputRef,
@@ -1545,8 +1665,8 @@ function AgentComposer({
1545
1665
  }
1546
1666
  )
1547
1667
  ] }),
1548
- onAttachFolder && /* @__PURE__ */ jsxs4(Fragment3, { children: [
1549
- /* @__PURE__ */ jsx4(
1668
+ onAttachFolder && /* @__PURE__ */ jsxs5(Fragment3, { children: [
1669
+ /* @__PURE__ */ jsx6(
1550
1670
  "button",
1551
1671
  {
1552
1672
  type: "button",
@@ -1555,10 +1675,10 @@ function AgentComposer({
1555
1675
  "aria-label": "Attach folder",
1556
1676
  title: "Attach folder",
1557
1677
  className: attachButtonClass,
1558
- children: /* @__PURE__ */ jsx4(Folder, { className: "size-4" })
1678
+ children: /* @__PURE__ */ jsx6(Folder, { className: "size-4" })
1559
1679
  }
1560
1680
  ),
1561
- /* @__PURE__ */ jsx4(
1681
+ /* @__PURE__ */ jsx6(
1562
1682
  "input",
1563
1683
  {
1564
1684
  ref: folderInputRef,
@@ -1574,8 +1694,8 @@ function AgentComposer({
1574
1694
  }
1575
1695
  )
1576
1696
  ] }),
1577
- /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
1578
- controls ?? /* @__PURE__ */ jsx4(
1697
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
1698
+ controls ?? /* @__PURE__ */ jsx6(
1579
1699
  AgentSessionControls,
1580
1700
  {
1581
1701
  context,
@@ -1585,9 +1705,9 @@ function AgentComposer({
1585
1705
  reasoning
1586
1706
  }
1587
1707
  ),
1588
- trailing && /* @__PURE__ */ jsx4("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
1708
+ trailing && /* @__PURE__ */ jsx6("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
1589
1709
  ] }),
1590
- busy && onCancel ? /* @__PURE__ */ jsx4(
1710
+ busy && onCancel ? /* @__PURE__ */ jsx6(
1591
1711
  "button",
1592
1712
  {
1593
1713
  type: "button",
@@ -1598,9 +1718,9 @@ function AgentComposer({
1598
1718
  "bg-[var(--status-error,#ff4d6d)]/15 text-[var(--status-error,#ff4d6d)] transition-colors",
1599
1719
  "hover:bg-[var(--status-error,#ff4d6d)]/25"
1600
1720
  ),
1601
- children: /* @__PURE__ */ jsx4(Square, { className: "size-3.5 fill-current" })
1721
+ children: /* @__PURE__ */ jsx6(Square, { className: "size-3.5 fill-current" })
1602
1722
  }
1603
- ) : /* @__PURE__ */ jsx4(
1723
+ ) : /* @__PURE__ */ jsx6(
1604
1724
  "button",
1605
1725
  {
1606
1726
  type: "button",
@@ -1611,26 +1731,26 @@ function AgentComposer({
1611
1731
  "flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
1612
1732
  "transition-opacity hover:opacity-90 disabled:opacity-40"
1613
1733
  ),
1614
- children: busy ? /* @__PURE__ */ jsx4(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx4(ArrowUp, { className: "size-4" })
1734
+ children: busy ? /* @__PURE__ */ jsx6(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx6(ArrowUp, { className: "size-4" })
1615
1735
  }
1616
1736
  )
1617
1737
  ] })
1618
1738
  ]
1619
1739
  }
1620
1740
  ),
1621
- focusShortcut && /* @__PURE__ */ jsx4("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground text-xs", children: [
1622
- /* @__PURE__ */ jsx4("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
1623
- /* @__PURE__ */ jsx4("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
1624
- /* @__PURE__ */ jsx4("span", { className: "ml-1", children: "to focus" })
1741
+ focusShortcut && /* @__PURE__ */ jsx6("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs5("span", { className: "text-muted-foreground text-xs", children: [
1742
+ /* @__PURE__ */ jsx6("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
1743
+ /* @__PURE__ */ jsx6("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
1744
+ /* @__PURE__ */ jsx6("span", { className: "ml-1", children: "to focus" })
1625
1745
  ] }) })
1626
1746
  ] });
1627
1747
  }
1628
1748
 
1629
1749
  // src/chat/artifact-agent-dock.tsx
1630
- import * as React5 from "react";
1750
+ import * as React7 from "react";
1631
1751
  import { Sparkles as Sparkles2, Send, Square as Square2, X as X2 } from "lucide-react";
1632
1752
  import { ChatMessage } from "@tangle-network/ui/chat";
1633
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1753
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1634
1754
  function defaultScopeLabel(scope) {
1635
1755
  if (scope.label) return scope.label;
1636
1756
  switch (scope.kind) {
@@ -1676,23 +1796,23 @@ function ArtifactAgentDock({
1676
1796
  onError,
1677
1797
  className
1678
1798
  }) {
1679
- const [threadId, setThreadId] = React5.useState(null);
1680
- const [messages, setMessages] = React5.useState([]);
1681
- const [composer, setComposer] = React5.useState(defaultPrompt ?? "");
1682
- const [streamContent, setStreamContent] = React5.useState("");
1683
- const [sending, setSending] = React5.useState(false);
1684
- const [loading, setLoading] = React5.useState(false);
1685
- const abortRef = React5.useRef(null);
1686
- const scrollRef = React5.useRef(null);
1687
- const scopeTokenRef = React5.useRef(0);
1688
- const reportError = React5.useCallback(
1799
+ const [threadId, setThreadId] = React7.useState(null);
1800
+ const [messages, setMessages] = React7.useState([]);
1801
+ const [composer, setComposer] = React7.useState(defaultPrompt ?? "");
1802
+ const [streamContent, setStreamContent] = React7.useState("");
1803
+ const [sending, setSending] = React7.useState(false);
1804
+ const [loading, setLoading] = React7.useState(false);
1805
+ const abortRef = React7.useRef(null);
1806
+ const scrollRef = React7.useRef(null);
1807
+ const scopeTokenRef = React7.useRef(0);
1808
+ const reportError = React7.useCallback(
1689
1809
  (message) => {
1690
1810
  if (onError) onError(message);
1691
1811
  else if (typeof console !== "undefined") console.error("[ArtifactAgentDock]", message);
1692
1812
  },
1693
1813
  [onError]
1694
1814
  );
1695
- React5.useEffect(() => {
1815
+ React7.useEffect(() => {
1696
1816
  if (!open) return;
1697
1817
  scopeTokenRef.current += 1;
1698
1818
  abortRef.current?.abort();
@@ -1728,11 +1848,11 @@ function ArtifactAgentDock({
1728
1848
  cancelled = true;
1729
1849
  };
1730
1850
  }, [open, scope.kind, scope.key, transport, reportError]);
1731
- React5.useEffect(() => {
1851
+ React7.useEffect(() => {
1732
1852
  if (!scrollRef.current) return;
1733
1853
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1734
1854
  }, [messages.length, streamContent]);
1735
- const send = React5.useCallback(async () => {
1855
+ const send = React7.useCallback(async () => {
1736
1856
  const text = composer.trim();
1737
1857
  if (!text || !threadId || sending) return;
1738
1858
  const token = scopeTokenRef.current;
@@ -1782,7 +1902,7 @@ function ArtifactAgentDock({
1782
1902
  if (abortRef.current === controller) abortRef.current = null;
1783
1903
  }
1784
1904
  }, [composer, threadId, sending, transport, reportError]);
1785
- const cancel = React5.useCallback(() => {
1905
+ const cancel = React7.useCallback(() => {
1786
1906
  if (threadId && transport.cancel) {
1787
1907
  try {
1788
1908
  void Promise.resolve(transport.cancel({ threadId })).catch(() => {
@@ -1796,7 +1916,7 @@ function ArtifactAgentDock({
1796
1916
  }, [threadId, transport]);
1797
1917
  if (!open) return null;
1798
1918
  const heading = defaultScopeLabel(scope);
1799
- return /* @__PURE__ */ jsxs5(
1919
+ return /* @__PURE__ */ jsxs6(
1800
1920
  "aside",
1801
1921
  {
1802
1922
  className: cn(
@@ -1805,38 +1925,38 @@ function ArtifactAgentDock({
1805
1925
  ),
1806
1926
  "aria-label": `Agent chat about ${heading}`,
1807
1927
  children: [
1808
- /* @__PURE__ */ jsxs5("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
1809
- /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-2", children: [
1810
- /* @__PURE__ */ jsx5(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1811
- /* @__PURE__ */ jsxs5("div", { className: "min-w-0", children: [
1812
- /* @__PURE__ */ jsx5("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
1813
- /* @__PURE__ */ jsx5("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
1928
+ /* @__PURE__ */ jsxs6("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
1929
+ /* @__PURE__ */ jsxs6("div", { className: "flex min-w-0 items-center gap-2", children: [
1930
+ /* @__PURE__ */ jsx7(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
1931
+ /* @__PURE__ */ jsxs6("div", { className: "min-w-0", children: [
1932
+ /* @__PURE__ */ jsx7("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
1933
+ /* @__PURE__ */ jsx7("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
1814
1934
  ] })
1815
1935
  ] }),
1816
- /* @__PURE__ */ jsx5(
1936
+ /* @__PURE__ */ jsx7(
1817
1937
  "button",
1818
1938
  {
1819
1939
  type: "button",
1820
1940
  onClick: () => onOpenChange(false),
1821
1941
  "aria-label": "Close artifact chat",
1822
1942
  className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
1823
- children: /* @__PURE__ */ jsx5(X2, { className: "h-4 w-4" })
1943
+ children: /* @__PURE__ */ jsx7(X2, { className: "h-4 w-4" })
1824
1944
  }
1825
1945
  )
1826
1946
  ] }),
1827
- /* @__PURE__ */ jsx5("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs5("div", { className: "px-3 py-12 text-center", children: [
1828
- /* @__PURE__ */ jsx5(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
1829
- /* @__PURE__ */ jsxs5("p", { className: "text-sm text-foreground", children: [
1947
+ /* @__PURE__ */ jsx7("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx7("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs6("div", { className: "px-3 py-12 text-center", children: [
1948
+ /* @__PURE__ */ jsx7(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
1949
+ /* @__PURE__ */ jsxs6("p", { className: "text-sm text-foreground", children: [
1830
1950
  "Ask the agent about this ",
1831
1951
  scope.kind === "vault-file" ? "document" : scope.kind,
1832
1952
  "."
1833
1953
  ] }),
1834
- /* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
1835
- ] }) : /* @__PURE__ */ jsxs5("div", { className: "space-y-3", children: [
1836
- messages.map((m) => /* @__PURE__ */ jsx5(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
1837
- streamContent && /* @__PURE__ */ jsx5(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
1954
+ /* @__PURE__ */ jsx7("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
1955
+ ] }) : /* @__PURE__ */ jsxs6("div", { className: "space-y-3", children: [
1956
+ messages.map((m) => /* @__PURE__ */ jsx7(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
1957
+ streamContent && /* @__PURE__ */ jsx7(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
1838
1958
  ] }) }),
1839
- /* @__PURE__ */ jsx5(
1959
+ /* @__PURE__ */ jsx7(
1840
1960
  "form",
1841
1961
  {
1842
1962
  className: "border-t border-[var(--md3-outline-variant)] bg-surface-container-low p-3",
@@ -1844,8 +1964,8 @@ function ArtifactAgentDock({
1844
1964
  e.preventDefault();
1845
1965
  void send();
1846
1966
  },
1847
- children: /* @__PURE__ */ jsxs5("div", { className: "flex items-end gap-2", children: [
1848
- /* @__PURE__ */ jsx5(
1967
+ children: /* @__PURE__ */ jsxs6("div", { className: "flex items-end gap-2", children: [
1968
+ /* @__PURE__ */ jsx7(
1849
1969
  "textarea",
1850
1970
  {
1851
1971
  value: composer,
@@ -1862,23 +1982,23 @@ function ArtifactAgentDock({
1862
1982
  className: "min-h-[44px] flex-1 resize-none rounded-[20px] border border-[var(--md3-outline-variant)] bg-surface-dim px-3.5 py-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus:outline-none focus:border-[var(--border-accent)] disabled:opacity-50"
1863
1983
  }
1864
1984
  ),
1865
- sending ? /* @__PURE__ */ jsx5(
1985
+ sending ? /* @__PURE__ */ jsx7(
1866
1986
  "button",
1867
1987
  {
1868
1988
  type: "button",
1869
1989
  onClick: cancel,
1870
1990
  "aria-label": "Cancel",
1871
1991
  className: "inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
1872
- children: /* @__PURE__ */ jsx5(Square2, { className: "h-4 w-4" })
1992
+ children: /* @__PURE__ */ jsx7(Square2, { className: "h-4 w-4" })
1873
1993
  }
1874
- ) : /* @__PURE__ */ jsx5(
1994
+ ) : /* @__PURE__ */ jsx7(
1875
1995
  "button",
1876
1996
  {
1877
1997
  type: "submit",
1878
1998
  disabled: !composer.trim() || !threadId,
1879
1999
  "aria-label": "Send",
1880
2000
  className: "inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
1881
- children: /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" })
2001
+ children: /* @__PURE__ */ jsx7(Send, { className: "h-4 w-4" })
1882
2002
  }
1883
2003
  )
1884
2004
  ] })