eve 0.26.0 → 0.26.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/src/cli/banner.d.ts +5 -0
  3. package/dist/src/cli/banner.js +1 -1
  4. package/dist/src/cli/dev/tui/blocks.d.ts +56 -8
  5. package/dist/src/cli/dev/tui/blocks.js +7 -7
  6. package/dist/src/cli/dev/tui/diagnostic-presentation.d.ts +2 -0
  7. package/dist/src/cli/dev/tui/diagnostic-presentation.js +3 -2
  8. package/dist/src/cli/dev/tui/file-content-cache.d.ts +35 -0
  9. package/dist/src/cli/dev/tui/file-content-cache.js +3 -0
  10. package/dist/src/cli/dev/tui/line-diff.d.ts +24 -0
  11. package/dist/src/cli/dev/tui/line-diff.js +1 -0
  12. package/dist/src/cli/dev/tui/markdown.d.ts +2 -1
  13. package/dist/src/cli/dev/tui/markdown.js +7 -3
  14. package/dist/src/cli/dev/tui/prompt-placeholder.d.ts +12 -0
  15. package/dist/src/cli/dev/tui/prompt-placeholder.js +1 -0
  16. package/dist/src/cli/dev/tui/question-panel.d.ts +24 -0
  17. package/dist/src/cli/dev/tui/question-panel.js +1 -0
  18. package/dist/src/cli/dev/tui/rail.d.ts +15 -0
  19. package/dist/src/cli/dev/tui/rail.js +1 -0
  20. package/dist/src/cli/dev/tui/runner.d.ts +22 -65
  21. package/dist/src/cli/dev/tui/runner.js +1 -1
  22. package/dist/src/cli/dev/tui/setup-commands.js +1 -1
  23. package/dist/src/cli/dev/tui/setup-issues.js +1 -1
  24. package/dist/src/cli/dev/tui/status-line.js +1 -1
  25. package/dist/src/cli/dev/tui/stream-format.d.ts +16 -3
  26. package/dist/src/cli/dev/tui/stream-format.js +1 -1
  27. package/dist/src/cli/dev/tui/subagent-pump.d.ts +127 -0
  28. package/dist/src/cli/dev/tui/subagent-pump.js +1 -0
  29. package/dist/src/cli/dev/tui/terminal-renderer.d.ts +34 -1
  30. package/dist/src/cli/dev/tui/terminal-renderer.js +11 -10
  31. package/dist/src/cli/dev/tui/test/mock-terminal.d.ts +7 -0
  32. package/dist/src/cli/dev/tui/test/mock-terminal.js +1 -1
  33. package/dist/src/cli/dev/tui/theme.d.ts +16 -2
  34. package/dist/src/cli/dev/tui/theme.js +1 -1
  35. package/dist/src/cli/dev/tui/todo-panel.d.ts +44 -0
  36. package/dist/src/cli/dev/tui/todo-panel.js +1 -0
  37. package/dist/src/cli/dev/tui/tool-block-groups.d.ts +40 -0
  38. package/dist/src/cli/dev/tui/tool-block-groups.js +1 -0
  39. package/dist/src/cli/dev/tui/tool-presentation.d.ts +67 -0
  40. package/dist/src/cli/dev/tui/tool-presentation.js +1 -0
  41. package/dist/src/cli/dev/tui/tool-rows.d.ts +15 -0
  42. package/dist/src/cli/dev/tui/tool-rows.js +2 -0
  43. package/dist/src/cli/dev/tui/turn-clock.d.ts +36 -0
  44. package/dist/src/cli/dev/tui/turn-clock.js +1 -0
  45. package/dist/src/cli/dev/tui/types.d.ts +2 -2
  46. package/dist/src/cli/dev/ui-options.js +1 -1
  47. package/dist/src/client/index.js +1 -1
  48. package/dist/src/client/session.js +1 -1
  49. package/dist/src/compiled/.vendor-stamp.json +2 -1
  50. package/dist/src/compiled/marked/LICENSE.md +44 -0
  51. package/dist/src/compiled/marked/index.d.ts +24 -0
  52. package/dist/src/compiled/marked/index.js +59 -0
  53. package/dist/src/evals/session.js +1 -1
  54. package/dist/src/execution/terminal-session-failure-step.js +1 -1
  55. package/dist/src/harness/tool-loop.js +2 -2
  56. package/dist/src/internal/application/package.js +1 -1
  57. package/dist/src/internal/nitro/routes/agent-info/build-agent-info-response-from-manifest.js +1 -1
  58. package/dist/src/internal/nitro/routes/info.js +1 -1
  59. package/dist/src/internal/resolve-model-endpoint-status.d.ts +46 -3
  60. package/dist/src/internal/resolve-model-endpoint-status.js +1 -1
  61. package/dist/src/setup/flows/link.d.ts +13 -3
  62. package/dist/src/setup/flows/link.js +1 -1
  63. package/dist/src/setup/flows/model.d.ts +4 -3
  64. package/dist/src/setup/flows/model.js +1 -1
  65. package/dist/src/setup/flows/provider.d.ts +3 -1
  66. package/dist/src/setup/flows/provider.js +2 -2
  67. package/dist/src/setup/scaffold/create/project.js +1 -1
  68. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # eve
2
2
 
3
+ ## 0.26.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 1f04ff7: The dev TUI renders Markdown through a real GFM parser (width-fitted tables, code blocks, task lists, links), presents every builtin tool with semantic activity copy (`Fetch <url>`, `Run <command>`, `Search <query>` — including `web_search` across provider input shapes, `todo`, `ask_question`, `agent`, and `connection_search`), and shows rejected tool approvals as denied instead of successful. Tool activity now accumulates in place: an in-flight batch renders as one counted header with its newest calls listed first behind a `│` rail and a `… (N more)` elision, then collapses to a single past-tense line (`▪ Fetched 30 URLs`) once every call settles — failures keep their itemized per-call error rail — and each `write_file` keeps its own block rendering a real line diff (`+`/`-` rows with context, computed from content the session has already seen — prior writes and full-file reads; new files show all additions). `ask_question` prompts open as a numbered overlay panel above the input (number keys select directly, the "Type your own answer" row focuses its inline editor on reach, Esc dismisses to plain text input), committing as `? question ⎿ answer` in the transcript. The `todo` tool no longer emits transcript blocks; it drives a pinned panel above the input showing per-task state, which commits as a completed block once every task settles. An empty prompt now shows a quiet `›` mark with a rotating placeholder of things to try, switching to the active `❯` once typing starts. Reasoning defaults to a fixed one-line thinking indicator that updates in place (persisting as `○ Thought for 12s` only for long thoughts; `--reasoning full` restores the streaming trace), the prompt stays anchored during a turn with Enter inert (the draft carries into the next prompt), and completed turns close with a `└ Done in <duration>` stats coda (per-turn token flow, context fill) when the turn was long or expensive. Subagent calls render as individual `※ subagent(<name>) #N` sections in the tool column whose child rows window to the three most recently active (failures stay itemized while settled work condenses to one counted row) and close on a corner that reports `Done` once the call's final message arrives; captured server output renders as one open-railed `○ stderr` / `○ stdout` stream section per source (streams are continuous — no closing corner, and interleaved activity never fragments them): the section sits at its newest write's position — everything that happened after the last error displays after it — and shows only that newest write, with earlier ones behind an `… (N more)` count (the stored-diagnostics pointer carries the history); the status bar names the model's actual routing and credential (`via ai-gateway(oidc:<project>)`, `via ai-gateway(api-key)`, or `via <provider>⌝` for a directly-authored endpoint); an empty prompt no longer submits; and a mid-conversation session replacement marks the context cut with a `┌── Session restarted, clear context.` boundary line.
8
+
3
9
  ## 0.26.0
4
10
 
5
11
  ### Minor Changes
@@ -5,3 +5,8 @@ export declare const EVE_WORDMARK = "eve";
5
5
  * pre-action hook so commands never compose their own variant.
6
6
  */
7
7
  export declare function eveCliBanner(): string;
8
+ /**
9
+ * The unstyled wordmark-and-version tag (`☰eve v0.24.5`) — the boot banner's
10
+ * plain-text form. The dev TUI dims it as its parting line on teardown.
11
+ */
12
+ export declare function eveVersionTag(): string;
@@ -1 +1 @@
1
- import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";var import_picocolors=__toESM(require_picocolors(),1);const EVE_WORDMARK=`eve`;function eveCliBanner(){let{version:e}=resolveInstalledPackageInfo();return`${import_picocolors.default.bgBlack(import_picocolors.default.white(`☰eve `))} ${import_picocolors.default.dim(`v${e}`)}`}export{EVE_WORDMARK,eveCliBanner};
1
+ import{__toESM}from"../_virtual/_rolldown/runtime.js";import{require_picocolors}from"../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js";import{resolveInstalledPackageInfo}from"#internal/application/package.js";var import_picocolors=__toESM(require_picocolors(),1);const EVE_WORDMARK=`eve`;function eveCliBanner(){let{version:e}=resolveInstalledPackageInfo();return`${import_picocolors.default.bgBlack(import_picocolors.default.white(`☰eve `))} ${import_picocolors.default.dim(`v${e}`)}`}function eveVersionTag(){let{version:e}=resolveInstalledPackageInfo();return`☰eve v${e}`}export{EVE_WORDMARK,eveCliBanner,eveVersionTag};
@@ -9,9 +9,11 @@
9
9
  * boxes anywhere. Every returned row is already styled and fits within the
10
10
  * given width, so the live region can place rows verbatim.
11
11
  */
12
+ import type { ToolDetailLine } from "./line-diff.js";
12
13
  import type { Theme } from "./theme.js";
14
+ import type { ToolGroupPresentation } from "./tool-presentation.js";
13
15
  export type ToolStatus = "running" | "done" | "error" | "denied" | "approval";
14
- export type BlockKind = "user" | "assistant" | "reasoning" | "tool" | "error" | "notice" | "warning" | "result" | "flow" | "command" | "question" | "subagent" | "subagent-step" | "subagent-tool" | "connection-auth" | "sandbox" | "log" | "agent-header";
16
+ export type BlockKind = "user" | "assistant" | "reasoning" | "tool" | "error" | "notice" | "warning" | "result" | "flow" | "command" | "question" | "subagent" | "subagent-step" | "subagent-tool" | "subagent-close" | "connection-auth" | "sandbox" | "log" | "turn-stats" | "session-boundary" | "todo-list" | "agent-header";
15
17
  /**
16
18
  * One renderable transcript unit. Fields are interpreted per `kind`; unset
17
19
  * fields are simply omitted from the rendered output.
@@ -22,10 +24,12 @@ export interface Block {
22
24
  id?: string;
23
25
  /** Nesting depth: 0 = top level, 1 = inside a subagent, etc. */
24
26
  depth?: number;
25
- /** Whether the block is still streaming / mutating (drives the spinner). */
27
+ /** Whether the block is still streaming / mutating (drives the activity pulse). */
26
28
  live?: boolean;
27
29
  /** Primary label — tool name, subagent name, log source, error title. */
28
30
  title?: string;
31
+ /** Past-tense tool label swapped in once the call settles successfully. */
32
+ doneTitle?: string;
29
33
  /** Compact secondary text — summarized tool args. */
30
34
  subtitle?: string;
31
35
  /** Main multi-line content (markdown for prose, plain for logs). */
@@ -54,15 +58,59 @@ export interface Block {
54
58
  /** Raw tool input / output for the expanded view. */
55
59
  toolInput?: unknown;
56
60
  toolOutput?: unknown;
61
+ /** Original execution name, kept separate from a semantic display title. */
62
+ toolName?: string;
63
+ /** Optional aggregation metadata; execution state remains on this call's block. */
64
+ toolGroup?: ToolGroupPresentation;
65
+ /** Salient body lines rendered behind the `│` rail under the tool header. */
66
+ detailLines?: readonly ToolDetailLine[];
67
+ /** When true, `detailLines` stay visible after the call settles (writes). */
68
+ keepDetailWhenDone?: boolean;
69
+ /** Links a subagent section's header and children so calls can coalesce. */
70
+ subagentCallId?: string;
71
+ /**
72
+ * Monotonic activity stamp, bumped on every push and in-place update.
73
+ * Recency windows key on it so a parallel-announced call that just
74
+ * settled counts as newer than a later-announced one still idle.
75
+ */
76
+ updateSeq?: number;
77
+ }
78
+ /**
79
+ * What the renderers actually draw: an execution {@link Block} plus the
80
+ * synthesized presentation the display grouping may attach. Only the
81
+ * grouping layer creates these fields, so an execution block can never
82
+ * smuggle display state — the type boundary enforces what used to be a
83
+ * comment.
84
+ */
85
+ export interface DisplayBlock extends Block {
86
+ /** Items listed when equivalent tool calls are coalesced into one row. */
87
+ toolGroupItems?: readonly ToolGroupItem[];
88
+ /**
89
+ * Stand-in for this many earlier sibling rows elided from a capped
90
+ * subagent run; renders as a single dim `… +N more` line.
91
+ */
92
+ elided?: number;
93
+ /**
94
+ * This block is the last of its section, so its final row swaps the
95
+ * nesting rule for the closing `└` — the rail ends on the newest child
96
+ * instead of a bare corner row.
97
+ */
98
+ closesRail?: boolean;
99
+ }
100
+ /** One coalesced call's row beneath an aggregated tool header. */
101
+ export interface ToolGroupItem {
102
+ readonly text: string;
103
+ /** Per-call failure summary, present when a failed batch is aggregated. */
104
+ readonly result?: string;
57
105
  }
58
106
  export interface RenderBlockContext {
59
- /** Current spinner frame for live blocks. */
60
- spinner: string;
107
+ /** Current shared square-pulse frame for live activity blocks. */
108
+ activityPulse: string;
61
109
  /**
62
110
  * Kind and title of the block rendered immediately above this one. Lets a
63
- * log block detect that it continues a same-source run (label suppressed,
64
- * lines hang under the previous block's label) without any mutable run
65
- * state — each captured write stays its own immediately-committed block.
111
+ * sandbox block detect that it continues a run (label suppressed, lines
112
+ * hang under the previous block's label) without any mutable run state —
113
+ * each captured write stays its own immediately-committed block.
66
114
  */
67
115
  previous?: {
68
116
  kind: BlockKind;
@@ -73,7 +121,7 @@ export interface RenderBlockContext {
73
121
  * Renders a block to its terminal rows. Each row is fully styled and clipped
74
122
  * to `width` visible columns.
75
123
  */
76
- export declare function renderBlockLines(block: Block, width: number, theme: Theme, context: RenderBlockContext): string[];
124
+ export declare function renderBlockLines(block: DisplayBlock, width: number, theme: Theme, context: RenderBlockContext): string[];
77
125
  /**
78
126
  * The setup attention line (`⚠ 1 setup issue: … · /model`): yellow glyph, body
79
127
  * at full intensity, slash commands painted blue so the fix reads as actionable
@@ -1,8 +1,8 @@
1
- import{isPromptControlCommand}from"./prompt-commands.js";import{formatValuePretty,truncate}from"./tool-format.js";import{renderMarkdown}from"./markdown.js";import{sliceVisible,visibleLength,wrapVisibleLine}from"#cli/ui/terminal-text.js";function renderBlockLines(e,t,n,r){let i=nestingPrefix(e.depth??0,n);return renderBody(e,Math.max(8,t-visibleLength(i)),n,r).map(e=>`${i}${e}`)}function nestingPrefix(e,t){return e<=0?``:`${t.colors.orange(t.glyph.rule)} `.repeat(e)}function renderBody(e,t,n,r){switch(e.kind){case`user`:return renderUser(e,t,n);case`assistant`:case`subagent-step`:return renderProse(e,t,n);case`reasoning`:return renderReasoning(e,t,n);case`tool`:case`subagent-tool`:return renderTool(e,t,n,r);case`error`:return renderError(e,t,n);case`notice`:return renderNotice(e,t,n);case`warning`:return renderWarning(e,t,n);case`result`:return renderResult(e,t,n);case`flow`:return renderFlow(e,t,n);case`command`:return renderCommand(e,n);case`question`:case`connection-auth`:return renderPreformatted(e,t,n);case`sandbox`:return renderSandbox(e,t,n,r);case`log`:return renderLog(e,t,n,r);case`subagent`:return renderSubagentHeader(e,t,n);case`agent-header`:return(e.body??``).split(`
2
- `)}}function renderUser(e,t,n){let r=n.colors.cyan(n.glyph.user);return wrap(e.body??``,t-2).map(e=>`${r} ${e}`)}function renderProse(e,t,n){let i=[],a=e.kind===`subagent-step`,s=a?``:`${n.colors.bold(n.colors.white(n.glyph.brand))} `,c=a?``:` `;e.reasoning&&e.reasoning.trim().length>0&&i.push(...renderReasoningLines(e.reasoning,t,n));let l=(e.body??``).trim();return l.length===0&&i.length===0?[`${s}${n.colors.dim(`thinking${n.glyph.ellipsis}`)}`]:(l.length>0&&renderMarkdown(l).split(`
3
- `).flatMap(e=>wrapVisibleLine(e,t-c.length)).forEach((e,t)=>{t===0&&!a&&i.length===0?i.push(`${s}${e}`):i.push(`${c}${e}`)}),i.length>0?i:[`${s}`])}function renderReasoning(e,t,n){return e.collapsed?[`${n.colors.gray(n.glyph.reasoning)} ${n.colors.dim(`thinking`)}`]:renderReasoningLines(e.body??``,t,n,n.glyph.reasoning)}function renderReasoningLines(e,t,n,r){let i=r?2:0,a=wrap(e.trim(),t-i);return a.length===0?[]:a.map((e,t)=>`${r?t===0?`${n.colors.gray(r)} `:` `:``}${n.colors.dim(n.colors.italic(e))}`)}function renderTool(e,t,r,i){let{icon:a,accent:o}=toolGlyph(e.status??`running`,r,i),s=e.title??`tool`,c=t-2,l=truncatePlain(s,c),u=`${a} ${r.colors.bold(l)}`,d=c-l.length-2,f=e.subtitle??``;f.length>0&&d>=6&&(u+=` ${r.colors.gray(truncate(f,d))}`);let p=[u];return e.expanded?p.push(...renderToolExpanded(e,t,r)):e.status===`done`&&e.result&&e.result.length>0?p.push(resultLine(r.glyph.arrow,e.result,t,r,o)):e.status===`error`&&e.result?p.push(resultLine(r.glyph.arrow,e.result,t,r,r.colors.red)):e.status===`denied`&&p.push(resultLine(r.glyph.arrow,`denied`,t,r,r.colors.yellow)),p}function renderToolExpanded(e,n,r){let i=[],push=(e,a,o)=>{if(a!==void 0){i.push(` ${r.colors.dim(e)}`);for(let e of wrap(formatValuePretty(a),n-4))i.push(` ${o(e)}`)}};return push(`input`,e.toolInput,r.colors.gray),e.status===`error`&&e.result?push(`error`,e.result,r.colors.red):push(`output`,e.toolOutput,r.colors.gray),i}function resultLine(e,t,r,i,a){let o=r-4;return` ${i.colors.dim(e)} ${a(truncate(t,o))}`}function toolGlyph(e,t,n){switch(e){case`done`:return{icon:t.colors.green(t.glyph.success),accent:t.colors.gray};case`error`:return{icon:t.colors.red(t.glyph.error),accent:t.colors.red};case`denied`:return{icon:t.colors.yellow(t.glyph.warning),accent:t.colors.yellow};case`approval`:return{icon:t.colors.yellow(t.glyph.question),accent:t.colors.yellow};default:return{icon:t.colors.yellow(n.spinner),accent:t.colors.gray}}}function renderError(e,t,n){let r=n.colors.red(n.colors.bold(n.glyph.error)),i=e.title??`Error`,a=[`${r} ${n.colors.red(n.colors.bold(i))}`];for(let r of wrap(e.body??``,t-2))a.push(` ${colorizeError(r,n)}`);if(e.hint!==void 0&&e.hint.trim().length>0)for(let[r,i]of wrap(e.hint,t-4).entries()){let e=r===0?`${n.glyph.arrow} `:` `;a.push(` ${n.colors.cyan(`${e}${i}`)}`)}return a.push(...renderErrorDetail(e.detail,t,n)),a}function renderErrorDetail(e,t,n){if(e===void 0||e.trim().length===0)return[];let r=e.split(`
4
- `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(t,n){return t.replace(/\/[a-z:-]+/g,t=>isPromptControlCommand(t)?n.colors.blue(t):t)}function renderCommand(e,t){let n=t.colors,r=e.status===`error`?`${n.red(t.glyph.error)} `:``;return[`${n.cyan(t.glyph.user)} ${r}${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,n){let r=e.kind===`connection-auth`?n.colors.yellow(n.glyph.connection):n.colors.yellow(n.colors.bold(n.glyph.question)),i=wrap(e.title??``,t-2),a=i.length===0?[`${r} `]:i.map((e,t)=>t===0?`${r} ${n.colors.bold(e)}`:` ${n.colors.bold(e)}`);for(let n of(e.body??``).split(`
5
- `))for(let e of wrapVisibleLine(n,t-2))a.push(` ${e}`);return a}function renderSandbox(e,t,n,r){let i=n.colors.cyan(n.glyph.rule),s=n.colors.dim(`sandbox ${n.glyph.dot} `),c=visibleLength(s),l=` `.repeat(c),u=r.previous?.kind===`sandbox`,d=(e.body??``).split(`
6
- `),f=[];for(let e of d){let r=wrapVisibleLine(e,Math.max(1,t-2-c));for(let e of r){let t=f.length===0&&!u?s:l;f.push(`${i} ${t}${n.colors.gray(e)}`)}}return f.length>0?f:[`${i}`]}function renderLog(e,t,n,r){let i=e.title===`stderr`,s=i?n.colors.red:n.colors.gray,c=n.colors.dim(n.glyph.rule),l=i?`stderr`:`stdout`,u=n.colors.dim(`${l} ${n.glyph.dot} `),d=visibleLength(u),f=` `.repeat(d),p=r.previous?.kind===`log`&&r.previous.title===e.title,m=(e.body??``).split(`
7
- `),h=[];for(let e of m){let r=wrapVisibleLine(e,Math.max(1,t-2-d));for(let e of r){let t=h.length===0&&!p?u:f;h.push(`${c} ${t}${n.colors.dim(s(e))}`)}}return h.length>0?h:[`${c}`]}function renderSubagentHeader(e,t,n){let r=truncatePlain(e.title??`subagent`,Math.max(8,t-14));return[`${n.colors.orange(n.glyph.subagent)} ${n.colors.bold(r)} ${n.colors.dim(`subagent`)}`]}function wrap(e,t){return e.trim().length===0?[]:e.split(`
1
+ import{isPromptControlCommand}from"./prompt-commands.js";import{renderMarkdown}from"./markdown.js";import{elisionText}from"./rail.js";import{renderTool}from"./tool-rows.js";import{clipVisible,sliceVisible,visibleLength,wrapVisibleLine}from"#cli/ui/terminal-text.js";function renderBlockLines(e,t,n,r){let i=e.depth??0,a=nestingPrefix(i,n),s=renderBody(e,Math.max(8,t-visibleLength(a)),n,r);if(e.closesRail===!0&&i>0){let e=closingPrefix(i,n);return s.map((t,n)=>`${n===s.length-1?e:a}${t}`)}return s.map(e=>`${a}${e}`)}function nestingPrefix(e,t){return e<=0?``:` ${`${t.colors.dim(t.glyph.rule)} `.repeat(e)}`}function closingPrefix(e,t){return` ${`${t.colors.dim(t.glyph.rule)} `.repeat(e-1)}${t.colors.dim(t.glyph.corner)} `}function renderBody(e,t,a,o){if(e.elided!==void 0&&e.kind===`subagent-step`)return[` ${elisionText(e.elided,a)}`];switch(e.kind){case`user`:return renderUser(e,t,a);case`assistant`:case`subagent-step`:return renderProse(e,t,a);case`reasoning`:return renderReasoning(e,t,a);case`tool`:case`subagent-tool`:return renderTool(e,t,a,o);case`error`:return renderError(e,t,a);case`notice`:return renderNotice(e,t,a);case`warning`:return renderWarning(e,t,a);case`result`:return renderResult(e,t,a);case`flow`:return renderFlow(e,t,a);case`command`:return renderCommand(e,a);case`question`:case`connection-auth`:return renderPreformatted(e,t,a);case`sandbox`:return renderSandbox(e,t,a,o);case`log`:return renderLog(e,t,a);case`subagent`:return renderSubagentHeader(e,t,a);case`subagent-close`:{let n=` ${a.colors.dim(a.glyph.corner)}`;return e.body!==void 0&&e.body.length>0?[clipVisible(`${n} ${a.colors.dim(e.body)}`,Math.max(1,t))]:[n]}case`turn-stats`:return renderTurnStats(e,t,a);case`session-boundary`:case`todo-list`:case`agent-header`:return(e.body??``).split(`
2
+ `)}}function renderUser(e,t,n){let r=n.colors.cyan(n.glyph.user);return wrap(e.body??``,t-2).map(e=>`${r} ${e}`)}function renderProse(e,n,r){let i=[],o=e.kind===`subagent-step`;if(o&&e.collapsed===!0){let t=firstNonEmptyLine(e.body)??(e.reasoning===void 0?void 0:firstNonEmptyLine(e.reasoning));return t===void 0?[]:[r.colors.dim(sliceVisible(t,Math.max(1,n)))]}let c=o?``:`${r.colors.bold(r.glyph.brand)} `,l=o?``:` `;e.reasoning&&e.reasoning.trim().length>0&&i.push(...renderReasoningLines(e.reasoning,n,r));let u=(e.body??``).trim();return u.length===0&&i.length===0?[`${c}${r.colors.dim(`thinking${r.glyph.ellipsis}`)}`]:(u.length>0&&renderMarkdown(u,n-l.length).split(`
3
+ `).flatMap(e=>wrapVisibleLine(e,n-l.length)).forEach((e,t)=>{t===0&&!o&&i.length===0?i.push(`${c}${e}`):i.push(`${l}${e}`)}),i.length>0?i:[`${c}`])}function renderReasoning(e,t,n){return e.collapsed?[`${n.colors.gray(n.glyph.reasoning)} ${n.colors.dim(e.title??`thinking`)}`]:renderReasoningLines(e.body??``,t,n,n.glyph.reasoning)}function renderReasoningLines(e,t,n,r){let i=r?2:0,a=wrap(e.trim(),t-i);return a.length===0?[]:a.map((e,t)=>`${r?t===0?`${n.colors.gray(r)} `:` `:``}${n.colors.dim(n.colors.italic(e))}`)}function renderError(e,t,n){let r=n.colors.red(n.colors.bold(n.glyph.error)),i=e.title??`Error`,a=[`${r} ${n.colors.red(n.colors.bold(i))}`];for(let r of wrap(e.body??``,t-2))a.push(` ${colorizeError(r,n)}`);if(e.hint!==void 0&&e.hint.trim().length>0)for(let[r,i]of wrap(e.hint,t-4).entries()){let e=r===0?`${n.glyph.arrow} `:` `;a.push(` ${n.colors.cyan(`${e}${i}`)}`)}return a.push(...renderErrorDetail(e.detail,t,n)),a}function renderErrorDetail(e,t,n){if(e===void 0||e.trim().length===0)return[];let r=e.split(`
4
+ `),i=r.slice(0,12),a=i.map(e=>` ${n.colors.dim(truncatePlain(e,Math.max(1,t-2)))}`),o=r.length-i.length;return o>0&&a.push(` ${n.colors.dim(`${n.glyph.ellipsis} +${o} more line${o===1?``:`s`}`)}`),a}const URL_PATTERN=/(https?:\/\/\S+)/u;function colorizeError(e,t){return URL_PATTERN.test(e)?e.split(URL_PATTERN).map((e,n)=>n%2==1?t.colors.cyan(e):t.colors.red(e)).join(``):t.colors.red(e)}function renderNotice(e,t,n){let r=n.colors.dim(n.glyph.dot),i=wrap(e.body??``,t-2);return i.length===0?[r]:i.map(e=>`${r} ${n.colors.dim(e)}`)}function renderAttentionRows(e,t,n){let r=n.colors.yellow(n.glyph.warning);return wrap(e,t-2).map((e,t)=>`${t===0?r:` `} ${paintCommands(e,n)}`)}function renderWarning(e,t,n){return renderAttentionRows(e.body??``,t,n)}function paintCommands(t,n){return t.replace(/\/[a-z:-]+/g,t=>isPromptControlCommand(t)?n.colors.blue(t):t)}function renderCommand(e,t){let n=t.colors,r=e.status===`error`?`${n.red(t.glyph.error)} `:``;return[`${n.cyan(t.glyph.user)} ${r}${n.blue(e.body??``)}`]}function renderFlow(e,t,n){let r=n.colors,i=e.title??`info`,a=i===`success`?r.green(n.glyph.success):i===`warning`?r.yellow(n.glyph.warning):i===`error`?r.red(n.glyph.error):r.dim(n.glyph.dot),o=wrap(e.body??``,t-2),paint=e=>i===`info`?r.dim(e):e;return o.map((e,t)=>`${t===0?a:` `} ${paint(e)}`)}function renderResult(e,t,n){let r=n.colors.dim(n.glyph.elbow),i=wrap(e.body??``,t-7);if(i.length===0)return[` ${r}`];let dim=e=>n.colors.dim(e.replaceAll(`\x1B[22m`,`\x1B[22m\x1B[2m`));return i.map((e,t)=>t===0?` ${r} ${dim(e)}`:` ${dim(e)}`)}function renderPreformatted(e,t,n){let r=e.kind===`connection-auth`?n.colors.yellow(n.glyph.connection):n.colors.yellow(n.colors.bold(n.glyph.question)),i=e.kind===`question`?` `:` `,a=wrap(e.title??``,t-2),o=a.length===0?[`${r} `]:a.map((e,t)=>t===0?`${r} ${n.colors.bold(e)}`:` ${n.colors.bold(e)}`);for(let n of(e.body??``).split(`
5
+ `))for(let e of wrapVisibleLine(n,Math.max(1,t-i.length)))o.push(`${i}${e}`);return o}function renderSandbox(e,t,n,r){let i=n.colors.cyan(n.glyph.rule),a=n.colors.dim(`sandbox ${n.glyph.dot} `),c=visibleLength(a),l=` `.repeat(c),u=r.previous?.kind===`sandbox`,d=(e.body??``).split(`
6
+ `),f=[];for(let e of d){let r=wrapVisibleLine(e,Math.max(1,t-2-c));for(let e of r){let t=f.length===0&&!u?a:l;f.push(`${i} ${t}${n.colors.gray(e)}`)}}return f.length>0?f:[`${i}`]}function renderLog(e,t,r){let i=e.title===`stderr`,a=i?r.colors.red:r.colors.gray,o=r.colors.dim(r.glyph.rule),c=i?`stderr`:`stdout`,l=[`${r.colors.dim(r.glyph.reasoning)} ${r.colors.dim(c)}`];e.elided!==void 0&&e.elided>0&&l.push(`${o} ${elisionText(e.elided,r)}`);for(let n of(e.body??``).split(`
7
+ `))for(let e of wrapVisibleLine(n,Math.max(1,t-2)))l.push(`${o} ${r.colors.dim(a(e))}`);return l}function renderTurnStats(e,t,n){let r=`${n.glyph.corner} ${e.body??``}`;return[n.colors.dim(truncatePlain(r,Math.max(1,t)))]}function renderSubagentHeader(e,t,n){let r=truncatePlain(e.title===void 0||e.title===`agent`?`self`:e.title,Math.max(8,t-16)),i=e.subtitle!==void 0&&e.subtitle.startsWith(`#`),a=i?`:${e.subtitle.slice(1)}`:``,o=` ${e.status===`done`?n.colors.green(n.glyph.subagent):n.colors.orange(n.glyph.subagent)} ${n.colors.bold(`subagent(${r}${a})`)}`;return!i&&e.subtitle!==void 0&&e.subtitle.length>0&&(o+=` ${n.colors.dim(e.subtitle)}`),[o]}function firstNonEmptyLine(e){return e===void 0?void 0:e.split(/\r?\n/u).find(e=>e.trim().length>0)?.trim()}function wrap(e,t){return e.trim().length===0?[]:e.split(`
8
8
  `).flatMap(e=>wrapVisibleLine(e,Math.max(1,t)))}function truncatePlain(e,t){return visibleLength(e)<=t?e:sliceVisible(e,t)}export{renderAttentionRows,renderBlockLines};
@@ -4,6 +4,8 @@ export type DiagnosticPresentation = {
4
4
  } | {
5
5
  readonly kind: "stored";
6
6
  readonly summary: string;
7
+ /** First error-naming line of the collapsed body, when one exists. */
8
+ readonly headline?: string;
7
9
  readonly omittedLines: number;
8
10
  readonly path: string;
9
11
  };
@@ -1,2 +1,3 @@
1
- function presentDiagnostic(e,t){let n=e.split(`
2
- `);if(n.length<=4&&e.length<=600)return{kind:`inline`,text:e};let r=n.find(e=>e.trim().length>0)?.trim()??`Diagnostic output`;return{kind:`stored`,summary:r.length<=240?r:`${r.slice(0,239)}…`,omittedLines:Math.max(0,n.length-1),path:t}}function formatStoredDiagnostic(e){let t=e.omittedLines,n=t===0?`diagnostic details`:`${t} diagnostic line${t===1?``:`s`}`;return`${e.summary}\n… ${n} · details: ${e.path}`}export{formatStoredDiagnostic,presentDiagnostic};
1
+ const ERROR_HEADLINE_PATTERN=/^\s*(?:message|name):\s*\S|^\s*[A-Z][\w$]*Error\b/u;function presentDiagnostic(t,n){let r=t.split(`
2
+ `);if(r.length<=4&&t.length<=600)return{kind:`inline`,text:t};let i=r.findIndex(e=>e.trim().length>0),a=i===-1?`Diagnostic output`:r[i].trim(),o=r.slice(i+1).find(t=>ERROR_HEADLINE_PATTERN.test(t))?.trim(),s=1+(o===void 0?0:1),c={kind:`stored`,summary:clip(a),omittedLines:Math.max(0,r.length-s),path:n};return o===void 0?c:{...c,headline:clip(o)}}function formatStoredDiagnostic(e){let t=e.omittedLines,n=t===0?`diagnostic details`:`${t} diagnostic line${t===1?``:`s`}`,r=[e.summary];return e.headline!==void 0&&r.push(e.headline),r.push(`… ${n} · details: ${e.path}`),r.join(`
3
+ `)}function clip(e){return e.length<=240?e:`${e.slice(0,239)}…`}export{formatStoredDiagnostic,presentDiagnostic};
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Session-local knowledge of file contents, so the write block can show a
3
+ * real diff without any protocol addition. Two feeds keep it current: every
4
+ * `write_file` input is a full replacement (exact), and every full-file
5
+ * `read_file` result reconstructs to the file's exact lines. Files touched
6
+ * outside these two paths (e.g. by `bash`) are invisible here — a write over
7
+ * unknown prior content renders plain rather than pretending to be a diff.
8
+ */
9
+ export declare class FileContentCache {
10
+ #private;
11
+ /**
12
+ * Records one write and returns the content it replaced. Tool blocks
13
+ * re-render on every lifecycle event of the same call, so a repeated
14
+ * observation of `callId` keeps returning the original previous content
15
+ * instead of diffing the write against itself.
16
+ */
17
+ observeWrite(input: {
18
+ path: string;
19
+ content: string;
20
+ callId: string;
21
+ }): string | undefined;
22
+ /**
23
+ * Forgets everything. A new conversation runs against a fresh session —
24
+ * possibly a fresh sandbox — so stale bases would produce confidently
25
+ * wrong diffs.
26
+ */
27
+ clear(): void;
28
+ /**
29
+ * Records a read result when it provably covers the whole file: not
30
+ * truncated, starting at line 1, with exactly `totalLines` numbered lines.
31
+ * Partial or truncated reads are ignored — caching them would produce
32
+ * confidently wrong diffs later.
33
+ */
34
+ observeRead(output: unknown): void;
35
+ }
@@ -0,0 +1,3 @@
1
+ const MAX_CACHED_CONTENT_CHARS=2e5;var FileContentCache=class{#e=new Map;observeWrite(t){let n=this.#e.get(t.path);if(n?.writerCallId===t.callId)return n.previous;if(t.content.length>MAX_CACHED_CONTENT_CHARS){this.#e.delete(t.path);return}let r=n?.content,i={content:t.content,writerCallId:t.callId};return r!==void 0&&(i.previous=r),this.#e.set(t.path,i),r}clear(){this.#e.clear()}observeRead(t){let n=readFileResultShape(t);if(n===void 0||n.truncated||n.content.length>MAX_CACHED_CONTENT_CHARS)return;let r=reconstructNumberedLines(n.content);r===void 0||r.length!==n.totalLines||this.#e.set(n.path,{content:r.join(`
2
+ `)})}};function readFileResultShape(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let{content:t,path:n,totalLines:r,truncated:i}=e;if(!(typeof t!=`string`||typeof n!=`string`)&&!(typeof r!=`number`||typeof i!=`boolean`))return{content:t,path:n,totalLines:r,truncated:i}}function reconstructNumberedLines(e){if(e===``)return[];let t=[];for(let[n,r]of e.split(`
3
+ `).entries()){let e=/^(\d+): (.*)$/su.exec(r);if(e===null||Number(e[1])!==n+1)return;t.push(e[2])}return t}export{FileContentCache};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pure line diffing for the write block's rail. The TUI computes diffs
3
+ * client-side from content it has already seen (write inputs, full-file read
4
+ * results) — putting prior content into the tool's output would feed it
5
+ * straight back into model context, paying tokens for something only the
6
+ * terminal needs.
7
+ */
8
+ /** One rail row under a tool header. `kind` is unset for plain/context text. */
9
+ export interface ToolDetailLine {
10
+ readonly text: string;
11
+ readonly kind?: "added" | "removed" | "gap";
12
+ }
13
+ /**
14
+ * Builds the write rail for `content` against what it replaced.
15
+ *
16
+ * - `previous` known → a windowed line diff (changes with two context lines,
17
+ * unchanged stretches collapsed behind `gap` rows); identical content
18
+ * yields no rows.
19
+ * - `previous` unknown but the file provably did not exist → every line is
20
+ * an addition.
21
+ * - otherwise → plain content rows, because inventing `+` markers for an
22
+ * overwrite would misread as "everything changed".
23
+ */
24
+ export declare function diffWriteDetail(previous: string | undefined, content: string, existed?: boolean): ToolDetailLine[];
@@ -0,0 +1 @@
1
+ function diffWriteDetail(e,t,n){let r=splitContentLines(t);if(e===void 0)return n===!1?r.map(e=>({text:e,kind:`added`})):r.map(e=>({text:e}));let i=splitContentLines(e);return(i.length+1)*(r.length+1)>25e4?r.map(e=>({text:e})):windowHunks(lcsDiff(i,r))}function splitContentLines(e){let t=e.split(/\r?\n/u);for(;t.length>0&&t.at(-1)?.trim()===``;)t.pop();return t}function lcsDiff(e,t){let n=t.length+1,r=new Uint32Array((e.length+1)*n);for(let i=e.length-1;i>=0;--i)for(let a=t.length-1;a>=0;--a)r[i*n+a]=e[i]===t[a]?r[(i+1)*n+a+1]+1:Math.max(r[(i+1)*n+a],r[i*n+a+1]);let i=[],a=0,o=0;for(;a<e.length&&o<t.length;)e[a]===t[o]?(i.push({text:e[a]}),a+=1,o+=1):r[(a+1)*n+o]>=r[a*n+o+1]?(i.push({text:e[a],kind:`removed`}),a+=1):(i.push({text:t[o],kind:`added`}),o+=1);for(;a<e.length;a+=1)i.push({text:e[a],kind:`removed`});for(;o<t.length;o+=1)i.push({text:t[o],kind:`added`});return i}function windowHunks(e){let t=e.map(e=>e.kind===`added`||e.kind===`removed`);if(!t.includes(!0))return[];let n=e.map((n,r)=>{for(let n=Math.max(0,r-2);n<=Math.min(e.length-1,r+2);n+=1)if(t[n])return!0;return!1}),r=[];for(let[t,i]of e.entries())n[t]?r.push(i):r.at(-1)?.kind!==`gap`&&r.push({text:``,kind:`gap`});return r}export{diffWriteDetail};
@@ -1 +1,2 @@
1
- export declare function renderMarkdown(input: string): string;
1
+ /** Renders parsed GFM blocks to terminal text, fitting tables to `width`. */
2
+ export declare function renderMarkdown(input: string, width?: number): string;
@@ -1,3 +1,7 @@
1
- import{visibleLength}from"#cli/ui/terminal-text.js";const ansi={bold:`\x1B[1m`,boldOff:`\x1B[22m`,italic:`\x1B[3m`,italicOff:`\x1B[23m`};function renderMarkdown(e){let t=e.split(`
2
- `),n=[];for(let e=0;e<t.length;e+=1){let r=parseTable(t,e);if(r!=null){n.push(...renderTable(r)),e=r.endIndex-1;continue}n.push(renderMarkdownLine(t[e]??``))}return n.join(`
3
- `)}function renderMarkdownLine(e){if(e.startsWith(`### `))return renderInlineMarkdown(`▶ ${e.slice(4)}`);if(e.startsWith(`## `))return renderInlineMarkdown(`■ ${e.slice(3)}`);if(e.startsWith(`# `))return renderInlineMarkdown(`█ ${e.slice(2)}`);let t=e.match(/^(\s*)[-+*]\s+(.*)$/);if(t){let[,e,n=``]=t;return renderInlineMarkdown(`${e}•${n.length>0?` ${n}`:``}`)}return/^\d+\. /.test(e)?renderInlineMarkdown(e.replace(/^(\d+)\. /,`$1. `)):e.startsWith(`> `)?renderInlineMarkdown(`│ ${e.slice(2)}`):renderInlineMarkdown(e)}function renderInlineMarkdown(e){let n=[];return e.replaceAll(/https?:\/\/\S+/g,e=>{let t=`\uE000${n.length}\uE001`;return n.push(e),t}).replaceAll(/`([^`]+)`/g,`$1`).replaceAll(/\*\*([^*\n]+)\*\*/g,`${ansi.bold}$1${ansi.boldOff}`).replaceAll(/__([^_\n]+)__/g,`${ansi.bold}$1${ansi.boldOff}`).replaceAll(/\*([^*\n]+)\*/g,`${ansi.italic}$1${ansi.italicOff}`).replaceAll(/_([^_\n]+)_/g,`${ansi.italic}$1${ansi.italicOff}`).replaceAll(/\uE000(\d+)\uE001/g,(e,t)=>n[Number(t)]??``)}function parseTable(e,t){let n=parseTableCells(e[t]??``);if(n==null||n.length<2)return;let r=parseTableCells(e[t+1]??``);if(r==null||r.length!==n.length)return;let i=parseTableAlignments(r);if(i==null)return;let a=[],o=t+2;for(;o<e.length;){let t=parseTableCells(e[o]??``);if(t==null)break;a.push(normalizeTableRow(t,n.length)),o+=1}return{alignments:i,endIndex:o,header:n,rows:a}}function parseTableCells(e){if(!e.includes(`|`))return;let t=e.trim();t.startsWith(`|`)&&(t=t.slice(1)),t.endsWith(`|`)&&!t.endsWith(`\\|`)&&(t=t.slice(0,-1));let n=[],r=``;for(let e=0;e<t.length;e+=1){let i=t[e],a=t[e+1];if(i===`\\`&&a===`|`){r+=`|`,e+=1;continue}if(i===`|`){n.push(r.trim()),r=``;continue}r+=i}return n.push(r.trim()),n}function parseTableAlignments(e){let t=[];for(let n of e){let e=n.match(/^(:)?-{3,}(:)?$/);if(e==null)return;let[,r,i]=e;t.push(r!=null&&i!=null?`center`:i==null?`left`:`right`)}return t}function normalizeTableRow(e,t){return Array.from({length:t},(t,n)=>e[n]??``)}function renderTable(n){let r=n.header.map(e=>`${ansi.bold}${renderInlineMarkdown(e)}${ansi.boldOff}`),i=n.rows.map(e=>e.map(renderInlineMarkdown)),a=[r,...i],o=n.alignments.map((t,n)=>Math.max(3,...a.map(t=>visibleLength(t[n]??``))));return[formatTableRow(r,o,n.alignments),o.map(e=>`─`.repeat(e)).join(` `),...i.map(e=>formatTableRow(e,o,n.alignments))]}function formatTableRow(e,t,n){return e.map((e,r)=>alignTableCell(e,t[r]??0,n[r]??`left`)).join(` `)}function alignTableCell(t,n,r){let i=Math.max(0,n-visibleLength(t));if(r===`right`)return`${` `.repeat(i)}${t}`;if(r===`center`){let e=Math.floor(i/2),n=i-e;return`${` `.repeat(e)}${t}${` `.repeat(n)}`}return`${t}${` `.repeat(i)}`}export{renderMarkdown};
1
+ import{sliceVisible,visibleLength}from"#cli/ui/terminal-text.js";import{lexer}from"#compiled/marked/index.js";const ansi={bold:`\x1B[1m`,boldOff:`\x1B[22m`,cyan:`\x1B[36m`,cyanOff:`\x1B[39m`,dim:`\x1B[2m`,dimOff:`\x1B[22m`,italic:`\x1B[3m`,italicOff:`\x1B[23m`,strike:`\x1B[9m`,strikeOff:`\x1B[29m`};function renderMarkdown(e,t=1/0){return trimTrailingBlankRows(renderBlocks(lexer(e),t)).join(`
2
+ `)}function renderBlocks(e,t){let n=[];for(let i of e)switch(i.type){case`space`:pushBlankRow(n);break;case`heading`:{let e=i.depth===1?`█`:i.depth===2?`■`:`▶`;n.push(`${e} ${ansi.bold}${renderInline(i.tokens)}${ansi.boldOff}`);break}case`paragraph`:case`text`:n.push(...renderInline(i.tokens,i.text).split(`
3
+ `));break;case`code`:n.push(...renderCodeBlock(i));break;case`blockquote`:n.push(...renderBlocks(i.tokens??[],Math.max(8,t-2)).map(e=>`${ansi.dim}│${ansi.dimOff} ${e}`));break;case`list`:n.push(...renderList(i,t));break;case`table`:n.push(...renderTable(i,t));break;case`hr`:n.push(ansi.dim+`─`.repeat(horizontalRuleWidth(t))+ansi.dimOff);break;case`html`:n.push(...(i.text??i.raw??``).split(`
4
+ `));break;case`def`:break;default:i.tokens===void 0?i.text!==void 0&&n.push(i.text):n.push(...renderBlocks(i.tokens,t));break}return n}function renderCodeBlock(e){let t=[];e.lang?.trim()&&t.push(` ${ansi.dim}${e.lang.trim()}${ansi.dimOff}`);let n=(e.text??``).replace(/\n$/u,``).split(`
5
+ `);for(let e of n)t.push(`${ansi.dim}│${ansi.dimOff} ${ansi.cyan}${e}${ansi.cyanOff}`);return t}function renderList(e,t){let n=[],r=typeof e.start==`number`?e.start:1;for(let i of e.items??[]){let a=e.ordered?`${r}.`:`•`,o=i.checked===!0?`☑ `:i.checked===!1?`☐ `:``,s=renderBlocks(i.tokens??[],Math.max(8,t-a.length-1));(s.length===0?[``]:s).forEach((e,t)=>{n.push(t===0?`${a} ${o}${e}`:`${` `.repeat(a.length+1)}${e}`)}),r+=1}return n}function renderInline(e,t=``){return e===void 0?t:e.map(renderInlineToken).join(``)}function renderInlineToken(e){switch(e.type){case`text`:return renderInline(e.tokens,e.text??``);case`strong`:return`${ansi.bold}${renderInline(e.tokens,e.text)}${ansi.boldOff}`;case`em`:return`${ansi.italic}${renderInline(e.tokens,e.text)}${ansi.italicOff}`;case`del`:return`${ansi.strike}${renderInline(e.tokens,e.text)}${ansi.strikeOff}`;case`codespan`:return`${ansi.cyan}${e.text??``}${ansi.cyanOff}`;case`link`:{let t=renderInline(e.tokens,e.text),n=e.href??``;return n.length===0||t===n?`${ansi.cyan}${t}${ansi.cyanOff}`:`${t} (${ansi.cyan}${n}${ansi.cyanOff})`}case`image`:{let t=e.text?.trim()||`image`,n=e.href??``;return n.length===0?`[${t}]`:`[${t}] (${ansi.cyan}${n}${ansi.cyanOff})`}case`br`:return`
6
+ `;case`checkbox`:return e.checked?`☑ `:`☐ `;case`escape`:return e.text??``;case`html`:return e.text??e.raw??``;default:return renderInline(e.tokens,e.text??e.raw??``)}}function renderTable(e,n){let i=e.header??[];if(i.length===0)return[];let a=i.map((t,n)=>normalizeAlignment(e.align?.[n])),o=i.map(renderTableCell),s=(e.rows??[]).map(e=>normalizeTableRow(e,i.length).map(renderTableCell)),c=[o,...s],l=a.map((e,n)=>Math.max(3,...c.map(e=>visibleLength(e[n]??``))));return fitTableWidths(l,n),[formatTableRow(o.map(e=>`${ansi.bold}${e}${ansi.boldOff}`),l,a),l.map(e=>`─`.repeat(e)).join(` `),...s.map(e=>formatTableRow(e,l,a))]}function renderTableCell(e){return renderInline(e.tokens,e.text).replaceAll(`
7
+ `,` `)}function normalizeTableRow(e,t){return Array.from({length:t},(t,n)=>e[n]??{text:``,tokens:[]})}function normalizeAlignment(e){return e??`left`}function fitTableWidths(e,t){if(!Number.isFinite(t))return;let n=Math.max(0,e.length-1)*2,r=Math.max(e.length*3,Math.floor(t)-n);for(;e.reduce((e,t)=>e+t,0)>r;){let t=Math.max(...e),n=e.findIndex(e=>e===t&&e>3);if(n===-1)return;e[n]=t-1}}function formatTableRow(e,t,n){return e.map((e,r)=>alignTableCell(fitTableCell(e,t[r]??3),t[r]??3,n[r]??`left`)).join(` `)}function fitTableCell(n,r){return visibleLength(n)<=r?n:`${sliceVisible(n,Math.max(1,r-1))}…`}function alignTableCell(e,n,r){let i=Math.max(0,n-visibleLength(e));if(r===`right`)return`${` `.repeat(i)}${e}`;if(r===`center`){let t=Math.floor(i/2);return`${` `.repeat(t)}${e}${` `.repeat(i-t)}`}return`${e}${` `.repeat(i)}`}function horizontalRuleWidth(e){return Number.isFinite(e)?Math.max(3,Math.min(40,Math.floor(e))):40}function pushBlankRow(e){e.length>0&&e.at(-1)!==``&&e.push(``)}function trimTrailingBlankRows(e){for(;e.at(-1)===``;)e.pop();return e}export{renderMarkdown};
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The empty prompt's rotating invitation, written in the agent's own voice
3
+ * ("my capabilities") since the prompt is a message to it. Messages point at
4
+ * things an eve agent can actually do out of the box, so an idle prompt
5
+ * doubles as a hint surface. The caret-blink repaint keeps the rotation
6
+ * moving without its own timer.
7
+ */
8
+ export declare const PROMPT_PLACEHOLDER_MESSAGES: readonly string[];
9
+ /** How long each message holds before the rotation advances. */
10
+ export declare const promptPlaceholderCycleMs = 6000;
11
+ /** Picks the message for the given time since the renderer started. */
12
+ export declare function promptPlaceholder(elapsedMs: number): string;
@@ -0,0 +1 @@
1
+ const PROMPT_PLACEHOLDER_MESSAGES=[`Ask about my capabilities`,`Have me explore the workspace`,`Refine my instructions`],promptPlaceholderCycleMs=6e3;function promptPlaceholder(e){return PROMPT_PLACEHOLDER_MESSAGES[Math.floor(Math.max(0,e)/promptPlaceholderCycleMs)%PROMPT_PLACEHOLDER_MESSAGES.length]}export{PROMPT_PLACEHOLDER_MESSAGES,promptPlaceholder,promptPlaceholderCycleMs};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pure rendering for the HITL question panel — the overlay the agent's
3
+ * `ask_question` tool opens above the input area. A full-width rule separates
4
+ * it from the transcript, options render as numbered rows with their
5
+ * descriptions always visible, and the trailing "Type your own answer" row
6
+ * carries an inline elbow editor that receives focus the moment the cursor
7
+ * rests on it (the provider-key grammar from the setup panel). The renderer
8
+ * hosts lifecycle and keys; this module only paints rows.
9
+ */
10
+ import type { AgentTUIInputOption } from "./runner.js";
11
+ import { type LineState } from "./line-editor.js";
12
+ import type { Theme } from "./theme.js";
13
+ export interface QuestionPanelState {
14
+ readonly prompt: string;
15
+ readonly options: readonly AgentTUIInputOption[];
16
+ /** Row index under the cursor; `options.length` is the freeform row. */
17
+ readonly cursor: number;
18
+ /** Whether the trailing freeform row exists. */
19
+ readonly allowFreeform: boolean;
20
+ /** The freeform row's inline editor; focused while the cursor rests on it. */
21
+ readonly editor: LineState;
22
+ readonly caretVisible: boolean;
23
+ }
24
+ export declare function renderQuestionPanel(state: QuestionPanelState, theme: Theme, width: number): string[];
@@ -0,0 +1 @@
1
+ import{visibleLine}from"./line-editor.js";import{clipVisible,renderInputWithBlockCursor,wrapVisibleLine}from"#cli/ui/terminal-text.js";function selectedRow(e,t){let n=t.colors;return`${n.inverse(n.blue(` ${t.glyph.selectedPointer} ${e} `))} ${n.dim(`↵`)}`}function renderQuestionPanel(e,n,i){let a=n.colors,o=n.glyph,s=[a.dim(o.hrule.repeat(Math.max(1,i)))];for(let t of e.prompt.split(/\r?\n/u))for(let e of wrapVisibleLine(t,Math.max(8,i-2)))s.push(` ${a.bold(e)}`);s.push(``);for(let[t,r]of e.options.entries())s.push(...optionRows(r.label,r.description,t,e,n));if(e.allowFreeform){let t=e.options.length,r=e.cursor===t;s.push(...optionRows(`Type your own answer`,void 0,t,e,n)),(r||e.editor.text.length>0)&&s.push(` ${a.dim(o.elbow)} ${freeformEditorBody(e,r,n,i)}`)}return s.push(``,` ${a.dim(`Esc to dismiss`)}`),s.map(e=>clipVisible(e,i))}function optionRows(e,t,n,r,i){let a=i.colors,o=`${n+1}. ${e}`,s=[r.cursor===n?` ${selectedRow(o,i)}`:` ${a.dim(`${n+1}.`)} ${e}`];return t!==void 0&&t.length>0&&s.push(` ${a.dim(t)}`),s}function freeformEditorBody(t,r,i,a){let o=i.colors,s=Math.max(4,a-12);if(!r){let n=visibleLine(t.editor,s,i.glyph.ellipsis);return o.dim(`${n.before}${n.under}${n.after}`)}return renderInputWithBlockCursor({...visibleLine(t.editor,s,i.glyph.ellipsis),visible:t.caretVisible,inverse:o.inverse})}export{renderQuestionPanel};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The shared atoms of the transcript's rail grammar. Persistent sections
3
+ * (tool rows, subagent sections, the todo panel) all hang their marks at the
4
+ * same two-cell tool column and elide overflow with the same counted row —
5
+ * one definition keeps the alignment a checked fact instead of a set of
6
+ * parallel comments.
7
+ */
8
+ import type { Theme } from "./theme.js";
9
+ /**
10
+ * The two-cell indent that puts a section's mark at the prose text column
11
+ * (the character after the `│`/`▲` gutter marks).
12
+ */
13
+ export declare const TOOL_COLUMN_LEAD = " ";
14
+ /** The dim `… (N more)` elision label used by every capped region. */
15
+ export declare function elisionText(hidden: number, theme: Theme): string;
@@ -0,0 +1 @@
1
+ const TOOL_COLUMN_LEAD=` `;function elisionText(e,t){return t.colors.dim(`${t.glyph.ellipsis} (${e} more)`)}export{TOOL_COLUMN_LEAD,elisionText};
@@ -1,4 +1,6 @@
1
1
  import { type AgentInfoResult, type ConnectionAuthorizationOutcome, type InputRequest, Client, ClientSession } from "#client/index.js";
2
+ import { type SubagentView } from "./subagent-pump.js";
3
+ export type { SubagentRun, SubagentStepUpdate, SubagentToolUpdate, SubagentView, } from "./subagent-pump.js";
2
4
  import { type DevBootProgressReporter } from "#internal/dev-boot-progress.js";
3
5
  import { type PromptCommand, type PromptCommandSpec } from "./prompt-commands.js";
4
6
  import { type RemoteConnectionController, type RemoteConnectionControllerOptions, type RemoteConnectionSnapshot } from "./remote-connection.js";
@@ -43,6 +45,10 @@ export type AgentTUIStreamEvent = {
43
45
  } | {
44
46
  type: "reasoning-complete";
45
47
  id: string;
48
+ } | {
49
+ type: "tool-call-preparing";
50
+ toolCallId: string;
51
+ toolName: string;
46
52
  } | {
47
53
  type: "tool-call";
48
54
  toolCallId: string;
@@ -60,6 +66,10 @@ export type AgentTUIStreamEvent = {
60
66
  type: "tool-error";
61
67
  toolCallId: string;
62
68
  errorText: string;
69
+ } | {
70
+ type: "tool-rejected";
71
+ toolCallId: string;
72
+ reason: string;
63
73
  } | {
64
74
  type: "error";
65
75
  errorText: string;
@@ -140,6 +150,12 @@ export type AgentTUIRenderer = {
140
150
  * recovery and slash-command results. Optional.
141
151
  */
142
152
  renderNotice?(text: string): void;
153
+ /**
154
+ * Commits the session boundary (`┌── Session restarted, clear context.`)
155
+ * when a dead session is replaced mid-conversation. Optional; renderers
156
+ * without it get the plain notice.
157
+ */
158
+ renderSessionBoundary?(): void;
143
159
  /**
144
160
  * Commits one development sandbox lifecycle line to the transcript.
145
161
  * Optional so non-terminal renderers can ignore local prewarm progress.
@@ -157,22 +173,13 @@ export type AgentTUIRenderer = {
157
173
  readInputQuestion?(question: AgentTUIInputQuestion, options?: AgentTUISessionOptions): Promise<AgentTUIInputQuestionResponse | undefined>;
158
174
  renderStream(result: AgentTUIStreamResult, options?: AgentTUISessionOptions): Promise<void>;
159
175
  /**
160
- * Out-of-band update for one child step (reasoning + message text) of a
161
- * subagent dispatch. Called by the runner as child-session stream events
162
- * arrive. The renderer renders this as a body section colored by the
163
- * subagent palette.
176
+ * The renderer's whole subagent surface sections, nested steps and
177
+ * tools, ghost sweeps, completion. One optional capability with required
178
+ * members: a renderer either has a subagent view or it doesn't, and a
179
+ * type-legal partial implementation (which would ghost placeholders or
180
+ * duplicate parent tool rows) cannot exist.
164
181
  */
165
- upsertSubagentStep?(update: SubagentStepUpdate): void;
166
- /**
167
- * Out-of-band update for one child tool call of a subagent dispatch.
168
- */
169
- upsertSubagentTool?(update: SubagentToolUpdate): void;
170
- /**
171
- * Registers a tool call id as originating from a subagent's child
172
- * session. The renderer must skip or remove parent-level tool blocks for
173
- * these ids — they are surfaced via {@link upsertSubagentTool} instead.
174
- */
175
- markChildToolCallId?(callId: string): void;
182
+ readonly subagents?: SubagentView;
176
183
  /**
177
184
  * Out-of-band update for one MCP connection authorization lifecycle.
178
185
  * Called by the runner as `authorization.*` events arrive.
@@ -322,56 +329,6 @@ export declare class EveTUIRunner {
322
329
  constructor(options: EveTUIRunnerOptions);
323
330
  run(): Promise<void>;
324
331
  }
325
- type SubagentChildStep = {
326
- reasoning: string;
327
- message: string;
328
- finalized: boolean;
329
- };
330
- type SubagentToolState = {
331
- toolName: string;
332
- input: unknown;
333
- status: "approval-requested" | "executing" | "done" | "failed";
334
- output?: unknown;
335
- errorText?: string;
336
- };
337
- export type SubagentRun = {
338
- name: string;
339
- /**
340
- * One entry per logical "child message" — independent of the child's
341
- * `stepIndex` field, which the harness can reuse across multiple
342
- * assistant messages within a turn (e.g. a message before a tool call
343
- * and another message after the tool result both arrive under
344
- * `stepIndex: 0`). The key is a monotonic counter so each
345
- * `message.completed` opens a new box on the next inbound delta.
346
- */
347
- steps: Map<number, SubagentChildStep>;
348
- /**
349
- * Section currently accepting reasoning/message deltas. `null` means
350
- * the next delta opens a new section.
351
- */
352
- currentSectionKey: number | null;
353
- /** Monotonic counter for new section keys. */
354
- nextSectionKey: number;
355
- tools: Map<string, SubagentToolState>;
356
- };
357
- export type SubagentStepUpdate = {
358
- callId: string;
359
- subagentName: string;
360
- sectionKey: number;
361
- reasoning: string;
362
- message: string;
363
- finalized: boolean;
364
- };
365
- export type SubagentToolUpdate = {
366
- callId: string;
367
- subagentName: string;
368
- childCallId: string;
369
- toolName: string;
370
- input: unknown;
371
- status: "approval-requested" | "executing" | "done" | "failed";
372
- output?: unknown;
373
- errorText?: string;
374
- };
375
332
  export type ConnectionAuthChallenge = {
376
333
  url?: string;
377
334
  userCode?: string;