@tangle-network/agent-app 0.45.63 → 0.45.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/web-react/controls.tsx","../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 * `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 sandbox-ui `validateComposerFiles` import becomes a small\n * accept-list matcher re-implemented locally (`isAcceptedFileType`,\n * mirroring its `accept`-string matching byte-for-byte) — this module\n * stays free of the sandbox-ui peer;\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 {\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/** `<input accept>`-style matcher: extension (`.pdf`), wildcard mime\n * (`image/*`), or exact mime. Reimplemented locally (NOT imported from\n * `@tangle-network/sandbox-ui`'s `validateComposerFiles`/`isAcceptedType`) so\n * this module has no sandbox-ui dependency; the matching semantics are kept\n * identical so a rejection reads the same either side of the fence. */\nfunction isAcceptedFileType(file: File, accept: string): boolean {\n const patterns = accept.split(',').map((p) => p.trim()).filter((p) => p.length > 0)\n if (patterns.length === 0) return true\n const name = file.name.toLowerCase()\n const type = (file.type || '').toLowerCase()\n return patterns.some((pattern) => {\n const lower = pattern.toLowerCase()\n if (lower.startsWith('.')) return name.endsWith(lower)\n if (lower.endsWith('/*')) return type.startsWith(lower.slice(0, -1))\n return type === lower\n })\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 + count cap, mirroring sandbox-ui's\n // `validateComposerFiles` semantics (accept checked before count, count\n // checked against currently-staged + already-accepted-this-batch).\n const currentCount = stagedRef.current.length\n const countAccepted: File[] = []\n for (const file of list) {\n if (!isAcceptedFileType(file, accept)) {\n opts.onReject?.(`\"${file.name}\" is not an accepted file type (${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 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 })),\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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/session-shell/path.ts","../src/session-shell/nav-guard.ts","../src/session-shell/index.ts"],"sourcesContent":["/**\n * Path normalisation shared by the shell's routing helpers. Segment-aligned\n * comparison is the invariant: `/vault` must never claim `/vault-archive`, so\n * every prefix test here works on whole segments rather than string prefixes.\n */\n\nexport function stripTrailingSlashes(value: string): string {\n return value.replace(/\\/+$/, '')\n}\n\n/** Bare segment name, so a caller may pass `'/settings'` or `'settings'`. */\nexport function stripSlashes(value: string): string {\n return value.replace(/^\\/+|\\/+$/g, '')\n}\n\n/** Path with query + fragment removed and trailing slashes trimmed. A caller\n * passing a full href instead of a pathname would otherwise match nothing. */\nexport function normalizePath(pathname: string): string {\n const withoutHash = pathname.split('#')[0] ?? ''\n const withoutQuery = withoutHash.split('?')[0] ?? ''\n return stripTrailingSlashes(withoutQuery)\n}\n\n/** True when `path` is `prefix` or a segment-aligned descendant of it, so\n * `/vault` never claims `/vault-archive`. */\nexport function isUnderPrefix(path: string, prefix: string): boolean {\n const p = stripTrailingSlashes(prefix)\n if (p === '') return true\n return path === p || path.startsWith(`${p}/`)\n}\n\n/** Non-empty segments of a path or route pattern. Leading/trailing/duplicate\n * slashes collapse, so `/app//x/` and `app/x` compare equal. */\nexport function toSegments(value: string): string[] {\n return value.split('/').filter((segment) => segment.length > 0)\n}\n\n/** Canonical display form: rooted, no trailing slash, no empty segments. */\nexport function toRootedPath(value: string): string {\n return `/${toSegments(value).join('/')}`\n}\n","/**\n * Nav destinations, and the guard that proves every href the rail renders\n * resolves to a route the product's router actually registered.\n *\n * A rail row's href is assembled from a base plus a relative path, and nothing\n * downstream re-checks it: the sidebar renders a link, the click navigates, and\n * the router answers 404. A unit test written against the nav builder alone\n * cannot catch that — it asserts the href the builder produced, which is the\n * same wrong string the user clicks.\n *\n * Two mechanisms, meant to be used together:\n *\n * 1. `NavDestination` makes the base a REQUIRED discriminant (`scope`). An\n * optional `absolute?: boolean`-style flag has the opposite property:\n * omitting it type-checks, and the destination silently resolves under the\n * workspace prefix instead of the app-level one. A required literal union\n * turns that omission into a compile error, and widening `TScope` demands a\n * base for the new scope rather than defaulting to a wrong one.\n * 2. `assertNavHrefsRegistered` matches every resolved href against the route\n * table, so a destination the router never registered fails a test instead\n * of a user's click. It reads the product's real route table, so it cannot\n * agree with the builder's mistake the way a hand-maintained expected-href\n * list does.\n */\n\nimport { isUnderPrefix, normalizePath, stripTrailingSlashes, toRootedPath, toSegments } from './path'\n\n// ---------------------------------------------------------------------------\n// Destinations — the base is a required discriminant, never an optional flag\n// ---------------------------------------------------------------------------\n\n/** The bases a product routes rail rows under. `workspace` is the per-workspace\n * prefix (`/app/ws_123`); `app` is the account-level one (`/app`), where\n * singleton surfaces such as a shared terminal or billing live. */\nexport type NavScope = 'workspace' | 'app'\n\n/** A base path per scope. Widening `TScope` widens this record, so a product\n * that adds a scope cannot compile until it supplies that scope's base. */\nexport type NavScopeBases<TScope extends string = NavScope> = Readonly<Record<TScope, string>>\n\n/** One rail destination as the product declares it, before a base is applied. */\nexport interface NavDestination<TScope extends string = NavScope> {\n id: string\n /** Path relative to the base named by `scope`. `''` is the base itself.\n * Must be empty or start with `/` — a bare `'vault'` would concatenate into\n * `/app/ws_123vault`, so it is rejected rather than silently repaired. */\n path: string\n /** Which base `path` resolves against. Required on purpose. */\n scope: TScope\n}\n\n/** A destination with its base applied. */\nexport interface ResolvedNavDestination<TScope extends string = NavScope> {\n id: string\n href: string\n scope: TScope\n}\n\n/** Apply a destination's scope base to its path.\n *\n * Throws when the scope has no base configured — a product that assembles\n * `bases` dynamically can defeat the type-level guarantee, and a missing base\n * would otherwise produce `undefined/vault`. */\nexport function resolveNavHref<TScope extends string>(\n destination: NavDestination<TScope>,\n bases: NavScopeBases<TScope>,\n): string {\n const base = bases[destination.scope]\n if (typeof base !== 'string') {\n throw new Error(\n `Nav destination '${destination.id}' uses scope '${destination.scope}', which has no configured base`,\n )\n }\n if (destination.path !== '' && !destination.path.startsWith('/')) {\n throw new Error(\n `Nav destination '${destination.id}' path must be empty or start with '/' (got '${destination.path}')`,\n )\n }\n const rooted = `${stripTrailingSlashes(base)}${destination.path}`\n return rooted === '' ? '/' : stripTrailingSlashes(rooted)\n}\n\n/** Apply the bases to every destination, preserving declaration order. */\nexport function resolveNavDestinations<TScope extends string>(\n destinations: readonly NavDestination<TScope>[],\n bases: NavScopeBases<TScope>,\n): ResolvedNavDestination<TScope>[] {\n return destinations.map((destination) => ({\n id: destination.id,\n href: resolveNavHref(destination, bases),\n scope: destination.scope,\n }))\n}\n\nexport interface ResolveScopedActiveNavIdOptions<TScope extends string = NavScope> {\n pathname: string\n destinations: readonly NavDestination<TScope>[]\n bases: NavScopeBases<TScope>\n /** Extra ABSOLUTE prefixes that light an existing row, e.g.\n * `{ '/app/ws_1/agents': 'integrations' }`. Same longest-prefix contest. */\n aliases?: Readonly<Record<string, string>>\n /** ABSOLUTE prefixes that deliberately highlight nothing, beating any shorter\n * match. */\n claimsNothing?: readonly string[]\n}\n\n/**\n * The rail row to highlight, across scopes.\n *\n * `resolveActiveNavId` resolves rows against ONE base, so an app-level row can\n * only be highlighted by a second, hand-rolled scan — the same split that lets\n * an app-level destination render under the workspace base. This resolves the\n * hrefs first and runs a single longest-prefix contest over absolute paths, so\n * declaration order cannot change the answer and no scope needs its own pass.\n *\n * Prefixes in `aliases` / `claimsNothing` are absolute here, unlike\n * `resolveActiveNavId`'s base-relative ones, because the contest itself is\n * absolute.\n */\nexport function resolveScopedActiveNavId<TScope extends string>({\n pathname,\n destinations,\n bases,\n aliases,\n claimsNothing,\n}: ResolveScopedActiveNavIdOptions<TScope>): string | undefined {\n const path = normalizePath(pathname)\n let bestLength = -1\n let bestId: string | undefined\n const consider = (candidate: string, id: string | undefined, winsTies = false): void => {\n const full = stripTrailingSlashes(candidate)\n if (!isUnderPrefix(path, full)) return\n if (full.length > bestLength || (winsTies && full.length === bestLength)) {\n bestLength = full.length\n bestId = id\n }\n }\n for (const resolved of resolveNavDestinations(destinations, bases)) consider(resolved.href, resolved.id)\n for (const [prefix, id] of Object.entries(aliases ?? {})) consider(prefix, id)\n // Declared last and wins an exact-length tie: naming a prefix here is a\n // deliberate override of the row that owns it.\n for (const prefix of claimsNothing ?? []) consider(prefix, undefined, true)\n return bestId\n}\n\n// ---------------------------------------------------------------------------\n// Route table — structurally the product's own router config\n// ---------------------------------------------------------------------------\n\n/**\n * One entry of a registered route table. Structurally compatible with\n * react-router's `RouteConfigEntry`, so a product passes its real `routes.ts`\n * default export straight in — the point of the guard is that it reads the\n * router's own truth rather than a second list that can agree with the bug.\n */\nexport interface RegisteredRoute {\n /** Absent on a pathless layout route: its children inherit the parent path. */\n path?: string\n index?: boolean\n children?: readonly RegisteredRoute[]\n}\n\n/** A route table entry is either a bare pattern string or a router config node. */\nexport type NavRouteTable = readonly (string | RegisteredRoute)[]\n\nfunction joinPattern(parent: string, child: string): string {\n if (child.startsWith('/')) return child\n if (child === '') return parent\n return `${parent}/${child}`\n}\n\n/**\n * Every path pattern the table registers, rooted and de-duplicated.\n *\n * Parent nodes contribute their own cumulative path as well as their children's:\n * a router matches a parent route with an index child at the parent path, and a\n * parent without one still matches with an empty outlet, so treating parents as\n * unregistered would flag working hrefs.\n */\nexport function flattenRouteTable(table: NavRouteTable): string[] {\n const patterns: string[] = []\n const walk = (entries: NavRouteTable, parent: string): void => {\n for (const entry of entries) {\n if (typeof entry === 'string') {\n patterns.push(toRootedPath(joinPattern(parent, entry)))\n continue\n }\n const own = entry.path === undefined ? parent : joinPattern(parent, entry.path)\n patterns.push(toRootedPath(own))\n if (entry.children) walk(entry.children, own)\n }\n }\n walk(table, '')\n return [...new Set(patterns)]\n}\n\n/**\n * Whole-path segment match of a concrete path against one route pattern.\n *\n * Supports the three pattern forms a router uses: literal segments, `:param`\n * (exactly one non-empty segment), optional `:param?` / `segment?` (zero or\n * one), and a trailing `*` splat (zero or more). Matching is recursive because\n * an optional segment forks the walk — a linear scan silently mismatches\n * `/a/b` against `a/:x?/b`.\n */\nfunction matchesPattern(pathSegments: readonly string[], patternSegments: readonly string[], caseSensitive: boolean): boolean {\n if (patternSegments.length === 0) return pathSegments.length === 0\n const head = patternSegments[0] ?? ''\n if (head === '*') return true\n const rest = patternSegments.slice(1)\n const optional = head.endsWith('?')\n const core = optional ? head.slice(0, -1) : head\n const first = pathSegments[0]\n if (first !== undefined) {\n const hit = core.startsWith(':')\n ? first.length > 0\n : caseSensitive\n ? core === first\n : core.toLowerCase() === first.toLowerCase()\n if (hit && matchesPattern(pathSegments.slice(1), rest, caseSensitive)) return true\n }\n return optional ? matchesPattern(pathSegments, rest, caseSensitive) : false\n}\n\n// ---------------------------------------------------------------------------\n// The guard\n// ---------------------------------------------------------------------------\n\n/**\n * A nav row as the guard needs to see it. Structurally satisfied by\n * `SessionRailNavItem` / `SessionRailSubItem` and by sandbox-ui's\n * `SidebarLayoutNavItem`, so the guard runs over the builder's real output\n * rather than a re-declaration of it.\n */\nexport interface NavHrefItem {\n id: string\n href: string\n subItems?: readonly NavHrefItem[]\n}\n\nexport type NavHrefProblemReason =\n /** No registered pattern matches the resolved href. */\n | 'unregistered'\n /** Empty, fragment-only, or not rooted at `/` — the row navigates nowhere\n * predictable regardless of the route table. */\n | 'not-a-path'\n /** Leaves the router (scheme or protocol-relative) while `allowExternal` is\n * off. */\n | 'external'\n\nexport interface NavHrefProblem {\n id: string\n href: string\n reason: NavHrefProblemReason\n /** Registered patterns ending in the same segment. A destination resolved\n * under the wrong base lands here as its correctly-based twin, which is what\n * names the missing scope in the failure message. */\n nearest: string[]\n message: string\n}\n\nexport interface NavHrefReport {\n /** Hrefs examined, including nested sub-items. */\n checked: number\n problems: NavHrefProblem[]\n /** Off-router destinations accepted because `allowExternal` is on. */\n external: string[]\n /** The flattened route table the check ran against. */\n patterns: string[]\n}\n\nexport interface NavHrefCheckOptions {\n /** Hrefs to skip, compared after query/fragment removal. For a destination\n * served outside this route table (a static asset, another worker). */\n ignore?: readonly string[]\n /** Absolute URLs / `mailto:` / `tel:` are reported under `external` instead\n * of failing. Default true. */\n allowExternal?: boolean\n /** Compare literal segments case-sensitively. Default true — a router that\n * matches case-insensitively still renders a link the deploy's CDN or a\n * case-sensitive origin may not. */\n caseSensitive?: boolean\n}\n\n/** `scheme:` or `//host` — anything the router will not resolve as a path. */\nconst OFF_ROUTER_HREF = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i\n\nfunction flattenItems(items: readonly NavHrefItem[], out: NavHrefItem[] = []): NavHrefItem[] {\n for (const item of items) {\n out.push(item)\n if (item.subItems) flattenItems(item.subItems, out)\n }\n return out\n}\n\n/**\n * Check every nav href against the product's route table.\n *\n * Pure — returns the full report so a caller can assert on parts of it. Use\n * {@link assertNavHrefsRegistered} in tests; it turns the report into a failure\n * that names the offending row, its resolved href, and the near-miss pattern.\n */\nexport function checkNavHrefs(\n items: readonly NavHrefItem[],\n routes: NavRouteTable,\n options: NavHrefCheckOptions = {},\n): NavHrefReport {\n const { ignore, allowExternal = true, caseSensitive = true } = options\n const patterns = flattenRouteTable(routes)\n const patternSegments = patterns.map((pattern) => ({ pattern, segments: toSegments(pattern) }))\n const ignored = new Set((ignore ?? []).map((href) => normalizePath(href)))\n const problems: NavHrefProblem[] = []\n const external: string[] = []\n const flat = flattenItems(items)\n let checked = 0\n\n for (const item of flat) {\n const raw = item.href\n const path = normalizePath(raw)\n if (ignored.has(path)) continue\n if (OFF_ROUTER_HREF.test(raw)) {\n if (allowExternal) {\n external.push(raw)\n continue\n }\n // Counted as checked: it was examined and rejected, so the vacuous-pass\n // guard must not read this run as \"nothing was looked at\".\n checked += 1\n problems.push({\n id: item.id,\n href: raw,\n reason: 'external',\n nearest: [],\n message: `Nav item '${item.id}' href '${raw}' leaves the router, and external destinations are rejected`,\n })\n continue\n }\n checked += 1\n if (raw === '' || raw.startsWith('#') || !raw.startsWith('/')) {\n problems.push({\n id: item.id,\n href: raw,\n reason: 'not-a-path',\n nearest: [],\n message: `Nav item '${item.id}' href '${raw}' is not a rooted path — it cannot resolve to a registered route`,\n })\n continue\n }\n const segments = toSegments(path)\n if (patternSegments.some((candidate) => matchesPattern(segments, candidate.segments, caseSensitive))) continue\n const nearest = nearestPatterns(segments, patternSegments.map((candidate) => candidate.pattern), caseSensitive)\n problems.push({\n id: item.id,\n href: raw,\n reason: 'unregistered',\n nearest,\n message:\n `Nav item '${item.id}' href '${raw}' matches no registered route` +\n (nearest.length ? ` — nearest registered: ${nearest.join(', ')}` : ''),\n })\n }\n\n return { checked, problems, external, patterns }\n}\n\n/** Registered patterns whose last segment equals the href's last segment: the\n * same destination under a different base is the near-miss worth printing. */\nfunction nearestPatterns(segments: readonly string[], patterns: readonly string[], caseSensitive: boolean): string[] {\n const tail = segments[segments.length - 1]\n if (tail === undefined) return []\n const same = (a: string, b: string): boolean => (caseSensitive ? a === b : a.toLowerCase() === b.toLowerCase())\n return patterns\n .filter((pattern) => {\n const patternTail = toSegments(pattern).at(-1)\n return patternTail !== undefined && same(patternTail, tail)\n })\n .slice(0, 5)\n}\n\n/**\n * Fail unless every nav href resolves to a registered route.\n *\n * Throws on an empty item list or an empty route table as well: a guard that\n * examined nothing reports safety it does not provide, and both are what a\n * mis-wired import looks like.\n */\nexport function assertNavHrefsRegistered(\n items: readonly NavHrefItem[],\n routes: NavRouteTable,\n options: NavHrefCheckOptions = {},\n): void {\n if (items.length === 0) {\n throw new Error('assertNavHrefsRegistered received no nav items — the check would pass without examining anything')\n }\n const report = checkNavHrefs(items, routes, options)\n if (report.patterns.length === 0) {\n throw new Error('assertNavHrefsRegistered received an empty route table — every href would fail or nothing would be proven')\n }\n // Real problems are reported before the vacuous-pass guard: rejected external\n // hrefs are problems that were never \"checked\", and the guard's message would\n // otherwise hide them.\n if (report.problems.length > 0) {\n const detail = report.problems.map((problem) => ` - ${problem.message}`).join('\\n')\n throw new Error(\n `${report.problems.length} of ${report.checked} nav hrefs do not resolve to a registered route:\\n${detail}\\n` +\n `Registered patterns (${report.patterns.length}): ${report.patterns.join(', ')}`,\n )\n }\n if (report.checked === 0) {\n throw new Error(\n `assertNavHrefsRegistered examined 0 hrefs (${report.external.length} external, ${ignoredCount(items, options)} ignored) — the check would pass without examining anything`,\n )\n }\n}\n\nfunction ignoredCount(items: readonly NavHrefItem[], options: NavHrefCheckOptions): number {\n const ignored = new Set((options.ignore ?? []).map((href) => normalizePath(href)))\n return flattenItems(items).filter((item) => ignored.has(normalizePath(item.href))).length\n}\n","/**\n * Session shell — the app-shell mechanism every agent product needs around the\n * chat surface: a list of past sessions in the rail, an entry point for a new\n * one, and a paged history view behind it.\n *\n * `/web-react` already owns the chat SURFACE (composer, transcript, cards); it\n * owned no session SHELL, so all four products hand-rolled one and drifted.\n * This module is the shell's pure half: no React, no DOM, no peer imports, so a\n * server loader can call `readRailCollapsedCookie` without dragging React into\n * a worker bundle (`/web-react` holds the rendered half).\n *\n * Domain stays a parameter. A \"session\" here is only an id, a title and a\n * timestamp — a gtm thread, a tax session and a legal matter are all the same\n * shape to the shell, and the product supplies routing through `hrefForSession`\n * rather than the shell knowing any URL.\n */\n\nimport { isUnderPrefix, normalizePath, stripSlashes, stripTrailingSlashes } from './path'\n\nexport * from './nav-guard'\n\n/** One session as the shell needs to see it. Products map their own row\n * (thread / session / matter) onto this before handing it over. */\nexport interface SessionSummary {\n id: string\n /** `null`/empty renders as the untitled placeholder rather than a blank row. */\n title: string | null\n /** ISO-8601. `null` when the product has no timestamp to show. */\n updatedAt: string | null\n isPinned?: boolean\n /** Unread for the viewer. Use `resolveSessionUnread` to fold live overlays in. */\n unread?: boolean\n /** Free-form product label (gtm categories, legal matter types). Passed\n * through untouched — the shell never interprets it. */\n category?: string | null\n}\n\n/** One fetched page of sessions with an optional continuation cursor. */\nexport interface SessionPage {\n items: SessionSummary[]\n /** Opaque continuation token; absent/null ⇒ no further pages. */\n nextCursor?: string | null\n}\n\n/** Sort order for the history view. The product's fetcher decides what these\n * mean against its own storage; the shell only round-trips the value. */\nexport type SessionSort = 'newest' | 'oldest'\n\n// ---------------------------------------------------------------------------\n// Rail items — structurally assignable to sandbox-ui's SidebarLayout types\n// ---------------------------------------------------------------------------\n\n/**\n * These mirror `@tangle-network/sandbox-ui/dashboard`'s `SidebarLayoutNavItem`\n * / `RailExpandableSubItem` STRUCTURALLY rather than importing them, so this\n * module stays free of the optional peer (invariant 3 — structural over\n * hard-dep when the surface is small). `tests/session-shell/rail-contract.test.ts`\n * assigns the builder output to the real sandbox-ui types, so a drift in either\n * direction fails CI instead of silently dropping a field at runtime.\n *\n * `TIcon` is the product's icon component type (lucide, custom, anything) —\n * generic so this file needs no React types.\n */\nexport interface SessionRailAction<TIcon = unknown> {\n id: string\n label: string\n icon?: TIcon\n destructive?: boolean\n onSelect: () => void\n}\n\nexport type RailPrefetch = 'none' | 'intent' | 'render' | 'viewport'\n\nexport interface SessionRailSubItem<TIcon = unknown> {\n id: string\n label: string\n href: string\n prefetch?: RailPrefetch\n /** Live working indicator — the session is mid-turn. */\n isLoading?: boolean\n /** Bold + leading dot. sandbox-ui suppresses it while `isLoading`. */\n unread?: boolean\n /** Emphasised row, used for the trailing \"view all\" overflow link. */\n emphasis?: boolean\n actions?: SessionRailAction<TIcon>[]\n}\n\nexport interface SessionRailNavItem<TIcon = unknown> {\n id: string\n /** REQUIRED, mirroring sandbox-ui — the rail renders `<Icon />` unguarded, so\n * an omitted icon is a blank/crashing row rather than a styling nit. */\n icon: TIcon\n label: string\n href: string\n badge?: number\n expandable?: boolean\n defaultOpen?: boolean\n subItems?: SessionRailSubItem<TIcon>[]\n subActiveIds?: string[]\n emptyLabel?: string\n prefetch?: RailPrefetch\n}\n\n/** Per-row rename/delete wiring. Supplied by the layout that owns the dialogs;\n * omitted (or `canEdit: false`) leaves rows read-only. */\nexport interface SessionRowActions<TIcon = unknown> {\n canEdit: boolean\n renameIcon?: TIcon\n deleteIcon?: TIcon\n renameLabel?: string\n deleteLabel?: string\n /**\n * Omit when the product cannot rename a session — the row then offers delete\n * only, instead of a menu item that does nothing.\n *\n * Independently optional, matching `SessionHistoryPanel`, which has always\n * rendered whichever of the two it was given. The rail builder used to demand\n * both, so a product with archive-but-no-rename (tax) could either fake a\n * rename or ship no row actions at all.\n */\n onRename?: (session: SessionSummary) => void\n onDelete?: (session: SessionSummary) => void\n /**\n * Row actions this shell has no opinion about — pin, categorise, duplicate,\n * share. Evaluated per session so a label can read that row's state\n * (\"Pin\" vs \"Unpin\"), and ordered between rename and delete so the\n * destructive action stays last.\n *\n * `id` must not be `rename` or `delete`; those are the shell's own.\n */\n extraActions?: (session: SessionSummary) => SessionRailAction<TIcon>[]\n}\n\nexport const UNTITLED_SESSION_LABEL = 'Untitled chat'\n\n/** Display title for a session row — trims, and falls back rather than\n * rendering an empty row the user cannot aim at. */\nexport function sessionLabel(session: SessionSummary, untitled = UNTITLED_SESSION_LABEL): string {\n return session.title?.trim() || untitled\n}\n\nexport interface BuildSessionSubItemsOptions<TIcon = unknown> {\n sessions: SessionSummary[]\n /** The product's route for one session. The shell never builds a URL itself. */\n hrefForSession: (sessionId: string) => string\n /** Ids currently mid-turn — renders the working indicator. */\n respondingSessionIds?: ReadonlySet<string>\n actions?: SessionRowActions<TIcon>\n untitledLabel?: string\n prefetch?: RailPrefetch\n /** Trailing \"view all\" row, appended when the capped list hides sessions. */\n overflow?: { href: string; label?: string }\n}\n\n/** Session rows for the rail's expandable history item. */\nexport function buildSessionSubItems<TIcon = unknown>({\n sessions,\n hrefForSession,\n respondingSessionIds,\n actions,\n untitledLabel = UNTITLED_SESSION_LABEL,\n prefetch = 'intent',\n overflow,\n}: BuildSessionSubItemsOptions<TIcon>): SessionRailSubItem<TIcon>[] {\n /**\n * Only the handlers the product actually supplied. An empty result becomes\n * `undefined` rather than `[]`, because sandbox-ui renders the kebab trigger\n * whenever `actions` is an array — an empty one is a button that opens an\n * empty menu.\n */\n const rowActions = (session: SessionSummary): SessionRailAction<TIcon>[] | undefined => {\n if (!actions?.canEdit) return undefined\n const built: SessionRailAction<TIcon>[] = []\n const { onRename, onDelete, extraActions } = actions\n if (onRename) {\n built.push({\n id: 'rename',\n label: actions.renameLabel ?? 'Rename',\n icon: actions.renameIcon,\n onSelect: () => onRename(session),\n })\n }\n if (extraActions) built.push(...extraActions(session))\n if (onDelete) {\n built.push({\n id: 'delete',\n label: actions.deleteLabel ?? 'Delete',\n icon: actions.deleteIcon,\n destructive: true,\n onSelect: () => onDelete(session),\n })\n }\n return built.length ? built : undefined\n }\n\n const rows: SessionRailSubItem<TIcon>[] = sessions.map((session) => ({\n id: session.id,\n label: sessionLabel(session, untitledLabel),\n href: hrefForSession(session.id),\n prefetch,\n isLoading: respondingSessionIds?.has(session.id) ?? false,\n unread: Boolean(session.unread),\n actions: rowActions(session),\n }))\n if (!overflow) return rows\n return [\n ...rows,\n {\n id: 'view-all',\n label: overflow.label ?? 'View all chats',\n href: overflow.href,\n prefetch,\n emphasis: true,\n },\n ]\n}\n\nexport interface BuildSessionNavItemOptions<TIcon = unknown>\n extends BuildSessionSubItemsOptions<TIcon> {\n /** Nav id the product highlights against (`activeNavId === id`). */\n id?: string\n label?: string\n /** The product's icon component. Required — see `SessionRailNavItem.icon`. */\n icon: TIcon\n /** The expandable row's own destination — the full history page. */\n href: string\n /** Session currently open, highlighted inside the expandable. */\n activeSessionId?: string | null\n emptyLabel?: string\n defaultOpen?: boolean\n}\n\n/**\n * The rail's session entry: one expandable nav row whose sub-items are the\n * recent sessions. This is the structure the owner asked for — history lives IN\n * the rail, not in a second sidebar panel beside it.\n */\nexport function buildSessionNavItem<TIcon = unknown>({\n id = 'history',\n label = 'History',\n icon,\n href,\n activeSessionId,\n emptyLabel = 'No chats yet',\n defaultOpen = true,\n ...subItemOptions\n}: BuildSessionNavItemOptions<TIcon>): SessionRailNavItem<TIcon> {\n return {\n id,\n icon,\n label,\n href,\n expandable: true,\n defaultOpen,\n subItems: buildSessionSubItems<TIcon>(subItemOptions),\n subActiveIds: activeSessionId ? [activeSessionId] : undefined,\n emptyLabel,\n prefetch: subItemOptions.prefetch ?? 'intent',\n }\n}\n\n// ---------------------------------------------------------------------------\n// Routing / selection\n// ---------------------------------------------------------------------------\n\nexport interface ActiveSessionIdOptions {\n pathname: string\n /** Workspace-scoped route base, e.g. `/app/ws_123`. */\n base: string\n /** Route segment sessions live under. Default `chat` ⇒ `${base}/chat/:id`.\n * Pass `''` when sessions sit DIRECTLY under the base (`/app/:sessionId`),\n * which is how one product routes them — then `reserved` is mandatory. */\n segment?: string\n /** Segment that means \"composing a new session\", not an id. Default `new`. */\n newSegment?: string\n /**\n * First segments that are OTHER routes, not session ids. Only meaningful\n * with `segment: ''`, where `/app/settings` is otherwise indistinguishable\n * from a session called `settings` — and resolving it as one would highlight\n * and prefetch a session that does not exist. Pass the product's own nav\n * paths; unknown-but-reserved is a routing bug, so this fails closed.\n */\n reserved?: readonly string[]\n}\n\n/**\n * The session id the current route has open, or `null` on the new-session\n * composer / anywhere else.\n *\n * Anchored at `base` on purpose. A bare `/\\/chat\\/([^/]+)/` scan — the shape\n * three products shipped — matches the FIRST `/chat/` anywhere in the path, so\n * a workspace or vault folder named `chat` resolves a neighbouring segment as a\n * session id and the rail highlights (and prefetches) a session the user is not\n * in. Same class as attaching to a stale box: it looks right and points at the\n * wrong row.\n */\nexport function activeSessionIdFromPath({\n pathname,\n base,\n segment = 'chat',\n newSegment = 'new',\n reserved,\n}: ActiveSessionIdOptions): string | null {\n const path = normalizePath(pathname)\n const root = stripTrailingSlashes(base)\n const prefix = segment ? `${root}/${segment}` : root\n if (!isUnderPrefix(path, prefix) || path === prefix) return null\n const id = path.slice(prefix.length + 1).split('/')[0] ?? ''\n if (!id || id === newSegment) return null\n // `/app/settings` under a segment-less route is a sibling page, not a\n // session named \"settings\".\n if (reserved?.some((name) => stripSlashes(name) === id)) return null\n return decodeURIComponent(id)\n}\n\n/** One rail destination. `path` is relative to the workspace base. */\nexport interface NavRouteDef {\n id: string\n path: string\n}\n\nexport interface ResolveActiveNavIdOptions {\n pathname: string\n base: string\n /** The product's rail rows, in any order — resolution is longest-prefix. */\n routes: NavRouteDef[]\n /** Extra prefixes that light an existing row: `{ '/agents': 'integrations' }`.\n * Participates in the same longest-prefix contest. */\n aliases?: Record<string, string>\n /** Prefixes that deliberately highlight NOTHING, beating any shorter match.\n * gtm uses this so an open chat lights no rail row while `/chat/new` still\n * lights \"New\". */\n claimsNothing?: string[]\n}\n\n/**\n * The rail row to highlight for the current route.\n *\n * Longest-prefix wins, so declaration order cannot change the answer. The\n * per-product versions this replaces were first-match over an array, which made\n * `/chat/new` vs `/chat` an ordering accident rather than a rule.\n */\nexport function resolveActiveNavId({\n pathname,\n base,\n routes,\n aliases,\n claimsNothing,\n}: ResolveActiveNavIdOptions): string | undefined {\n const path = normalizePath(pathname)\n const root = stripTrailingSlashes(base)\n let bestLength = -1\n let bestId: string | undefined\n const consider = (relative: string, id: string | undefined, winsTies = false) => {\n const full = stripTrailingSlashes(`${root}${relative}`)\n if (!isUnderPrefix(path, full)) return\n if (full.length > bestLength || (winsTies && full.length === bestLength)) {\n bestLength = full.length\n bestId = id\n }\n }\n for (const route of routes) consider(route.path, route.id)\n for (const [prefix, id] of Object.entries(aliases ?? {})) consider(prefix, id)\n // Declared last and wins an exact-length tie: naming a prefix in\n // `claimsNothing` is a deliberate override of the row that owns it, so\n // `claimsNothing: ['/chat']` beats a `{ id: 'chat', path: '/chat' }` row while\n // a longer `/chat/new` still wins on specificity.\n for (const prefix of claimsNothing ?? []) consider(prefix, undefined, true)\n return bestId\n}\n\n// ---------------------------------------------------------------------------\n// Sidebar list composition\n// ---------------------------------------------------------------------------\n\nexport interface ResolveSessionUnreadOptions {\n sessionId: string\n /** Server-computed unread from the route loader. */\n loaderUnread: boolean\n /** Live \"went unread\" ids from the workspace channel. */\n liveUnreadIds?: ReadonlySet<string>\n /** Ids this tab has already opened since the loader ran. */\n locallyReadIds?: ReadonlySet<string>\n /** The open session is never unread to its own viewer. */\n currentSessionId?: string | null\n}\n\n/**\n * Effective unread for one row. The loader's value can be stale — a layout\n * loader that survives same-workspace navigation keeps reporting a session as\n * unread after the user opened it — so live and local overlays win over it, and\n * the currently-open session always reads as read.\n */\nexport function resolveSessionUnread({\n sessionId,\n loaderUnread,\n liveUnreadIds,\n locallyReadIds,\n currentSessionId,\n}: ResolveSessionUnreadOptions): boolean {\n if (sessionId === currentSessionId) return false\n if (liveUnreadIds?.has(sessionId)) return true\n if (locallyReadIds?.has(sessionId)) return false\n return loaderUnread\n}\n\nexport interface ComposeSidebarSessionsOptions {\n /** Server-rendered rows, already ordered by the product's query. */\n loaderSessions: SessionSummary[]\n /** Optimistic rows from the live channel (a chat created in another tab). */\n optimisticSessions?: SessionSummary[]\n /** Rail cap. The full list lives on the history page. */\n limit: number\n /** Total sessions the product holds, used to decide the overflow row. */\n totalCount?: number\n liveUnreadIds?: ReadonlySet<string>\n locallyReadIds?: ReadonlySet<string>\n currentSessionId?: string | null\n}\n\nexport interface ComposedSidebarSessions {\n sessions: SessionSummary[]\n /** More sessions exist than the rail shows ⇒ render the \"view all\" row. */\n hasMore: boolean\n}\n\n/**\n * The rail's session list: optimistic rows first, then the loader's, capped,\n * with unread resolved per row.\n *\n * Optimistic rows are deduped against the loader by id — once a revalidation\n * brings a live-created session back from the server it must not appear twice\n * (duplicate React keys, and the row's actions would target the same session\n * from two places).\n */\nexport function composeSidebarSessions({\n loaderSessions,\n optimisticSessions = [],\n limit,\n totalCount,\n liveUnreadIds,\n locallyReadIds,\n currentSessionId,\n}: ComposeSidebarSessionsOptions): ComposedSidebarSessions {\n const loaderIds = new Set(loaderSessions.map((session) => session.id))\n const pendingNew = optimisticSessions.filter((session) => !loaderIds.has(session.id))\n const merged = [...pendingNew, ...loaderSessions]\n const sessions = merged.slice(0, Math.max(0, limit)).map((session) => ({\n ...session,\n unread: resolveSessionUnread({\n sessionId: session.id,\n loaderUnread: Boolean(session.unread),\n liveUnreadIds,\n locallyReadIds,\n currentSessionId,\n }),\n }))\n const known = (totalCount ?? loaderSessions.length) + pendingNew.length\n return { sessions, hasMore: known > sessions.length }\n}\n\n/**\n * Append a fetched page to held rows, dropping ids already shown. A session\n * bumped to the top between two page fetches otherwise arrives twice — once in\n * the page it moved out of and once in the page it moved into.\n */\nexport function mergeSessionPages(\n existing: SessionSummary[],\n incoming: SessionSummary[],\n): SessionSummary[] {\n const seen = new Set(existing.map((session) => session.id))\n return [...existing, ...incoming.filter((session) => !seen.has(session.id))]\n}\n\n// ---------------------------------------------------------------------------\n// Rail collapse cookie (SSR-seeded so the first paint matches the client)\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_RAIL_COOKIE_NAME = 'agent-sidebar-rail-collapsed'\n\n/**\n * Read the persisted rail-collapse state from a request's `Cookie` header, so\n * the server renders the rail in the state the user left it and the first\n * client render does not re-flow.\n *\n * Parses the header rather than building a `RegExp` from the cookie name (the\n * shape the products shipped): a name containing a regex metacharacter would\n * silently match the wrong cookie or none at all.\n */\nexport function readRailCollapsedCookie(\n cookieHeader: string | null | undefined,\n name: string = DEFAULT_RAIL_COOKIE_NAME,\n): boolean {\n for (const pair of (cookieHeader ?? '').split(';')) {\n const eq = pair.indexOf('=')\n if (eq === -1) continue\n if (pair.slice(0, eq).trim() !== name) continue\n return pair.slice(eq + 1).trim() === '1'\n }\n return false\n}\n\nexport interface RailCookieOptions {\n name?: string\n /** Seconds. Default one year. */\n maxAge?: number\n path?: string\n /** Omit to auto-detect: `secure` on https, off on http://localhost — a Secure\n * cookie is dropped there and the rail state would not persist in dev. */\n secure?: boolean\n}\n\n/** The cookie string for a collapse state. Usable as `document.cookie` or as a\n * `Set-Cookie` value. Exported separately so it is testable without a DOM. */\nexport function railCollapsedCookie(\n collapsed: boolean,\n { name = DEFAULT_RAIL_COOKIE_NAME, maxAge = 31_536_000, path = '/', secure }: RailCookieOptions = {},\n): string {\n const isSecure =\n secure ?? (typeof location !== 'undefined' && location.protocol === 'https:')\n return `${name}=${collapsed ? '1' : '0'}; path=${path}; max-age=${maxAge}; samesite=lax${isSecure ? '; secure' : ''}`\n}\n\n/** Persist the rail-collapse state from the browser. No-op without a document\n * so a shared toggle handler is safe to call during SSR. */\nexport function writeRailCollapsedCookie(collapsed: boolean, options: RailCookieOptions = {}): void {\n if (typeof document === 'undefined') return\n document.cookie = railCollapsedCookie(collapsed, options)\n}\n"],"mappings":"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","names":[]}