@workerdeck/ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +7 -0
  2. package/build/{SessionPanel-B9CHoq8x.d.mts → SessionPanel-CnU_IJ3-.d.mts} +36 -9
  3. package/build/{SessionPanel-DII9MmQ8.mjs → SessionPanel-DPx8Iz8a.mjs} +1432 -384
  4. package/build/SessionPanel-DPx8Iz8a.mjs.map +1 -0
  5. package/build/{format-DfI_je9S.d.mts → format-ljc3lKpA.d.mts} +1 -1
  6. package/build/format.d.mts +16 -5
  7. package/build/format.mjs +2 -3
  8. package/build/index.d.mts +288 -8
  9. package/build/index.mjs +620 -165
  10. package/build/index.mjs.map +1 -1
  11. package/build/{format-DqR56Y8l.mjs → status-BE-zg88x.mjs} +154 -2
  12. package/build/status-BE-zg88x.mjs.map +1 -0
  13. package/build/workspace.d.mts +4 -1
  14. package/build/workspace.mjs +4 -3
  15. package/build/workspace.mjs.map +1 -1
  16. package/package.json +8 -6
  17. package/src/components/agent/ContextRing.tsx +41 -0
  18. package/src/components/agent/EngineIcon.tsx +40 -0
  19. package/src/components/agent/ProjectIcon.tsx +119 -0
  20. package/src/components/agent/SessionBrowser.tsx +191 -17
  21. package/src/components/agent/SessionPanel.tsx +177 -2
  22. package/src/components/agent/SessionSteps.tsx +233 -0
  23. package/src/components/agent/SessionWorkspace.tsx +4 -0
  24. package/src/components/agent/StatusBar.tsx +4 -2
  25. package/src/components/agent/SubagentStrip.tsx +134 -0
  26. package/src/components/agent/ToolCallCard.tsx +72 -5
  27. package/src/components/agent/Transcript.tsx +212 -23
  28. package/src/components/agent/tool-result-fetch.tsx +36 -0
  29. package/src/components/agent/tool-result-image.tsx +209 -0
  30. package/src/components/agent/transcript-rows.ts +122 -23
  31. package/src/components/terminal/TerminalTranscript.tsx +154 -2
  32. package/src/components/terminal/affordances.tsx +34 -0
  33. package/src/components/terminal/blocks.ts +260 -0
  34. package/src/components/terminal/height.ts +73 -6
  35. package/src/components/terminal/image-box.ts +53 -0
  36. package/src/components/terminal/items.tsx +116 -79
  37. package/src/components/terminal/result-preview.ts +20 -6
  38. package/src/components/terminal/scrubber.tsx +172 -26
  39. package/src/components/terminal/tool-run.ts +177 -0
  40. package/src/index.ts +20 -1
  41. package/src/lib/status.ts +16 -3
  42. package/src/styles/terminal.css +85 -5
  43. package/src/styles/theme.css +42 -0
  44. package/build/SessionPanel-DII9MmQ8.mjs.map +0 -1
  45. package/build/format-DqR56Y8l.mjs.map +0 -1
  46. package/build/status-Ydzi7n6j.mjs +0 -143
  47. package/build/status-Ydzi7n6j.mjs.map +0 -1
package/build/index.mjs CHANGED
@@ -1,12 +1,11 @@
1
- import { $ as TranscriptDensityProvider, A as PermissionModeSelect, At as MenuTrigger, B as TerminalSurface, Bt as Button, C as TerminalQuestionPrompt, Ct as DialogHeader, D as parseUserQuestions, Dt as MenuContent, E as QuestionPrompt, Et as Menu, F as McpDialog, Ft as SelectTrigger, G as Blank, H as TerminalDiff, Ht as cn, I as HostFilesDialog, It as SelectValue, J as CopyAction, K as Ink, L as ContextDialog, Lt as Input, M as permissionModeMeta, Mt as SelectContent, N as ModelSelect, Nt as SelectItem, O as PermissionPrompt, Ot as MenuItem, P as SkillsDialog, Pt as SelectItemText, Q as toolIcon, R as Composer, Rt as Badge, S as SessionInfoDialog, St as DialogContent, T as QUESTION_BEHAVIORS, Tt as DialogTrigger, U as previewPatch, V as TerminalMarkdown, Vt as buttonVariants, W as Band, X as useAffordances, Y as WithActions, Z as isMutatingTool, _ as Conversation, _t as TooltipContent, a as Transcript, at as mentionTrigger, b as StatusBar, bt as DialogBody, c as ToolCallCard, ct as plainTextToSegments, d as Response, dt as Splitter, et as TranscriptVariantProvider, f as PromptTokenText, ft as CodeBlock, g as FileCard, gt as Tip, h as Loader, ht as copyText, i as useMinuteClock, it as hashtagTrigger, j as permissionModeChoices, jt as Select, k as PERMISSION_MODES, kt as MenuSeparator, l as SessionEmptyState, lt as segmentsToPlainText, m as MessageContent, mt as CopyButton, n as UsageDialog, nt as useTranscriptVariant, o as TerminalItemView, ot as usePromptAreaState, p as Message, pt as Spinner, q as Row, r as UsageMeters, rt as commandTrigger, s as TerminalTranscript, st as PromptArea, t as SessionPanel, tt as useTranscriptDensity, u as Reasoning, ut as ProgressRing, v as ConversationContent, vt as TooltipProvider, w as TerminalPermissionPrompt, wt as DialogRow, x as STATUS_META, xt as DialogClose, y as ConversationScrollButton, yt as Dialog, z as skillPrompt, zt as badgeVariants } from "./SessionPanel-DII9MmQ8.mjs";
2
- import { a as formatDuration, c as formatRelativeTime, d as rateLimitWindowSeconds, f as toolInputPreview, i as formatCountdown, l as formatTokens, n as formatBytes, o as formatRateLimitWindow, r as formatCost, s as formatRateLimitWindowLong, t as formatAgoPrecise, u as friendlyModel } from "./format-DqR56Y8l.mjs";
3
- import { a as statusPresentation, c as windowLabel, i as modelLabel, o as tightestWindow, r as meterSeverity, t as contextSeverity } from "./status-Ydzi7n6j.mjs";
1
+ import { $ as toolIcon, A as PERMISSION_MODES, At as MenuSeparator, B as skillPrompt, Bt as badgeVariants, C as TerminalQuestionPrompt, Ct as DialogContent, D as parseUserQuestions, Dt as Menu, E as QuestionPrompt, Et as DialogTrigger, F as SkillsDialog, Ft as SelectItemText, G as Band, H as TerminalMarkdown, Ht as buttonVariants, I as McpDialog, It as SelectTrigger, J as Row, K as Blank, L as HostFilesDialog, Lt as SelectValue, M as permissionModeChoices, Mt as Select, N as permissionModeMeta, Nt as SelectContent, O as SubagentStrip, Ot as MenuContent, P as ModelSelect, Pt as SelectItem, Q as isMutatingTool, R as ContextDialog, Rt as Input, S as SessionInfoDialog, St as DialogClose, T as QUESTION_BEHAVIORS, Tt as DialogRow, U as TerminalDiff, Ut as cn, V as TerminalSurface, Vt as Button, W as previewPatch, X as WithActions, Y as CopyAction, Z as useAffordances, _ as Conversation, _t as Tip, a as Transcript, at as hashtagTrigger, b as StatusBar, bt as Dialog, c as ToolCallCard, ct as PromptArea, d as Response, dt as ProgressRing, et as TranscriptDensityProvider, f as PromptTokenText, ft as Splitter, g as FileCard, gt as copyText, h as Loader, ht as CopyButton, i as useMinuteClock, it as commandTrigger, j as PermissionModeSelect, jt as MenuTrigger, k as PermissionPrompt, kt as MenuItem, l as SessionEmptyState, lt as plainTextToSegments, m as MessageContent, mt as Spinner, n as UsageDialog, nt as useTranscriptDensity, o as TerminalItemView, ot as mentionTrigger, p as Message, pt as CodeBlock, q as Ink, r as UsageMeters, rt as useTranscriptVariant, s as TerminalTranscript, st as usePromptAreaState, t as SessionPanel, tt as TranscriptVariantProvider, u as Reasoning, ut as segmentsToPlainText, v as ConversationContent, vt as TooltipContent, w as TerminalPermissionPrompt, wt as DialogHeader, x as STATUS_META, xt as DialogBody, y as ConversationScrollButton, yt as TooltipProvider, z as Composer, zt as Badge } from "./SessionPanel-DPx8Iz8a.mjs";
2
+ import { _ as formatRelativeTime, a as modelLabel, b as rateLimitWindowSeconds, d as formatBytes, f as formatCost, g as formatRateLimitWindowLong, h as formatRateLimitWindow, i as meterSeverity, l as windowLabel, m as formatDuration, o as statusPresentation, p as formatCountdown, r as meterColorClass, s as tightestWindow, t as contextSeverity, u as formatAgoPrecise, v as formatTokens, x as toolInputPreview, y as friendlyModel } from "./status-BE-zg88x.mjs";
4
3
  import { forwardRef, useEffect, useMemo, useRef, useState } from "react";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- import { BellRing, CircleAlert, CircleSlash, Layers, Moon, PauseCircle, Pencil, Search, SearchX, Trash2, X } from "lucide-react";
4
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Anchor, ArrowRight, Atom, Beaker, BellRing, Bike, Binary, Blocks, Bot, Box, Brain, Briefcase, Brush, Bug, Building2, Cable, Camera, Car, Cat, Check, ChevronDown, ChevronRight, CircleAlert, CircleSlash, CircuitBoard, Cloud, Code, Coffee, Cog, Compass, Container, Cpu, CreditCard, Database, Diamond, Dog, Dot, Feather, Film, Flame, FlaskConical, Folder, Gamepad2, Gauge, Gem, Ghost, Gift, GitBranch, Globe, GraduationCap, Hammer, Hexagon, House, Image, Key, Landmark, Laptop, Layers, Leaf, Library, Lightbulb, Lock, Mail, Map as Map$1, MessageCircle, Mic, Monitor, Moon, Mountain, Music, Network, Newspaper, Notebook, Orbit, Package, Palette, PauseCircle, PenTool, Pencil, Phone, PiggyBank, Plane, Plug, Puzzle, Radar, Radio, Receipt, Rocket, Ruler, Satellite, Scale, Scissors, Search, SearchX, Server, Shield, Ship, ShoppingCart, Signal, Smartphone, Sparkles, Sprout, Square, Star, Sun, Table, Tag, Target, Telescope, Terminal, TestTube, Trash2, TreePine, Trophy, Truck, Umbrella, Users, Wallet, Wand, Waves, Webhook, Wifi, Wrench, X, Zap } from "lucide-react";
7
6
  import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
8
7
  import { Toaster as Toaster$1, toast } from "sonner";
9
- import { STATE_LABELS, STATE_ORDER, adaptersOf, clearFilters, filterRows, groupRows, hasFacetFilter, sessionLabel, subsetSummary } from "@workerdeck/protocol";
8
+ import { STATE_LABELS, STATE_ORDER, adaptersOf, clearFilters, filterRows, groupRows, hasFacetFilter, isAgentRecord, projectLabel, projectSubpath, projectsOf, sessionLabel, subagentLabel, subsetSummary } from "@workerdeck/protocol";
10
9
  //#region src/components/ui/Card.tsx
11
10
  function Card({ className, ...props }) {
12
11
  return /* @__PURE__ */ jsx("div", {
@@ -311,6 +310,436 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
311
310
  });
312
311
  }
313
312
  //#endregion
313
+ //#region src/components/agent/ContextRing.tsx
314
+ /**
315
+ * How full a session's context window is, sized for a **list row**.
316
+ *
317
+ * The reading the session screen has always drawn, moved somewhere it answers a
318
+ * question the session screen cannot: *which* of these thirty sessions is
319
+ * bloating. That is also why it is a ring and not a number — across a list, a
320
+ * ring is read at a glance and `71%` has to be read one row at a time.
321
+ *
322
+ * **Absent draws nothing, and absent is not zero.** A promptless session, a
323
+ * parked one from before the field existed, or an engine that reports no window
324
+ * has no reading; an empty ring would claim its context is empty rather than
325
+ * unknown. The title carries the exact numbers for anyone who wants them.
326
+ *
327
+ * Shared rather than spelled per client: the dashboard's row and the VS Code
328
+ * sidebar's card both draw it, and two copies would be two sets of thresholds
329
+ * the day one of them is nudged.
330
+ */
331
+ function ContextRing({ usage, size = 11, className }) {
332
+ if (usage === void 0) return null;
333
+ return /* @__PURE__ */ jsx("span", {
334
+ title: `Context ${usage.percentage.toFixed(0)}% · ${formatTokens(usage.totalTokens)} / ${formatTokens(usage.maxTokens)}`,
335
+ className: cn("shrink-0 leading-none", meterColorClass(usage.percentage), className),
336
+ children: /* @__PURE__ */ jsx(ProgressRing, {
337
+ value: usage.percentage,
338
+ size,
339
+ strokeWidth: 2
340
+ })
341
+ });
342
+ }
343
+ //#endregion
344
+ //#region src/components/agent/EngineIcon.tsx
345
+ /**
346
+ * The engine's own mark, monochrome.
347
+ *
348
+ * Inlined rather than depended on: the React package these come from
349
+ * (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of
350
+ * lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.
351
+ * These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already
352
+ * `fill="currentColor"` single-path marks, so they take the row's colour like
353
+ * any other glyph and need no light/dark variants.
354
+ *
355
+ * The marks are trademarks of their owners (Anthropic, OpenAI, Google,
356
+ * DeepSeek, Moonshot), used here only to identify which engine a session runs
357
+ * on.
358
+ *
359
+ * Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's
360
+ * mark is the one a person recognises at 12px.
361
+ */
362
+ const PATHS = {
363
+ claude: {
364
+ title: "Claude",
365
+ d: "M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"
366
+ },
367
+ codex: {
368
+ title: "OpenAI",
369
+ d: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"
370
+ },
371
+ gemini: {
372
+ title: "Gemini",
373
+ d: "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"
374
+ },
375
+ deepseek: {
376
+ title: "DeepSeek",
377
+ d: "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
378
+ },
379
+ moonshot: {
380
+ title: "Moonshot",
381
+ d: "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"
382
+ }
383
+ };
384
+ /**
385
+ * Which mark a session wears.
386
+ *
387
+ * The two first-party engines are named directly. A `provider` session is
388
+ * whatever the host wired up, so its *model* is the only thing that says whose
389
+ * it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
390
+ * `kimi-k2`), and falling through to no mark rather than guessing wrong.
391
+ */
392
+ function engineMark(engine, model) {
393
+ if (engine === "claude") return "claude";
394
+ if (engine === "codex") return "codex";
395
+ const id = model?.toLowerCase() ?? "";
396
+ if (!id) return void 0;
397
+ if (id.includes("gemini")) return "gemini";
398
+ if (id.includes("deepseek")) return "deepseek";
399
+ if (id.includes("moonshot") || id.includes("kimi")) return "moonshot";
400
+ if (id.includes("claude")) return "claude";
401
+ if (id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.includes("openai")) return "codex";
402
+ }
403
+ /**
404
+ * Which engines wear their vendor's colour, and **how far it reaches**.
405
+ *
406
+ * `MARK` is the glyph, `TEXT` is the model name beside it. They are the same for
407
+ * Anthropic and deliberately not for OpenAI, because the two brands differ in
408
+ * kind rather than in hue: coral is an accent, and OpenAI's guidelines forbid
409
+ * adding colour to the mark at all, so theirs is pure white/black. At full
410
+ * contrast that is right on a 12px glyph and wrong on an 11px label — a
411
+ * pure-white model name is *brighter than the session title above it*, which
412
+ * inverts the row's whole hierarchy to say something the mark has already said.
413
+ * So OpenAI's name keeps the muted line it always had, which is also the most
414
+ * literal reading of "don't add any colors".
415
+ *
416
+ * Anything absent falls through to muted on both counts.
417
+ */
418
+ const VENDOR_MARK = {
419
+ claude: "text-vendor-claude",
420
+ codex: "text-vendor-openai"
421
+ };
422
+ const VENDOR_TEXT = { claude: "text-vendor-claude" };
423
+ /** The colour a session's engine mark wears. Pass it to `EngineIcon`'s
424
+ * `className` rather than to a parent: the svg carries its own `text-fg-3`,
425
+ * and only a class on the element itself is merged over it. */
426
+ function vendorMarkClass(engine, model) {
427
+ return VENDOR_MARK[engineMark(engine, model) ?? ""] ?? "text-fg-3";
428
+ }
429
+ /** The colour the model *name* wears beside that mark — muted unless the vendor
430
+ * is one whose accent survives at label size. See `VENDOR_TEXT`.
431
+ *
432
+ * The fallback is `text-fg-3` rather than the metadata line's own `text-fg-4`,
433
+ * so an unbranded model still sits one step above the project and gateway
434
+ * beside it — the order the spec lists them in, held without a colour. */
435
+ function vendorTextClass(engine, model) {
436
+ return VENDOR_TEXT[engineMark(engine, model) ?? ""] ?? "text-fg-3";
437
+ }
438
+ function EngineIcon({ engine, model, className }) {
439
+ const mark = PATHS[engineMark(engine, model) ?? ""];
440
+ if (!mark) return /* @__PURE__ */ jsx("span", {
441
+ "aria-hidden": true,
442
+ title: engine,
443
+ className: cn("shrink-0 text-fg-3", className),
444
+ children: "·"
445
+ });
446
+ return /* @__PURE__ */ jsxs("svg", {
447
+ viewBox: "0 0 24 24",
448
+ fill: "currentColor",
449
+ fillRule: "evenodd",
450
+ role: "img",
451
+ "aria-label": mark.title,
452
+ className: cn("size-3 shrink-0 text-fg-3", className),
453
+ children: [/* @__PURE__ */ jsx("title", { children: mark.title }), /* @__PURE__ */ jsx("path", { d: mark.d })]
454
+ });
455
+ }
456
+ //#endregion
457
+ //#region src/components/agent/ProjectIcon.tsx
458
+ /**
459
+ * A project's icon — the render side of protocol's `ProjectIcon`, for a list
460
+ * row or a group header.
461
+ *
462
+ * **The glyph arm is a curated set, and that is the design rather than a
463
+ * shortcut.** The gateway validates a glyph name by *shape* only (lucide's
464
+ * lowercase-kebab convention) and explicitly declines to grow an icon catalog,
465
+ * so the contract already says a client must fall back on a name it does not
466
+ * know.
467
+ *
468
+ * The alternative was measured rather than argued, against the VS Code
469
+ * sidebar's own bundle: `sidebar.js` is **31 KB** with one glyph, **77 KB**
470
+ * with the 110 below (~418 bytes each), and **927 KB** with a
471
+ * `import * as` over lucide's ~1,600 — which is what it costs, because a
472
+ * namespace import defeats tree-shaking by construction. (`DynamicIcon` is the
473
+ * third option and worse in a different currency: ~1,600 chunk files shipped
474
+ * inside the `.vsix`.) Twelve times the bundle to render a name nobody has
475
+ * declared yet is not a trade worth making; 46 KB to cover the names people
476
+ * actually use is.
477
+ *
478
+ * The set is chosen to cover what people call a project; an unlisted-but-valid
479
+ * name draws {@link Folder}, which is what the row drew before this feature
480
+ * existed. **Grow it freely** — each addition is ~400 bytes and the fallback
481
+ * means a missing one is a shrug, not a hole.
482
+ *
483
+ * **The image arm draws nothing until its bytes arrive**, and takes them as a
484
+ * resolved `src` rather than fetching: the wire carries only an address
485
+ * (media type, size, content hash), and *who* fetches it differs per client —
486
+ * a VS Code webview cannot reach a gateway at all and must be handed a data
487
+ * URL by its extension host, while a browser can fetch and `createObjectURL`.
488
+ * A component that fetched would be wrong on one of them. Absent `src` renders
489
+ * nothing rather than a placeholder box: an icon is decoration beside a name
490
+ * that is already there, and a box that becomes a picture a beat later is more
491
+ * movement than the picture is worth.
492
+ *
493
+ * One thing an image icon cannot do, and a glyph can: **take the row's
494
+ * colour.** An `<img>`-embedded SVG is its own document, so `currentColor` does
495
+ * not reach it and its own `prefers-color-scheme` resolves against the *OS*
496
+ * rather than the host's theme (measured: it does resolve, which is the
497
+ * surprise — so a monochrome mark tuned for both schemes is right on a dark OS
498
+ * in a dark editor and wrong on a light OS in a dark one). A repo that wants a
499
+ * mark that always matches its row should declare a glyph; one that wants its
500
+ * brand should declare the image and accept that it is a picture.
501
+ */
502
+ const GLYPHS = {
503
+ anchor: Anchor,
504
+ atom: Atom,
505
+ beaker: Beaker,
506
+ bike: Bike,
507
+ binary: Binary,
508
+ blocks: Blocks,
509
+ bot: Bot,
510
+ box: Box,
511
+ brain: Brain,
512
+ briefcase: Briefcase,
513
+ brush: Brush,
514
+ bug: Bug,
515
+ "building-2": Building2,
516
+ cable: Cable,
517
+ camera: Camera,
518
+ car: Car,
519
+ cat: Cat,
520
+ "circuit-board": CircuitBoard,
521
+ cloud: Cloud,
522
+ code: Code,
523
+ coffee: Coffee,
524
+ cog: Cog,
525
+ compass: Compass,
526
+ container: Container,
527
+ cpu: Cpu,
528
+ "credit-card": CreditCard,
529
+ database: Database,
530
+ diamond: Diamond,
531
+ dog: Dog,
532
+ feather: Feather,
533
+ film: Film,
534
+ flame: Flame,
535
+ "flask-conical": FlaskConical,
536
+ folder: Folder,
537
+ "gamepad-2": Gamepad2,
538
+ gauge: Gauge,
539
+ gem: Gem,
540
+ ghost: Ghost,
541
+ gift: Gift,
542
+ "git-branch": GitBranch,
543
+ globe: Globe,
544
+ "graduation-cap": GraduationCap,
545
+ hammer: Hammer,
546
+ hexagon: Hexagon,
547
+ house: House,
548
+ image: Image,
549
+ key: Key,
550
+ landmark: Landmark,
551
+ laptop: Laptop,
552
+ layers: Layers,
553
+ leaf: Leaf,
554
+ library: Library,
555
+ lightbulb: Lightbulb,
556
+ lock: Lock,
557
+ mail: Mail,
558
+ map: Map$1,
559
+ "message-circle": MessageCircle,
560
+ mic: Mic,
561
+ monitor: Monitor,
562
+ moon: Moon,
563
+ mountain: Mountain,
564
+ music: Music,
565
+ network: Network,
566
+ newspaper: Newspaper,
567
+ notebook: Notebook,
568
+ orbit: Orbit,
569
+ package: Package,
570
+ palette: Palette,
571
+ "pen-tool": PenTool,
572
+ phone: Phone,
573
+ "piggy-bank": PiggyBank,
574
+ plane: Plane,
575
+ plug: Plug,
576
+ puzzle: Puzzle,
577
+ radar: Radar,
578
+ radio: Radio,
579
+ receipt: Receipt,
580
+ rocket: Rocket,
581
+ ruler: Ruler,
582
+ satellite: Satellite,
583
+ scale: Scale,
584
+ scissors: Scissors,
585
+ server: Server,
586
+ shield: Shield,
587
+ ship: Ship,
588
+ "shopping-cart": ShoppingCart,
589
+ signal: Signal,
590
+ smartphone: Smartphone,
591
+ sparkles: Sparkles,
592
+ sprout: Sprout,
593
+ square: Square,
594
+ star: Star,
595
+ sun: Sun,
596
+ table: Table,
597
+ tag: Tag,
598
+ target: Target,
599
+ telescope: Telescope,
600
+ terminal: Terminal,
601
+ "test-tube": TestTube,
602
+ "tree-pine": TreePine,
603
+ trophy: Trophy,
604
+ truck: Truck,
605
+ umbrella: Umbrella,
606
+ users: Users,
607
+ wallet: Wallet,
608
+ wand: Wand,
609
+ waves: Waves,
610
+ webhook: Webhook,
611
+ wifi: Wifi,
612
+ wrench: Wrench,
613
+ zap: Zap
614
+ };
615
+ function ProjectIcon({ icon, src, name, className }) {
616
+ if (!icon) return null;
617
+ if (icon.type === "image") {
618
+ if (!src) return null;
619
+ return /* @__PURE__ */ jsx("img", {
620
+ src,
621
+ alt: "",
622
+ "aria-hidden": true,
623
+ title: name,
624
+ className: cn("inline-block size-3 shrink-0 object-contain", className)
625
+ });
626
+ }
627
+ return /* @__PURE__ */ jsx(GLYPHS[icon.name] ?? Folder, { className: cn("inline-block size-3 shrink-0", className) });
628
+ }
629
+ //#endregion
630
+ //#region src/components/agent/SessionSteps.tsx
631
+ function sessionSteps(info, onSelectSubagent) {
632
+ return (info.subagents ?? []).map((sub) => ({
633
+ key: sub.toolUseId,
634
+ label: subagentLabel(sub),
635
+ noun: "agent",
636
+ kind: isAgentRecord(sub) ? "agent" : "task",
637
+ state: stepState(sub.status),
638
+ detail: sub.toolCount > 0 ? String(sub.toolCount) : void 0,
639
+ title: `${subagentLabel(sub)} · ${sub.toolCount} tool${sub.toolCount === 1 ? "" : "s"}`,
640
+ onSelect: () => onSelectSubagent(sub.toolUseId)
641
+ }));
642
+ }
643
+ function stepState(status) {
644
+ switch (status) {
645
+ case "running": return "running";
646
+ case "failed": return "failed";
647
+ default: return "done";
648
+ }
649
+ }
650
+ /** How many of these are still going — the live half of the disclosure's count. */
651
+ function runningSteps(steps) {
652
+ return steps.filter((s) => s.state === "running").length;
653
+ }
654
+ /**
655
+ * The disclosure, which is also the reading: `3 agents` — or `2 of 3 agents`
656
+ * while some have settled, because "how many are still going" is the live
657
+ * question and a bare total answers it wrong the moment one finishes.
658
+ *
659
+ * Sub-agents are an annotation on a working row rather than a state of their own
660
+ * (see `runningSubagents` in protocol's `session-list.ts`), so this never
661
+ * competes with the row's status glyph: that still says what the *session* is
662
+ * doing.
663
+ */
664
+ function StepToggle({ expanded, running, total, noun, onToggle }) {
665
+ const label = running > 0 && running < total ? `${running}/${total}` : String(total);
666
+ const words = running > 0 && running < total ? `${running} of ${total} ${noun}s running` : `${total} ${noun}${total === 1 ? "" : "s"}`;
667
+ const Chevron = expanded ? ChevronDown : ChevronRight;
668
+ return /* @__PURE__ */ jsxs("button", {
669
+ type: "button",
670
+ "aria-expanded": expanded,
671
+ "aria-label": `${expanded ? "Hide" : "Show"} ${words}`,
672
+ title: `${expanded ? "Hide" : "Show"} ${words}`,
673
+ onClick: (e) => {
674
+ e.stopPropagation();
675
+ onToggle();
676
+ },
677
+ className: cn("flex shrink-0 items-center gap-0.5 rounded px-0.5 outline-none", "hover:bg-surface-hover hover:text-fg-2", running > 0 ? "text-info" : "text-fg-4"),
678
+ children: [/* @__PURE__ */ jsx(Chevron, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
679
+ className: "tabular-nums",
680
+ children: label
681
+ })]
682
+ });
683
+ }
684
+ /**
685
+ * One step under its session. Pressing an **agent** hands the panel over to that
686
+ * agent's own work — it is not a session and never becomes one, but it does now
687
+ * have a surface (`SessionPanel.openSubagent`). A host that cannot frame one
688
+ * falls back to revealing its `Task` row, which was this row's only meaning
689
+ * before the takeover existed. A **task** is not pressable at all; see
690
+ * {@link Step.kind}.
691
+ *
692
+ * Divided from the row's header and from each other by a rule rather than by
693
+ * indentation: these are a list *inside* the row, and at 11px an indent is not
694
+ * enough to say so. The rule is black at 25% so it darkens whatever the row is
695
+ * filled with, selected or not, without needing a colour per state.
696
+ */
697
+ function StepRow({ step, onSelect }) {
698
+ const agent = step.kind === "agent";
699
+ const body = step.state === "failed" ? "text-danger" : agent ? "text-success" : "text-fg-4";
700
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
701
+ /* @__PURE__ */ jsx(StepIcon, {
702
+ state: step.state,
703
+ kind: step.kind
704
+ }),
705
+ /* @__PURE__ */ jsx("span", {
706
+ className: "min-w-0 flex-1 truncate",
707
+ children: step.label
708
+ }),
709
+ step.detail ? /* @__PURE__ */ jsx("span", {
710
+ className: "shrink-0 tabular-nums text-fg-4",
711
+ children: step.detail
712
+ }) : null,
713
+ agent ? /* @__PURE__ */ jsx(ArrowRight, { className: "size-3 shrink-0 opacity-60" }) : null
714
+ ] });
715
+ const shape = "flex w-full items-center gap-2 border-t border-black/25 py-1 pl-3 pr-2 text-left text-label outline-none";
716
+ if (!agent) return /* @__PURE__ */ jsx("div", {
717
+ title: step.title,
718
+ onClick: (e) => e.stopPropagation(),
719
+ className: cn(shape, body),
720
+ children: content
721
+ });
722
+ return /* @__PURE__ */ jsx("button", {
723
+ type: "button",
724
+ title: step.title,
725
+ onClick: (e) => {
726
+ e.stopPropagation();
727
+ onSelect();
728
+ },
729
+ className: cn(shape, "hover:bg-surface-hover", body),
730
+ children: content
731
+ });
732
+ }
733
+ function StepIcon({ state, kind }) {
734
+ if (kind === "task" && state !== "running" && state !== "failed") return /* @__PURE__ */ jsx(Dot, { className: "size-3 shrink-0" });
735
+ switch (state) {
736
+ case "running": return /* @__PURE__ */ jsx(Spinner, { className: "size-3 shrink-0" });
737
+ case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: "size-3 shrink-0" });
738
+ case "pending": return /* @__PURE__ */ jsx(PauseCircle, { className: "size-3 shrink-0" });
739
+ default: return /* @__PURE__ */ jsx(Check, { className: "size-3 shrink-0" });
740
+ }
741
+ }
742
+ //#endregion
314
743
  //#region src/components/agent/SessionBrowser.tsx
315
744
  /**
316
745
  * How a list row is drawn, in one place, so `SidebarRow` in `web` matches this
@@ -331,7 +760,7 @@ function SessionList({ sessions, activeId, onSelect, onDelete, emptyText = "No s
331
760
  function rowShapeClass(active) {
332
761
  return cn("px-2 py-1.5 hover:bg-row-hover", active ? "mr-1 ml-0 rounded-r-md border-l-4 border-l-accent" : "mx-1 rounded-md");
333
762
  }
334
- function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelect, onDelete, onRename, emptyState, showControls = true, className }) {
763
+ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelect, onDelete, onRename, onSelectSubagent, emptyState, showControls = true, projectIcons, className }) {
335
764
  const visible = useMemo(() => filterRows(rows, config, scope), [
336
765
  rows,
337
766
  config,
@@ -340,6 +769,7 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
340
769
  const groups = useMemo(() => groupRows(visible, config), [visible, config]);
341
770
  const subset = subsetSummary(config, scope, visible.length, rows.length);
342
771
  const adapters = useMemo(() => adaptersOf(rows), [rows]);
772
+ const projects = useMemo(() => projectsOf(rows), [rows]);
343
773
  const gateways = useMemo(() => {
344
774
  const seen = /* @__PURE__ */ new Map();
345
775
  for (const row of rows) seen.set(row.hostId, row.hostName);
@@ -405,6 +835,18 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
405
835
  onChange: (gateways) => set({ gateways })
406
836
  })
407
837
  }) : null,
838
+ projects.length > 1 ? /* @__PURE__ */ jsx(FilterRow, {
839
+ label: "Project",
840
+ children: /* @__PURE__ */ jsx(FacetSelect, {
841
+ label: "Project",
842
+ value: config.projects ?? [],
843
+ options: projects.map((p) => ({
844
+ value: p.key,
845
+ label: p.label
846
+ })),
847
+ onChange: (next) => set({ projects: next })
848
+ })
849
+ }) : null,
408
850
  /* @__PURE__ */ jsx(FilterRow, {
409
851
  label: "Group",
410
852
  children: /* @__PURE__ */ jsx(OneOfSelect, {
@@ -423,6 +865,10 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
423
865
  value: "adapter",
424
866
  label: "By engine"
425
867
  },
868
+ ...projects.length > 1 ? [{
869
+ value: "project",
870
+ label: "By project"
871
+ }] : [],
426
872
  ...gateways.length > 1 ? [{
427
873
  value: "gateway",
428
874
  label: "By gateway"
@@ -449,6 +895,10 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
449
895
  value: "state",
450
896
  label: "State"
451
897
  },
898
+ ...projects.length > 1 ? [{
899
+ value: "project",
900
+ label: "Project"
901
+ }] : [],
452
902
  ...gateways.length > 1 ? [{
453
903
  value: "gateway",
454
904
  label: "Gateway"
@@ -491,114 +941,197 @@ function SessionBrowser({ rows, config, onConfigChange, scope, activeId, onSelec
491
941
  children: groups.map((group) => /* @__PURE__ */ jsxs("div", {
492
942
  className: "flex flex-col gap-1",
493
943
  children: [config.groupBy !== "none" && group.label ? /* @__PURE__ */ jsxs("div", {
494
- className: "flex items-baseline gap-2 px-3 text-label font-medium text-fg-4",
495
- children: [/* @__PURE__ */ jsx("span", {
496
- className: "uppercase tracking-wide",
497
- children: group.label
498
- }), /* @__PURE__ */ jsx("span", {
499
- className: "text-fg-4/70",
500
- children: group.rows.length
501
- })]
944
+ className: "flex items-center gap-2 px-3 text-label font-medium text-fg-4",
945
+ children: [
946
+ config.groupBy === "project" ? /* @__PURE__ */ jsx(ProjectIcon, {
947
+ icon: group.rows[0]?.info.project?.icon,
948
+ src: iconSrcOf(group.rows[0], projectIcons),
949
+ name: group.label
950
+ }) : null,
951
+ /* @__PURE__ */ jsx("span", {
952
+ className: "uppercase tracking-wide",
953
+ children: group.label
954
+ }),
955
+ /* @__PURE__ */ jsx("span", {
956
+ className: "text-fg-4/70",
957
+ children: group.rows.length
958
+ })
959
+ ]
502
960
  }) : null, group.rows.map((row) => /* @__PURE__ */ jsx(SessionRowItem, {
503
961
  row,
504
962
  active: row.info.id === activeId,
505
- showGateway: gateways.length > 1,
963
+ showGateway: gateways.length > 1 && config.groupBy !== "gateway",
964
+ showProject: config.groupBy !== "project",
965
+ projectIcons,
506
966
  onSelect,
507
967
  onDelete,
508
- onRename
968
+ onRename,
969
+ onSelectSubagent
509
970
  }, `${row.hostId}:${row.info.id}`))]
510
971
  }, group.key))
511
972
  })
512
973
  ]
513
974
  });
514
975
  }
515
- function SessionRowItem({ row, active, showGateway, onSelect, onDelete, onRename }) {
976
+ /** The bytes for a row's project icon, if it has an image one and the caller has
977
+ * fetched it yet. Shared by the row and its group header so the two cannot draw
978
+ * different pictures for one project. */
979
+ function iconSrcOf(row, icons) {
980
+ const icon = row?.info.project?.icon;
981
+ return icon?.type === "image" ? icons?.[icon.hash] : void 0;
982
+ }
983
+ function SessionRowItem({ row, active, showGateway, showProject = true, projectIcons, onSelect, onDelete, onRename, onSelectSubagent }) {
516
984
  const { info } = row;
517
985
  const [editing, setEditing] = useState(false);
518
- const folder = info.cwd.split("/").filter(Boolean).pop() ?? info.cwd;
986
+ const [expanded, setExpanded] = useState(false);
987
+ const project = showProject ? projectLabel(row) : projectSubpath(row);
988
+ const projectIcon = showProject ? info.project?.icon : void 0;
989
+ const engine = info.engine ?? "claude";
519
990
  const details = [
991
+ project,
520
992
  showGateway ? row.hostName : void 0,
521
- friendlyModel(info.model),
522
- folder,
523
993
  info.profile ? `@${info.profile}` : void 0,
524
994
  formatCost(info.totalCostUsd)
525
995
  ].filter(Boolean);
996
+ const steps = sessionSteps(info, (toolUseId) => onSelectSubagent ? onSelectSubagent(row, toolUseId) : onSelect?.(row));
997
+ const cut = project === void 0 ? -1 : details.indexOf(project);
526
998
  return /* @__PURE__ */ jsxs("div", {
527
999
  "data-slot": "session-row",
528
1000
  "data-active": active || void 0,
529
1001
  onClick: () => !editing && onSelect?.(row),
530
1002
  className: cn("group flex cursor-pointer flex-col gap-0.5 text-left transition-colors", rowShapeClass(active === true)),
531
- children: [/* @__PURE__ */ jsxs("div", {
532
- className: "flex items-center gap-1.5",
533
- children: [
534
- editing && onRename ? /* @__PURE__ */ jsx(NameEditor, {
535
- initial: info.title ?? "",
536
- onCommit: (title) => {
537
- setEditing(false);
538
- onRename(row, title);
539
- },
540
- onCancel: () => setEditing(false)
541
- }) : /* @__PURE__ */ jsx("button", {
542
- type: "button",
543
- className: cn("min-w-0 flex-1 truncate text-left text-body-sm outline-none", active ? "font-medium text-fg-1" : "text-fg-2"),
544
- children: sessionLabel(info)
545
- }),
546
- row.unseen > 0 ? /* @__PURE__ */ jsx("span", {
547
- title: `${row.unseen} new`,
548
- className: "shrink-0 rounded-full bg-accent px-1.5 text-label text-accent-fg",
549
- children: row.unseen
550
- }) : null,
551
- /* @__PURE__ */ jsx("span", {
552
- className: "shrink-0 text-label text-fg-4",
553
- children: formatRelativeTime(info.lastActivityAt ?? info.createdAt)
554
- }),
555
- /* @__PURE__ */ jsx(SessionStatusIcon, { row })
556
- ]
557
- }), /* @__PURE__ */ jsxs("div", {
558
- className: "flex items-center gap-1 text-label text-fg-4",
559
- children: [
560
- /* @__PURE__ */ jsx("button", {
561
- type: "button",
562
- tabIndex: -1,
563
- className: "min-w-0 flex-1 truncate text-left font-mono outline-none",
564
- children: details.join(" · ")
565
- }),
566
- onRename && !editing ? /* @__PURE__ */ jsx(Button, {
567
- variant: "ghost",
568
- size: "icon-sm",
569
- "aria-label": "Rename session",
570
- className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
571
- onClick: (e) => {
572
- e.stopPropagation();
573
- setEditing(true);
574
- },
575
- children: /* @__PURE__ */ jsx(Pencil, { className: "size-3 text-fg-3" })
576
- }) : null,
577
- onDelete ? /* @__PURE__ */ jsx(Button, {
578
- variant: "ghost",
579
- size: "icon-sm",
580
- "aria-label": "Close session",
581
- className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
582
- onClick: (e) => {
583
- e.stopPropagation();
584
- onDelete(row);
585
- },
586
- children: /* @__PURE__ */ jsx(Trash2, { className: "size-3 text-fg-3" })
587
- }) : null
588
- ]
589
- })]
1003
+ children: [
1004
+ /* @__PURE__ */ jsxs("div", {
1005
+ className: "flex items-center gap-1.5",
1006
+ children: [
1007
+ /* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(SessionStatusIcon, { row }) }),
1008
+ editing && onRename ? /* @__PURE__ */ jsx(NameEditor, {
1009
+ initial: info.title ?? "",
1010
+ onCommit: (title) => {
1011
+ setEditing(false);
1012
+ onRename(row, title);
1013
+ },
1014
+ onCancel: () => setEditing(false)
1015
+ }) : /* @__PURE__ */ jsx("button", {
1016
+ type: "button",
1017
+ className: cn("min-w-0 flex-1 truncate text-left text-body-sm outline-none", active ? "font-medium text-fg-1" : "text-fg-2"),
1018
+ children: sessionLabel(info)
1019
+ }),
1020
+ row.unseen > 0 ? /* @__PURE__ */ jsx("span", {
1021
+ title: `${row.unseen} new`,
1022
+ className: "shrink-0 rounded-full bg-accent px-1.5 text-label text-accent-fg",
1023
+ children: row.unseen
1024
+ }) : null,
1025
+ /* @__PURE__ */ jsx("span", {
1026
+ className: "shrink-0 text-label text-fg-4",
1027
+ children: formatRelativeTime(info.lastActivityAt ?? info.createdAt)
1028
+ }),
1029
+ /* @__PURE__ */ jsx(ContextRing, { usage: info.contextUsage })
1030
+ ]
1031
+ }),
1032
+ /* @__PURE__ */ jsxs("div", {
1033
+ className: "flex items-center gap-1 text-label text-fg-4",
1034
+ children: [
1035
+ /* @__PURE__ */ jsx(Gutter, { children: /* @__PURE__ */ jsx(EngineIcon, {
1036
+ engine,
1037
+ model: info.model,
1038
+ className: vendorMarkClass(engine, info.model)
1039
+ }) }),
1040
+ /* @__PURE__ */ jsxs("button", {
1041
+ type: "button",
1042
+ tabIndex: -1,
1043
+ className: "min-w-0 flex-1 truncate text-left font-mono outline-none",
1044
+ children: [
1045
+ /* @__PURE__ */ jsx("span", {
1046
+ className: vendorTextClass(engine, info.model),
1047
+ children: friendlyModel(info.model)
1048
+ }),
1049
+ details.length > 0 ? " · " : "",
1050
+ cut < 0 ? details.join(" · ") : /* @__PURE__ */ jsxs(Fragment$1, { children: [
1051
+ details.slice(0, cut).map((part) => `${part} · `),
1052
+ /* @__PURE__ */ jsx(ProjectIcon, {
1053
+ icon: projectIcon,
1054
+ src: iconSrcOf(row, projectIcons),
1055
+ name: project,
1056
+ className: "mr-1 align-[-0.2em]"
1057
+ }),
1058
+ details.slice(cut).join(" · ")
1059
+ ] })
1060
+ ]
1061
+ }),
1062
+ steps.length > 0 ? /* @__PURE__ */ jsx(StepToggle, {
1063
+ expanded,
1064
+ running: runningSteps(steps),
1065
+ total: steps.length,
1066
+ noun: steps[0].noun,
1067
+ onToggle: () => setExpanded((v) => !v)
1068
+ }) : null,
1069
+ onRename && !editing ? /* @__PURE__ */ jsx(Button, {
1070
+ variant: "ghost",
1071
+ size: "icon-sm",
1072
+ "aria-label": "Rename session",
1073
+ className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
1074
+ onClick: (e) => {
1075
+ e.stopPropagation();
1076
+ setEditing(true);
1077
+ },
1078
+ children: /* @__PURE__ */ jsx(Pencil, { className: "size-3 text-fg-3" })
1079
+ }) : null,
1080
+ onDelete ? /* @__PURE__ */ jsx(Button, {
1081
+ variant: "ghost",
1082
+ size: "icon-sm",
1083
+ "aria-label": "Close session",
1084
+ className: "size-5 shrink-0 opacity-0 transition-opacity group-hover:opacity-100",
1085
+ onClick: (e) => {
1086
+ e.stopPropagation();
1087
+ onDelete(row);
1088
+ },
1089
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-3 text-fg-3" })
1090
+ }) : null
1091
+ ]
1092
+ }),
1093
+ expanded ? steps.map((step) => /* @__PURE__ */ jsx(StepRow, {
1094
+ step,
1095
+ onSelect: step.onSelect
1096
+ }, step.key)) : null
1097
+ ]
1098
+ });
1099
+ }
1100
+ /**
1101
+ * The one 14px cell both lines hang their glyph in.
1102
+ *
1103
+ * A shared column rather than a leading character: the state and the engine mark
1104
+ * are different widths and a bare glyph would let the two text columns start at
1105
+ * different x. Centring inside a fixed cell is what makes them agree —
1106
+ * `packages/ui`'s terminal-gutter argument at row scale.
1107
+ */
1108
+ function Gutter({ children }) {
1109
+ return /* @__PURE__ */ jsx("span", {
1110
+ className: "flex w-3.5 shrink-0 items-center justify-center",
1111
+ children
590
1112
  });
591
1113
  }
592
1114
  /**
593
- * State as one glyph on the right edge — a ringing bell when it wants a human, a
594
- * spinner while it works, a moon when it is only sleeping. Replaces the text
595
- * badge: in a sidebar the word costs more room than it earns, and the states
596
- * that matter are the two you can recognise without reading.
1115
+ * State as one glyph — a ringing bell when it wants a human, a spinner while it
1116
+ * works, a moon when it is only sleeping. Replaces the text badge: in a sidebar
1117
+ * the word costs more room than it earns, and the states that matter are the two
1118
+ * you can recognise without reading.
1119
+ *
1120
+ * **It reads `row.state`, not `info.status`, and that distinction is the whole
1121
+ * point of the row model.** `sessionState` already folds in the arm this glyph
1122
+ * cannot see for itself: a *background* sub-agent outlives its turn by design, so
1123
+ * the turn ends, `status` comes to rest at `idle`, and the agent keeps working.
1124
+ * Reading the raw status drew a **moon on a row filed under the "Working"
1125
+ * header** — the list contradicting itself on one line, which is exactly what a
1126
+ * derived view model exists to prevent. The value was in scope and unread.
1127
+ *
1128
+ * The terminal statuses still come off `info.status`, because `ended` collapses
1129
+ * `failed` and `closed` into one bucket and those are worth telling apart here.
597
1130
  */
598
1131
  function SessionStatusIcon({ row }) {
599
1132
  const { info } = row;
600
- if (info.pendingPermissionCount > 0 || info.status === "awaiting_approval") return /* @__PURE__ */ jsx(BellRing, { className: "size-3.5 shrink-0 animate-pulse text-warning" });
601
- if (info.status === "running" || info.status === "starting") return /* @__PURE__ */ jsx(Spinner, { className: "size-3.5 shrink-0 text-info" });
1133
+ if (row.state === "attention") return /* @__PURE__ */ jsx(BellRing, { className: "size-3.5 shrink-0 animate-pulse text-warning" });
1134
+ if (row.state === "working") return /* @__PURE__ */ jsx(Spinner, { className: "size-3.5 shrink-0 text-info" });
602
1135
  switch (info.status) {
603
1136
  case "failed": return /* @__PURE__ */ jsx(CircleAlert, { className: "size-3.5 shrink-0 text-danger" });
604
1137
  case "closed": return /* @__PURE__ */ jsx(CircleSlash, { className: "size-3.5 shrink-0 text-fg-4" });
@@ -692,84 +1225,6 @@ function OneOfSelect({ label, value, options, onChange }) {
692
1225
  });
693
1226
  }
694
1227
  //#endregion
695
- //#region src/components/agent/EngineIcon.tsx
696
- /**
697
- * The engine's own mark, monochrome.
698
- *
699
- * Inlined rather than depended on: the React package these come from
700
- * (`@lobehub/icons`) pulls antd, `@lobehub/ui` and a second copy of
701
- * lucide-react — an absurd amount of bundle for a few glyphs in a sidebar.
702
- * These are paths from `@lobehub/icons-static-svg` (MIT, © LobeHub), already
703
- * `fill="currentColor"` single-path marks, so they take the row's colour like
704
- * any other glyph and need no light/dark variants.
705
- *
706
- * The marks are trademarks of their owners (Anthropic, OpenAI, Google,
707
- * DeepSeek, Moonshot), used here only to identify which engine a session runs
708
- * on.
709
- *
710
- * Codex is drawn as OpenAI: the session's engine *is* OpenAI's, and OpenAI's
711
- * mark is the one a person recognises at 12px.
712
- */
713
- const PATHS = {
714
- claude: {
715
- title: "Claude",
716
- d: "M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"
717
- },
718
- codex: {
719
- title: "OpenAI",
720
- d: "M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"
721
- },
722
- gemini: {
723
- title: "Gemini",
724
- d: "M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"
725
- },
726
- deepseek: {
727
- title: "DeepSeek",
728
- d: "M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 01-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 00-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 01-.465.137 9.597 9.597 0 00-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 001.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 011.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 01.415-.287.302.302 0 01.2.288.306.306 0 01-.31.307.303.303 0 01-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 01-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 01.016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 01-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z"
729
- },
730
- moonshot: {
731
- title: "Moonshot",
732
- d: "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"
733
- }
734
- };
735
- /**
736
- * Which mark a session wears.
737
- *
738
- * The two first-party engines are named directly. A `provider` session is
739
- * whatever the host wired up, so its *model* is the only thing that says whose
740
- * it is — sniffed loosely on purpose (`gemini-2.5-pro`, `deepseek-chat`,
741
- * `kimi-k2`), and falling through to no mark rather than guessing wrong.
742
- */
743
- function engineMark(engine, model) {
744
- if (engine === "claude") return "claude";
745
- if (engine === "codex") return "codex";
746
- const id = model?.toLowerCase() ?? "";
747
- if (!id) return void 0;
748
- if (id.includes("gemini")) return "gemini";
749
- if (id.includes("deepseek")) return "deepseek";
750
- if (id.includes("moonshot") || id.includes("kimi")) return "moonshot";
751
- if (id.includes("claude")) return "claude";
752
- if (id.startsWith("gpt") || id.startsWith("o1") || id.startsWith("o3") || id.includes("openai")) return "codex";
753
- }
754
- function EngineIcon({ engine, model, className }) {
755
- const mark = PATHS[engineMark(engine, model) ?? ""];
756
- if (!mark) return /* @__PURE__ */ jsx("span", {
757
- "aria-hidden": true,
758
- title: engine,
759
- className: cn("shrink-0 text-fg-3", className),
760
- children: "·"
761
- });
762
- return /* @__PURE__ */ jsxs("svg", {
763
- viewBox: "0 0 24 24",
764
- fill: "currentColor",
765
- fillRule: "evenodd",
766
- role: "img",
767
- "aria-label": mark.title,
768
- className: cn("size-3 shrink-0 text-fg-3", className),
769
- children: [/* @__PURE__ */ jsx("title", { children: mark.title }), /* @__PURE__ */ jsx("path", { d: mark.d })]
770
- });
771
- }
772
- //#endregion
773
- export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, segmentsToPlainText, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant };
1228
+ export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, AlertDialogTrigger, Badge, Band, Blank, Button, Card, CardContent, CardHeader, CardTitle, CodeBlock, Composer, ContextDialog, ContextRing, Conversation, ConversationContent, ConversationScrollButton, CopyAction, CopyButton, Dialog, DialogBody, DialogClose, DialogContent, DialogHeader, DialogRow, DialogTrigger, Empty, EmptyKey, EngineIcon, FileCard, HostFilesDialog, Ink, Input, Loader, McpDialog, Menu, MenuContent, MenuItem, MenuSeparator, MenuTrigger, Message, MessageContent, ModelSelect, PERMISSION_MODES, PermissionModeSelect, PermissionPrompt, ProgressRing, ProjectIcon, PromptArea, PromptTokenText, QUESTION_BEHAVIORS, QuestionPrompt, Reasoning, Response, Row, STATUS_META, Select, SelectContent, SelectItem, SelectItemText, SelectTrigger, SelectValue, SessionBrowser, SessionEmptyState, SessionInfoDialog, SessionList, SessionListItem, SessionPanel, SessionStatusIcon, SkillsDialog, Spinner, Splitter, StatusBar, StepRow, StepToggle, SubagentStrip, TerminalDiff, TerminalItemView, TerminalMarkdown, TerminalPermissionPrompt, TerminalQuestionPrompt, TerminalStatusLine, TerminalSurface, TerminalTranscript, Textarea, Tip, Toaster, ToolCallCard, TooltipContent, TooltipProvider, Transcript, TranscriptDensityProvider, TranscriptVariantProvider, UsageDialog, UsageMeters, WithActions, badgeVariants, buttonVariants, cn, commandTrigger, copyText, engineMark, formatAgoPrecise, formatBytes, formatCost, formatCountdown, formatDuration, formatRateLimitWindow, formatRateLimitWindowLong, formatRelativeTime, formatTokens, friendlyModel, getChipsByTrigger, hashtagTrigger, isMutatingTool, isSegmentsEmpty, mentionTrigger, parseUserQuestions, permissionModeChoices, permissionModeMeta, plainTextToSegments, previewPatch, rateLimitWindowSeconds, rowShapeClass, runningSteps, segmentsToPlainText, sessionSteps, skillPrompt, toast, toolIcon, toolInputPreview, useAffordances, useMinuteClock, usePromptAreaState, useTranscriptDensity, useTranscriptVariant, vendorMarkClass, vendorTextClass };
774
1229
 
775
1230
  //# sourceMappingURL=index.mjs.map