@workerdeck/ui 0.17.0 → 0.19.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.
- package/build/{SessionPanel-DMPhsNlW.mjs → SessionPanel-CHktI2CF.mjs} +440 -75
- package/build/SessionPanel-CHktI2CF.mjs.map +1 -0
- package/build/{SessionPanel-CnNYEX80.d.mts → SessionPanel-CnU_IJ3-.d.mts} +24 -9
- package/build/{format-DfI_je9S.d.mts → format-ljc3lKpA.d.mts} +1 -1
- package/build/format.d.mts +16 -5
- package/build/format.mjs +2 -3
- package/build/index.d.mts +237 -8
- package/build/index.mjs +399 -167
- package/build/index.mjs.map +1 -1
- package/build/{format-DqR56Y8l.mjs → status-BE-zg88x.mjs} +154 -2
- package/build/status-BE-zg88x.mjs.map +1 -0
- package/build/workspace.d.mts +4 -1
- package/build/workspace.mjs +4 -3
- package/build/workspace.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/agent/ContextRing.tsx +41 -0
- package/src/components/agent/EngineIcon.tsx +40 -0
- package/src/components/agent/PermissionModeSelect.tsx +5 -1
- package/src/components/agent/SessionBrowser.tsx +115 -22
- package/src/components/agent/SessionPanel.tsx +153 -3
- package/src/components/agent/SessionSteps.tsx +233 -0
- package/src/components/agent/SessionWorkspace.tsx +4 -0
- package/src/components/agent/StatusBar.tsx +4 -2
- package/src/components/agent/SubagentStrip.tsx +134 -0
- package/src/components/agent/Transcript.tsx +138 -19
- package/src/components/agent/transcript-rows.ts +9 -1
- package/src/components/terminal/TerminalTranscript.tsx +77 -3
- package/src/components/terminal/affordances.tsx +34 -0
- package/src/components/terminal/blocks.ts +28 -0
- package/src/components/terminal/height.ts +24 -0
- package/src/components/terminal/items.tsx +1 -1
- package/src/components/terminal/scrubber.tsx +28 -3
- package/src/components/terminal/tool-run.ts +49 -5
- package/src/index.ts +19 -1
- package/src/lib/status.ts +16 -3
- package/src/styles/terminal.css +10 -0
- package/src/styles/theme.css +42 -0
- package/build/SessionPanel-DMPhsNlW.mjs.map +0 -1
- package/build/format-DqR56Y8l.mjs.map +0 -1
- package/build/status-Ydzi7n6j.mjs +0 -143
- package/build/status-Ydzi7n6j.mjs.map +0 -1
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
5350
|
-
|
|
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) {
|
|
@@ -7166,7 +7257,7 @@ const PERMISSION_MODES = [
|
|
|
7166
7257
|
value: "auto",
|
|
7167
7258
|
label: "Auto",
|
|
7168
7259
|
shortLabel: "Auto",
|
|
7169
|
-
description: "
|
|
7260
|
+
description: "The agent handles permission decisions",
|
|
7170
7261
|
icon: Zap
|
|
7171
7262
|
},
|
|
7172
7263
|
{
|
|
@@ -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
|
-
|
|
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) !==
|
|
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) ===
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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" : "
|
|
10690
|
+
tone: failed ? "red" : "green",
|
|
10436
10691
|
children: taskSummary(block.task, children)
|
|
10437
10692
|
})
|
|
10438
|
-
}), open ? /* @__PURE__ */
|
|
10693
|
+
}), open ? /* @__PURE__ */ jsxs("div", {
|
|
10439
10694
|
className: "term-nested",
|
|
10440
|
-
children:
|
|
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
|
-
|
|
10695
|
-
|
|
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(
|
|
11042
|
-
|
|
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,
|
|
11334
|
+
...fold(boundary, items.length)
|
|
11050
11335
|
];
|
|
11051
11336
|
}, [
|
|
11052
|
-
|
|
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: [
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
|
|
11076
|
-
|
|
11077
|
-
|
|
11078
|
-
|
|
11079
|
-
replaying,
|
|
11080
|
-
|
|
11081
|
-
|
|
11082
|
-
|
|
11083
|
-
|
|
11084
|
-
|
|
11085
|
-
|
|
11086
|
-
|
|
11087
|
-
|
|
11088
|
-
|
|
11089
|
-
|
|
11090
|
-
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
|
|
11097
|
-
|
|
11098
|
-
|
|
11099
|
-
|
|
11100
|
-
|
|
11101
|
-
|
|
11102
|
-
|
|
11103
|
-
|
|
11104
|
-
|
|
11105
|
-
|
|
11106
|
-
|
|
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 {
|
|
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-
|
|
12175
|
+
//# sourceMappingURL=SessionPanel-CHktI2CF.mjs.map
|