@workerdeck/ui 0.17.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 (40) hide show
  1. package/build/{SessionPanel-CnNYEX80.d.mts → SessionPanel-CnU_IJ3-.d.mts} +24 -9
  2. package/build/{SessionPanel-DMPhsNlW.mjs → SessionPanel-DPx8Iz8a.mjs} +439 -74
  3. package/build/SessionPanel-DPx8Iz8a.mjs.map +1 -0
  4. package/build/{format-DfI_je9S.d.mts → format-ljc3lKpA.d.mts} +1 -1
  5. package/build/format.d.mts +16 -5
  6. package/build/format.mjs +2 -3
  7. package/build/index.d.mts +237 -8
  8. package/build/index.mjs +399 -167
  9. package/build/index.mjs.map +1 -1
  10. package/build/{format-DqR56Y8l.mjs → status-BE-zg88x.mjs} +154 -2
  11. package/build/status-BE-zg88x.mjs.map +1 -0
  12. package/build/workspace.d.mts +4 -1
  13. package/build/workspace.mjs +4 -3
  14. package/build/workspace.mjs.map +1 -1
  15. package/package.json +4 -4
  16. package/src/components/agent/ContextRing.tsx +41 -0
  17. package/src/components/agent/EngineIcon.tsx +40 -0
  18. package/src/components/agent/SessionBrowser.tsx +115 -22
  19. package/src/components/agent/SessionPanel.tsx +153 -3
  20. package/src/components/agent/SessionSteps.tsx +233 -0
  21. package/src/components/agent/SessionWorkspace.tsx +4 -0
  22. package/src/components/agent/StatusBar.tsx +4 -2
  23. package/src/components/agent/SubagentStrip.tsx +134 -0
  24. package/src/components/agent/Transcript.tsx +138 -19
  25. package/src/components/agent/transcript-rows.ts +9 -1
  26. package/src/components/terminal/TerminalTranscript.tsx +77 -3
  27. package/src/components/terminal/affordances.tsx +34 -0
  28. package/src/components/terminal/blocks.ts +28 -0
  29. package/src/components/terminal/height.ts +24 -0
  30. package/src/components/terminal/items.tsx +1 -1
  31. package/src/components/terminal/scrubber.tsx +28 -3
  32. package/src/components/terminal/tool-run.ts +49 -5
  33. package/src/index.ts +19 -1
  34. package/src/lib/status.ts +16 -3
  35. package/src/styles/terminal.css +10 -0
  36. package/src/styles/theme.css +42 -0
  37. package/build/SessionPanel-DMPhsNlW.mjs.map +0 -1
  38. package/build/format-DqR56Y8l.mjs.map +0 -1
  39. package/build/status-Ydzi7n6j.mjs +0 -143
  40. package/build/status-Ydzi7n6j.mjs.map +0 -1
@@ -1,15 +1,15 @@
1
- import { a as formatDuration, c as formatRelativeTime, d as rateLimitWindowSeconds, f as toolInputPreview, i as formatCountdown, l as formatTokens, n as formatBytes, r as formatCost, s as formatRateLimitWindowLong, t as formatAgoPrecise } from "./format-DqR56Y8l.mjs";
1
+ import { _ as formatRelativeTime, b as rateLimitWindowSeconds, d as formatBytes, f as formatCost, g as formatRateLimitWindowLong, m as formatDuration, p as formatCountdown, r as meterColorClass, u as formatAgoPrecise, v as formatTokens, x as toolInputPreview } from "./status-BE-zg88x.mjs";
2
2
  import { Fragment, createContext, forwardRef, memo, useCallback, useContext, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { clsx } from "clsx";
5
5
  import { extendTailwindMerge } from "tailwind-merge";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  import { Select } from "@base-ui/react/select";
8
- import { ArrowDown, ArrowDownCircle, ArrowUp, AtSign, Brain, ChartPie, Check, CheckSquare, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, ClipboardList, Clock, Code, Copy, Download, File, FileDiff, FileText, Folder, FolderSearch, FolderTree, Gauge, Globe, Hand, Image, Info, Link2, LoaderCircle, MessageCircleQuestion, MessageSquareText, MoreHorizontal, Paperclip, PencilLine, Plug, Power, PowerOff, Puzzle, RefreshCw, RotateCcw, RotateCw, Search, ShieldCheck, SlashSquare, Sparkles, Square, SquarePen, Terminal, TriangleAlert, UsersRound, WifiOff, Wrench, X, Zap } from "lucide-react";
8
+ import { ArrowDown, ArrowDownCircle, ArrowLeft, ArrowUp, AtSign, Brain, ChartPie, Check, CheckSquare, ChevronDown, ChevronLeft, ChevronRight, ChevronsUpDown, ClipboardList, Clock, Code, Copy, Download, File, FileDiff, FileText, Folder, FolderSearch, FolderTree, Gauge, Globe, Hand, Image, Info, Link2, LoaderCircle, MessageCircleQuestion, MessageSquareText, MoreHorizontal, Paperclip, PencilLine, Plug, Power, PowerOff, Puzzle, RefreshCw, RotateCcw, RotateCw, Search, ShieldCheck, SlashSquare, Sparkles, Square, SquarePen, Terminal, TriangleAlert, UsersRound, WifiOff, Wrench, X, Zap } from "lucide-react";
9
9
  import { Menu } from "@base-ui/react/menu";
10
10
  import { Dialog } from "@base-ui/react/dialog";
11
11
  import { Tooltip } from "@base-ui/react/tooltip";
12
- import { PROTOCOL_VERSION, mergeUsage, orderUsageWindows, usageInfos } from "@workerdeck/protocol";
12
+ import { PROTOCOL_VERSION, mergeUsage, orderUsageWindows, subagentLabel, usageInfos } from "@workerdeck/protocol";
13
13
  import { recapLine, scanPromptTokens, summarizeSince, useAttachments, useClaudeSession, useHostFileSearch, useProfileUsage, useToolCallHost } from "@workerdeck/react";
14
14
  import { Streamdown } from "streamdown";
15
15
  import { createPortal } from "react-dom";
@@ -4579,6 +4579,30 @@ function WithActions({ actions, children, className }) {
4579
4579
  * else is on, and reads as a button borrowed from another application. The tick
4580
4580
  * replaces the glyph in place, so the confirmation costs no width either.
4581
4581
  */
4582
+ /**
4583
+ * Open a sub-agent's own surface, as a character.
4584
+ *
4585
+ * A row **action** and not the row's press, because the press already means
4586
+ * expand/collapse and that is the cheaper, more common intent — a reader
4587
+ * glancing at what an agent did wants the four rows inline, not a new screen.
4588
+ * Taking over the panel is the deliberate move, so it gets the deliberate
4589
+ * target. Same zero-layout contract as `CopyAction`: it lives in the hover
4590
+ * actions overlay, so a collapsed Task row is exactly as tall with it as
4591
+ * without, which is what keeps the height book honest.
4592
+ */
4593
+ function OpenSubagentAction({ onOpen, label = "Open sub-agent" }) {
4594
+ return /* @__PURE__ */ jsx("button", {
4595
+ type: "button",
4596
+ className: "term-action",
4597
+ title: label,
4598
+ "aria-label": label,
4599
+ onClick: (event) => {
4600
+ event.stopPropagation();
4601
+ onOpen();
4602
+ },
4603
+ children: "⤢"
4604
+ });
4605
+ }
4582
4606
  function CopyAction({ text, label = "Copy" }) {
4583
4607
  const [copied, setCopied] = useState(false);
4584
4608
  return /* @__PURE__ */ jsx("button", {
@@ -5343,11 +5367,54 @@ const trimmed = (value) => typeof value === "string" && value.trim() !== "" ? va
5343
5367
  * emptier than a plain tool row's.
5344
5368
  */
5345
5369
  function taskLabel(task) {
5370
+ return `${task.name}(${taskIdentity(task)})`;
5371
+ }
5372
+ /**
5373
+ * The inner half of {@link taskLabel} — `Explore · find the auth check` — without
5374
+ * the `Task(…)` wrapper.
5375
+ *
5376
+ * Split out for the sub-agent takeover's header, which names the agent it is
5377
+ * showing and has no room (or reason) to repeat the tool's own name: the whole
5378
+ * surface *is* that Task. Extracted rather than re-spelled so the header and the
5379
+ * row it was opened from cannot drift, and so both keep matching protocol's
5380
+ * `subagentLabel`, which reads the same two input fields for the sessions list.
5381
+ */
5382
+ function taskIdentity(task) {
5346
5383
  const input = task.input;
5347
5384
  const description = trimmed(input?.description);
5348
5385
  const agent = trimmed(input?.subagent_type);
5349
- const inner = agent && description ? `${agent} · ${clip(description)}` : agent ?? (description ? clip(description) : toolInputPreview(task.input));
5350
- return `${task.name}(${inner})`;
5386
+ return agent && description ? `${agent} · ${clip(description)}` : agent ?? (description ? clip(description) : toolInputPreview(task.input));
5387
+ }
5388
+ /**
5389
+ * **What this agent was actually asked** — the sub-agent's brief, or undefined
5390
+ * when the engine did not give us one.
5391
+ *
5392
+ * Measured against a live session rather than assumed: the Agent SDK's call
5393
+ * carries `{description, subagent_type, run_in_background, prompt}`, and
5394
+ * `prompt` is the instruction.
5395
+ *
5396
+ * **Whether it also arrives in the stream depends on the agent.** A foreground
5397
+ * `Task` forwards its brief as a real nested `user_message` (that is what
5398
+ * `forwardSubagentText` buys and what the reducer stamps a `parentToolUseId`
5399
+ * on), so `subagentItems` picks it up and the frame already has it. A
5400
+ * **background** agent does not: measured on a session with eight of them,
5401
+ * zero items carried a `parentToolUseId` on a `user` kind. Those runs are
5402
+ * exactly the ones you open a takeover on — an agent working while you read
5403
+ * something else — and their brief was nowhere at all.
5404
+ *
5405
+ * So this is the *fallback*, not the source: the callers splice it in only when
5406
+ * the frame carries no brief of its own, or the same instruction would be drawn
5407
+ * twice.
5408
+ *
5409
+ * `description` is deliberately not a fallback: it is the 3–5 word label the
5410
+ * header already prints, and repeating it as a brief would claim we know the
5411
+ * instruction when we do not. **Codex genuinely has none** — its `spawn_agent`
5412
+ * message is an encrypted blob on the wire — so on that engine this is
5413
+ * undefined and the row is not drawn, rather than drawn empty.
5414
+ */
5415
+ function taskBrief(task) {
5416
+ const input = task.input;
5417
+ return trimmed(input?.prompt);
5351
5418
  }
5352
5419
  const callBusy = (call) => call.status === "running" || call.status === "pending";
5353
5420
  /** Did this one call fail? Both spellings are needed: an out-of-loop execution
@@ -5451,6 +5518,30 @@ function taskSummary(task, children) {
5451
5518
  function parentOf(item) {
5452
5519
  return ("parentToolUseId" in item ? item.parentToolUseId : void 0) ?? void 0;
5453
5520
  }
5521
+ /**
5522
+ * **The frame membership rule**: the items a sub-agent produced, and nothing
5523
+ * else — what the takeover renders instead of the whole conversation.
5524
+ *
5525
+ * One exported function rather than a `filter` at each call site, because this
5526
+ * is a rule two renderers have to agree on: iOS mirrors the terminal model out
5527
+ * of this module, and a phone that framed a sub-agent slightly differently would
5528
+ * be a second answer to "what is this agent doing".
5529
+ *
5530
+ * It picks up the brief (a `user` item *with* a parent), the thinking, the
5531
+ * streamed text (deltas are namespaced per sidechain, so `streaming:<parentId>`
5532
+ * carries the parent like any other item), every tool call with its result, and
5533
+ * the final report. It excludes the spawning `Task` call itself — that is the
5534
+ * frame, not a row in it — and every other agent's work.
5535
+ *
5536
+ * The slice is safe to hand straight to {@link terminalBlocks} at offset 0:
5537
+ * nothing in it is top-level, so nothing absorbs, and consecutive calls still
5538
+ * fold into runs because {@link foldsTogether} keys on an *equal* parent rather
5539
+ * than on absence of one. Row indices are internally consistent because the rows
5540
+ * and the `items` they came from are the same array.
5541
+ */
5542
+ function subagentItems(items, parentToolUseId) {
5543
+ return items.filter((item) => parentOf(item) === parentToolUseId);
5544
+ }
5454
5545
  /** Is this a row the transcript folds into a run? Any tool call is — see
5455
5546
  * `tool-run.ts` for why this is no longer shell-only. */
5456
5547
  function isRunCall(item) {
@@ -7377,6 +7468,101 @@ function PermissionPrompt({ request, onApprove, onDeny, className }) {
7377
7468
  });
7378
7469
  }
7379
7470
  //#endregion
7471
+ //#region src/components/agent/SubagentStrip.tsx
7472
+ /**
7473
+ * The one line above a sub-agent takeover: who this is, how it is doing, and the
7474
+ * way back.
7475
+ *
7476
+ * **It claims exactly what the `TaskRow` it was opened from claims** — the same
7477
+ * `taskBusy` / `taskFailed` / tool count over the same items — and deliberately
7478
+ * *not* `SubagentInfo.status`. Protocol documents those two as divergent on
7479
+ * purpose (`index.ts:1476-1487`), but that divergence is transcript-versus-*list*
7480
+ * and this surface **is** the transcript. The disagreement that must not exist
7481
+ * here is between a header and the rows directly beneath it.
7482
+ *
7483
+ * The rollup is still allowed one job — naming an agent whose `Task` call is not
7484
+ * in the transcript — because a label is not content, and `SessionInfo.subagents`
7485
+ * keeps only eight settled records (`SUBAGENT_HISTORY`), so it can never be the
7486
+ * source of what a frame *shows*.
7487
+ *
7488
+ * Both variants, because the panel has two and a strip that only existed in one
7489
+ * would make the takeover a terminal-theme feature rather than a session feature.
7490
+ */
7491
+ function SubagentStrip({ task, items, label, onBack, terminal, fontSize, lineHeight }) {
7492
+ const busy = task ? taskBusy(task, items) : false;
7493
+ const failed = task ? taskFailed(task) : false;
7494
+ const pulse = usePulse(busy);
7495
+ const tools = items.reduce((n, item) => n + (item.kind === "tool_call" ? 1 : 0), 0);
7496
+ const startedAt = busy ? task?.ts : void 0;
7497
+ const now = useTicker(startedAt !== void 0);
7498
+ const elapsed = startedAt === void 0 ? void 0 : formatDuration(now - startedAt);
7499
+ const name = task ? taskIdentity(task) : label;
7500
+ /**
7501
+ * Running or finished, in the theme's own words rather than new ones:
7502
+ * `taskSummary` already says `working…` and `done` for exactly this state one
7503
+ * row over, and a header that said "running"/"completed" about the same agent
7504
+ * would be a second vocabulary for one fact. The trailing ellipsis is the
7505
+ * theme's in-flight signal; the pulse beside it is the beat.
7506
+ *
7507
+ * Unknown when the transcript has no `Task` call to read — better silent than
7508
+ * confidently wrong about an agent we cannot see.
7509
+ */
7510
+ const status = !task ? void 0 : failed ? "failed" : busy ? `${pulse} working…` : "done";
7511
+ const detail = [tools > 0 ? `${tools} tool${tools === 1 ? "" : "s"}` : void 0, elapsed].filter(Boolean).join(" · ");
7512
+ if (terminal) return /* @__PURE__ */ jsx(TerminalSurface, {
7513
+ fontSize,
7514
+ lineHeight,
7515
+ className: "shrink-0",
7516
+ children: /* @__PURE__ */ jsx("button", {
7517
+ type: "button",
7518
+ onClick: onBack,
7519
+ "aria-label": "Back to the session",
7520
+ className: "block w-full cursor-pointer text-left",
7521
+ children: /* @__PURE__ */ jsxs(Row, {
7522
+ glyph: "←",
7523
+ glyphTone: "dim",
7524
+ indent: 1,
7525
+ tone: failed ? "red" : "green",
7526
+ children: [
7527
+ name,
7528
+ status ? /* @__PURE__ */ jsxs(Ink, {
7529
+ tone: failed ? "red" : busy ? "mark" : "dim",
7530
+ children: [" · ", status]
7531
+ }) : null,
7532
+ detail ? /* @__PURE__ */ jsxs(Ink, {
7533
+ tone: "faint",
7534
+ children: [" · ", detail]
7535
+ }) : null
7536
+ ]
7537
+ })
7538
+ })
7539
+ });
7540
+ return /* @__PURE__ */ jsxs("div", {
7541
+ className: "flex shrink-0 items-center gap-2 border-b border-border px-3 py-1.5",
7542
+ children: [
7543
+ /* @__PURE__ */ jsxs(Button, {
7544
+ variant: "ghost",
7545
+ size: "sm",
7546
+ onClick: onBack,
7547
+ className: "h-6 gap-1 px-1.5",
7548
+ children: [/* @__PURE__ */ jsx(ArrowLeft, { className: "size-3.5" }), "Back"]
7549
+ }),
7550
+ /* @__PURE__ */ jsx("span", {
7551
+ className: cn("min-w-0 flex-1 truncate text-body-sm", failed ? "text-danger" : "text-fg-2"),
7552
+ children: name
7553
+ }),
7554
+ status ? /* @__PURE__ */ jsx("span", {
7555
+ className: cn("shrink-0 text-label", failed ? "text-danger" : busy ? "text-accent" : "text-fg-3"),
7556
+ children: status
7557
+ }) : null,
7558
+ detail ? /* @__PURE__ */ jsx("span", {
7559
+ className: "shrink-0 text-label text-fg-4",
7560
+ children: detail
7561
+ }) : null
7562
+ ]
7563
+ });
7564
+ }
7565
+ //#endregion
7380
7566
  //#region src/components/agent/QuestionPrompt.tsx
7381
7567
  /** The AskUserQuestion policies surfaced on job/session creation forms. */
7382
7568
  const QUESTION_BEHAVIORS = [
@@ -8273,7 +8459,9 @@ function useNow(intervalMs = 3e4) {
8273
8459
  }, [intervalMs]);
8274
8460
  return now;
8275
8461
  }
8276
- const utilizationColor = (pct) => pct >= 95 ? "text-danger" : pct >= 80 ? "text-warning" : "text-fg-3";
8462
+ /** The shared ramp, aliased for the two call sites below the thresholds live
8463
+ * in `lib/status.ts` beside the severity they mirror. */
8464
+ const utilizationColor = (pct) => meterColorClass(pct);
8277
8465
  /** The CLI reports category colors as its own theme token names ('inactive',
8278
8466
  * 'promptBorder', ...), not CSS colors — only pass through what CSS can render. */
8279
8467
  const cssColor = (color) => typeof CSS !== "undefined" && CSS.supports("color", color) ? color : void 0;
@@ -9015,6 +9203,19 @@ function estimateBlockPx(block, epoch) {
9015
9203
  epoch.cache.set(block.item, computed);
9016
9204
  return computed.px;
9017
9205
  }
9206
+ /**
9207
+ * The collapsed brief row's height — the frame's first row, which is synthetic
9208
+ * and so cannot go through {@link estimateBlockPx}.
9209
+ *
9210
+ * Clipped, so it is `min(wrapped, BRIEF_LINES)` lines plus the row the header
9211
+ * occupies. Expanding is local state on a *mounted* row, which the virtualizer
9212
+ * re-measures; what has to be right before it mounts is the collapsed height,
9213
+ * exactly as a task row is always collapsed when unmounted for the same reason.
9214
+ */
9215
+ function briefPx(text, m) {
9216
+ const cols = Math.max(1, Math.floor(m.width / m.ch + EPS));
9217
+ return (Math.min(textLines(text, cols).lines, 4) + 1) * m.line;
9218
+ }
9018
9219
  /** Measure the advance of `0` on the live surface. A DOM read — call it from
9019
9220
  * the epoch's measurement pass (an effect / ResizeObserver callback), never
9020
9221
  * from render. The probe is absolutely positioned, so it contributes no layout
@@ -9744,7 +9945,7 @@ function excerpt(item) {
9744
9945
  * rows at all — came back with an empty right lane.
9745
9946
  */
9746
9947
  function buildClusters(props, railH) {
9747
- const { items, bookmarks, recapRow, pendingApprovals, rowIndexFor, offsetOfRow, sizeOfRow, positionInRow, totalSize, viewportH } = props;
9948
+ const { items, bookmarks, frameParentId, recapRow, pendingApprovals, rowIndexFor, offsetOfRow, sizeOfRow, positionInRow, totalSize, viewportH } = props;
9748
9949
  const marks = [];
9749
9950
  const subagentParents = /* @__PURE__ */ new Set();
9750
9951
  for (const item of items) {
@@ -9753,7 +9954,7 @@ function buildClusters(props, railH) {
9753
9954
  }
9754
9955
  const rowOutcome = /* @__PURE__ */ new Map();
9755
9956
  items.forEach((item, index) => {
9756
- if (item.kind !== "tool_call" || parentOf(item) !== void 0) return;
9957
+ if (item.kind !== "tool_call" || parentOf(item) !== frameParentId) return;
9757
9958
  rowOutcome.set(rowIndexFor(index), index);
9758
9959
  });
9759
9960
  let segment = {};
@@ -9773,7 +9974,7 @@ function buildClusters(props, railH) {
9773
9974
  itemIndex: index,
9774
9975
  rowIndex: rowIndexFor(index)
9775
9976
  });
9776
- if (item.kind === "user" && parentOf(item) === void 0) {
9977
+ if (item.kind === "user" && parentOf(item) === frameParentId) {
9777
9978
  closeSegment();
9778
9979
  marks.push({
9779
9980
  kind: "user",
@@ -9794,7 +9995,17 @@ function buildClusters(props, railH) {
9794
9995
  itemIndex: index,
9795
9996
  rowIndex: rowIndexFor(index)
9796
9997
  });
9797
- else if (item.kind === "assistant_text" && item.parentToolUseId == null) segment.response = index;
9998
+ else if (item.kind === "assistant_text" && parentOf(item) === frameParentId) {
9999
+ if (frameParentId !== void 0) {
10000
+ marks.push({
10001
+ kind: "turn",
10002
+ itemIndex: index,
10003
+ rowIndex: rowIndexFor(index)
10004
+ });
10005
+ return;
10006
+ }
10007
+ segment.response = index;
10008
+ }
9798
10009
  });
9799
10010
  closeSegment();
9800
10011
  for (const index of bookmarks) if (index >= 0 && index < items.length) marks.push({
@@ -10416,14 +10627,58 @@ function TerminalItemView({ item, fileUrl }) {
10416
10627
  * they fold among themselves: a subagent's consecutive tool calls are as much
10417
10628
  * an aside inside its frame as they are in the main thread.
10418
10629
  */
10419
- function TaskRow({ block, fileUrl }) {
10630
+ /**
10631
+ * **What the agent was asked** — the sub-agent's brief, clipped to
10632
+ * {@link BRIEF_LINES} and pressable for the whole of it.
10633
+ *
10634
+ * It leads the takeover's frame and the inline task expansion alike, because
10635
+ * both are answering the same question and an answer without its question is
10636
+ * half a transcript. This is the one row here built from something other than
10637
+ * the stream, and it exists for the case the stream does not cover: a
10638
+ * **background** agent forwards no brief, where a foreground `Task` forwards a
10639
+ * real nested user item that renders as an ordinary prompt row. The callers
10640
+ * splice this in only when that row is absent. Codex draws nothing either way —
10641
+ * its spawn message is encrypted on the wire, so there is no brief to give.
10642
+ *
10643
+ * `>` and blue, the prompt's own marker and colour, because that is what this
10644
+ * is: somebody's instruction, one level in. The clip is `line-clamp`, which
10645
+ * cuts on the same wrapped lines `briefPx` counts — one rule, so the reserved
10646
+ * height and the drawn height cannot disagree.
10647
+ */
10648
+ function BriefRow({ text, terminal }) {
10649
+ const [open, setOpen] = useState(false);
10650
+ if (!terminal) return /* @__PURE__ */ jsx("div", {
10651
+ "data-slot": "brief",
10652
+ className: "px-4 py-2 text-body-sm whitespace-pre-wrap text-fg-2",
10653
+ children: text
10654
+ });
10655
+ return /* @__PURE__ */ jsx("div", {
10656
+ "data-slot": "brief",
10657
+ children: /* @__PURE__ */ jsx(Pressable, {
10658
+ onPress: () => setOpen((v) => !v),
10659
+ expanded: open,
10660
+ children: /* @__PURE__ */ jsx(Row, {
10661
+ glyph: ">",
10662
+ glyphTone: "blue",
10663
+ tone: "dim",
10664
+ children: /* @__PURE__ */ jsx("span", {
10665
+ className: cn("whitespace-pre-wrap", !open && "term-brief-clip"),
10666
+ style: open ? void 0 : { WebkitLineClamp: 4 },
10667
+ children: text
10668
+ })
10669
+ })
10670
+ })
10671
+ });
10672
+ }
10673
+ function TaskRow({ block, fileUrl, onOpenSubagent }) {
10420
10674
  const [open, setOpen] = useState(false);
10421
10675
  const reveal = useRevealOnOpen(open);
10422
10676
  const children = useMemo(() => taskChildItems(block), [block]);
10677
+ const brief = children.some((item) => item.kind === "user") ? void 0 : taskBrief(block.task);
10423
10678
  const busy = taskBusy(block.task, children);
10424
10679
  const failed = taskFailed(block.task);
10425
10680
  const pulse = usePulse(busy);
10426
- return /* @__PURE__ */ jsxs("div", {
10681
+ const row = /* @__PURE__ */ jsxs("div", {
10427
10682
  ref: reveal,
10428
10683
  className: open ? "term-open" : void 0,
10429
10684
  children: [/* @__PURE__ */ jsx(Pressable, {
@@ -10432,17 +10687,24 @@ function TaskRow({ block, fileUrl }) {
10432
10687
  children: /* @__PURE__ */ jsx(Row, {
10433
10688
  glyph: busy ? pulse : "●",
10434
10689
  glyphTone: failed ? "red" : busy ? "mark" : "dim",
10435
- tone: failed ? "red" : "fg",
10690
+ tone: failed ? "red" : "green",
10436
10691
  children: taskSummary(block.task, children)
10437
10692
  })
10438
- }), open ? /* @__PURE__ */ jsx("div", {
10693
+ }), open ? /* @__PURE__ */ jsxs("div", {
10439
10694
  className: "term-nested",
10440
- children: block.children.map((leaf, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(block.children[index - 1], leaf) ? /* @__PURE__ */ jsx(Blank, {}) : null, "run" in leaf ? /* @__PURE__ */ jsx(ToolRunRow, { items: leaf.run }) : /* @__PURE__ */ jsx(TerminalItemView, {
10695
+ children: [brief ? /* @__PURE__ */ jsx(BriefRow, {
10696
+ text: brief,
10697
+ terminal: true
10698
+ }) : null, block.children.map((leaf, index) => /* @__PURE__ */ jsxs(Fragment, { children: [index > 0 && blockNeedsBlank(block.children[index - 1], leaf) ? /* @__PURE__ */ jsx(Blank, {}) : null, "run" in leaf ? /* @__PURE__ */ jsx(ToolRunRow, { items: leaf.run }) : /* @__PURE__ */ jsx(TerminalItemView, {
10441
10699
  item: leaf.item,
10442
10700
  fileUrl
10443
- })] }, leaf.key))
10701
+ })] }, leaf.key))]
10444
10702
  }) : null]
10445
10703
  });
10704
+ return onOpenSubagent === void 0 ? row : /* @__PURE__ */ jsx(WithActions, {
10705
+ actions: /* @__PURE__ */ jsx(OpenSubagentAction, { onOpen: () => onOpenSubagent(block.task.id) }),
10706
+ children: row
10707
+ });
10446
10708
  }
10447
10709
  /** When the current run began — the clock the working line counts from. Held
10448
10710
  * here, not in the row, because the row comes and goes within a single turn (it
@@ -10690,9 +10952,15 @@ function read(boundary, index) {
10690
10952
  }
10691
10953
  /** Rows produced inside a subagent (`parentToolUseId != null`) are stepped in
10692
10954
  * behind a rule, so a Task's own output reads as belonging to the tool call
10693
- * above it rather than as the main thread carrying on. */
10694
- function nestedClass(item) {
10695
- return "parentToolUseId" in item && item.parentToolUseId != null ? "border-l-2 border-border pl-3" : void 0;
10955
+ * above it rather than as the main thread carrying on.
10956
+ *
10957
+ * **Except inside that sub-agent's own frame**, where those same items are the
10958
+ * top level and there is no main thread to be an aside from — stepping every row
10959
+ * in would draw a rule down the whole surface saying "this happened somewhere
10960
+ * else" about the only thing on screen. */
10961
+ function nestedClass(item, frameParentId) {
10962
+ const parent = "parentToolUseId" in item ? item.parentToolUseId : void 0;
10963
+ return parent != null && parent !== frameParentId ? "border-l-2 border-border pl-3" : void 0;
10696
10964
  }
10697
10965
  /**
10698
10966
  * A prompt row's sticky lane — the strip spanning its turn, leading with the
@@ -10792,7 +11060,7 @@ function StickyPromptLane({ top, height, gapClass, scrollRoot, index, measureRef
10792
11060
  * transient UI state (an expanded tool card, an opened reasoning block) resets
10793
11061
  * once the row scrolls far enough away to unmount.
10794
11062
  */
10795
- function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyPrompt, gap, fontSize, lineHeight, items, pendingApprovals, scrubber, scrubberMarks, affordances, fileUrl, attachmentUrl, hostImage, jumpToRecapRef, repinRef, reveal }) {
11063
+ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyPrompt, frameParentId, onOpenSubagent, gap, fontSize, lineHeight, items, pendingApprovals, scrubber, scrubberMarks, affordances, fileUrl, attachmentUrl, hostImage, jumpToRecapRef, repinRef, reveal }) {
10796
11064
  const stick = useStickToBottomContext();
10797
11065
  const [scrollElement, setScrollElement] = useState(null);
10798
11066
  const promptRows = useMemo(() => rows.flatMap((row, index) => "item" in row && row.item.kind === "user" && parentOf(row.item) === void 0 ? [index] : []), [rows]);
@@ -10844,6 +11112,7 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
10844
11112
  if (terminal && epoch) {
10845
11113
  const row = rows[index];
10846
11114
  const gapPx = index > 0 && gapBefore(rows, index) ? epoch.line : 0;
11115
+ if (row && "text" in row && row.key === "brief") return briefPx(row.text, epoch) + gapPx;
10847
11116
  if (row && !("line" in row)) return estimateBlockPx(row, epoch) + gapPx;
10848
11117
  return epoch.line + gapPx;
10849
11118
  }
@@ -10973,7 +11242,7 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
10973
11242
  className: cn(read(boundary, row.index) && "opacity-45"),
10974
11243
  children: /* @__PURE__ */ jsx(ToolRunRow, { items: row.run })
10975
11244
  }) : "item" in row ? /* @__PURE__ */ jsx("div", {
10976
- className: cn(read(boundary, row.index) && "opacity-45", nestedClass(row.item)),
11245
+ className: cn(read(boundary, row.index) && "opacity-45", nestedClass(row.item, frameParentId)),
10977
11246
  children: /* @__PURE__ */ jsx(TranscriptItemView, {
10978
11247
  item: row.item,
10979
11248
  fileUrl,
@@ -10981,6 +11250,9 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
10981
11250
  hostImage,
10982
11251
  terminal
10983
11252
  })
11253
+ }) : "text" in row && row.key === "brief" ? /* @__PURE__ */ jsx(BriefRow, {
11254
+ text: row.text,
11255
+ terminal
10984
11256
  }) : "line" in row ? /* @__PURE__ */ jsx(RecapRow, {
10985
11257
  line: row.line,
10986
11258
  since,
@@ -10989,7 +11261,8 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
10989
11261
  className: cn(read(boundary, row.index) && "opacity-45"),
10990
11262
  children: /* @__PURE__ */ jsx(TaskRow, {
10991
11263
  block: row,
10992
- fileUrl
11264
+ fileUrl,
11265
+ onOpenSubagent
10993
11266
  })
10994
11267
  });
10995
11268
  if (terminal && stickyPrompt && "item" in row && row.item.kind === "user" && parentOf(row.item) === void 0) {
@@ -11017,6 +11290,7 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
11017
11290
  pendingApprovals,
11018
11291
  recapRow,
11019
11292
  bookmarks: scrubberMarks ?? [],
11293
+ frameParentId,
11020
11294
  rowIndexFor: (itemIndex) => rowIndexForItem(rows, itemIndex),
11021
11295
  positionInRow: (itemIndex) => positionInRow(rows, itemIndex),
11022
11296
  offsetOfRow: (rowIndex) => virtualizer.measurementsCache[rowIndex]?.start ?? 0,
@@ -11031,28 +11305,40 @@ function TranscriptRows({ rows, boundary, since, terminal, replaying, stickyProm
11031
11305
  }), scrollElement.parentElement) : null]
11032
11306
  });
11033
11307
  }
11034
- function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", density = "comfortable", fontSize, lineHeight, affordances, stickyPrompt = false, scrubber, scrubberMarks, replaying = false, catchUp, jumpToRecapRef, repinRef, reveal, className }) {
11308
+ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage, variant = "cards", density = "comfortable", fontSize, lineHeight, affordances, stickyPrompt = false, scrubber, scrubberMarks, replaying = false, catchUp, jumpToRecapRef, repinRef, reveal, frame, onOpenSubagent, className }) {
11035
11309
  const terminal = variant === "terminal";
11310
+ const items = useMemo(() => frame ? subagentItems(state.items, frame.parentToolUseId) : state.items, [state.items, frame]);
11311
+ const frameTask = useMemo(() => frame ? state.items.find((item) => item.kind === "tool_call" && item.id === frame.parentToolUseId) : void 0, [state.items, frame]);
11036
11312
  const gap = ROW_GAP[variant][density];
11037
11313
  const runStartedAt = useRunStart(state.status);
11038
- const boundary = catchUp && catchUp.from > 0 && catchUp.from < state.items.length ? catchUp.from : void 0;
11314
+ const boundary = !frame && catchUp && catchUp.from > 0 && catchUp.from < state.items.length ? catchUp.from : void 0;
11039
11315
  const recap = useMemo(() => boundary === void 0 ? void 0 : recapLine(summarizeSince(state, boundary)), [state, boundary]);
11316
+ const brief = useMemo(() => frame && frameTask && !items.some((item) => item.kind === "user") ? taskBrief(frameTask) : void 0, [
11317
+ frame,
11318
+ frameTask,
11319
+ items
11320
+ ]);
11040
11321
  const rows = useMemo(() => {
11041
- const fold = (from, to) => terminalBlocks(state.items.slice(from, to), from, terminal);
11042
- if (boundary === void 0 || !recap) return fold(0, state.items.length);
11322
+ const fold = (from, to) => terminalBlocks(items.slice(from, to), from, terminal);
11323
+ const lead = brief ? [{
11324
+ key: "brief",
11325
+ text: brief
11326
+ }] : [];
11327
+ if (boundary === void 0 || !recap) return [...lead, ...fold(0, items.length)];
11043
11328
  return [
11044
11329
  ...fold(0, boundary),
11045
11330
  {
11046
11331
  key: "recap",
11047
11332
  line: recap
11048
11333
  },
11049
- ...fold(boundary, state.items.length)
11334
+ ...fold(boundary, items.length)
11050
11335
  ];
11051
11336
  }, [
11052
- state.items,
11337
+ items,
11053
11338
  boundary,
11054
11339
  recap,
11055
- terminal
11340
+ terminal,
11341
+ brief
11056
11342
  ]);
11057
11343
  return /* @__PURE__ */ jsx(TranscriptVariantProvider, {
11058
11344
  value: variant,
@@ -11066,44 +11352,53 @@ function Transcript({ state, fileUrl, attachmentUrl, canBrowseFiles, hostImage,
11066
11352
  fontSize,
11067
11353
  lineHeight,
11068
11354
  affordances,
11069
- children: [state.items.length === 0 && state.status !== "starting" ? /* @__PURE__ */ jsx(SessionEmptyState, {
11070
- cwd: state.cwd,
11071
- hasCommands: !!state.commands?.length,
11072
- hasSkills: !!state.skills?.some((s) => s.enabled),
11073
- canBrowseFiles
11074
- }) : /* @__PURE__ */ jsx(TranscriptRows, {
11075
- rows,
11076
- boundary,
11077
- since: catchUp?.since,
11078
- terminal,
11079
- replaying,
11080
- stickyPrompt,
11081
- gap,
11082
- fontSize,
11083
- lineHeight,
11084
- items: state.items,
11085
- pendingApprovals: state.pendingApprovals,
11086
- scrubber,
11087
- scrubberMarks,
11088
- affordances,
11089
- fileUrl,
11090
- attachmentUrl,
11091
- hostImage,
11092
- jumpToRecapRef,
11093
- repinRef,
11094
- reveal
11095
- }), showLoader(state) ? terminal ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx("div", {
11096
- className: "term-blank",
11097
- "aria-hidden": true
11098
- }) : null, /* @__PURE__ */ jsx(WorkingRow, {
11099
- label: state.status === "starting" ? "Starting…" : "Working…",
11100
- startedAt: runStartedAt,
11101
- tokens: state.contextUsage?.totalTokens
11102
- })] }) : /* @__PURE__ */ jsx(Loader, {
11103
- label: state.status === "starting" ? "Starting session…" : void 0,
11104
- startedAt: runStartedAt,
11105
- tokens: state.contextUsage?.totalTokens
11106
- }) : null]
11355
+ children: [
11356
+ frame ? frameTask === void 0 && !replaying ? /* @__PURE__ */ jsx("div", {
11357
+ className: cn(terminal ? "term-row text-fg-4" : "p-4 text-body-sm text-fg-4"),
11358
+ children: "This sub-agent's work is not in this transcript."
11359
+ }) : null : items.length === 0 && state.status !== "starting" ? /* @__PURE__ */ jsx(SessionEmptyState, {
11360
+ cwd: state.cwd,
11361
+ hasCommands: !!state.commands?.length,
11362
+ hasSkills: !!state.skills?.some((s) => s.enabled),
11363
+ canBrowseFiles
11364
+ }) : null,
11365
+ frame && frameTask === void 0 && !replaying ? null : /* @__PURE__ */ jsx(TranscriptRows, {
11366
+ rows,
11367
+ boundary,
11368
+ since: catchUp?.since,
11369
+ terminal,
11370
+ replaying,
11371
+ stickyPrompt: !frame && stickyPrompt,
11372
+ gap,
11373
+ fontSize,
11374
+ lineHeight,
11375
+ items,
11376
+ pendingApprovals: state.pendingApprovals,
11377
+ scrubber,
11378
+ scrubberMarks: frame ? void 0 : scrubberMarks,
11379
+ affordances,
11380
+ fileUrl,
11381
+ attachmentUrl,
11382
+ hostImage,
11383
+ jumpToRecapRef,
11384
+ repinRef,
11385
+ reveal: frame ? void 0 : reveal,
11386
+ frameParentId: frame?.parentToolUseId,
11387
+ onOpenSubagent: frame ? void 0 : onOpenSubagent
11388
+ }),
11389
+ (frame ? frameTask !== void 0 && taskBusy(frameTask, items) : showLoader(state)) ? terminal ? /* @__PURE__ */ jsxs(Fragment$1, { children: [state.items.length > 0 ? /* @__PURE__ */ jsx("div", {
11390
+ className: "term-blank",
11391
+ "aria-hidden": true
11392
+ }) : null, /* @__PURE__ */ jsx(WorkingRow, {
11393
+ label: state.status === "starting" ? "Starting…" : "Working…",
11394
+ startedAt: runStartedAt,
11395
+ tokens: state.contextUsage?.totalTokens
11396
+ })] }) : /* @__PURE__ */ jsx(Loader, {
11397
+ label: state.status === "starting" ? "Starting session…" : void 0,
11398
+ startedAt: runStartedAt,
11399
+ tokens: state.contextUsage?.totalTokens
11400
+ }) : null
11401
+ ]
11107
11402
  })
11108
11403
  }),
11109
11404
  /* @__PURE__ */ jsx(ConversationScrollButton, {}),
@@ -11325,18 +11620,71 @@ const INTERACTIVE = [
11325
11620
  * the engine name — an absent capability hides the control instead of offering
11326
11621
  * one that can only fail.
11327
11622
  */
11328
- function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", statusPlacement = "top", onOpenPanel, onVitals, transcriptVariant = "cards", transcriptDensity = "comfortable", transcriptFont = "sans", affordances, terminalMetrics, scrubber = false, scrubberMarks, reveal, stickyPrompt = false, controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, readOnly = false, toolHost, cacheTranscript, className }) {
11623
+ function SessionPanel({ client, sessionId, header, panelSurface = "internal", statusSurface = "internal", statusPlacement = "top", onOpenPanel, onVitals, transcriptVariant = "cards", transcriptDensity = "comfortable", transcriptFont = "sans", affordances, terminalMetrics, scrubber = false, scrubberMarks, reveal, openSubagent, stickyPrompt = false, controlsSurface = "internal", onControls, focusComposerOnClick = false, unseen, readOnly = false, toolHost, cacheTranscript, className }) {
11329
11624
  const external = panelSurface === "external";
11330
11625
  const statusExternal = statusSurface === "external";
11331
11626
  const controlsInStatus = controlsSurface === "status" && !statusExternal;
11332
11627
  const controlsExternal = controlsSurface === "external" || controlsInStatus;
11333
11628
  const [protocolError, setProtocolError] = useState(void 0);
11334
11629
  const [panel, setPanel] = useState();
11630
+ /**
11631
+ * The sub-agent takeover: which `Task` call the body is currently showing, or
11632
+ * undefined for the conversation.
11633
+ *
11634
+ * Deliberately **not** a `Panel` member. Those route outward under
11635
+ * `panelSurface: 'external'` so a host can draw them natively — and a host
11636
+ * cannot draw this one: it is a transcript, and the transcript is in here. It
11637
+ * is a mode of the conversation surface, not a screen over it.
11638
+ */
11639
+ const [subagentId, setSubagentId] = useState(void 0);
11640
+ /**
11641
+ * Where to land on the way back — the row of the Task you entered from,
11642
+ * asked for through the ordinary reveal seam.
11643
+ *
11644
+ * The alternative was keeping the main transcript mounted-but-hidden to
11645
+ * preserve its scroll offset, which costs two live virtualizers and an
11646
+ * absolute-position layout on a panel whose height just changed (the composer
11647
+ * came back). Re-revealing is both cheaper and more honest: you return to the
11648
+ * row you left from rather than to wherever you happened to be scrolled.
11649
+ */
11650
+ const [returnReveal, setReturnReveal] = useState(void 0);
11335
11651
  const { state, connection, replaying, protocolMismatch, models, effectiveModel, handle, send, approve, deny, interrupt, setModel, setPermissionMode, reconnectNow, loadFullResult } = useClaudeSession(client, sessionId, {
11336
11652
  onProtocolError: setProtocolError,
11337
11653
  cacheTranscript
11338
11654
  });
11339
11655
  useEffect(() => setProtocolError(void 0), [sessionId]);
11656
+ useEffect(() => {
11657
+ setSubagentId(void 0);
11658
+ setReturnReveal(void 0);
11659
+ }, [sessionId]);
11660
+ const openSubagentNonce = openSubagent?.nonce;
11661
+ const openSubagentId = openSubagent?.toolUseId;
11662
+ useEffect(() => {
11663
+ if (openSubagentId === void 0) return;
11664
+ setSubagentId(openSubagentId);
11665
+ }, [openSubagentNonce]);
11666
+ const revealNonce = reveal?.nonce;
11667
+ useEffect(() => {
11668
+ if (revealNonce === void 0) return;
11669
+ setSubagentId(void 0);
11670
+ }, [revealNonce]);
11671
+ const leaveSubagent = useCallback(() => {
11672
+ setSubagentId((current) => {
11673
+ if (current !== void 0) setReturnReveal({
11674
+ toolUseId: current,
11675
+ nonce: Date.now()
11676
+ });
11677
+ });
11678
+ }, []);
11679
+ useEffect(() => {
11680
+ if (subagentId === void 0) return;
11681
+ const onKey = (event) => {
11682
+ if (event.key !== "Escape" || event.defaultPrevented) return;
11683
+ leaveSubagent();
11684
+ };
11685
+ window.addEventListener("keydown", onKey);
11686
+ return () => window.removeEventListener("keydown", onKey);
11687
+ }, [subagentId, leaveSubagent]);
11340
11688
  const [caughtUp, setCaughtUp] = useState(false);
11341
11689
  useEffect(() => {
11342
11690
  setCaughtUp(false);
@@ -11357,6 +11705,12 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11357
11705
  }, [reconnectNow]);
11358
11706
  useToolCallHost(handle, toolHost === false ? { enabled: false } : toolHost);
11359
11707
  const terminal = transcriptVariant === "terminal";
11708
+ const subagentFrameItems = useMemo(() => subagentId === void 0 ? [] : subagentItems(state.items, subagentId), [state.items, subagentId]);
11709
+ const subagentTask = useMemo(() => subagentId === void 0 ? void 0 : state.items.find((item) => item.kind === "tool_call" && item.id === subagentId), [state.items, subagentId]);
11710
+ const subagentFallbackLabel = useMemo(() => {
11711
+ const record = state.session?.subagents?.find((sub) => sub.toolUseId === subagentId);
11712
+ return record ? subagentLabel(record) : "Sub-agent";
11713
+ }, [state.session, subagentId]);
11360
11714
  const capabilities = state.capabilities;
11361
11715
  const { usage: profileUsage } = useProfileUsage(client, state.session?.profile, { enabled: capabilities.rateLimits });
11362
11716
  const usage = useMemo(() => mergeUsage({
@@ -11561,6 +11915,15 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11561
11915
  onDismiss: () => setProtocolError(void 0),
11562
11916
  children: protocolError
11563
11917
  }) : null,
11918
+ subagentId !== void 0 ? /* @__PURE__ */ jsx(SubagentStrip, {
11919
+ task: subagentTask,
11920
+ items: subagentFrameItems,
11921
+ label: subagentFallbackLabel,
11922
+ onBack: leaveSubagent,
11923
+ terminal,
11924
+ fontSize: terminalMetrics?.fontSize,
11925
+ lineHeight: terminalMetrics?.lineHeight
11926
+ }) : null,
11564
11927
  /* @__PURE__ */ jsx(Transcript, {
11565
11928
  state,
11566
11929
  fileUrl: sessionId ? (path) => client.sessionFileUrl(sessionId, path) : void 0,
@@ -11580,11 +11943,13 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11580
11943
  from: catchUp.itemCount,
11581
11944
  since: catchUp.since
11582
11945
  } : void 0,
11583
- reveal,
11946
+ reveal: returnReveal ?? reveal,
11947
+ frame: subagentId === void 0 ? void 0 : { parentToolUseId: subagentId },
11948
+ onOpenSubagent: setSubagentId,
11584
11949
  jumpToRecapRef: jumpToRecap,
11585
11950
  repinRef: repinTranscript
11586
- }),
11587
- catchUp && newCount > 0 && !replaying ? /* @__PURE__ */ jsx("div", {
11951
+ }, subagentId ?? "session"),
11952
+ catchUp && newCount > 0 && !replaying && subagentId === void 0 ? /* @__PURE__ */ jsx("div", {
11588
11953
  className: "px-3 pb-1",
11589
11954
  children: /* @__PURE__ */ jsxs("div", {
11590
11955
  "data-slot": "catch-up",
@@ -11648,7 +12013,7 @@ function SessionPanel({ client, sessionId, header, panelSurface = "internal", st
11648
12013
  })
11649
12014
  })
11650
12015
  }) : null,
11651
- readOnly ? null : /* @__PURE__ */ jsx(Composer, {
12016
+ readOnly || subagentId !== void 0 ? null : /* @__PURE__ */ jsx(Composer, {
11652
12017
  ref: composerRef,
11653
12018
  onSend: handleSend,
11654
12019
  onInterrupt: interrupt,
@@ -11805,6 +12170,6 @@ function Notice({ level, onDismiss, children }) {
11805
12170
  });
11806
12171
  }
11807
12172
  //#endregion
11808
- export { TranscriptDensityProvider as $, PermissionModeSelect as A, MenuTrigger as At, TerminalSurface as B, Button as Bt, TerminalQuestionPrompt as C, DialogHeader as Ct, parseUserQuestions as D, MenuContent as Dt, QuestionPrompt as E, Menu$1 as Et, McpDialog as F, SelectTrigger as Ft, Blank as G, TerminalDiff as H, cn as Ht, HostFilesDialog as I, SelectValue as It, CopyAction as J, Ink as K, ContextDialog as L, Input as Lt, permissionModeMeta as M, SelectContent as Mt, ModelSelect as N, SelectItem as Nt, PermissionPrompt as O, MenuItem as Ot, SkillsDialog as P, SelectItemText as Pt, toolIcon as Q, Composer as R, Badge as Rt, SessionInfoDialog as S, DialogContent as St, QUESTION_BEHAVIORS as T, DialogTrigger as Tt, previewPatch as U, TerminalMarkdown as V, buttonVariants as Vt, Band as W, useAffordances as X, WithActions as Y, isMutatingTool as Z, Conversation as _, TooltipContent as _t, Transcript as a, mentionTrigger as at, StatusBar as b, DialogBody as bt, ToolCallCard as c, plainTextToSegments as ct, Response as d, Splitter as dt, TranscriptVariantProvider as et, PromptTokenText as f, CodeBlock as ft, FileCard as g, Tip as gt, Loader as h, copyText as ht, useMinuteClock as i, hashtagTrigger as it, permissionModeChoices as j, Select$1 as jt, PERMISSION_MODES as k, MenuSeparator as kt, SessionEmptyState as l, segmentsToPlainText as lt, MessageContent as m, CopyButton as mt, UsageDialog as n, useTranscriptVariant as nt, TerminalItemView as o, usePromptAreaState as ot, Message as p, Spinner as pt, Row as q, UsageMeters as r, commandTrigger as rt, TerminalTranscript as s, PromptArea as st, SessionPanel as t, useTranscriptDensity as tt, Reasoning as u, ProgressRing as ut, ConversationContent as v, TooltipProvider as vt, TerminalPermissionPrompt as w, DialogRow as wt, STATUS_META as x, DialogClose as xt, ConversationScrollButton as y, Dialog$1 as yt, skillPrompt as z, badgeVariants as zt };
12173
+ export { toolIcon as $, PERMISSION_MODES as A, MenuSeparator as At, skillPrompt as B, badgeVariants as Bt, TerminalQuestionPrompt as C, DialogContent as Ct, parseUserQuestions as D, Menu$1 as Dt, QuestionPrompt as E, DialogTrigger as Et, SkillsDialog as F, SelectItemText as Ft, Band as G, TerminalMarkdown as H, buttonVariants as Ht, McpDialog as I, SelectTrigger as It, Row as J, Blank as K, HostFilesDialog as L, SelectValue as Lt, permissionModeChoices as M, Select$1 as Mt, permissionModeMeta as N, SelectContent as Nt, SubagentStrip as O, MenuContent as Ot, ModelSelect as P, SelectItem as Pt, isMutatingTool as Q, ContextDialog as R, Input as Rt, SessionInfoDialog as S, DialogClose as St, QUESTION_BEHAVIORS as T, DialogRow as Tt, TerminalDiff as U, cn as Ut, TerminalSurface as V, Button as Vt, previewPatch as W, WithActions as X, CopyAction as Y, useAffordances as Z, Conversation as _, Tip as _t, Transcript as a, hashtagTrigger as at, StatusBar as b, Dialog$1 as bt, ToolCallCard as c, PromptArea as ct, Response as d, ProgressRing as dt, TranscriptDensityProvider as et, PromptTokenText as f, Splitter as ft, FileCard as g, copyText as gt, Loader as h, CopyButton as ht, useMinuteClock as i, commandTrigger as it, PermissionModeSelect as j, MenuTrigger as jt, PermissionPrompt as k, MenuItem as kt, SessionEmptyState as l, plainTextToSegments as lt, MessageContent as m, Spinner as mt, UsageDialog as n, useTranscriptDensity as nt, TerminalItemView as o, mentionTrigger as ot, Message as p, CodeBlock as pt, Ink as q, UsageMeters as r, useTranscriptVariant as rt, TerminalTranscript as s, usePromptAreaState as st, SessionPanel as t, TranscriptVariantProvider as tt, Reasoning as u, segmentsToPlainText as ut, ConversationContent as v, TooltipContent as vt, TerminalPermissionPrompt as w, DialogHeader as wt, STATUS_META as x, DialogBody as xt, ConversationScrollButton as y, TooltipProvider as yt, Composer as z, Badge as zt };
11809
12174
 
11810
- //# sourceMappingURL=SessionPanel-DMPhsNlW.mjs.map
12175
+ //# sourceMappingURL=SessionPanel-DPx8Iz8a.mjs.map