@tangle-network/agent-app 0.45.66 → 0.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/assistant/index.js +3 -3
  2. package/dist/chat-react/index.js +2 -2
  3. package/dist/chunk-2RTYQU4W.js +441 -0
  4. package/dist/chunk-2RTYQU4W.js.map +1 -0
  5. package/dist/{chunk-23J72UUA.js → chunk-DINGA2MO.js} +94 -560
  6. package/dist/chunk-DINGA2MO.js.map +1 -0
  7. package/dist/{chunk-AWM4H5XR.js → chunk-MBKNKAN2.js} +6 -4
  8. package/dist/{chunk-AWM4H5XR.js.map → chunk-MBKNKAN2.js.map} +1 -1
  9. package/dist/chunk-YDOQE47G.js +518 -0
  10. package/dist/chunk-YDOQE47G.js.map +1 -0
  11. package/dist/studio/generation.d.ts +14 -23
  12. package/dist/studio/index.d.ts +1 -0
  13. package/dist/studio/index.js +27 -15
  14. package/dist/studio/model-options.d.ts +61 -0
  15. package/dist/studio-react/composer-option-controls.d.ts +140 -0
  16. package/dist/studio-react/generation-notice.d.ts +18 -0
  17. package/dist/studio-react/index.d.ts +5 -9
  18. package/dist/studio-react/index.js +912 -630
  19. package/dist/studio-react/index.js.map +1 -1
  20. package/dist/studio-react/studio-composer.d.ts +34 -0
  21. package/dist/studio-react/studio.css +51 -0
  22. package/dist/theme/tokens.css +18 -0
  23. package/dist/web-react/index.js +11 -11
  24. package/package.json +1 -1
  25. package/dist/chunk-23J72UUA.js.map +0 -1
  26. package/dist/chunk-E2CWFUUP.js +0 -250
  27. package/dist/chunk-E2CWFUUP.js.map +0 -1
  28. package/dist/chunk-MLG6XKPV.js +0 -44
  29. package/dist/chunk-MLG6XKPV.js.map +0 -1
  30. package/dist/studio-react/avatar-composer.d.ts +0 -8
  31. package/dist/studio-react/composer-hero.d.ts +0 -10
  32. package/dist/studio-react/composer-shell.d.ts +0 -18
  33. package/dist/studio-react/image-composer.d.ts +0 -8
  34. package/dist/studio-react/speech-composer.d.ts +0 -4
  35. package/dist/studio-react/transcription-composer.d.ts +0 -12
  36. package/dist/studio-react/video-composer.d.ts +0 -10
@@ -61,6 +61,56 @@
61
61
  }
62
62
  .animate-row-in { animation: studio-row-in 0.25s ease-out both; }
63
63
 
64
+ /* ── Studio composer: the option band ──
65
+ The control row's middle section scrolls horizontally and must not show a
66
+ scrollbar (it sits 34px tall inside a chat card, where a bar eats a third of
67
+ the row) — neither `scrollbar-width` nor the WebKit pseudo-element has a
68
+ Tailwind utility. The edge fade is a mask whose DEPTH is the actual hidden
69
+ width capped at 32px, written per side by the component's scroll handler, so
70
+ a band with 4px hidden fades 4px and never lies about how much is off-screen.
71
+ `padding: 5px; margin: -5px` keeps a focused pill's ring inside the scroll
72
+ box without moving the row; `scroll-margin-inline` scrolls a focused pill
73
+ fully into view rather than flush against the clipped edge. */
74
+ .studio-band {
75
+ --fade-start: 0px;
76
+ --fade-end: 0px;
77
+ padding: 5px;
78
+ margin: -5px;
79
+ scrollbar-width: none;
80
+ -ms-overflow-style: none;
81
+ }
82
+ .studio-band::-webkit-scrollbar { display: none; }
83
+ .studio-band > * { scroll-margin-inline: 10px; }
84
+ .studio-band:not([data-overflow='none']) {
85
+ -webkit-mask-image: linear-gradient(
86
+ to right,
87
+ transparent 0,
88
+ #000 var(--fade-start),
89
+ #000 calc(100% - var(--fade-end)),
90
+ transparent 100%
91
+ );
92
+ mask-image: linear-gradient(
93
+ to right,
94
+ transparent 0,
95
+ #000 var(--fade-start),
96
+ #000 calc(100% - var(--fade-end)),
97
+ transparent 100%
98
+ );
99
+ }
100
+
101
+ /* The per-type pills rise in when the media type changes — the control set
102
+ itself changed, and a swap with no motion reads as a repaint glitch. */
103
+ @keyframes studio-pill-in {
104
+ from { opacity: 0; transform: translateY(3px); }
105
+ to { opacity: 1; transform: translateY(0); }
106
+ }
107
+ .studio-pill-in { animation: studio-pill-in 190ms cubic-bezier(0.2, 0.7, 0.3, 1) both; }
108
+
109
+ /* The one gradient in the system. A Tailwind arbitrary `bg-[var(--…)]` compiles
110
+ to `background-color`, which drops a gradient silently, so the token is read
111
+ here instead. */
112
+ .studio-send { background-image: var(--studio-send-gradient); }
113
+
64
114
  @media (prefers-reduced-motion: reduce) {
65
115
  .studio-sheet-content[data-state='open'],
66
116
  .studio-sheet-content[data-state='closed'],
@@ -69,5 +119,6 @@
69
119
  .studio-bloom,
70
120
  .studio-shimmer,
71
121
  .shimmer,
122
+ .studio-pill-in,
72
123
  .animate-row-in { animation: none; }
73
124
  }
@@ -334,6 +334,17 @@
334
334
  the token, not at every component. */
335
335
  --shadow-raised: 0 1px 2px hsl(var(--foreground) / 0.05), 0 12px 28px hsl(var(--foreground) / 0.07);
336
336
  --shadow-overlay: 0 2px 6px hsl(var(--foreground) / 0.06), 0 16px 40px hsl(var(--foreground) / 0.1);
337
+
338
+ /* ── Studio composer ─────────────────────────────────────────────────────
339
+ The generate button's fill is the one gradient in the system, so it is a
340
+ token rather than two hex literals inside a component — it is a LITERAL
341
+ pair and therefore restated in the dark scope. The notice chip's surface
342
+ is derived (`color-mix` over `--primary`/`--card`/`--border`), so it
343
+ re-themes through the cascade; dark still restates it because the same
344
+ percentages over a near-black card read as no tint at all. */
345
+ --studio-send-gradient: linear-gradient(135deg, #4f46e5, #5b4ed4);
346
+ --studio-notice-bg: color-mix(in srgb, hsl(var(--primary)) 8%, hsl(var(--card)));
347
+ --studio-notice-border: color-mix(in srgb, hsl(var(--primary)) 28%, hsl(var(--border)));
337
348
  }
338
349
 
339
350
  /**
@@ -423,6 +434,13 @@
423
434
  /* Doubled alphas against the near-black surfaces — @see :root. */
424
435
  --shadow-raised: 0 1px 2px hsl(var(--foreground) / 0.14), 0 12px 28px hsl(var(--foreground) / 0.22);
425
436
  --shadow-overlay: 0 2px 6px hsl(var(--foreground) / 0.12), 0 16px 40px hsl(var(--foreground) / 0.2);
437
+
438
+ /* Studio composer — @see :root. The gradient lightens (an indigo mixed for a
439
+ white page goes muddy on a dark one) and the notice chip's mixes are
440
+ stronger, because 8% of a tint over a near-black card is not a tint. */
441
+ --studio-send-gradient: linear-gradient(135deg, #4c3fc5, #7c6cf6);
442
+ --studio-notice-bg: color-mix(in srgb, hsl(var(--primary)) 15%, hsl(var(--card)));
443
+ --studio-notice-border: color-mix(in srgb, hsl(var(--primary)) 40%, hsl(var(--border)));
426
444
  }
427
445
 
428
446
  /**
@@ -137,7 +137,7 @@ import {
137
137
  withoutRecordGridCreated,
138
138
  withoutRecordGridRemoved,
139
139
  withoutRecordGridUpdate
140
- } from "../chunk-AWM4H5XR.js";
140
+ } from "../chunk-MBKNKAN2.js";
141
141
  import "../chunk-FBVLEGEG.js";
142
142
  import {
143
143
  EvidenceLineageTable,
@@ -156,30 +156,30 @@ import {
156
156
  } from "../chunk-GEYACSFW.js";
157
157
  import {
158
158
  AgentSessionControls,
159
+ HarnessGlyph,
160
+ acceptRejectionReason,
161
+ filterAcceptedFiles,
162
+ isAcceptedFileType,
163
+ renamePastedImages,
164
+ useComposerAttachments
165
+ } from "../chunk-DINGA2MO.js";
166
+ import {
159
167
  DEFAULT_EFFORT_LEVELS,
160
168
  EFFORT_METER_SEGMENTS,
161
169
  EffortMeter,
162
170
  EffortPicker,
163
- HarnessGlyph,
164
171
  ModelPicker,
165
172
  OVERLAY_SHADOW,
166
173
  POPOVER_SURFACE_ATTR,
167
174
  PopoverSurface,
168
- acceptRejectionReason,
175
+ ProviderLogo,
169
176
  effortLevelLabel,
170
177
  effortLevelsFromIds,
171
178
  effortMeterFill,
172
- filterAcceptedFiles,
173
- isAcceptedFileType,
174
179
  reconcileEffortLevels,
175
- renamePastedImages,
176
- useComposerAttachments,
177
180
  usePending,
178
181
  usePopover
179
- } from "../chunk-23J72UUA.js";
180
- import {
181
- ProviderLogo
182
- } from "../chunk-MLG6XKPV.js";
182
+ } from "../chunk-YDOQE47G.js";
183
183
  import {
184
184
  tabTerminalConnectionId,
185
185
  useSandboxTerminalConnection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-app",
3
- "version": "0.45.66",
3
+ "version": "0.46.1",
4
4
  "packageManager": "pnpm@11.17.0",
5
5
  "description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
6
6
  "keywords": [
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/web-react/controls.tsx","../src/web-react/composer-file-accept.ts","../src/web-react/use-composer-attachments.ts","../src/web-react/harness-glyphs.tsx","../src/web-react/agent-session-controls.tsx"],"sourcesContent":["/**\n * Shared chat-shell control primitives — the LEAF that both the web-react barrel\n * (`./index`) and the composer children (`./agent-session-controls`,\n * `./seat-paywall`) import directly, so neither child has to reach back through\n * the barrel (which would re-create an import cycle). The barrel re-exports the\n * public names (`usePopover`, `usePending`, `ModelPicker`, `EffortPicker`, …)\n * unchanged, so the published export surface is identical.\n *\n * Styling contract matches the rest of `web-react`: Tailwind classes against the\n * shared design tokens; the glyphs are inline SVGs, no icon-library dependency.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type RefObject,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { ProviderLogo } from './provider-logo'\nimport type { CatalogModel } from '../runtime/model-catalog'\n\n// ── shared glyphs (no icon-library dependency) ────────────────────────────\n\nexport function ChevronDown({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nfunction SearchGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n )\n}\n\nfunction SparkleGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <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\" />\n </svg>\n )\n}\n\n/** lucide `brain` (v1.27) inlined — `/web-react` ships no icon-library\n * dependency, so the thinking glyph follows the same pattern as the rest of\n * this set. */\nexport function BrainGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 18V5\" />\n <path d=\"M15 13a4.17 4.17 0 0 1-3-4 4.17 4.17 0 0 1-3 4\" />\n <path d=\"M17.598 6.5A3 3 0 1 0 12 5a3 3 0 1 0-5.598 1.5\" />\n <path d=\"M17.997 5.125a4 4 0 0 1 2.526 5.77\" />\n <path d=\"M18 18a4 4 0 0 0 2-7.464\" />\n <path d=\"M19.967 17.483A4 4 0 1 1 12 18a4 4 0 1 1-7.967-.517\" />\n <path d=\"M6 18a4 4 0 0 1-2-7.464\" />\n <path d=\"M6.003 5.125a4 4 0 0 0-2.526 5.77\" />\n </svg>\n )\n}\n\n/** lucide `check` — the selected-row mark in the picker menus. */\nexport function CheckGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )\n}\n\n/**\n * Keyboard + pointer model for a trigger-and-popover pair, dependency-free.\n * Outside-mousedown and Escape both close; Escape also returns focus to the\n * trigger so keyboard users aren't dropped at the top of the document. The\n * returned `triggerProps` carry the ARIA contract (`aria-haspopup`/\n * `aria-expanded`); spread them onto the trigger button.\n *\n * `panelRef` belongs to the popover panel and MUST be wired when the panel is\n * rendered through {@link PopoverSurface}: a portaled panel is not inside\n * `containerRef`, so a container-only outside test reads every click on the\n * menu's own rows as an outside click and closes before the row's handler runs.\n */\nexport function usePopover(open: boolean, setOpen: (open: boolean) => void) {\n const containerRef = useRef<HTMLDivElement>(null)\n const triggerRef = useRef<HTMLButtonElement>(null)\n const panelRef = useRef<HTMLDivElement>(null)\n\n useEffect(() => {\n if (!open) return\n function onMouseDown(e: MouseEvent) {\n const target = e.target as Node\n if (containerRef.current?.contains(target)) return\n const panel = panelRef.current\n if (panel?.contains(target)) return\n // A popover opened FROM this one portals as a SIBLING at body level, so\n // `contains` cannot see it and a click on its rows would read as outside.\n // Surfaces stamp their ancestor chain, so descendancy survives the portal.\n const ownPath = panel?.getAttribute(POPOVER_SURFACE_ATTR)\n const hitPath =\n target instanceof Element\n ? target.closest(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR)\n : null\n if (ownPath && hitPath && (hitPath === ownPath || hitPath.startsWith(`${ownPath}${POPOVER_PATH_SEPARATOR}`))) return\n setOpen(false)\n }\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n setOpen(false)\n triggerRef.current?.focus()\n }\n }\n document.addEventListener('mousedown', onMouseDown)\n document.addEventListener('keydown', onKeyDown)\n return () => {\n document.removeEventListener('mousedown', onMouseDown)\n document.removeEventListener('keydown', onKeyDown)\n }\n }, [open, setOpen])\n\n return {\n containerRef,\n triggerRef,\n panelRef,\n triggerProps: {\n ref: triggerRef,\n 'aria-haspopup': true as const,\n 'aria-expanded': open,\n },\n }\n}\n\n// ── PopoverSurface ────────────────────────────────────────────────────────\n\n/** Distance between the trigger and the panel it opens. */\nconst POPOVER_GAP = 8\n/** Minimum distance the panel keeps from every viewport edge. */\nconst POPOVER_VIEWPORT_MARGIN = 16\n/** Floor for the computed max-height, so a cramped side still shows rows\n * rather than collapsing to a sliver the user cannot read. */\nconst POPOVER_MIN_HEIGHT = 120\n\n/**\n * Marks the portaled panel in the DOM. Products and audits (see\n * `playground/scripts/popover-hit-test.mjs`) select on this rather than on a\n * Tailwind class, which is presentation and free to change.\n *\n * Its VALUE is the surface's ancestor path (`outer/inner`), which is what\n * restores \"is this click inside my popover\" after the portal flattens two\n * nested panels into two siblings of `<body>`.\n */\nexport const POPOVER_SURFACE_ATTR = 'data-agent-app-popover'\nconst POPOVER_PATH_SEPARATOR = '/'\n\n/**\n * `PopoverSurface` is now mounted (closed) inside every server-rendered\n * composer, so its hooks run on the server once per composer per request.\n * React 18 logs \"useLayoutEffect does nothing on the server\" for that; React 19\n * does not (measured silent on react-dom 19.2.8). The peer floor is `react >=18`,\n * so bind the synchronous hook only where there is a DOM and the effect hook\n * elsewhere — placement never runs on the server either way.\n */\nconst useBrowserLayoutEffect = typeof document !== 'undefined' ? useLayoutEffect : useEffect\n\nexport interface PopoverSurfaceProps {\n open: boolean\n /** The trigger the panel anchors to — `usePopover`'s `triggerRef`. */\n triggerRef: RefObject<HTMLElement | null>\n /** `usePopover`'s `panelRef`; also what the outside-click test consults. */\n panelRef: RefObject<HTMLDivElement | null>\n /** Presentation classes. Placement and elevation are owned here — a caller\n * must not pass `absolute`/`fixed`/`top-*`/`bottom-*`/`z-*`. */\n className?: string\n role?: string\n id?: string\n /** Make the panel at least as wide as its trigger. A portaled panel has no\n * `w-full` to inherit — the trigger is no longer its offset parent — so a\n * menu that used to stretch to a full-width trigger declares it here. */\n matchTriggerWidth?: boolean\n children: ReactNode\n}\n\n/**\n * The floating panel every canonical picker opens.\n *\n * It renders through a PORTAL to `document.body` and anchors itself to the\n * trigger in viewport coordinates, because an in-place `absolute` panel's\n * visibility is decided by markup this package does not own. Measured in\n * production: the shipped chat composer docks these controls inside a\n * horizontally scrolling rail (`overflow-x-auto`), and a scroll container\n * clips every positioned descendant whose containing block sits inside it —\n * so a correct 420x457 menu with correct coordinates painted zero pixels and\n * could not be clicked. An ancestor `transform`/`filter`/`contain` would trap\n * it the same way through the stacking context instead of the clip. Leaving\n * the DOM subtree is the only placement a host cannot re-break.\n *\n * Placement prefers ABOVE the trigger (these controls dock at the bottom of a\n * composer), flips below when there is more room there, clamps horizontally\n * into the viewport, and caps its own height to the space on the chosen side.\n * The panel is `visibility: hidden` for the measure pass so it never paints at\n * the pre-placement origin.\n */\nexport function PopoverSurface({\n open,\n triggerRef,\n panelRef,\n className,\n role,\n id,\n matchTriggerWidth,\n children,\n}: PopoverSurfaceProps) {\n const surfaceId = useId()\n const [style, setStyle] = useState<CSSProperties>(() => ({\n position: 'fixed',\n top: 0,\n left: 0,\n visibility: 'hidden',\n }))\n\n const place = useCallback(() => {\n const trigger = triggerRef.current\n const panel = panelRef.current\n if (!trigger || !panel) return\n const anchor = trigger.getBoundingClientRect()\n const viewportWidth = window.innerWidth\n const viewportHeight = window.innerHeight\n\n // `scrollHeight` is the panel's CONTENT height, so it does not feed back\n // on the `maxHeight` this function just applied — reading `offsetHeight`\n // here would measure the previous pass's clamp and ratchet the panel\n // smaller on every scroll event.\n const contentHeight = panel.scrollHeight\n const panelWidth = panel.offsetWidth\n\n const roomAbove = anchor.top - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN\n const roomBelow = viewportHeight - anchor.bottom - POPOVER_GAP - POPOVER_VIEWPORT_MARGIN\n const above = contentHeight <= roomAbove || roomAbove >= roomBelow\n const maxHeight = Math.max(POPOVER_MIN_HEIGHT, above ? roomAbove : roomBelow)\n const height = Math.min(contentHeight, maxHeight)\n\n const top = above ? Math.max(POPOVER_VIEWPORT_MARGIN, anchor.top - POPOVER_GAP - height) : anchor.bottom + POPOVER_GAP\n const rightBound = Math.max(POPOVER_VIEWPORT_MARGIN, viewportWidth - panelWidth - POPOVER_VIEWPORT_MARGIN)\n const left = Math.min(Math.max(POPOVER_VIEWPORT_MARGIN, anchor.left), rightBound)\n\n setStyle({\n position: 'fixed',\n top,\n left,\n maxHeight,\n visibility: 'visible',\n ...(matchTriggerWidth ? { minWidth: anchor.width } : {}),\n })\n }, [matchTriggerWidth, panelRef, triggerRef])\n\n // Layout effect: placement is resolved before the browser paints, so the\n // panel is never seen at the origin it mounts at.\n useBrowserLayoutEffect(() => {\n if (!open) {\n setStyle({ position: 'fixed', top: 0, left: 0, visibility: 'hidden' })\n return\n }\n place()\n }, [open, place])\n\n useEffect(() => {\n if (!open) return\n const onViewportChange = () => place()\n // `capture` so the rail's OWN scroll re-anchors the panel — a scroll inside\n // an ancestor does not bubble to window.\n window.addEventListener('scroll', onViewportChange, true)\n window.addEventListener('resize', onViewportChange)\n return () => {\n window.removeEventListener('scroll', onViewportChange, true)\n window.removeEventListener('resize', onViewportChange)\n }\n }, [open, place])\n\n if (!open || typeof document === 'undefined') return null\n\n const ownerPath = triggerRef.current?.closest?.(`[${POPOVER_SURFACE_ATTR}]`)?.getAttribute(POPOVER_SURFACE_ATTR)\n const path = ownerPath ? `${ownerPath}${POPOVER_PATH_SEPARATOR}${surfaceId}` : surfaceId\n\n return createPortal(\n <div\n ref={panelRef}\n id={id}\n role={role}\n style={style}\n {...{ [POPOVER_SURFACE_ATTR]: path }}\n className={`z-[1000] ${className ?? ''}`}\n >\n {children}\n </div>,\n document.body,\n )\n}\n\n/**\n * Focus treatment for a row inside a popover panel.\n *\n * The ring itself now comes from the `:focus-visible` floor in tokens.css, so\n * this no longer restates a width or a colour. What it still has to say is\n * WHERE the ring is drawn: a popover option is a full-width row inside a panel\n * that clips its own corners (`overflow-hidden rounded-xl`), and an outward\n * ring on the first or last row is clipped away by that panel. Pulling the\n * offset negative draws the same ring just inside the row instead.\n */\nexport const POPOVER_OPTION_FOCUS = 'focus-visible:[outline-offset:-2px]'\n\n/**\n * The one overlay elevation for floating surfaces — picker menus, popovers,\n * drawers, modals. Reads the theme's `--shadow-overlay` token, so every overlay\n * lifts with the same shadow and re-themes from one source. The floating\n * composer uses the quieter `shadow-raised` rung instead.\n *\n * Written as an arbitrary value rather than the preset's `shadow-overlay`\n * utility, because that utility only exists where the preset is part of the\n * Tailwind build. A host that gets its tokens through a precompiled bundle —\n * `@tangle-network/sandbox-ui` ships brand's tokens inlined, with no `@theme`\n * block surviving the compile — receives `--shadow-overlay` as a plain custom\n * property, from which no `shadow-overlay` utility can be generated, and these\n * surfaces render flat. The arbitrary form emits from the class alone and works\n * either way.\n */\nexport const OVERLAY_SHADOW = 'shadow-[var(--shadow-overlay)]'\n\n/**\n * Root geometry for a picker — the box that holds the trigger — in one place\n * because every picker in this family has to agree on it.\n *\n * Shrink-wrapping (`inline-flex`) is the default: these controls dock on a\n * composer row where an expanding pill shoves its neighbours around. A STACKED\n * panel wants the opposite — the compact `AgentSessionControls` gear popover\n * lays its controls out in a column, and a shrink-wrapped root there makes a\n * trigger's own `w-full` a no-op, since it fills a box the trigger itself\n * sized. That is what left Agent backend short of the panel edge and Thinking\n * narrower still.\n *\n * The panel is portaled ({@link PopoverSurface}), so widening the root widens\n * the TRIGGER only. A menu that should follow it declares `matchTriggerWidth`.\n */\nexport function pickerRootClass(fullWidth: boolean): string {\n return `relative ${fullWidth ? 'flex w-full' : 'inline-flex'}`\n}\n\n/**\n * Guard an async action against double-submit. `run` ignores re-entrant calls\n * while a promise is in flight and flips `pending` so the caller can disable\n * the control — the fix for double-charge / double-approve on a slow network.\n * Settles (success or throw) before clearing, and no-ops state updates after\n * unmount.\n */\nexport function usePending(): { pending: boolean; run: (action: () => void | Promise<void>) => void } {\n const [pending, setPending] = useState(false)\n const inFlight = useRef(false)\n const mounted = useRef(true)\n useEffect(() => {\n mounted.current = true\n return () => {\n mounted.current = false\n }\n }, [])\n const run = (action: () => void | Promise<void>) => {\n if (inFlight.current) return\n let result: void | Promise<void>\n try {\n result = action()\n } catch {\n return\n }\n if (!(result instanceof Promise)) return\n inFlight.current = true\n setPending(true)\n void result.finally(() => {\n inFlight.current = false\n if (mounted.current) setPending(false)\n })\n }\n return { pending, run }\n}\n\n// ── ModelPicker ───────────────────────────────────────────────────────────\n\nexport interface ModelPickerProps {\n value: string\n onChange: (id: string) => void\n /** Catalogue models — from `GET`ing the app's catalogue route (see\n * `runtime/model-catalog`), plus any product-specific entries appended. */\n models: CatalogModel[]\n loading?: boolean\n /** Render a provider logo/badge; default is a generic sparkle. */\n renderProviderBadge?: (provider: string) => ReactNode\n /** Section label for `featured` models. */\n recommendedLabel?: string\n /** Pin a labeled section to the TOP of the list (above Recommended) for the\n * models a product wants surfaced first — e.g. a tuner app's own fine-tuned\n * models (`{ label: 'Your Fine-Tuned Models', match: (m) => m.provider === 'tuner' }`).\n * Matching models are shown only in this section, not duplicated below. */\n priorityGroup?: {\n label: string\n match: (model: CatalogModel) => boolean\n }\n}\n\nfunction formatPrice(p?: string): string | undefined {\n if (!p) return undefined\n const n = Number(p)\n if (isNaN(n) || n === 0) return undefined\n const perM = n * 1_000_000\n return perM >= 1 ? `$${perM.toFixed(0)}/M` : `$${perM.toFixed(2)}/M`\n}\n\nfunction formatContext(len?: number): string | undefined {\n if (!len) return undefined\n if (len >= 1_000_000) return `${(len / 1_000_000).toFixed(1)}M ctx`\n if (len >= 1_000) return `${Math.round(len / 1_000)}K ctx`\n return `${len} ctx`\n}\n\nfunction SectionHeader({ children }: { children: ReactNode }) {\n return (\n <div className=\"px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {children}\n </div>\n )\n}\n\nfunction ModelRow({\n model,\n selected,\n onSelect,\n renderProviderBadge,\n}: {\n model: CatalogModel\n selected: boolean\n onSelect: () => void\n renderProviderBadge?: (provider: string) => ReactNode\n}) {\n const price = formatPrice(model.pricing?.prompt)\n const ctx = formatContext(model.contextLength)\n return (\n <button\n type=\"button\"\n onClick={onSelect}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n selected ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n {renderProviderBadge ? renderProviderBadge(model.provider) : <ProviderLogo provider={model.provider} size={16} />}\n <span className=\"truncate\">{model.name}</span>\n {!model.supportsTools && (\n <span className=\"shrink-0 rounded bg-secondary px-1.5 py-0.5 text-xs font-medium text-muted-foreground\">\n no tools\n </span>\n )}\n <span className=\"ml-auto flex shrink-0 items-center gap-2 text-xs text-muted-foreground\">\n {ctx && <span>{ctx}</span>}\n {price && <span>{price}</span>}\n </span>\n </button>\n )\n}\n\n/**\n * Searchable model picker pill + popover: a featured/recommended section\n * first, then per-provider groups in catalogue order (the server already\n * sorts providers by tier).\n *\n * This is the CANONICAL ecosystem model picker (see \"UI chrome ownership\n * (picker canon)\" in AGENTS.md). sandbox-ui's `dashboard/ModelPicker` is\n * legacy — deprecated, frozen, removed at sandbox-ui's next major; new code\n * belongs here.\n */\nexport function ModelPicker({ value, onChange, models, loading, renderProviderBadge, recommendedLabel = 'Recommended', priorityGroup }: ModelPickerProps) {\n const [open, setOpen] = useState(false)\n const [query, setQuery] = useState('')\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const inputRef = useRef<HTMLInputElement>(null)\n const panelId = useId()\n\n useEffect(() => {\n if (open) inputRef.current?.focus()\n }, [open])\n\n const selected = models.find((m) => m.id === value)\n\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase()\n if (!q) return null\n return models.filter(\n (m) =>\n m.id.toLowerCase().includes(q) ||\n m.name.toLowerCase().includes(q) ||\n (m.description?.toLowerCase() ?? '').includes(q) ||\n m.provider.toLowerCase().includes(q),\n )\n }, [models, query])\n\n const sections = useMemo(() => {\n const isPriority = priorityGroup ? (m: CatalogModel) => priorityGroup.match(m) : () => false\n const priority = priorityGroup ? models.filter(isPriority) : []\n const recommended = models.filter((m) => m.featured && !isPriority(m))\n const byProvider: Array<{ provider: string; items: CatalogModel[] }> = []\n for (const m of models) {\n if (m.featured || isPriority(m)) continue\n const last = byProvider[byProvider.length - 1]\n if (last && last.provider === m.provider) last.items.push(m)\n else byProvider.push({ provider: m.provider, items: [m] })\n }\n return { priority, recommended, byProvider }\n }, [models, priorityGroup])\n\n const select = (id: string) => {\n onChange(id)\n setOpen(false)\n setQuery('')\n }\n\n return (\n <div ref={containerRef} className=\"relative inline-flex\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n onClick={() => setOpen(!open)}\n className=\"inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent\"\n >\n {selected ? (renderProviderBadge ? renderProviderBadge(selected.provider) : <ProviderLogo provider={selected.provider} size={16} />) : <SparkleGlyph className=\"h-3.5 w-3.5 text-muted-foreground\" />}\n <span className=\"max-w-[160px] truncate\">{selected?.name ?? value}</span>\n <ChevronDown className=\"h-3.5 w-3.5 text-muted-foreground\" />\n </button>\n\n <PopoverSurface\n open={open}\n id={panelId}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`flex w-[420px] max-w-[calc(100vw-2rem)] flex-col overflow-hidden rounded-xl border border-card-edge bg-popover ${OVERLAY_SHADOW}`}\n >\n <div className=\"shrink-0 border-b border-border px-3 py-2\">\n <div className=\"flex items-center gap-2 rounded-lg border border-strong bg-background px-3 py-2\">\n <SearchGlyph className=\"h-3.5 w-3.5 text-muted-foreground\" />\n <input\n ref={inputRef}\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search models...\"\n className=\"flex-1 bg-transparent text-sm placeholder:text-muted-foreground\"\n />\n </div>\n </div>\n {/* `min-h-0` is what lets the list absorb the surface's computed\n max-height on a short viewport instead of overflowing the panel. */}\n <div className=\"max-h-[400px] min-h-0 overflow-y-auto p-1 pb-2\">\n {loading && <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">Loading models...</div>}\n {!loading && filtered && (\n <>\n {filtered.length === 0 && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No models match your search</div>\n )}\n {filtered.map((m) => (\n <ModelRow key={m.id} model={m} selected={m.id === value} onSelect={() => select(m.id)} renderProviderBadge={renderProviderBadge} />\n ))}\n </>\n )}\n {!loading && !filtered && models.length === 0 && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No models available</div>\n )}\n {!loading && !filtered && models.length > 0 && (\n <>\n {priorityGroup && sections.priority.length > 0 && (\n <>\n <SectionHeader>{priorityGroup.label}</SectionHeader>\n {sections.priority.map((m) => (\n <ModelRow key={m.id} model={m} selected={m.id === value} onSelect={() => select(m.id)} renderProviderBadge={renderProviderBadge} />\n ))}\n </>\n )}\n {sections.recommended.length > 0 && (\n <>\n <SectionHeader>{recommendedLabel}</SectionHeader>\n {sections.recommended.map((m) => (\n <ModelRow key={m.id} model={m} selected={m.id === value} onSelect={() => select(m.id)} renderProviderBadge={renderProviderBadge} />\n ))}\n </>\n )}\n {sections.byProvider.map((g) => (\n <div key={g.provider}>\n <SectionHeader>{g.provider}</SectionHeader>\n {g.items.map((m) => (\n <ModelRow key={m.id} model={m} selected={m.id === value} onSelect={() => select(m.id)} renderProviderBadge={renderProviderBadge} />\n ))}\n </div>\n ))}\n </>\n )}\n </div>\n </PopoverSurface>\n </div>\n )\n}\n\n// ── EffortPicker ──────────────────────────────────────────────────────────\n\n/** One reasoning-budget level: the engine `id` is unchanged (the value the\n * product sends to the loop); only the user-facing `label` is renamed to the\n * plainer \"how hard should it think\" vocabulary from docs/product-surfaces.md.\n * `low`→Quick, `medium`→Standard, `high`→Extended. The mapping is overridable\n * via `EffortPickerProps.levels`, so a product can relabel without losing the\n * ids the runtime expects. */\nexport interface EffortLevel {\n id: string\n label: string\n}\n\n/**\n * The engine ids this package NAMES, and the word each one reads as.\n *\n * Deliberately WIDER than {@link DEFAULT_EFFORT_LEVELS}: the default list is\n * what a picker OFFERS when a product declares nothing, while this is the\n * vocabulary — the labels a named id keeps wherever it appears. The two are\n * different questions, and folding them together is what made a real engine\n * rung read as its own id: `xhigh` and `ultracode` are rungs claude-code\n * applies above `high`, so a product declaring them got \"Xhigh\" while every\n * neighbouring rung had a plain English word. Naming them here is what stops\n * each product inventing its own — the drift `effortLevelsFromIds` exists to\n * prevent. They stay OUT of the offered default because most backends do not\n * apply them, and offering a rung the backend ignores is the defect\n * `effortLevels` was added to close.\n */\nconst KNOWN_EFFORT_LABELS = {\n off: 'Off',\n low: 'Quick',\n medium: 'Standard',\n high: 'Extended',\n xhigh: 'Extra',\n ultracode: 'Ultra',\n} as const\n\nexport const DEFAULT_EFFORT_LEVELS: readonly EffortLevel[] = [\n { id: 'off', label: KNOWN_EFFORT_LABELS.off },\n { id: 'low', label: KNOWN_EFFORT_LABELS.low },\n { id: 'medium', label: KNOWN_EFFORT_LABELS.medium },\n { id: 'high', label: KNOWN_EFFORT_LABELS.high },\n]\n\n/**\n * The user-facing label for an engine level id: the canonical vocabulary when\n * the id is one this package names, otherwise the id itself made readable\n * (`auto` -> \"Auto\", `ultra-code` -> \"Ultra code\"). Never invents a depth word,\n * so an id nobody declared a label for still reads as ITSELF and never as some\n * other level.\n */\nexport function effortLevelLabel(id: string): string {\n const known = (KNOWN_EFFORT_LABELS as Record<string, string | undefined>)[id]\n if (known) return known\n const words = id.replace(/[-_]+/g, ' ').trim()\n return words ? words.charAt(0).toUpperCase() + words.slice(1) : id\n}\n\n/**\n * Build a levels list from the engine ids a backend applies — the shape the\n * removed `ComposerAgentControls` took as `reasoning.available`, so a product\n * migrating that list has one call to make instead of a hand-written label map\n * per product (which is how \"Quick\" and \"Low\" drift apart across surfaces).\n */\nexport function effortLevelsFromIds(ids: readonly string[]): readonly EffortLevel[] {\n return ids.map((id) => ({ id, label: effortLevelLabel(id) }))\n}\n\n/**\n * The list {@link EffortPicker} RENDERS for `value` — the declared levels, plus\n * `value` itself when the declaration omits it.\n *\n * A picker cannot honestly resolve a selected value it was not given, and the\n * failure it used to take instead — fall back to the middle entry — renders the\n * session's real depth as a DIFFERENT level's name. That is exactly the defect\n * `levels` was added to prevent: the legacy adapter's `available` list excluded\n * the `auto` sentinel because the old picker injected it, so a product mapping\n * that list straight across ships a session running on `auto` labelled\n * \"Extended\". Admitting the value is not offering a new choice — it is\n * reporting the state the session is already in, and it disappears from the\n * list as soon as the user picks a declared level.\n *\n * A blank value is not admitted (there is no honest label for it); the picker\n * renders no selection instead.\n */\nexport function reconcileEffortLevels(\n value: string,\n levels: readonly EffortLevel[] = DEFAULT_EFFORT_LEVELS,\n): readonly EffortLevel[] {\n if (!value || levels.some((l) => l.id === value)) return levels\n return [{ id: value, label: effortLevelLabel(value) }, ...levels]\n}\n\n// ── effort strength meter ─────────────────────────────────────────────────\n\n/** Segments the meter draws — fixed geometry so the ladder stays tabular\n * across levels (and across the trigger and its menu rows). */\nexport const EFFORT_METER_SEGMENTS = 4\n\n/** Filled-segment opacity ladder: translucent on the left, heavy on the\n * right — the ramp carries strength even at a glance, the count carries it\n * exactly. Unfilled segments sit at a fixed ghost opacity. */\nconst EFFORT_METER_FILL_OPACITY = [0.25, 0.5, 0.75, 1] as const\nconst EFFORT_METER_GHOST_OPACITY = 0.15\n\n/** Level ids that mean \"no reasoning\" — the meter renders all-ghost. */\nconst OFF_LEVEL_IDS: ReadonlySet<string> = new Set(['off', 'none'])\n\n/**\n * Ids that name a POLICY rather than a depth: `auto` means \"let the harness and\n * model decide\", so it has no position on a strength ladder.\n *\n * Without this it lands wherever the declaration puts it and draws a filled\n * meter — measured on a product that offers `auto` first: every harness read\n * `Auto` at a FULL four bars, including `cli-base`, which has no agent to think\n * at all. A meter is a claim about how hard the run will think, and a sentinel\n * cannot make that claim.\n */\nconst UNPLACEABLE_LEVEL_IDS: ReadonlySet<string> = new Set(['auto'])\n\n/**\n * Filled-segment count for a level: 0 for off/none (or an id the levels list\n * does not carry); otherwise the level's position among the non-off choices\n * scaled onto the meter, so the ladder reads low < medium < high and the top\n * level fills the whole scale. The canonical four levels land 0 / 1 / 2 / 4.\n */\nexport function effortMeterFill(\n levelId: string,\n levels: readonly EffortLevel[] = DEFAULT_EFFORT_LEVELS,\n): number {\n if (OFF_LEVEL_IDS.has(levelId) || UNPLACEABLE_LEVEL_IDS.has(levelId)) return 0\n const active = levels.filter((l) => !OFF_LEVEL_IDS.has(l.id) && !UNPLACEABLE_LEVEL_IDS.has(l.id))\n const index = active.findIndex((l) => l.id === levelId)\n if (index < 0 || active.length === 0) return 0\n return Math.max(1, Math.floor(((index + 1) * EFFORT_METER_SEGMENTS) / active.length))\n}\n\n/**\n * The thinking-strength meter: four 12px bars, filled count = level, filled\n * opacity ramping 25→100% left to right (unfilled at a faint ghost). Purely\n * decorative — the level name is always rendered as text beside it, so the\n * meter is `aria-hidden` and adds no second accessible name.\n */\nexport function EffortMeter({ fill, className }: { fill: number; className?: string }) {\n return (\n <span aria-hidden className={`inline-flex items-center gap-[2px] ${className ?? ''}`}>\n {Array.from({ length: EFFORT_METER_SEGMENTS }, (_, i) => (\n <span\n key={i}\n className=\"h-3 w-[3px] rounded-full bg-current\"\n style={{ opacity: i < fill ? EFFORT_METER_FILL_OPACITY[i] : EFFORT_METER_GHOST_OPACITY }}\n />\n ))}\n </span>\n )\n}\n\nexport interface EffortPickerProps {\n value: string\n onChange: (id: string) => void\n /** Selectable levels (engine id + user-facing label). Defaults to the plain\n * \"Thinking\" vocabulary; override to relabel without changing the ids the\n * runtime receives.\n *\n * A list that omits the current `value` is not a rendering error the picker\n * papers over: `value` is reconciled INTO the rendered list under its own\n * name (see {@link reconcileEffortLevels}), because a control must report\n * the depth the session is running at and never some other list entry. */\n levels?: readonly EffortLevel[]\n /** Prefix shown before the active level on the pill — the \"what is this\"\n * context the bare value lacked. Default \"Thinking\". Pass '' to hide it. */\n label?: string\n /** Fill the container instead of shrink-wrapping — opt-in, default `false`;\n * see {@link pickerRootClass} for when and why. */\n fullWidth?: boolean\n}\n\n/** Thinking-budget selector pill, styled to match {@link ModelPicker}. Show\n * it only when the selected model `supportsReasoning`. \"Thinking\" is the\n * plain-English name for what was internally called \"effort\".\n *\n * The CANONICAL ecosystem effort picker — sandbox-ui's reasoning menu (inside\n * its `chat/AgentSessionControls`) is legacy and frozen. */\nexport function EffortPicker({ value, onChange, levels = DEFAULT_EFFORT_LEVELS, label = 'Thinking', fullWidth = false }: EffortPickerProps) {\n const [open, setOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n const rendered = reconcileEffortLevels(value, levels)\n // The strength ladder is computed over the DECLARED levels only, so admitting\n // the selected value cannot shift where the declared ones sit on the meter.\n // A level the declaration does not carry has no position on that ladder, so\n // it renders with NO meter — an all-ghost meter is what `off` looks like, and\n // \"we cannot place this\" is not \"no thinking\".\n const isDeclared = (id: string) => levels.some((l) => l.id === id) && !UNPLACEABLE_LEVEL_IDS.has(id)\n const selected = rendered.find((l) => l.id === value)\n\n return (\n <div ref={containerRef} className={pickerRootClass(fullWidth)}>\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n onClick={() => setOpen(!open)}\n title={label ? `${label} — how hard the agent reasons before answering` : 'Reasoning effort'}\n className={`inline-flex min-h-[36px] shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent ${fullWidth ? 'w-full' : ''}`}\n >\n <BrainGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n {/* Full width gives the label the slack, so the meter and chevron park\n on the trailing edge and the glyph stays against the text. */}\n <span className={fullWidth ? 'flex-1 truncate text-left' : undefined}>\n {label ? <span className=\"text-muted-foreground\">{label}: </span> : null}\n {selected ? selected.label : '—'}\n </span>\n {selected && isDeclared(selected.id) && (\n <EffortMeter fill={effortMeterFill(selected.id, levels)} className=\"shrink-0 text-foreground\" />\n )}\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n </button>\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n // A portaled panel has no `w-full` to inherit, so a full-width trigger\n // hands its measured width to the panel instead.\n matchTriggerWidth={fullWidth}\n className={`w-44 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {rendered.map((l) => (\n <button\n key={l.id}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={l.id === value}\n onClick={() => {\n onChange(l.id)\n setOpen(false)\n }}\n className={`flex min-h-[40px] w-full items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n l.id === value ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n <BrainGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"truncate\">{l.label}</span>\n {isDeclared(l.id) && (\n <EffortMeter fill={effortMeterFill(l.id, levels)} className=\"ml-auto text-foreground\" />\n )}\n {l.id === value && (\n <CheckGlyph className={`${isDeclared(l.id) ? '' : 'ml-auto '}h-3.5 w-3.5 shrink-0 text-primary`} />\n )}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n","/**\n * The composer's file-ingress filter, and the clipboard rename that goes with\n * it.\n *\n * A file reaches a composer by three routes — the picker dialog, a drag-and-drop,\n * and a clipboard paste — and only the picker gets a native `accept` filter (one\n * the user can defeat with \"All Files\"). Every route therefore funnels through\n * {@link filterAcceptedFiles}, so a type the picker will not offer cannot arrive\n * by another route instead.\n *\n * One route can still reach a different verdict, and it does so deliberately.\n * Paste is the only route that RENAMES, and {@link renamePastedImages} names a\n * file after the type it declares. So a clipboard bitmap called `image.png` that\n * declares `image/heic` is judged as `.heic` on paste, while the picker and a\n * drop judge the name they were handed and admit it under `accept=\".png\"`. The\n * filter is the same on all three; what differs is the name it is given, and\n * paste is stricter precisely because a rename that kept the contradicting name\n * would let the composer manufacture its own way past the filter.\n *\n * ONE accept matcher serves the package: `ChatComposer` gates its ingress on it\n * and `useComposerAttachments` gates `addFiles` on it. A second implementation\n * of the `accept` grammar is how the two ends of the same staging path start\n * disagreeing about what a file is.\n *\n * Pure data in, pure data out — nothing here throws, logs, or touches the DOM,\n * so a caller decides how a rejection is surfaced. Import-free beyond the\n * browser's own `File`, which keeps it usable from `/web-react`'s client bundle.\n */\n\n/** A file the `accept` list refused, with the reason to show for it. */\nexport interface ComposerFileRejection {\n file: File\n reason: string\n}\n\n/**\n * Checks one file against a comma-separated `accept` list, using the grammar of\n * the native `<input accept>` attribute: extensions (`.png`), exact MIME types\n * (`image/png`), and MIME wildcards (`image/*`). An absent or empty list accepts\n * everything, which is what an unset `accept` prop means.\n */\nexport function isAcceptedFileType(file: File, accept?: string): boolean {\n if (!accept || accept.trim().length === 0) return true\n\n const patterns = accept\n .split(',')\n .map((pattern) => pattern.trim())\n .filter((pattern) => pattern.length > 0)\n if (patterns.length === 0) return true\n\n const name = file.name.toLowerCase()\n const type = (file.type || '').toLowerCase()\n\n return patterns.some((pattern) => {\n const lower = pattern.toLowerCase()\n if (lower.startsWith('.')) return name.endsWith(lower)\n if (lower.endsWith('/*')) {\n // Keep the trailing \"/\" so `image/*` cannot match `imagex/png`, and\n // require a real subtype after it: `image/` and `image//png` are\n // malformed types, and a `File.type` can carry either.\n const prefix = lower.slice(0, -1)\n if (!type.startsWith(prefix)) return false\n const subtype = type.slice(prefix.length)\n return subtype.length > 0 && !subtype.includes('/')\n }\n return type === lower\n })\n}\n\n/** The reason an `accept` list refused a file. One wording for every ingress\n * route, so the same file reads the same whether it was picked or dropped. */\nexport function acceptRejectionReason(file: File, accept: string): string {\n return `\"${file.name}\" is not an accepted file type (${accept}).`\n}\n\n/**\n * Splits a batch into what the `accept` list admits and what it refuses. Size\n * and count limits are NOT applied here: they belong to the staging queue, which\n * knows what is already staged (`useComposerAttachments`), while this runs at the\n * composer's edge where that is unknown.\n */\nexport function filterAcceptedFiles(\n files: File[] | FileList,\n accept?: string,\n): { accepted: File[]; rejected: ComposerFileRejection[] } {\n const list = Array.isArray(files) ? files : Array.from(files)\n const accepted: File[] = []\n const rejected: ComposerFileRejection[] = []\n for (const file of list) {\n if (isAcceptedFileType(file, accept)) accepted.push(file)\n else rejected.push({ file, reason: acceptRejectionReason(file, accept ?? '') })\n }\n return { accepted, rejected }\n}\n\n/**\n * Extensions that truthfully name each image type, most canonical first. A type\n * with more than one is the reason this is a LIST and not a single name: `.jpeg`\n * and `.jpg` are the same claim, so a rename that swapped one for the other\n * would make a paste fail an `accept=\".jpeg\"` list that the very same file\n * passes through the picker.\n *\n * A type absent from here derives its single extension from the MIME subtype.\n */\nconst IMAGE_EXTENSIONS_BY_MIME: Record<string, readonly string[]> = {\n 'image/png': ['png'],\n 'image/jpeg': ['jpg', 'jpeg', 'jpe'],\n 'image/jpg': ['jpg', 'jpeg'],\n 'image/gif': ['gif'],\n 'image/webp': ['webp'],\n 'image/bmp': ['bmp'],\n 'image/svg+xml': ['svg'],\n 'image/tiff': ['tiff', 'tif'],\n 'image/x-icon': ['ico'],\n 'image/vnd.microsoft.icon': ['ico'],\n}\n\n/**\n * Matches EXACTLY the generic names browsers give a clipboard bitmap: an empty\n * name, `image`, or `image.<ext>`. Those are the names that collide across\n * pastes. A name that is only an extension (`.png`) is a real, if unusual,\n * filename and is left alone.\n */\nfunction isGenericImageName(name: string): boolean {\n return /^(?:image(?:\\.[a-z0-9]+)?)?$/i.test(name.trim())\n}\n\n/**\n * The extension a renamed clipboard image should carry, or null when none can\n * be derived truthfully.\n *\n * It must never name a format the bytes are not. A rename that hands an\n * `accept=\".png\"` list a file satisfying it by its new name alone turns the\n * rename into a way around the very gate it is filtered by.\n *\n * So the DECLARED TYPE decides which extensions are truthful, and the filename\n * may only pick among those. A clipboard file can carry a name whose extension\n * contradicts its type — a bitmap named `image.png` that is really `image/heic`\n * — and letting the name win there is exactly the hole this order closes. But\n * when the name's extension is one the type itself allows, it is kept: an\n * `image.jpeg` of type `image/jpeg` stays `.jpeg`, because rewriting it to the\n * canonical `.jpg` would make the paste fail an `accept=\".jpeg\"` list that the\n * same file passes through the picker.\n *\n * A subtype only stands in for an extension when it is already shaped like one.\n * `image/heic` is; `image/vnd.microsoft.icon` is not, and squeezing the\n * punctuation out of it would yield `vndmicrosofticon` — a name no accept list\n * will ever match, so a perfectly good `.ico` would be admitted through the\n * picker and refused on paste. An unrecognised compound type therefore names\n * nothing, and the filename is consulted instead.\n *\n * The filename is consulted on its own ONLY when the type names nothing usable\n * — either no subtype at all (`image/`) or a compound one. That is not an\n * exception to the rule above: the only named files this function ever sees are\n * `image.<ext>` (see {@link isGenericImageName}), so the fallback can only ever\n * preserve an extension the name already carried — it cannot manufacture one,\n * and an accept list therefore decides the same way it would have without the\n * rename. When nothing yields an extension, the caller skips the rename rather\n * than guessing.\n */\nfunction imageExtension(file: File): string | null {\n const type = file.type.toLowerCase()\n const fromName = /\\.([a-z0-9]+)$/i.exec(file.name)?.[1]?.toLowerCase()\n\n const mapped = IMAGE_EXTENSIONS_BY_MIME[type]\n const subtype = type.startsWith('image/') ? type.slice('image/'.length) : ''\n const bare = subtype.split('+')[0] ?? ''\n const truthful = mapped ?? (/^[a-z0-9]+$/.test(bare) ? [bare] : [])\n\n if (truthful.length === 0) return fromName ?? null\n if (fromName !== undefined && truthful.includes(fromName)) return fromName\n return truthful[0] ?? null\n}\n\n/** The `pasted-image-<n>` numbers these names already occupy. */\nfunction takenPastedImageIndexes(names: Iterable<string>): Set<number> {\n const taken = new Set<number>()\n for (const name of names) {\n const digits = /^pasted-image-(\\d+)(?:\\.[a-z0-9]+)?$/i.exec(name.trim())?.[1]\n if (digits === undefined) continue\n const parsed = Number.parseInt(digits, 10)\n if (Number.isSafeInteger(parsed)) taken.add(parsed)\n }\n return taken\n}\n\n/**\n * The lowest number above `after` that no name has claimed.\n *\n * Searching upward from the caller's own count — rather than from the highest\n * number any staged name happens to carry — is what keeps the usual case cheap:\n * staged numbers are avoided, never followed, so the walk advances at most once\n * per number already claimed.\n *\n * Termination does not rest on that, though, because `after` can be pushed near\n * the safe-integer ceiling and `+ 1` stops moving there — a walk that only\n * tested membership would spin on one value forever. The upward walk therefore\n * ends the moment a candidate leaves the safe range, and the search restarts\n * from the bottom, where a free number is guaranteed: `taken` is finite, so one\n * of the first `taken.size + 1` positive integers is always free. Every number\n * this returns is a safe integer, which is also what keeps an unsafe staged\n * name — invisible to {@link takenPastedImageIndexes} — impossible to collide\n * with.\n */\nfunction nextFreeIndex(taken: Set<number>, after: number): number {\n const start = Number.isSafeInteger(after) && after >= 0 ? after + 1 : 1\n for (let candidate = start; Number.isSafeInteger(candidate); candidate += 1) {\n if (!taken.has(candidate)) return candidate\n }\n for (let candidate = 1; ; candidate += 1) {\n if (!taken.has(candidate)) return candidate\n }\n}\n\n/**\n * Gives every generically-named clipboard image a distinct\n * `pasted-image-<n>.<ext>` name. Two pastes of the same bitmap otherwise arrive\n * as `image.png` twice, and a staging queue that keys on the name treats the\n * second as a duplicate of the first.\n *\n * A number is never reused. The search avoids every `pasted-image-<n>` already\n * present in `stagedNames` (the queue the host still holds, which outlives this\n * composer's own count) and in the batch itself (one paste can carry a file\n * already named that way beside a raw bitmap), so a collision is not reachable\n * rather than merely unlikely. `startIndex` is the caller's running count, and\n * `nextIndex` is the count to hand the next paste.\n *\n * Files that already carry a real name pass through untouched, so a copied\n * `report.pdf` keeps being `report.pdf`. So does an image whose extension\n * cannot be derived from what it declares — a renamed file must never claim a\n * format it is not. Only the name changes: the bytes, the type and the\n * modification time travel with it, so downstream fingerprinting still sees\n * the file the user pasted.\n */\nexport function renamePastedImages(\n files: File[],\n startIndex: number,\n stagedNames: Iterable<string> = [],\n): { files: File[]; nextIndex: number } {\n const taken = takenPastedImageIndexes([...stagedNames, ...files.map((file) => file.name)])\n // Whatever the caller's count is — a negative, a fraction, NaN, or a value at\n // the ceiling — `nextFreeIndex` normalises it and always answers with a safe\n // integer, so nothing here has to pre-screen it.\n let nextIndex = startIndex\n const renamed = files.map((file) => {\n const typed = file.type.startsWith('image/')\n // A clipboard file can arrive with no MIME type at all, and one named\n // `image.png` still collides across pastes exactly as a typed one does.\n // Its extension then comes from its own name, which cannot manufacture a\n // claim it did not already carry.\n if (!isGenericImageName(file.name) || (!typed && file.type !== '')) return file\n const extension = imageExtension(file)\n if (extension === null) return file\n nextIndex = nextFreeIndex(taken, nextIndex)\n taken.add(nextIndex)\n return new File([file], `pasted-image-${nextIndex}.${extension}`, {\n type: file.type,\n lastModified: file.lastModified,\n })\n })\n return { files: renamed, nextIndex }\n}\n","/**\n * `useComposerAttachments` — the composer's staged-upload lifecycle: validate\n * selected/dropped/pasted files against the shared limits (the SAME\n * `sniffBinary`/`checkAttachmentType`/size-cap vocabulary the store-backed\n * upload route enforces server-side, `../chat-routes/attachment-validation`\n * + `../chat-routes/binary-sniff`), upload each accepted file with one POST\n * request per file (so a single failure never poisons the batch), and track\n * every file's status so a host composer can render chips and gate sending.\n *\n * Ported from gtm-agent's `src/components/composer-attachments.tsx`\n * (gtm#584/#592/#593 hardened the sniff gate and batch semantics this leans\n * on), de-gtm-ified:\n * - the hardcoded `/api/vault/upload?workspaceId=` URL becomes\n * `uploadUrl`/`buildUploadRequest` (the latter wins — it hands back both\n * the URL and a `RequestInit` override, e.g. an auth header);\n * - `sonner` toasts become `onReject` (client pre-validation, never hits the\n * network) and `onError` (a request that reached the server and failed);\n * - the `accept`-string gate comes from `./composer-file-accept`, the one\n * matcher `ChatComposer` also funnels its picker/drop/paste ingress\n * through, so both ends of the staging path admit the same files;\n * - the response is expected to be `{ files: ChatAttachmentInput[] }` (full\n * server-authoritative descriptors — size/mediaType/kind — not gtm's\n * `{path, name}`), so `references` is a verbatim pass-through with no\n * client recompute;\n * - `workspaceId`'s truthiness gate becomes `enabled` (default `true`).\n *\n * Import-free beyond React + the browser-safe `/chat-routes` validation core:\n * this module ships through `/web-react` into client bundles\n * (`tests/browser-safe-subpaths.test.ts` walks the graph), so nothing here\n * may reach a Node builtin, `sandbox-ui`, or an engine package.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { ChatAttachmentInput, ChatAttachmentKind } from './chat-stream'\nimport type { ComposerFile } from './chat-composer'\nimport { acceptRejectionReason, isAcceptedFileType } from './composer-file-accept'\nimport {\n ATTACHMENT_ACCEPT,\n ATTACHMENT_MAX_COUNT,\n MAX_ATTACHMENT_TOTAL_BYTES,\n MAX_BINARY_ATTACHMENT_BYTES,\n MAX_TEXT_ATTACHMENT_BYTES,\n attachmentSizeErrorMessage,\n attachmentTotalSizeErrorMessage,\n checkAttachmentType,\n sanitizeAttachmentFileName,\n} from '../chat-routes/attachment-validation'\nimport { sniffBinary } from '../chat-routes/binary-sniff'\n\nexport { ATTACHMENT_ACCEPT } from '../chat-routes/attachment-validation'\n\n/** One staged file and its upload lifecycle. `file` is retained so a failed\n * upload can be retried without re-selecting; `previewUrl` is an object URL\n * for image thumbnails and must be revoked when the entry leaves the queue.\n * `reference` is the server's authoritative descriptor once the upload\n * lands — stored verbatim, never recomputed client-side. */\ninterface StagedAttachment {\n id: string\n file: File\n name: string\n size: number\n status: 'pending' | 'uploading' | 'ready' | 'error'\n reference?: ChatAttachmentInput\n previewUrl?: string\n errorMessage?: string\n}\n\n/** Define options for configuring file upload behavior and handling in a composer component */\nexport interface UseComposerAttachmentsOptions {\n /** Simple upload target: every file POSTs here. Ignored when\n * `buildUploadRequest` is provided. */\n uploadUrl?: string\n /** Full request-building seam (auth headers, per-file routing, …) — wins\n * over `uploadUrl` when both are set. */\n buildUploadRequest?: (args: { file: File; name: string; form: FormData }) => {\n url: string\n init?: Omit<RequestInit, 'body' | 'signal'>\n }\n /** Client pre-validation rejections — a file that never reaches the\n * network (bad type, over a size cap, over count, disallowed kind). */\n onReject?: (reason: string, file?: File) => void\n /** A file that reached the upload endpoint and failed (HTTP error,\n * transport error, malformed response). */\n onError?: (reason: string) => void\n limits?: {\n maxCount?: number\n maxBinaryBytes?: number\n maxTextBytes?: number\n maxTotalBytes?: number\n }\n /** Attachment kinds accepted, checked against the sniffed content's\n * mime. Default: both (`['image', 'file']` — i.e. no restriction). */\n allowedKinds?: ChatAttachmentKind[]\n /** `<input accept>`-style gate for the file picker/drop/paste path.\n * Default {@link ATTACHMENT_ACCEPT}. */\n accept?: string\n /** When `false`, `addFiles` rejects every call via `onReject` (and\n * `blockReason` explains why) instead of staging anything — the\n * replacement for gtm's `workspaceId`-truthiness gate (e.g. no workspace\n * loaded yet). Default `true`. */\n enabled?: boolean\n}\n\n/** Provide staged file chips, ready attachments, and methods to add, retry, or drop composer files */\nexport interface UseComposerAttachmentsResult {\n /** Chip models for `ChatComposer`'s `pendingFiles` prop, one per staged\n * file — `kind` is always `'file'` (agent-app's `ComposerFile.kind`\n * discriminates file-vs-folder chips, not attachment media type). */\n composerFiles: ComposerFile[]\n /** Ready-to-send attachment descriptors — only files whose upload\n * succeeded, straight from the server's response (no recompute). Feed\n * this into `ChatTurnRequestPayload.attachments`. */\n references: ChatAttachmentInput[]\n /** Validate + stage + upload the given files, one request per file. */\n addFiles: (files: File[] | FileList) => Promise<void>\n /** Re-upload a failed entry using its retained `File`. */\n retry: (id: string) => void\n /** Drop one staged entry, aborting its upload and revoking its preview. */\n removeAttachment: (id: string) => void\n /** Forget every staged entry (call after a successful send). */\n clear: () => void\n /** True while any file is still pending or uploading. */\n hasPending: boolean\n /** True while any file failed to upload. */\n hasError: boolean\n /** Why a send is blocked, or `null` when the queue is clean. */\n blockReason: string | null\n}\n\nfunction newId(): string {\n const cryptoObject = globalThis.crypto\n if (typeof cryptoObject?.randomUUID === 'function') return cryptoObject.randomUUID()\n return `att-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/** Suffix a name (`report.pdf` → `report-2.pdf`) until it's unused. The\n * server writes to a name-derived store path, so identical names would\n * overwrite. The suffix stays inside the store-path charset (see\n * `sanitizeAttachmentFileName`). Ported byte-for-byte from gtm's\n * `dedupeName`. */\nfunction dedupeName(name: string, taken: Set<string>): string {\n if (!taken.has(name)) return name\n const dot = name.lastIndexOf('.')\n const base = dot > 0 ? name.slice(0, dot) : name\n const ext = dot > 0 ? name.slice(dot) : ''\n let n = 2\n let candidate = `${base}-${n}${ext}`\n while (taken.has(candidate)) {\n n += 1\n candidate = `${base}-${n}${ext}`\n }\n return candidate\n}\n\n/** `image/*` → `'image'`, everything else → `'file'`. Deliberately\n * reimplemented here (not imported from `../chat-store/parts`, which pulls\n * the drizzle-adjacent `/chat-store` barrel): this module must stay reachable\n * from a browser bundle with only the `/chat-routes` validation core as a\n * dependency. */\nfunction kindForMime(mime: string): ChatAttachmentKind {\n return mime.startsWith('image/') ? 'image' : 'file'\n}\n\n/** Pull a human-readable message out of the upload endpoint's error body.\n * Ported from gtm's `parseUploadError`: handles both `{ error: string }`\n * (size/count/access errors) and `{ error: { message } }` (the\n * `createAttachmentUploadRoute` envelope, `{error:{code,message,path?}}`). */\nasync function parseUploadError(res: Response): Promise<string> {\n const detail = await res.json().catch(() => null)\n if (detail && typeof detail === 'object' && 'error' in detail) {\n const error = (detail as { error: unknown }).error\n if (typeof error === 'string' && error) return error\n if (error && typeof error === 'object' && 'message' in error) {\n const message = (error as { message: unknown }).message\n if (typeof message === 'string' && message) return message\n }\n }\n return `Upload failed (${res.status})`\n}\n\n/** Shown when neither `uploadUrl` nor `buildUploadRequest` is configured\n * while `enabled` — a product wiring bug, not a user-facing rejection, so it\n * lands each affected entry in `error` (with `onError`) rather than blocking\n * `addFiles` outright via `onReject`: the files still stage and can be\n * retried once the product fixes its config, instead of silently vanishing. */\nconst NO_UPLOAD_TARGET_MESSAGE = 'No upload destination configured (pass uploadUrl or buildUploadRequest)'\n\n/**\n * Owns the composer's attachment lifecycle: validate selected/dropped/pasted\n * files against the shared limits, upload each accepted file to the\n * product's store (one request per file), and track every file's status so\n * the composer can render chips and gate sending.\n *\n * Failures surface loud — a rejected file calls `onReject` and is never\n * uploaded; a failed upload calls `onError` and leaves an error chip the user\n * can retry or remove. `references` only ever contains files whose upload the\n * server actually confirmed.\n */\nexport function useComposerAttachments(\n options: UseComposerAttachmentsOptions,\n): UseComposerAttachmentsResult {\n // Latest options, read from inside stable callbacks — avoids re-creating\n // `addFiles`/`upload` (and therefore breaking referential stability for\n // effects a host might hang off them) every time a caller passes a fresh\n // options object literal.\n const optionsRef = useRef(options)\n optionsRef.current = options\n\n const [staged, setStagedState] = useState<StagedAttachment[]>([])\n // Mirror of `staged` kept in lockstep so dedupe/aggregate-cap/abort read\n // current values synchronously (setState callbacks alone can't answer\n // \"what's staged right now\" mid-validation).\n const stagedRef = useRef<StagedAttachment[]>([])\n const controllersRef = useRef<Map<string, AbortController>>(new Map())\n\n // Post-unmount calls reduce to a React no-op setState; the refs they touch\n // die with the instance.\n const setStaged = useCallback(\n (updater: StagedAttachment[] | ((prev: StagedAttachment[]) => StagedAttachment[])) => {\n const next =\n typeof updater === 'function'\n ? (updater as (prev: StagedAttachment[]) => StagedAttachment[])(stagedRef.current)\n : updater\n stagedRef.current = next\n setStagedState(next)\n },\n [],\n )\n\n const upload = useCallback(\n async (id: string, file: File, name: string) => {\n const opts = optionsRef.current\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'uploading', errorMessage: undefined } : s)),\n )\n const controller = new AbortController()\n controllersRef.current.set(id, controller)\n const form = new FormData()\n form.append('file', file, name)\n\n const request = opts.buildUploadRequest\n ? opts.buildUploadRequest({ file, name, form })\n : opts.uploadUrl\n ? { url: opts.uploadUrl }\n : null\n\n if (!request) {\n setStaged((prev) =>\n prev.map((s) =>\n s.id === id ? { ...s, status: 'error', errorMessage: NO_UPLOAD_TARGET_MESSAGE } : s,\n ),\n )\n opts.onError?.(NO_UPLOAD_TARGET_MESSAGE)\n controllersRef.current.delete(id)\n return\n }\n\n try {\n const res = await fetch(request.url, {\n method: 'POST',\n credentials: 'same-origin',\n ...request.init,\n body: form,\n signal: controller.signal,\n })\n if (!res.ok) {\n const message = await parseUploadError(res)\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n const data = (await res.json()) as { files?: ChatAttachmentInput[] }\n const uploaded = data.files?.[0]\n if (!uploaded) {\n const message = 'Upload returned no file'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n return\n }\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'ready', reference: uploaded } : s)),\n )\n } catch (err) {\n if ((err as Error).name === 'AbortError') return // silent removal — see removeAttachment/clear\n const message =\n err instanceof Error && err.message ? err.message : 'Upload failed — check your connection'\n setStaged((prev) =>\n prev.map((s) => (s.id === id ? { ...s, status: 'error', errorMessage: message } : s)),\n )\n opts.onError?.(message)\n } finally {\n controllersRef.current.delete(id)\n }\n },\n [setStaged],\n )\n\n const addFiles = useCallback(\n async (files: File[] | FileList) => {\n const opts = optionsRef.current\n const enabled = opts.enabled ?? true\n if (!enabled) {\n opts.onReject?.('Attachments are disabled')\n return\n }\n\n const accept = opts.accept ?? ATTACHMENT_ACCEPT\n const maxCount = opts.limits?.maxCount ?? ATTACHMENT_MAX_COUNT\n const maxBinaryBytes = opts.limits?.maxBinaryBytes ?? MAX_BINARY_ATTACHMENT_BYTES\n const maxTextBytes = opts.limits?.maxTextBytes ?? MAX_TEXT_ATTACHMENT_BYTES\n const maxTotalBytes = opts.limits?.maxTotalBytes ?? MAX_ATTACHMENT_TOTAL_BYTES\n const allowedKinds = opts.allowedKinds ?? (['image', 'file'] as ChatAttachmentKind[])\n\n const list = Array.isArray(files) ? files : Array.from(files)\n\n // Pass 1: accept-list, then count cap — accept first, and the count\n // checked against what is already staged plus what this batch has taken.\n const currentCount = stagedRef.current.length\n const countAccepted: File[] = []\n for (const file of list) {\n if (!isAcceptedFileType(file, accept)) {\n opts.onReject?.(acceptRejectionReason(file, accept), file)\n continue\n }\n if (currentCount + countAccepted.length >= maxCount) {\n opts.onReject?.(`\"${file.name}\" was not added — the ${maxCount}-file limit is already reached.`, file)\n continue\n }\n countAccepted.push(file)\n }\n\n // Pass 2: real content sniff + type gate + per-kind size cap +\n // allowed-kinds gate — the SAME checks the server enforces, so a\n // rejection never differs depending on which side classified the bytes\n // first. Nothing here ever reaches the network.\n const sizeAccepted: File[] = []\n for (const file of countAccepted) {\n const bytes = new Uint8Array(await file.arrayBuffer())\n const sniff = sniffBinary(bytes)\n const typeCheck = checkAttachmentType(file.name, sniff)\n if (!typeCheck.succeeded) {\n opts.onReject?.(typeCheck.message, file)\n continue\n }\n const limit = sniff.binary ? maxBinaryBytes : maxTextBytes\n if (file.size > limit) {\n opts.onReject?.(attachmentSizeErrorMessage(file.name, file.size, limit), file)\n continue\n }\n const mediaType = sniff.mime ?? file.type ?? ''\n const kind = kindForMime(mediaType)\n if (!allowedKinds.includes(kind)) {\n opts.onReject?.(`\"${file.name}\" is a ${kind} attachment, which isn't accepted here`, file)\n continue\n }\n sizeAccepted.push(file)\n }\n\n // Pass 3: running aggregate cap across this batch + everything already\n // staged (any status) — a partial batch can still land.\n const accepted: File[] = []\n let totalBytes = stagedRef.current.reduce((total, s) => total + s.size, 0)\n for (const file of sizeAccepted) {\n const nextTotalBytes = totalBytes + file.size\n if (nextTotalBytes > maxTotalBytes) {\n opts.onReject?.(attachmentTotalSizeErrorMessage(nextTotalBytes, maxTotalBytes), file)\n continue\n }\n accepted.push(file)\n totalBytes = nextTotalBytes\n }\n if (accepted.length === 0) return\n\n // Stage under the name the server will actually store, so the chip and\n // the message's attachment references never diverge.\n const taken = new Set(stagedRef.current.map((s) => s.name))\n const entries: StagedAttachment[] = accepted.map((file) => {\n const name = dedupeName(sanitizeAttachmentFileName(file.name), taken)\n taken.add(name)\n return {\n id: newId(),\n file,\n name,\n size: file.size,\n status: 'pending',\n previewUrl: file.type.startsWith('image/') ? URL.createObjectURL(file) : undefined,\n }\n })\n setStaged((prev) => [...prev, ...entries])\n for (const entry of entries) void upload(entry.id, entry.file, entry.name)\n },\n [setStaged, upload],\n )\n\n const retry = useCallback(\n (id: string) => {\n const entry = stagedRef.current.find((s) => s.id === id)\n if (!entry) return\n void upload(entry.id, entry.file, entry.name)\n },\n [upload],\n )\n\n const removeAttachment = useCallback(\n (id: string) => {\n controllersRef.current.get(id)?.abort()\n controllersRef.current.delete(id)\n const entry = stagedRef.current.find((s) => s.id === id)\n if (entry?.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n setStaged((prev) => prev.filter((s) => s.id !== id))\n },\n [setStaged],\n )\n\n const clear = useCallback(() => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n setStaged([])\n }, [setStaged])\n\n useEffect(\n () => () => {\n for (const controller of controllersRef.current.values()) controller.abort()\n controllersRef.current.clear()\n for (const entry of stagedRef.current) {\n if (entry.previewUrl) URL.revokeObjectURL(entry.previewUrl)\n }\n },\n [],\n )\n\n // `previewUrl` and `errorMessage` travel with the chip model. This hook is\n // the only place that knows either one — it mints the object URL and holds\n // the upload's failure text — so a projection that dropped them left the\n // composer rendering a red chip with no reason on it and no thumbnail for a\n // staged image.\n const composerFiles = useMemo<ComposerFile[]>(\n () =>\n staged.map((s) => ({\n id: s.id,\n name: s.name,\n size: s.size,\n kind: 'file' as const,\n status: s.status,\n previewUrl: s.previewUrl,\n errorMessage: s.errorMessage,\n })),\n [staged],\n )\n\n const references = useMemo<ChatAttachmentInput[]>(\n () =>\n staged\n .filter((s): s is StagedAttachment & { reference: ChatAttachmentInput } => s.status === 'ready' && !!s.reference)\n .map((s) => s.reference),\n [staged],\n )\n\n const hasPending = useMemo(\n () => staged.some((s) => s.status === 'pending' || s.status === 'uploading'),\n [staged],\n )\n const hasError = useMemo(() => staged.some((s) => s.status === 'error'), [staged])\n const enabled = options.enabled ?? true\n const blockReason = !enabled\n ? 'Attachments are disabled'\n : hasPending\n ? 'Attachments are still uploading'\n : hasError\n ? 'Remove failed attachments to send'\n : null\n\n return {\n composerFiles,\n references,\n addFiles,\n retry,\n removeAttachment,\n clear,\n hasPending,\n hasError,\n blockReason,\n }\n}\n","/**\n * Per-harness brand marks for the canonical pickers — the same marks the\n * legacy sandbox-ui harness picker (`dashboard/harness-logo.tsx`) shipped,\n * vendored as inline SVG so `/web-react` stays dependency-free beyond React:\n * sandbox-ui (and its `@lobehub/icons-static-svg` bundle) is an OPTIONAL peer\n * the canonical pickers must not force on a consumer. Geometry is the lobehub\n * single-color artwork, rendered in `currentColor` exactly as the legacy\n * component painted it (a foreground-filled CSS mask), so every mark tracks\n * the theme. Harnesses with no published brand mark get an honest inline\n * lucide glyph — bot / plug / terminal, the same fallbacks the legacy picker\n * used — and an unknown id falls back to the neutral bot. Data-record\n * structure mirrors `./provider-logo`.\n */\n\nimport type { ReactNode } from 'react'\nimport type { Harness } from '../harness'\n\nexport interface HarnessGlyphProps {\n /** Harness to mark. Typed as the canonical union; an out-of-union runtime\n * value still renders — it gets the neutral fallback glyph. */\n harness: Harness\n className?: string\n}\n\n// ── brand marks (single-color lobehub artwork, fill) ──────────────────────\n\nconst BRAND_PATHS: Partial<Record<Harness, readonly string[]>> = {\n opencode: [\n 'M16 6H8v12h8V6zm4 16H4V2h16v20z',\n ],\n 'claude-code': [\n 'M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z',\n ],\n codex: [\n 'M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z',\n ],\n amp: [\n 'M15.087 23.18L12.03 24l-2.097-7.823-5.738 5.738-2.251-2.251 5.718-5.719-7.769-2.082.82-3.057 11.294 3.08 3.08 11.295z',\n 'M19.505 18.762l-3.057.82-2.564-9.573-9.572-2.564.819-3.057 11.295 3.079 3.08 11.295z',\n 'M23.893 14.374l-3.057.82-2.565-9.572L8.7 3.057 9.52 0l11.295 3.08 3.079 11.294z',\n ],\n 'kimi-code': [\n 'M1.052 16.916l9.539 2.552a21.007 21.007 0 00.06 2.033l5.956 1.593a11.997 11.997 0 01-5.586.865l-.18-.016-.044-.004-.084-.009-.094-.01a11.605 11.605 0 01-.157-.02l-.107-.014-.11-.016a11.962 11.962 0 01-.32-.051l-.042-.008-.075-.013-.107-.02-.07-.015-.093-.019-.075-.016-.095-.02-.097-.023-.094-.022-.068-.017-.088-.022-.09-.024-.095-.025-.082-.023-.109-.03-.062-.02-.084-.025-.093-.028-.105-.034-.058-.019-.08-.026-.09-.031-.066-.024a6.293 6.293 0 01-.044-.015l-.068-.025-.101-.037-.057-.022-.08-.03-.087-.035-.088-.035-.079-.032-.095-.04-.063-.028-.063-.027a5.655 5.655 0 01-.041-.018l-.066-.03-.103-.047-.052-.024-.096-.046-.062-.03-.084-.04-.086-.044-.093-.047-.052-.027-.103-.055-.057-.03-.058-.032a6.49 6.49 0 01-.046-.026l-.094-.053-.06-.034-.051-.03-.072-.041-.082-.05-.093-.056-.052-.032-.084-.053-.061-.039-.079-.05-.07-.047-.053-.035a7.785 7.785 0 01-.054-.036l-.044-.03-.044-.03a6.066 6.066 0 01-.04-.028l-.057-.04-.076-.054-.069-.05-.074-.054-.056-.042-.076-.057-.076-.059-.086-.067-.045-.035-.064-.052-.074-.06-.089-.073-.046-.039-.046-.039a7.516 7.516 0 01-.043-.037l-.045-.04-.061-.053-.07-.062-.068-.06-.062-.058-.067-.062-.053-.05-.088-.084a13.28 13.28 0 01-.099-.097l-.029-.028-.041-.042-.069-.07-.05-.051-.05-.053a6.457 6.457 0 01-.168-.179l-.08-.088-.062-.07-.071-.08-.042-.049-.053-.062-.058-.068-.046-.056a7.175 7.175 0 01-.027-.033l-.045-.055-.066-.082-.041-.052-.05-.064-.02-.025a11.99 11.99 0 01-1.44-2.402zm-1.02-5.794l11.353 3.037a20.468 20.468 0 00-.469 2.011l10.817 2.894a12.076 12.076 0 01-1.845 2.005L.657 15.923l-.016-.046-.035-.104a11.965 11.965 0 01-.05-.153l-.007-.023a11.896 11.896 0 01-.207-.741l-.03-.126-.018-.08-.021-.097-.018-.081-.018-.09-.017-.084-.018-.094c-.026-.141-.05-.283-.071-.426l-.017-.118-.011-.083-.013-.102a12.01 12.01 0 01-.019-.161l-.005-.047a12.12 12.12 0 01-.034-2.145zm1.593-5.15l11.948 3.196c-.368.605-.705 1.231-1.01 1.875l11.295 3.022c-.142.82-.368 1.612-.668 2.365l-11.55-3.09L.124 10.26l.015-.1.008-.049.01-.067.015-.087.018-.098c.026-.148.056-.295.088-.442l.028-.124.02-.085.024-.097c.022-.09.045-.18.07-.268l.028-.102.023-.083.03-.1.025-.082.03-.096.026-.082.031-.095a11.896 11.896 0 011.01-2.232zm4.442-4.4L17.352 4.59a20.77 20.77 0 00-1.688 1.721l7.823 2.093c.267.852.442 1.744.513 2.665L2.106 5.213l.045-.065.027-.04.04-.055.046-.065.055-.076.054-.072.064-.086.05-.065.057-.073.055-.07.06-.074.055-.069.065-.077.054-.066.066-.077.053-.06.072-.082.053-.06.067-.074.054-.058.073-.078.058-.06.063-.067.168-.17.1-.098.059-.056.076-.071a12.084 12.084 0 012.272-1.677zM12.017 0h.097l.082.001.069.001.054.002.068.002.046.001.076.003.047.002.06.003.054.002.087.005.105.007.144.011.088.007.044.004.077.008.082.008.047.005.102.012.05.006.108.014.081.01.042.006.065.01.207.032.07.012.065.011.14.026.092.018.11.022.046.01.075.016.041.01L14.7.3l.042.01.065.015.049.012.071.017.096.024.112.03.113.03.113.032.05.015.07.02.078.024.073.023.05.016.05.016.076.025.099.033.102.036.048.017.064.023.093.034.11.041.116.045.1.04.047.02.06.024.041.018.063.026.04.018.057.025.11.048.1.046.074.035.075.036.06.028.092.046.091.045.102.052.053.028.049.026.046.024.06.033.041.022.052.029.088.05.106.06.087.051.057.034.053.032.096.059.088.055.098.062.036.024.064.041.084.056.04.027.062.042.062.043.023.017c.054.037.108.075.161.114l.083.06.065.048.056.043.086.065.082.064.04.03.05.041.086.069.079.065.085.071c.712.6 1.353 1.283 1.909 2.031L7.222.994l.062-.027.065-.028.081-.034.086-.035c.113-.045.227-.09.341-.131l.096-.035.093-.033.084-.03.096-.031c.087-.03.176-.058.264-.085l.091-.027.086-.025.102-.03.085-.023.1-.026L9.04.37l.09-.023.091-.022.095-.022.09-.02.098-.021.091-.02.095-.018.092-.018.1-.018.091-.016.098-.017.092-.014.097-.015.092-.013.102-.013.091-.012.105-.012.09-.01.105-.01c.093-.01.186-.018.28-.024l.106-.008.09-.005.11-.006.093-.004.1-.004.097-.002.099-.002.197-.002z',\n ],\n openclaw: [\n 'M9.046 7.104a.527.527 0 110 1.055.527.527 0 010-1.055z',\n 'M15.376 7.104a.528.528 0 110 1.056.528.528 0 010-1.056z',\n 'M16.877 1.912c.58-.27 1.14-.323 1.616-.037a.317.317 0 01-.326.542c-.227-.136-.547-.153-1.022.068-.352.165-.765.45-1.234.866 2.683 1.17 4.4 3.5 5.148 5.921a6.421 6.421 0 00-.704.184c-.578.016-1.174.204-1.502.735-.338.55-.268 1.276.072 2.069l.005.012.007.014c.523 1.045 1.318 1.91 2.2 2.284-.912 3.274-3.44 6.144-5.972 6.988v2.109h-2.11v-2.11c-1.043.417-2.086.01-2.11 0v2.11h-2.11v-2.11c-2.531-.843-5.061-3.713-5.973-6.987.882-.373 1.678-1.238 2.2-2.284l.007-.014.006-.012c.34-.793.41-1.518.071-2.069-.327-.531-.923-.719-1.503-.735a6.409 6.409 0 00-.704-.183c.749-2.421 2.466-4.751 5.149-5.922-.47-.416-.88-.701-1.234-.866-.474-.221-.794-.204-1.021-.068a.318.318 0 01-.435-.109.317.317 0 01.109-.433c.476-.286 1.036-.233 1.615.037.49.229 1.031.628 1.621 1.182A9.924 9.924 0 0112 2.568c1.199 0 2.284.19 3.256.526.59-.554 1.13-.953 1.62-1.182zM8.835 6.577a1.266 1.266 0 100 2.532 1.266 1.266 0 000-2.532zm6.33 0a1.267 1.267 0 100 2.533 1.267 1.267 0 000-2.533z',\n 'M.395 13.118c-.966-1.932-.163-3.863 2.41-3.365v-.001l.05.01c.084.018.17.038.26.06.033.009.067.017.1.027.084.022.168.048.255.076l.09.027c.528 0 .95.158 1.16.501.212.343.212.87-.105 1.61-.085.17-.178.333-.276.489l-.01.017a4.967 4.967 0 01-.62.791l-.019.02c-1.092 1.117-2.496 1.336-3.295-.262z',\n 'M21.193 9.753c2.574-.5 3.378 1.433 2.411 3.365-.58 1.159-1.476 1.361-2.342.96l-.011-.005a2.419 2.419 0 01-.114-.056l-.019-.01a2.751 2.751 0 01-.115-.067l-.023-.014c-.035-.022-.071-.044-.106-.068l-.05-.035c-.55-.388-1.062-1.007-1.44-1.76-.276-.647-.311-1.132-.174-1.472.176-.439.636-.639 1.23-.639.032-.011.066-.02.099-.03.08-.026.16-.05.238-.072l.117-.03a5.502 5.502 0 01.3-.067z',\n ],\n hermes: [\n 'M5.938 12.835c.127-.039.285.02.373.143.028.038.036.092.046.14.003.014-.02.033-.04.05-.124-.098-.24-.194-.354-.291-.011-.01-.016-.027-.025-.042zM8.396 9.412c.195-.032.39-.06.588-.05a.54.54 0 01.148.026c.202.071.402.147.601.224.028.01.05.036.075.055l-.013.027a9.203 9.203 0 01-.26-.089c-.115-.038-.213-.077-.315-.098-.25-.05-.25-.046-.292-.014l.574.144c.275.139.55.276.823.417.042.022.09.057.107.098.026.06.063.076.117.072.066-.006.132-.017.213-.027l-.04.086c.051.08.142.02.216.064-.074.13-.247.09-.334.199l.061.074-.12.087c0 .106-.038.168-.306.243l.026.085-.196.042.07.124h-.25l-.007.137c-.081-.01-.161-.018-.244-.027l-.053.123c-.027-.008-.052-.011-.073-.023-.067-.038-.128-.056-.195.006-.019.017-.063.014-.093.008-.026-.006-.05-.029-.07-.042-.11.095-.11.095-.208.003-.057.046-.12.074-.186.011-.063.027-.123-.02-.178-.014-.07.007-.097-.035-.133-.07l-.13.033c-.013-.236-.194-.19-.34-.203.005-.072.05-.092.095-.094a.474.474 0 01.159.022c.164.05.32.12.496.138.203.021.405.029.601-.015.265-.059.52-.149.707-.365.049-.056.083-.127.117-.195.019-.038.02-.084-.02-.116a1.397 1.397 0 00-.382-.217c.024.12-.031.182-.115.221 0 .014-.004.025 0 .03.08.115.084.16-.007.267a1.39 1.39 0 01-.218.211.477.477 0 01-.641-.05 1.36 1.36 0 01-.133-.152c-.078-.107-.076-.108-.033-.236-.165-.08-.128-.226-.104-.364.008-.05.028-.096.049-.163-.04.014-.067.017-.087.032a.897.897 0 00-.316.357c-.007.016-.01.034-.02.047-.012.015-.034.038-.045.035-.02-.006-.037-.027-.05-.045-.008-.012-.007-.032-.012-.057h-.126l.053-.172a14.82 14.82 0 00-.039-.049l.11-.284c-.06.026-.091.044-.124.051-.03.007-.064 0-.095 0 0-.031-.01-.07.004-.092.149-.22.305-.428.593-.476z',\n 'M8.06 10.788c-.003-.038-.004-.075.037-.062.016.006.034.048.028.067-.01.04-.038.032-.064-.005z',\n 'M11.981.009c.226-.012.453-.011.679 0 .247.01.495.024.74.062.401.064.798.157 1.19.273.463.138.92.299 1.356.511a7.31 7.31 0 012.948 2.642c.292.469.536.963.739 1.479.219.556.446 1.11.623 1.683.204.654.329 1.326.458 1.997.097.504.182 1.01.29 1.511.156.722.329 1.44.494 2.16.186.812.4 1.615.63 2.415.102.355.193.713.282 1.072.11.436.202.876.254 1.323.031.278.066.557.073.837a7.56 7.56 0 01-.017.88c-.037.413-.1.818-.226 1.212a5.017 5.017 0 01-.915 1.649l-.13.156.018.023c.043-.023.088-.041.127-.068.2-.138.373-.307.531-.49.4-.46.721-.973.975-1.529a3.59 3.59 0 00.325-1.72c-.024-.424-.097-.834-.3-1.213-.013-.027-.015-.06-.03-.121.05.035.082.048.101.072.107.13.22.258.315.398.33.494.46 1.052.486 1.64a3.75 3.75 0 01-.47 1.97c-.36.655-.887 1.14-1.526 1.506-.193.111-.394.21-.595.308-.157.078-.248.211-.318.365a.522.522 0 00-.033.406.359.359 0 01.013.139c-.005.077-.077.155-.14.162-.054.006-.125-.043-.15-.116a1.206 1.206 0 01-.06-.233c-.04-.314-.155-.6-.308-.87a3.906 3.906 0 00-.73-.91 2.129 2.129 0 00-.897-.524 4.093 4.093 0 00-.692-.131c-.075-.008-.15-.04-.22.01.18.06.363.11.538.18.434.173.82.43 1.18.728.308.255.58.543.794.884.098.155.186.315.227.496.027.123.042.25.067.375.013.062-.002.109-.053.144-.047.033-.122.034-.163-.01a.455.455 0 01-.08-.14c-.03-.073-.038-.159-.078-.225a7.314 7.314 0 00-1.423-1.664c-.16-.137-.329-.26-.537-.323-.376-.114-.753-.203-1.15-.154-.213.025-.427.032-.64.053a1.6 1.6 0 00-.736.278 5.14 5.14 0 00-.834.72c-.329.342-.642.699-.955 1.055-.136.155-.264.319-.314.531a5.227 5.227 0 00-.012.051.096.096 0 01-.09.076h-.31c-.046 0-.082-.048-.072-.094.023-.108.045-.216.07-.324.075-.325.19-.635.368-.917.024-.039.04-.088.104-.08l.01.049.027.077c.28-.435.571-.834.996-1.135.283-.204.584-.378.89-.55a.196.196 0 00-.098-.002c-.162.043-.325.084-.485.134-.402.124-.764.33-1.11.566-.147.1-.298.193-.414.333a7.314 7.314 0 00-1.07 1.767.845.845 0 00-.04.12.075.075 0 01-.072.056h-.494c-.04 0-.062-.051-.036-.082.123-.14.246-.282.377-.415.275-.281.58-.532.777-.884.027-.048.063-.09.095-.135.238-.333.54-.607.818-.902.082-.086.175-.16.26-.24.029-.027.053-.057.079-.085l-.018-.025-.135.041c-.034.017-.07.031-.102.05-.248.144-.494.292-.743.433-.408.23-.825.439-1.209.711-.281.2-.591.358-.889.533-.02.012-.044.015-.08.028-.015-.135.143-.201.108-.336-.033.014-.064.02-.085.038-.111.096-.227.19-.328.296-.148.157-.284.325-.425.488-.125.143-.25.286-.373.431A.153.153 0 019.89 24H8.762a.316.316 0 00.016-.042c.028-.09.085-.172.083-.28-.091-.018-.162.001-.212.077a4.45 4.45 0 00-.136.215c-.01.016-.024.03-.042.03h-.093c-.019 0-.029-.022-.017-.037.071-.088.14-.178.209-.268.001-.002-.006-.012-.012-.024-.014.004-.03.006-.045.013-.176.09-.352.181-.527.274a.363.363 0 01-.168.042H5.202c-.026 0-.039-.036-.019-.053.21-.178.402-.374.558-.605.335-.496.538-1.047.667-1.629.004-.02-.003-.043-.006-.091-.037.048-.059.072-.076.1a1.943 1.943 0 01-.334.415c-.28.258-.59.448-.983.464-.297.012-.588 0-.865-.127-.46-.21-.722-.57-.794-1.072-.025-.17-.017-.171-.182-.219A3.513 3.513 0 011.97 20.6a2.286 2.286 0 01-.808-1.13 3.569 3.569 0 01-.16-1.245c.002-.034.016-.067.024-.1.032.023.046.043.05.066.033.153.059.308.096.46.086.355.257.664.516.92.258.256.571.419.91.532.358.118.717.138 1.07-.016a1.89 1.89 0 00.621-.452c.328-.348.533-.76.648-1.223.009-.034.005-.071.007-.11-.015.006-.026.006-.03.011-.031.05-.064.1-.093.152-.284.502-.679.887-1.196 1.135-.351.17-.718.255-1.11.159a1.607 1.607 0 01-.971-.64 2.006 2.006 0 01-.368-.924 2.903 2.903 0 01.02-.886c.05-.439.466-1.17.742-1.271-.02.063-.035.112-.053.16-.043.116-.097.227-.13.345a1.901 1.901 0 00-.05.82c.033.212.09.416.204.6.147.236.346.407.62.465.11.023.225.014.338.018a.576.576 0 00.386-.131c.164-.128.282-.292.366-.481.168-.375.24-.777.309-1.179.05-.296.093-.594.133-.893.039-.281.071-.563.104-.845.026-.232.048-.464.074-.696.024-.228.052-.455.076-.683.024-.227.047-.455.069-.683.013-.14.022-.28.034-.42l.037-.417c.022-.25.041-.5.065-.748.008-.082-.02-.132-.09-.177a2.46 2.46 0 01-.492-.418c-.1-.109-.188-.228-.282-.342-.035-.042-.056-.097-.116-.118a2.084 2.084 0 00.275.597c.06.092.131.176.196.265.063.086.182.115.234.226-.028.003-.046.01-.06.006a4.74 4.74 0 01-.22-.057 2.71 2.71 0 01-1.287-.819c-.435-.487-.656-1.076-.71-1.723a5.206 5.206 0 01.014-1.06c.072-.602.22-1.186.45-1.745.155-.376.338-.741.526-1.102.205-.393.466-.75.765-1.076.512-.559 1.104-1.024 1.726-1.448.717-.49 1.478-.898 2.277-1.233C8.244.828 8.767.632 9.31.494c.655-.166 1.31-.33 1.982-.415.229-.03.458-.058.688-.07zm-1.847 22.82c-.07.06-.147.111-.207.18-.238.27-.464.549-.668.869l-.044.108a.177.177 0 00.093-.057c.174-.19.351-.378.519-.574.104-.122.195-.255.288-.386.024-.034.03-.08.046-.12l-.027-.02zm1.65-3.695a5.51 5.51 0 00-.653.593l-.37.386a.963.963 0 01-.377.25 1.372 1.372 0 01-.467.09c-.044 0-.087.006-.151.012.028.058.043.097.064.131.15.242.301.482.45.724.136.22.276.438.399.666.068.125.105.267.156.404.077.027.14-.018.202-.048.29-.135.579-.274.867-.412.213-.101.437-.186.636-.31.347-.215.68-.455 1.018-.685.015-.01.026-.028.042-.046-.023-.019-.038-.037-.056-.044-.287-.111-.527-.3-.77-.482a5.319 5.319 0 01-.506-.42 1.757 1.757 0 01-.41-.653c-.019-.049-.045-.095-.075-.156zm-5.847.264c-.06.096-.097.194-.132.293a3.38 3.38 0 01-.555 1.01c-.2.25-.455.412-.762.493-.23.06-.464.076-.7.07-.048-.002-.097.002-.158.005.016.04.021.066.035.085.1.145.23.246.4.295.157.046.316.034.498.023.181-.037.343-.115.485-.234.238-.199.402-.454.536-.732.175-.363.264-.751.342-1.144.01-.053.008-.11.011-.164zm14.945-4.586c.008.029.016.057.027.107.024.155.051.31.072.464.03.219.067.437.078.657.017.344.027.689-.014 1.033-.037.315-.063.633-.116.946a6.153 6.153 0 01-.46 1.518c-.008.018-.01.039-.02.082.047-.03.077-.042.098-.064.085-.083.17-.167.248-.255.271-.305.458-.66.596-1.043.18-.498.228-1.011.145-1.531-.103-.65-.33-1.263-.597-1.881a9.055 9.055 0 00-.024-.055l-.033.022zM5.797 8.29a.26.26 0 00.018.153c.124.251.25.501.379.75.025.049.066.09.03.163-.284.06-.578.119-.88.255.059.038.097.06.132.087.042.032.112.058.09.12-.01.033-.075.048-.117.072.017.01.043.021.067.036.166.102.33.207.447.368.138.192.229.404.188.644-.079.469-.306.85-.69 1.132-.054.04-.106.083-.161.122a.243.243 0 00-.103.245.77.77 0 00.055.195c.083.196.22.35.375.492.083.076.159.164.222.257a.37.37 0 01.025.377c-.023.05-.05.099-.076.148-.03.06-.028.111.022.162.041.042.08.089.112.138.038.058.078.079.147.05a.486.486 0 01.333-.006c.16.046.302.126.444.21.13.077.264.149.4.219.067.035.14.05.219.026.071-.022.124.01.145.076.02.064-.003.108-.074.139-.07.03-.137.063-.209.088-.1.035-.201.073-.314.077-.013-.107.11-.088.127-.159-.206-.126-.643-.145-.801-.034.063.112.035.21-.096.313-.13-.1-.025-.202.002-.3a.209.209 0 00-.249.17c-.015.101.067.216.178.224.108.007.218-.005.326-.012.06-.005.12-.027.199 0-.103.123-.248.127-.357.19.002.05.07.086.019.131-.053.048-.095-.001-.132-.03-.08-.063-.16-.126-.231-.197a.474.474 0 01-.157-.311.52.52 0 00-.043-.172c-.032-.074-.032-.137.033-.19-.018-.03-.028-.053-.045-.072a1.222 1.222 0 01-.196-.369c-.053-.137-.046-.264.048-.381.024-.03.05-.06.064-.095a.664.664 0 00.047-.168c.017-.165-.064-.287-.182-.387-.186-.156-.36-.322-.46-.551-.005-.011-.024-.017-.037-.026-.011.017-.024.027-.025.038-.019.185-.045.37-.052.557-.014.377.058.743.162 1.104.118.41.289.798.488 1.173.267.502.537 1.002.812 1.5.055.098.13.189.208.27.198.202.452.272.724.273.202 0 .404-.006.605-.026.295-.03.59-.073.884-.113.183-.025.365-.057.548-.08.21-.026.38.073.522.21.16.156.305.327.447.5.22.265.397.56.554.867.05.098.07.1.147.03.13-.121.26-.242.394-.36.067-.059.088-.12.067-.213a3.535 3.535 0 01-.085-.796c.002-.157.006-.314.018-.471.015-.224.03-.45.06-.672a59.114 59.114 0 01.362-2.298c.087-.493.182-.984.268-1.477.06-.347.118-.694.162-1.043.034-.273.055-.55.063-.825.011-.332.003-.665.002-.998 0-.077.004-.155-.01-.23-.028-.142-.01-.155-.162-.19a5.826 5.826 0 00-.607-.107c-.146-.018-.207-.053-.221-.19-.006-.049-.025-.098-.041-.146-.009-.025-.024-.048-.046-.09l-.025.264c-.009.096-.029.116-.127.115-.055 0-.11-.008-.164-.008-.476 0-.952-.008-1.426.032-.095.008-.173-.015-.226-.103-.04-.066-.088-.126-.134-.186-.063-.084-.086-.093-.182-.06-.195.068-.388.138-.582.21a2.71 2.71 0 00-.675.394.986.986 0 01-.323.168c-.033.01-.07.008-.127.013.02-.066.024-.114.047-.15.064-.105.135-.205.205-.306.023-.033.049-.063.073-.095l-.015-.023-.201.037c-.146.04-.296.07-.437.122-.148.053-.266.023-.386-.072a3.623 3.623 0 01-.733-.786l-.093-.132zm8.592 8.963l-.147.09c-.22.134-.44.266-.659.402-.093.058-.184.12-.27.188-.085.07-.124.161-.072.272.047.1.093.2.147.294.047.08.124.138.213.147.11.01.228.012.336-.012.217-.05.372-.205.528-.357a.291.291 0 00.087-.308c-.046-.18-.079-.365-.118-.547-.011-.052-.027-.103-.045-.169zm-.257-2.409c-.12.291-.205.597-.325.91-.151.433-.294.87-.435 1.323.036-.01.054-.01.067-.018.261-.16.522-.324.785-.484.054-.033.071-.078.065-.138-.012-.13-.024-.262-.034-.393l-.068-.886c-.008-.103-.02-.206-.029-.31-.009 0-.017-.002-.026-.004zm3.081-8.13l.099.285c.08.231.159.463.24.714l.58 1.952c.187.63.372 1.262.558 1.893.114.382.235.762.343 1.146.072.257.126.519.186.799.044.206.087.413.127.64.034.106.023.226.077.325l.025-.006-.068-.362c-.038-.206-.077-.412-.113-.638-.015-.07-.029-.141-.046-.211-.095-.396-.177-.796-.29-1.187-.196-.685-.413-1.364-.618-2.046-.165-.549-.322-1.1-.488-1.648-.069-.227-.15-.45-.226-.695l-.117-.336c-.037-.107-.075-.216-.115-.322-.04-.106-.084-.21-.127-.314a7.558 7.558 0 01-.027.01zM6.225 14.304c-.063-.001-.115.014-.134.083a.35.35 0 00.41.012 4.533 4.533 0 00-.276-.095zM5.23 11.98c-.026-.027-.057-.048-.075.002-.012.032-.007.07-.01.113.082-.037.082-.037.085-.115zm.062-1.189a.135.135 0 00-.088.056.197.197 0 00-.025.11c.005.152.01.306.026.457a.751.751 0 00.066.218c.061.136.157.167.288.101.055-.027.06-.054.025-.11a4.52 4.52 0 01-.129-.211c-.015-.068-.066-.131-.033-.207.04-.09-.076-.116-.074-.19V10.874c-.003-.038-.006-.087-.056-.083zm-.017-.968a.867.867 0 00-.467.127c-.076.045-.084.07-.05.158.034.087.07.173.115.254.064.117.09.125.21.077a.657.657 0 01.336-.053c.202.022.357.136.504.264l.092.077c.007-.006.014-.013.022-.018-.019-.105-.035-.226-.149-.264-.157-.053-.324-.075-.508-.117l-.24-.005c.24-.169.452-.044.687.009-.063-.115-.153-.147-.23-.193-.082-.05-.17-.092-.25-.144-.06-.037-.12-.08-.072-.172zm10.233.325c-.23-.01-.427.08-.608.211-.034.026-.06.065-.105.117.087.026.15.046.232.065.044-.015.088-.03.13-.046.306-.114.61-.115.904.031.126.063.237.04.366-.005-.02-.031-.03-.054-.045-.071a.986.986 0 00-.448-.273c-.14-.044-.284-.024-.426-.03zM7.99 6.483a.308.308 0 00.002.133c.08.321.156.643.242.962.104.387.27.75.456 1.103.02.037.061.08.098.087a.404.404 0 00.253-.051l-.472-.84c-.23-.448-.405-.92-.579-1.394zM10.397.497c-.2-.008-.405.004-.603.034-.236.035-.47.087-.7.152-.287.08-.569.18-.852.273-.04.013-.074.038-.11.058.028.014.05.018.07.014.287-.068.58-.085.873-.09.134-.002.269.009.402.025.19.024.382.048.57.09.456.104.874.3 1.265.556.464.306.888.66 1.257 1.078.205.232.395.475.56.739.17.274.315.561.449.856.273.601.456 1.232.6 1.876.04.173.07.348.1.524.017.104.065.167.17.19.122.028.2.105.22.251-.003.102-.06.174-.129.24a1.065 1.065 0 00-.268.358.164.164 0 00.083-.039c.08-.086.162-.172.235-.265a.56.56 0 00.13-.333c.009-.05.022-.1.024-.15.007-.124-.017-.15-.143-.168-.025-.004-.049-.014-.073-.015-.082-.007-.125-.063-.137-.131-.033-.198-.004-.355.247-.408.086-.018.174-.03.26-.042.158-.023.315-.053.473-.067.14-.012.19.033.226.167.008.029.018.057.021.087.019.179-.008.225-.141.288-.027.013-.055.024-.078.042a.148.148 0 00-.051.067c-.039.144.073.382.206.445l.673.32c.023.011.05.015.075.023l.018-.026c-.015-.008-.032-.013-.044-.024a2.27 2.27 0 00-.544-.32 4.898 4.898 0 00-.173-.075.203.203 0 01-.126-.191c-.003-.085.045-.154.128-.187l.059-.025c.099-.044.118-.076.112-.187a.384.384 0 00-.008-.063c-.067-.294-.123-.59-.205-.88a9.478 9.478 0 00-.826-2.036 7.465 7.465 0 00-1.39-1.805 4.536 4.536 0 00-1.177-.824 3.656 3.656 0 00-1.016-.328 6.155 6.155 0 00-.712-.074zm6.719 5.955c.01.014.018.028.038.034l-.022-.044-.016.01zM4.103 3.917a.062.062 0 01-.03.012.455.455 0 01-.04.039c-.01.01-.02.02-.045.04l-.363.354c-.088.085-.17.178-.266.253-.284.22-.425.53-.544.855a.132.132 0 00-.007.071c.013.055.033.108.052.168l.074.026c-.017.056-.03.105-.047.152-.058.164-.118.327-.175.491-.005.015.008.036.019.077.08-.175.158-.33.225-.489.228-.544.484-1.074.819-1.561.09-.133.182-.266.283-.401.004-.006.007-.013.022-.03.001-.016.003-.032.015-.04l.008-.017zm12.976 2.408a.023.023 0 01.009.019.073.073 0 00-.006.01.188.188 0 00.007.02l.018.022c.002-.007.007-.016.005-.021-.003-.01-.012-.018-.02-.038a1.331 1.331 0 01-.013-.012zM4.199 4.48c-.003.004-.008.008-.027.014-.005.013-.011.025-.031.047a2.085 2.085 0 01-.124.167c-.048.07-.116.055-.181.041-.134-.028-.228.016-.287.143-.089.187-.187.37-.273.56-.049.108-.11.216-.118.36.081.003.154.007.228.008h.228a2.563 2.563 0 01-.079.264c-.01.052-.022.103-.033.155l.02.004c.018-.046.037-.092.067-.153.066-.142.13-.285.2-.426.02-.04.034-.1.116-.092 0 .043.004.084 0 .124-.005.045-.017.09-.028.143.141.043.086.174.115.269.102-.022.104-.195.248-.144v.205l.017.002.439-1.059c-.13 0-.246-.02-.358.033-.024.011-.058-.001-.108-.004.075-.15.139-.278.211-.417a.128.128 0 01.025-.036c0-.015-.001-.03.008-.038l.006-.02c-.005.006-.01.011-.028.017-.004.012-.009.024-.026.045a.085.085 0 01-.032.033c-.123.157-.09.164-.258.106-.079-.027-.078-.028-.047-.144.028-.046.056-.093.098-.15 0-.016-.001-.032.007-.042L4.2 4.48zm2.073-.67c-.003.006-.007.011-.027.016-.094.125-.194.246-.28.377-.155.238-.301.481-.451.723-.14.224-.345.368-.575.481-.017.008-.04.006-.079.011.012-.059.016-.109.033-.153a6.076 6.076 0 01.229-.518l-.007-.02a.138.138 0 01-.035.025c-.028.05-.055.1-.093.164-.26.424-.443.817-.442.95.024.004.048.011.073.013.177.013.188.007.26-.165.03-.07.077-.12.147-.15l.175-.07c.044-.018.085-.057.146-.032.003.05-.01.11.014.145.042.062.044.125.047.193.002.049.017.098.026.147.029-.034.039-.065.05-.097.142-.39.277-.782.428-1.17.1-.256.22-.504.33-.756.013-.03.013-.067.03-.092V3.81zm3.987-.34c0 .045.01.084.021.123.042.16.094.318.124.48.024.133.023.27.028.406 0 .033-.019.067-.032.11-.094-.058-.047-.158-.106-.215h-.125c-.015.072-.01.152-.046.2-.066.085-.155.154-.236.227-.043.038-.078.018-.103-.025l-.046-.087c-.065.035-.117.069-.172.093-.116.051-.235.095-.35.147-.085.038-.09.053-.07.147.014.075.034.148.047.223.013.072.05.109.123.124.233.05.462.115.657.265.058-.102.058-.102.168-.151.03-.014.06-.03.092-.042.08-.03.115-.017.15.06.023.048.041.098.066.158.06-.14-.042-.267.017-.416.157.18.24.39.375.567a.235.235 0 00.022-.098c.002-.124 0-.247.002-.371 0-.034.013-.067.02-.1l.032-.003c.11.155.13.354.226.52a3.036 3.036 0 00-.01-.392c-.004-.045 0-.074.05-.088.08.036.116.14.215.158-.03-.275-.423-1.137-.798-1.635-.114-.127-.2-.28-.34-.386zm-2.667.696c-.019.034-.03.05-.037.067-.061.185-.125.37-.18.556-.031.105-.087.169-.195.19-.09.019-.178.052-.268.073-.038.009-.089.015-.118-.003-.024-.016-.025-.069-.036-.106-.064.076-.082.087-.17.047-.133-.062-.262-.135-.393-.201-.048-.025-.093-.063-.17-.03-.043.12-.091.25-.137.382-.099.28-.087.242.095.453.046.048.102.03.154.023.054-.009.106-.03.16-.036.13-.013.26-.08.367-.015.204-.064.387-.122.571-.178.05-.015.089.005.114.054.022.042.034.093.082.121.038-.056-.013-.128.063-.178l.14.241-.042-1.46zm.278.358c-.096-.01-.107.01-.11.108-.002.038-.003.078.002.115.03.2.099.386.174.57.002.006.012.01.022.015l.078-.05c.052.036.081.088.153.088.205-.002.41.014.616.012.099-.001.158.042.205.12.018.03.024.077.088.066l-.08-.394c-.05-.195-.085-.395-.172-.589-.057.057-.114.068-.18.046a.72.72 0 00-.135-.028c-.22-.028-.44-.059-.66-.08zm10.254-1.727c.089.163.155.316.139.491-.016.168.026.342-.044.516-.047-.033-.088-.082-.112-.075-.117.035-.164-.057-.227-.115a4.772 4.772 0 01-.286-.29l-.104-.113a4.856 4.856 0 01-.023.019c.035.046.07.093.11.156.04.064.084.127.122.193.034.058.065.118.031.205-.082-.01-.164-.019-.246-.032-.06-.01-.101 0-.124.07-.031.098-.037.096-.15.09.02.042.036.08.057.116.041.074.03.138-.03.196-.06.06-.118.122-.178.181a.175.175 0 01-.185.046c-.222-.061-.447-.113-.67-.174-.032-.009-.063-.04-.086-.068-.03-.04-.052-.087-.08-.13-.044-.07-.09-.138-.136-.207a.18.18 0 00-.014.105c.012.127.03.253.035.38.005.1-.024.12-.121.104-.104-.017-.206-.04-.31-.058-.064-.012-.131-.028-.202.03l.081.208c.09 0 .166-.01.237.002a.819.819 0 01.458.251c.078.083.154.168.241.26l.018-.005c-.004-.006-.008-.013-.01-.04.014-.056-.062-.118.018-.178.031.03.064.057.088.09.058.078.111.159.169.257l.089.141.024-.013a2093.819 2093.819 0 01-.427-.934c.055.007.083.007.108.016.193.07.385.142.577.216.074.028.147.06.219.094.062.028.112.018.157-.033.05-.056.102-.112.154-.167.05-.051.095-.046.132.014.016.025.026.053.04.08.071.138.143.277.217.433l.159.308.025-.011c-.044-.106-.07-.218-.138-.334-.057-.182-.168-.346-.206-.545.136.034.362.326.567.732l.057.074.018-.011a1.563 1.563 0 01-.052-.127c-.046-.145-.097-.29-.136-.436-.022-.083-.036-.173.022-.26l.109.058-.026-.207.027-.016c.022.02.05.036.065.06.073.108.143.22.215.33.01.016.029.029.043.043-.036-.217-.2-.38-.229-.626l.155.112c.014-.166.012-.319.042-.465.032-.158-.023-.297-.063-.445.024.004.036.006.055.025.092.124.183.249.277.371.02.027.05.047.069.087l.04.063.019-.015a.293.293 0 01-.053-.082 27.922 27.922 0 01-.332-.49c-.221-.311-.363-.467-.485-.521zm-6.57.327c-.003.161.092.275.069.415l-.368.087c.09.139.032.237-.052.331-.05.057-.092.122-.143.178-.037.04-.046.078-.018.126l.16.275c.029.048.072.066.128.064.076-.003.152 0 .228-.001.116-.003.216.022.275.137.006.014.02.024.044.052.004-.059-.003-.098.01-.13.016-.04.04-.099.072-.108.084-.023.173-.024.26-.03.013-.001.027.018.04.029l.071.065c.019-.11-.082-.198-.024-.31l.126.04c-.026-.123-.07-.245-.071-.366 0-.123.051-.243.115-.36.107.062.16.156.234.253.183.265.36.533.494.834.165-.078.27.068.407.088-.003-.106-.133-.441-.197-.492a.142.142 0 00-.102-.028c-.06.011-.119.039-.191.063-.025-.039-.056-.078-.077-.122a3.936 3.936 0 00-.473-.783c-.076-.094-.16-.182-.228-.26l-.391.285c-.049.035-.094.03-.132-.017l-.169-.207c-.025-.03-.053-.059-.097-.108z',\n ],}\n\n// ── honest fallbacks for harnesses with no published brand mark ───────────\n// Inline lucide paths (v1.27, ISC) — `/web-react` ships no icon-library\n// dependency, so the stroke glyphs here match the set in `./controls`.\n\nfunction BotGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 8V4H8\" />\n <rect width=\"16\" height=\"12\" x=\"4\" y=\"8\" rx=\"2\" />\n <path d=\"M2 14h2\" />\n <path d=\"M20 14h2\" />\n <path d=\"M15 13v2\" />\n <path d=\"M9 13v2\" />\n </svg>\n )\n}\n\nfunction PlugGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 22v-5\" />\n <path d=\"M15 8V2\" />\n <path d=\"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z\" />\n <path d=\"M9 8V2\" />\n </svg>\n )\n}\n\nfunction TerminalGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 19h8\" />\n <path d=\"m4 17 6-6-6-6\" />\n </svg>\n )\n}\n\n/** Lucide fallback per harness without a brand mark — the same assignments\n * the legacy picker shipped (`factory-droids`→bot, `nanoclaw`→plug,\n * `cli-base`→terminal). */\nconst FALLBACK_GLYPHS: Partial<Record<Harness, (props: { className?: string }) => ReactNode>> = {\n 'factory-droids': BotGlyph,\n nanoclaw: PlugGlyph,\n 'cli-base': TerminalGlyph,\n}\n\n/**\n * Brand mark for a harness — size it from the call site (`className=\"h-4\n * w-4\"`). Unknown ids render the neutral bot, never an invented logo.\n * `data-glyph` names the resolved mark so tests and stories can assert\n * brand-vs-fallback without snapshotting path data.\n */\nexport function HarnessGlyph({ harness, className }: HarnessGlyphProps): ReactNode {\n const brand = BRAND_PATHS[harness]\n if (brand) {\n return (\n <svg\n className={className}\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n fillRule=\"evenodd\"\n clipRule=\"evenodd\"\n role=\"img\"\n aria-label={harness}\n data-glyph={harness}\n >\n {brand.map((d) => (\n <path key={d.slice(0, 32)} d={d} />\n ))}\n </svg>\n )\n }\n const Fallback = FALLBACK_GLYPHS[harness] ?? BotGlyph\n const kind = Fallback === BotGlyph ? 'bot' : Fallback === PlugGlyph ? 'plug' : 'terminal'\n return (\n <span role=\"img\" aria-label={harness} data-glyph={kind} className=\"inline-flex\">\n <Fallback className={className} />\n </span>\n )\n}\n","/**\n * `AgentSessionControls` — the CANONICAL model + harness + reasoning-effort\n * cluster a chat composer docks (see \"UI chrome ownership (picker canon)\" in\n * AGENTS.md). One component so every product's two composers (and every\n * product) share the same control surface and harness↔model coherence policy.\n *\n * PICKER CANON. The model menu below IS `/web-react`'s `ModelPicker` and the\n * thinking-budget pill IS `EffortPicker` — the canonical ecosystem pickers.\n * sandbox-ui's `dashboard/ModelPicker` and the model menu inside sandbox-ui's\n * `chat/AgentSessionControls` are legacy (deprecated, frozen, removed at\n * sandbox-ui's next major), and the `/chat-react` `ComposerAgentControls`\n * adapter that rendered sandbox-ui's strip is REMOVED — a surface that still\n * renders the sandbox-ui strip is showing the old design; migrate it\n * (props mapping in `docs/ui-picker-canon.md`).\n *\n * Dependency-free beyond React by design: `/web-react` must not force the\n * optional sandbox-ui peer, so this component — the canonical one — can never\n * require it.\n *\n * Two layouts, additive — the default preserves the prior hand-rolled behavior:\n * - `layout=\"inline\"` (default): model, harness, and effort sit side by side as\n * pills. This is the original arrangement; existing call sites that mounted\n * `ModelPicker` + a harness picker + `EffortPicker` in a row get the same UI.\n * - `layout=\"compact\"`: the model picker stays inline and visible; the agent\n * backend (\"harness\") and reasoning-effort controls — internal jargon a user\n * rarely needs — tuck behind a single gear popover with plain-English copy.\n *\n * Harness ↔ model coherence is identical in both layouts, via the substrate's\n * snap helpers (`@tangle-network/agent-app/harness`): changing the harness snaps\n * an incompatible model to that harness's best catalog option; changing the\n * model switches to the model's native harness. Catalog model ids are canonical\n * (\"provider/model\"), which is exactly what the snap helpers expect — no id\n * translation is needed here.\n *\n * Dependency-free beyond React: inline SVG glyphs, CSS-var / Tailwind tokens the\n * app shell defines. The harness picker is rendered inline so this needs no\n * sandbox-ui dependency.\n */\n\nimport { useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n snapHarnessToModel,\n snapModelToHarness,\n type Harness,\n} from '../harness'\nimport type { CatalogModel } from '../runtime/model-catalog'\nimport { ModelPicker, EffortPicker, CheckGlyph, OVERLAY_SHADOW, pickerRootClass, PopoverSurface, usePopover } from './controls'\nimport type { EffortLevel } from './controls'\nimport { HarnessGlyph } from './harness-glyphs'\n\n/** Plain-English labels for the harnesses a product is likely to expose. Unknown\n * ids fall back to the raw value so a new backend still renders a usable label. */\nconst HARNESS_LABELS: Partial<Record<Harness, string>> = {\n opencode: 'OpenCode (any model)',\n 'claude-code': 'Claude Code (Anthropic)',\n codex: 'Codex (OpenAI)',\n 'kimi-code': 'Kimi (Moonshot)',\n amp: 'Amp',\n 'factory-droids': 'Factory Droids',\n cursor: 'Cursor',\n hermes: 'Hermes',\n forge: 'Forge',\n pi: 'Pi',\n openclaw: 'OpenClaw',\n acp: 'ACP',\n 'cli-base': 'CLI',\n}\n\nfunction harnessLabel(h: Harness): string {\n return HARNESS_LABELS[h] ?? h\n}\n\nfunction ChevronDown({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\n/** lucide `lock` — the closed padlock on a pinned harness trigger. */\nfunction LockGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\" />\n <path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n </svg>\n )\n}\n\nfunction GearGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z\" />\n </svg>\n )\n}\n\n/** Tailwind utilities for keyboard-visible focus on popover options + triggers. */\nconst FOCUS_RING =\n ''\n\n/**\n * Pill-styled harness picker — inline, no sandbox-ui dependency. The brand\n * marks come from `./harness-glyphs` (the set the legacy sandbox-ui picker\n * shipped, vendored inline).\n *\n * `rounded-full` + `min-h-[36px]`, not `rounded-lg` at whatever height the\n * padding gives: this pill sits beside `ModelPicker` and `EffortPicker` in\n * both layouts, and both of those are 36px pills. A single odd-shaped control\n * is what made the compact popover read as a pile of unrelated widgets rather\n * than one selector stack.\n *\n * `fullWidth` is opt-in and means what it means on `EffortPicker` — see\n * {@link pickerRootClass}.\n *\n * `lockReason` PINS the control: it keeps its selector shape and keeps\n * reporting the harness the thread is on, opens nothing, and explains itself\n * on hover AND on keyboard focus. Three deliberate choices there:\n *\n * - `aria-disabled`, never the native `disabled` attribute. A disabled button\n * is removed from the tab order and fires no pointer events in most\n * browsers, so the one control that has something to explain would become\n * the one control that can never be asked.\n * - the reason rides a permanent visually-hidden node that `aria-describedby`\n * points at, so assistive tech has it whether or not the floating hint is\n * up; the floating copy is `aria-hidden` so nothing is announced twice.\n * - the hint is a {@link PopoverSurface}, not an absolutely-positioned div —\n * the compact panel is an `overflow-y-auto` box, which clips a positioned\n * descendant, and that surface is this package's answer to exactly that.\n */\nfunction HarnessPicker({\n value,\n onChange,\n available,\n fullWidth = false,\n lockReason,\n}: {\n value: Harness\n onChange: (h: Harness) => void\n available?: ReadonlyArray<Harness>\n fullWidth?: boolean\n lockReason?: string\n}) {\n const [open, setOpen] = useState(false)\n const [hintOpen, setHintOpen] = useState(false)\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const hintPanelRef = useRef<HTMLDivElement>(null)\n const panelId = useId()\n const reasonId = useId()\n const locked = lockReason !== undefined\n const options = available ?? (Object.keys(HARNESS_LABELS) as Harness[])\n const showHint = () => setHintOpen(true)\n const hideHint = () => setHintOpen(false)\n return (\n <div ref={containerRef} className={pickerRootClass(fullWidth)}>\n <button\n type=\"button\"\n {...triggerProps}\n aria-haspopup={locked ? undefined : true}\n aria-expanded={locked ? undefined : open}\n aria-controls={!locked && open ? panelId : undefined}\n aria-disabled={locked || undefined}\n aria-describedby={locked ? reasonId : undefined}\n onClick={locked ? undefined : () => setOpen(!open)}\n onMouseEnter={locked ? showHint : undefined}\n onMouseLeave={locked ? hideHint : undefined}\n onFocus={locked ? showHint : undefined}\n onBlur={locked ? hideHint : undefined}\n title=\"Agent backend\"\n className={`inline-flex min-h-[36px] w-full items-center justify-between gap-1.5 rounded-full border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground transition ${\n locked ? 'cursor-default' : 'hover:bg-accent'\n } ${FOCUS_RING}`}\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n <HarnessGlyph harness={value} className=\"h-4 w-4 shrink-0 text-foreground\" />\n <span className=\"truncate\">{harnessLabel(value)}</span>\n </span>\n {locked ? (\n <LockGlyph className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n ) : (\n <ChevronDown className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n )}\n </button>\n {locked && (\n <>\n <span id={reasonId} className=\"sr-only\">\n {lockReason}\n </span>\n <PopoverSurface\n open={hintOpen}\n role=\"tooltip\"\n triggerRef={triggerRef}\n panelRef={hintPanelRef}\n matchTriggerWidth={fullWidth}\n className={`max-w-[248px] rounded-lg border border-card-edge bg-popover px-2.5 py-1.5 text-xs leading-snug text-muted-foreground ${OVERLAY_SHADOW}`}\n >\n <span aria-hidden>{lockReason}</span>\n </PopoverSurface>\n </>\n )}\n <PopoverSurface\n open={!locked && open}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n matchTriggerWidth\n className={`max-h-64 min-w-[248px] overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {options.map((h) => (\n <button\n key={h}\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={h === value}\n onClick={() => {\n onChange(h)\n setOpen(false)\n }}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-left text-sm transition ${FOCUS_RING} ${\n h === value ? 'bg-primary/10 font-medium' : 'hover:bg-accent'\n }`}\n >\n <HarnessGlyph harness={h} className=\"h-4 w-4 shrink-0 text-foreground\" />\n <span className=\"truncate\">{harnessLabel(h)}</span>\n {h === value && <CheckGlyph className=\"ml-auto h-3.5 w-3.5 shrink-0 text-primary\" />}\n </button>\n ))}\n </PopoverSurface>\n </div>\n )\n}\n\nexport interface AgentSessionControlsProps {\n /** Catalog models — canonical provider-prefixed ids. */\n models: CatalogModel[]\n modelsLoading?: boolean\n /** Selected canonical model id. */\n model: string\n onModelChange(modelId: string): void\n /** Current harness; harness↔model coherence is enforced on every change. */\n harness: Harness\n onHarnessChange(harness: Harness): void\n /** Harnesses to offer; defaults to the labeled set. */\n availableHarnesses?: ReadonlyArray<Harness>\n /** Reasoning-effort value + setter. Shown only when the selected model\n * `supportsReasoning`, matching `EffortPicker`'s guidance. */\n effort: string\n onEffortChange(effort: string): void\n /**\n * Levels to offer, forwarded verbatim to {@link EffortPicker}. Omit for the\n * default vocabulary.\n *\n * A product whose backend applies only a SUBSET of the levels for the\n * selected harness/model passes that subset here. Without it the strip\n * offers every level and the backend silently ignores the ones it does not\n * apply — a control that reports a choice the system never made.\n *\n * This is the COMPLETE renderable set, not an allow-list layered over a\n * default one — the removed `ComposerAgentControls`' `available` list was the\n * latter, and its picker injected the `auto` sentinel itself. A list that\n * omits the current {@link effort} is still safe: `EffortPicker` reconciles\n * the selected value into the rendered list under its own name rather than\n * resolving it to a different entry (`reconcileEffortLevels`). Build the list\n * from engine ids with `effortLevelsFromIds`; the migration is in\n * `docs/ui-picker-canon.md`.\n */\n effortLevels?: readonly EffortLevel[]\n /**\n * `inline` (default): model, harness, effort side by side — the prior\n * behavior. `compact`: model inline, harness + effort behind a gear popover.\n */\n layout?: 'inline' | 'compact'\n /** Hide the harness control entirely (single-harness products). */\n showHarness?: boolean\n /**\n * PIN the harness and say why, in the user's words (\"This thread already has\n * messages — start a new chat to switch backend\"). Presence IS the lock:\n * there is no separate boolean, because a lock a user cannot read is the\n * thing this prop exists to replace.\n *\n * The control stays VISIBLE and reports the harness the thread is on — the\n * shape a locked selector has to keep, since a thread whose backend is fixed\n * is exactly when a user wants to know what it is. Hiding it (`showHarness:\n * false`) is what pushed products into rendering their own lock label\n * outside the panel.\n *\n * While locked, `onHarnessChange` is never called — not from the picker, and\n * not from the model↔harness coherence policy either. See\n * {@link useCoherentHandlers}.\n */\n harnessLockReason?: string\n renderProviderBadge?: (provider: string) => ReactNode\n className?: string\n}\n\n/**\n * Apply the harness↔model coherence policy and emit the resulting change(s).\n * Returned from a hook-free helper so both layouts share one implementation.\n *\n * A LOCKED harness ({@link AgentSessionControlsProps.harnessLockReason}) is\n * authoritative over the snap: picking a model whose native backend differs\n * still changes the model, and leaves the harness alone. The alternative —\n * snapping a harness the UI has just told the user cannot change — is the one\n * behaviour a lock must not have.\n */\nfunction useCoherentHandlers(props: AgentSessionControlsProps) {\n const { model, models, harness, onModelChange, onHarnessChange, harnessLockReason } = props\n const canonicalIds = useMemo(() => models.map((m) => m.id), [models])\n const harnessLocked = harnessLockReason !== undefined\n\n const onModel = (next: string) => {\n onModelChange(next)\n if (harnessLocked) return\n const nextHarness = snapHarnessToModel(harness, next)\n if (nextHarness !== harness) onHarnessChange(nextHarness)\n }\n\n const onHarness = (next: Harness) => {\n onHarnessChange(next)\n const snapped = snapModelToHarness(next, model, canonicalIds)\n if (snapped !== model) onModelChange(snapped)\n }\n\n return { onModel, onHarness }\n}\n\nexport function AgentSessionControls(props: AgentSessionControlsProps) {\n const {\n models,\n modelsLoading,\n model,\n harness,\n availableHarnesses,\n effort,\n onEffortChange,\n effortLevels,\n layout = 'inline',\n showHarness = true,\n harnessLockReason,\n renderProviderBadge,\n className,\n } = props\n const { onModel, onHarness } = useCoherentHandlers(props)\n const [open, setOpen] = useState(false)\n const { containerRef: popoverRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n const panelId = useId()\n\n const selectedModel = models.find((m) => m.id === model)\n const showEffort = selectedModel?.supportsReasoning ?? true\n\n const modelPicker = (\n <ModelPicker\n value={model}\n onChange={onModel}\n models={models}\n loading={modelsLoading}\n renderProviderBadge={renderProviderBadge}\n />\n )\n\n if (layout === 'inline') {\n return (\n <div className={`flex items-center gap-1.5 ${className ?? ''}`}>\n {modelPicker}\n {showHarness && (\n <HarnessPicker value={harness} onChange={onHarness} available={availableHarnesses} lockReason={harnessLockReason} />\n )}\n {showEffort && <EffortPicker value={effort} onChange={onEffortChange} levels={effortLevels} />}\n </div>\n )\n }\n\n // compact: model inline; harness + effort behind a gear popover.\n const hasAdvanced = showHarness || showEffort\n return (\n <div className={`flex items-center gap-1.5 ${className ?? ''}`}>\n {modelPicker}\n {hasAdvanced && (\n <div ref={popoverRef} className=\"relative inline-flex\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-controls={open ? panelId : undefined}\n onClick={() => setOpen(!open)}\n title=\"Model settings — pick the agent backend and how hard it thinks\"\n className={`flex h-8 w-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground data-[state=open]:bg-muted ${FOCUS_RING}`}\n data-state={open ? 'open' : 'closed'}\n >\n <GearGlyph className=\"h-4 w-4\" />\n </button>\n <PopoverSurface\n open={open}\n id={panelId}\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`w-72 space-y-3 overflow-y-auto rounded-xl border border-card-edge bg-popover p-3 ${OVERLAY_SHADOW}`}\n >\n {showHarness && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-foreground\">Agent backend</p>\n <HarnessPicker\n value={harness}\n onChange={onHarness}\n available={availableHarnesses}\n fullWidth\n lockReason={harnessLockReason}\n />\n <p className=\"text-xs leading-snug text-muted-foreground\">\n The engine that runs the agent. Switching it keeps your model choice compatible.\n </p>\n </div>\n )}\n {showEffort && (\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-foreground\">Thinking</p>\n <EffortPicker value={effort} onChange={onEffortChange} levels={effortLevels} label=\"\" fullWidth />\n <p className=\"text-xs leading-snug text-muted-foreground\">\n How hard the agent thinks before answering. Higher is slower but more thorough.\n </p>\n </div>\n )}\n </PopoverSurface>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["useCallback","useEffect","useMemo","useRef","useState","useRef","useState","useCallback","enabled","useEffect","useMemo","jsx","jsxs","useId","useMemo","useRef","useState","Fragment","jsx","jsxs","ChevronDown","useState","useRef","useId","useMemo"]}