@oh-my-pi/pi-tui 18.8.8 → 18.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +56 -221
  2. package/dist/types/charts/chart-plan.d.ts +595 -13
  3. package/dist/types/charts/chart-svg.d.ts +38 -9
  4. package/dist/types/charts/table-data.d.ts +70 -2
  5. package/dist/types/chat/table-chart.d.ts +3 -2
  6. package/dist/types/chat/user-message.d.ts +12 -1
  7. package/dist/types/chrome/shared.d.ts +9 -0
  8. package/dist/types/chrome/transcript-container.d.ts +1 -0
  9. package/dist/types/components/editor.d.ts +6 -0
  10. package/dist/types/components/form.d.ts +1 -0
  11. package/dist/types/components/markdown.d.ts +7 -0
  12. package/dist/types/components/tree-view.d.ts +7 -0
  13. package/dist/types/kitty-graphics.d.ts +2 -2
  14. package/dist/types/loop-watchdog.d.ts +8 -7
  15. package/dist/types/multiplexers/cmux.d.ts +16 -0
  16. package/dist/types/multiplexers/herdr.d.ts +26 -0
  17. package/dist/types/multiplexers/notify.d.ts +13 -0
  18. package/dist/types/multiplexers/orca.d.ts +12 -0
  19. package/dist/types/multiplexers/screen.d.ts +9 -0
  20. package/dist/types/multiplexers/tmux.d.ts +29 -0
  21. package/dist/types/multiplexers/types.d.ts +100 -0
  22. package/dist/types/multiplexers/wmux.d.ts +12 -0
  23. package/dist/types/multiplexers/zellij.d.ts +12 -0
  24. package/dist/types/overlays/agent-hub-projection.d.ts +4 -3
  25. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  26. package/dist/types/overlays/annotation-overlay.d.ts +2 -0
  27. package/dist/types/overlays/model-browser.d.ts +9 -1
  28. package/dist/types/overlays/settings-defs.d.ts +1 -0
  29. package/dist/types/overlays/usage-display.d.ts +3 -1
  30. package/dist/types/setup/scenes/types.d.ts +1 -0
  31. package/dist/types/status-line/component.d.ts +2 -0
  32. package/dist/types/status-line/host.d.ts +4 -1
  33. package/dist/types/status-line/types.d.ts +11 -2
  34. package/dist/types/terminal-capabilities.d.ts +24 -32
  35. package/dist/types/terminal-multiplexer.d.ts +127 -14
  36. package/dist/types/terminal.d.ts +12 -4
  37. package/dist/types/theme/schema.d.ts +11 -2
  38. package/dist/types/theme/theme-class.d.ts +7 -0
  39. package/dist/types/tmux.d.ts +1 -4
  40. package/dist/types/tools/eval.d.ts +21 -2
  41. package/dist/types/tools/output-meta.d.ts +11 -1
  42. package/dist/types/tools/streaming-output.d.ts +9 -0
  43. package/package.json +9 -9
  44. package/src/charts/chart-plan.ts +4802 -88
  45. package/src/charts/chart-svg.ts +4315 -351
  46. package/src/charts/table-data.ts +1076 -34
  47. package/src/chat/assistant-message.ts +27 -9
  48. package/src/chat/chat-transcript-builder.ts +1 -3
  49. package/src/chat/svg-figure.ts +45 -8
  50. package/src/chat/table-chart.ts +7 -11
  51. package/src/chat/user-message.ts +40 -2
  52. package/src/chrome/shared.ts +53 -0
  53. package/src/chrome/transcript-container.ts +32 -9
  54. package/src/components/editor.ts +38 -5
  55. package/src/components/form.ts +8 -0
  56. package/src/components/markdown.ts +45 -2
  57. package/src/components/settings-list.ts +2 -0
  58. package/src/components/tree-view.ts +38 -8
  59. package/src/kitty-graphics.ts +4 -4
  60. package/src/loop-watchdog.ts +18 -13
  61. package/src/multiplexers/cmux.ts +29 -0
  62. package/src/multiplexers/herdr.ts +64 -0
  63. package/src/multiplexers/notify.ts +24 -0
  64. package/src/multiplexers/orca.ts +16 -0
  65. package/src/multiplexers/screen.ts +15 -0
  66. package/src/multiplexers/tmux.ts +40 -0
  67. package/src/multiplexers/types.ts +110 -0
  68. package/src/multiplexers/wmux.ts +16 -0
  69. package/src/multiplexers/zellij.ts +24 -0
  70. package/src/overlays/agent-hub-projection.ts +51 -14
  71. package/src/overlays/agent-hub-types.ts +6 -0
  72. package/src/overlays/annotation-overlay.ts +2 -1
  73. package/src/overlays/model-browser.ts +43 -7
  74. package/src/overlays/model-hub.ts +12 -19
  75. package/src/overlays/settings-defs.ts +4 -3
  76. package/src/overlays/settings-selector.ts +10 -1
  77. package/src/overlays/tree-selector.ts +21 -16
  78. package/src/overlays/usage-display.ts +10 -4
  79. package/src/setup/scenes/model.ts +2 -1
  80. package/src/setup/scenes/types.ts +1 -0
  81. package/src/status-line/component.ts +65 -11
  82. package/src/status-line/host.ts +4 -1
  83. package/src/status-line/segments.ts +21 -16
  84. package/src/status-line/types.ts +12 -2
  85. package/src/terminal-capabilities.ts +65 -181
  86. package/src/terminal-multiplexer.ts +112 -33
  87. package/src/terminal.ts +73 -32
  88. package/src/theme/schema-validation.ts +1 -0
  89. package/src/theme/schema.ts +14 -1
  90. package/src/theme/session-color.ts +1 -7
  91. package/src/theme/theme-class.ts +26 -1
  92. package/src/theme/theme-schema.json +5 -1
  93. package/src/theme/theme.ts +7 -4
  94. package/src/tmux.ts +4 -11
  95. package/src/tools/eval.ts +56 -16
  96. package/src/tools/output-meta.ts +23 -3
  97. package/src/tools/streaming-output.ts +29 -2
  98. package/src/tools/todo.ts +4 -1
  99. package/src/tui.ts +33 -18
  100. package/src/windows-input-mode.ts +8 -0
@@ -6,13 +6,39 @@
6
6
  * so the host resolves them against the live theme (`prepareSvg`) for both
7
7
  * the rasterized terminal image and the native TSP blob.
8
8
  *
9
- * Geometry follows the terminal: 16 user units per row and a monospace face,
10
- * so 13-unit labels land near the terminal's own glyph size. Marks are flat
11
- * and square (no rounding, gradients or shadows); bolded table cells draw at
12
- * full strength while their neighbours recede.
9
+ * Styled after Apple's charts and Tern's UI: a UI sans (Geist where Tern
10
+ * provides it), muted labels, hairline gridlines that recede behind the
11
+ * marks, bars and cells with rounded corners, smooth lines (over a soft area
12
+ * fill where the axis starts at zero), dot legends, one hue across small
13
+ * multiples' columns with gray for context. 13-unit labels land near the terminal's own glyph size
14
+ * at 16 user units per row; bolded table cells draw at full strength while
15
+ * their neighbours recede. Grouped categories ({@link ChartSpec.groups}) name
16
+ * each group once beside its members, the groups parted by a gap; a name too
17
+ * long for its column wraps to two lines ({@link categoryLabels}). A few
18
+ * outliers far above the rest break a bar axis instead of flattening it
19
+ * ({@link outlierTier}); reference rows ({@link ChartSpec.reference}) draw a
20
+ * dashed rule through their group.
21
+ *
22
+ * Every chart opens with a header ({@link drawHeader}): the semibold title a
23
+ * planner set ({@link ChartSpec.title}, the one fact worth stating; its key
24
+ * figure in the accent), a muted
25
+ * note on how the data is encoded, and the series key right-aligned on the
26
+ * title's line when it fits. Marks size to
27
+ * the row count ({@link barMetrics}); strong heat cells and share segments
28
+ * print their figures in a theme-adaptive dark {@link ink}. Reusable
29
+ * annotation primitives — {@link band}, {@link referenceRule},
30
+ * {@link callout} — draw the {@link ChartSpec.annotations} a planner lays on.
31
+ *
32
+ * Axes tick on round steps in one unit (`0, 2, 4` for counts, `50s, 100s`,
33
+ * `25 GiB` where the table wrote binary sizes, thousands separated). Meaning
34
+ * layers on top: each contest's best value carries a quiet `best` tag (an
35
+ * outline in a heatmap), estimates (`~15`) among measured figures draw as
36
+ * dashed, faint bars ({@link barRect}), moves of a measure with a polarity read in `success`
37
+ * or `error` (transitions, factors, signed deltas), and status cells badge
38
+ * their row's name with a colored dot keyed under the chart.
13
39
  */
14
- import type { ChartSpec } from "./chart-plan.js";
15
- import type { Dimension } from "./table-data.js";
40
+ import { type ChartSpec } from "./chart-plan.js";
41
+ import { type Dimension } from "./table-data.js";
16
42
  /** A drawn chart: the SVG document and its size in user units. */
17
43
  export interface ChartSvg {
18
44
  readonly svg: string;
@@ -24,7 +50,10 @@ export declare function renderChartSvg(spec: ChartSpec): ChartSvg;
24
50
  /** One-line description of the chart, for screen readers and terminals that show the alt text. */
25
51
  export declare function chartAlt(spec: ChartSpec): string;
26
52
  /**
27
- * A value in base units as an axis label: durations and sizes in the unit
28
- * their magnitude reads best in, counts compacted (`12k`, `3.4M`).
53
+ * A value in base units as an axis label: durations and sizes in the unit,
54
+ * counts compacted under the suffix (`12k`, `3.4M`), that `magnitude` reads
55
+ * best in — the value's own size by default, an axis' largest tick to keep
56
+ * its labels in one unit. Sizes read in decimal units (`20 GB`), or binary
57
+ * ones (`16 GiB`) when `unit` is `iB`.
29
58
  */
30
- export declare function formatValue(value: number, dim: Dimension, unit: string): string;
59
+ export declare function formatValue(value: number, dim: Dimension, unit: string, magnitude?: number): string;
@@ -42,8 +42,23 @@ export interface NumberCell {
42
42
  readonly toFigure?: string;
43
43
  /** `a–b`: `b`, in the same base units. */
44
44
  readonly upper?: number;
45
- /** `a/b`: `b`. */
45
+ /**
46
+ * `a/b`: `b`. {@link analyzeTable} reads a score (`12/12`, `154/160`, a mean
47
+ * like `0.6/4`) as the percent of `b` reached, unless its column also holds a
48
+ * plain fraction; a pair (`85 / 147` under `Edit / read calls`) keeps `a`.
49
+ */
46
50
  readonly denominator?: number;
51
+ /**
52
+ * `a ± e` (`3.67s ± 0.69`, `~404ms ± 60`, `$0.72 (±0.16)`): `e`, in the same base units — the
53
+ * spread the table states around the value; {@link figure} then writes both (`3.67s ± 0.69`).
54
+ */
55
+ readonly error?: number;
56
+ /**
57
+ * A slash list of measurements in one unit (`79 / 124 µs`, `1,864 ms /
58
+ * 2,678 ms`, `**675ms** / 680ms`): every listed value, the first included,
59
+ * in base units, 2–4 of them. `value` is the first.
60
+ */
61
+ readonly values?: readonly number[];
47
62
  }
48
63
  /** A cell that is not a quantity. */
49
64
  export interface OtherCell {
@@ -70,17 +85,49 @@ export interface TableColumn {
70
85
  readonly unit: string;
71
86
  /** Numeric cells disagree on dimension (a metric-per-row table). */
72
87
  readonly mixed: boolean;
88
+ /** Numeric cells are mostly scores out of a total (`12/12`), read as percent of it. */
89
+ readonly scores: boolean;
90
+ /**
91
+ * Per row, the status its cell leads with ({@link cellStatus}), when most
92
+ * of the column's cells lead with one: a pass/fail or progress column.
93
+ */
94
+ readonly statuses?: readonly (Status | undefined)[];
73
95
  /** Every numeric data cell holds the same value: an echoed setting, not a measure. */
74
96
  readonly constant: boolean;
75
97
  /** Share of numeric cells written with an explicit sign. */
76
98
  readonly signedShare: number;
77
99
  /** Share of numeric cells written as `a → b`. */
78
100
  readonly arrowShare: number;
101
+ /**
102
+ * Index of the measure column this one restates, read from the values: a
103
+ * proportional copy (`pass` 33/38 beside `pass%` 87%, `Samples` beside `% of
104
+ * Total`, `Leverage vs $200` beside the cost) or a running total of it
105
+ * (`Offset` summing `Verts`). Such a column is never a measure of its own.
106
+ */
107
+ readonly restates?: number;
108
+ }
109
+ /**
110
+ * Measure columns that add up, read from the values: leaf `parts` summing to
111
+ * `whole` in every row (`Cached + Uncached input = Input`, `Input + Output =
112
+ * Total` → parts Cached, Uncached input, Output of Total), or — when not
113
+ * `exact` — parts falling within a `Total` column, the rest unlisted (`New +
114
+ * Edited ≤ Prose total`).
115
+ */
116
+ export interface Composition {
117
+ /** Column the parts make up; absent when only a judge says the parts form a whole. */
118
+ readonly whole: number | undefined;
119
+ /** Leaf part columns, in table order. */
120
+ readonly parts: readonly number[];
121
+ /** The parts sum to the whole within the written precision in every row. */
122
+ readonly exact: boolean;
79
123
  }
80
124
  /** A table read for charting. */
81
125
  export interface TableAnalysis {
82
126
  readonly columns: readonly TableColumn[];
83
- /** Category labels: the first text or temporal column, else a sequence or index column. */
127
+ /**
128
+ * Category labels: the first text or temporal column not made only of codes
129
+ * (`#2233`, `00`), else the first text column, else a sequence or index column.
130
+ */
84
131
  readonly label: TableColumn | undefined;
85
132
  /** Plottable measure columns, constant ones excluded. */
86
133
  readonly measures: readonly TableColumn[];
@@ -88,7 +135,21 @@ export interface TableAnalysis {
88
135
  readonly rows: readonly number[];
89
136
  /** Total/summary rows (`Total`, `Average`, …), kept out of the scale. */
90
137
  readonly totals: readonly number[];
138
+ /** Measure columns adding up to a whole, when the values show it. */
139
+ readonly composition?: Composition;
91
140
  }
141
+ /**
142
+ * What a status cell (`ok`, `FAIL`, `pending`, ✅) says about its row: `good`
143
+ * passed or landed, `bad` failed or broke, `warn` partly or flaky, `pending`
144
+ * not settled yet.
145
+ */
146
+ export type Status = "good" | "bad" | "warn" | "pending";
147
+ /**
148
+ * The status a cell's text leads with (`FAIL`, `✓ via mmdc`, `**pending**`,
149
+ * `1 ok`), or `undefined` for any other text. A count before the word
150
+ * (`3 ok`) still reads as the word's status.
151
+ */
152
+ export declare function cellStatus(text: string): Status | undefined;
92
153
  /**
93
154
  * Strip inline Markdown from a cell: links keep their text; bold, italics,
94
155
  * strikethrough, code spans and `<br>` keep their content.
@@ -101,6 +162,13 @@ export declare function plainCell(markdown: string): {
101
162
  export declare function parseCell(markdown: string): Cell;
102
163
  /** Read a table's columns and rows for charting. Cells are the raw Markdown of each cell. */
103
164
  export declare function analyzeTable(header: readonly string[], rows: readonly (readonly string[])[]): TableAnalysis;
165
+ /**
166
+ * Half the last written digit of `cell`, in base units (`6,313k` → 500,
167
+ * `38.5%` → 0.05): how far its value may sit from the exact quantity it
168
+ * rounds. A score (`33/38` read as percent) and a whole count written in full
169
+ * (`46`, `1,980`) are exact.
170
+ */
171
+ export declare function writtenSlack(cell: NumberCell): number;
104
172
  /** Strictly increasing or strictly decreasing. */
105
173
  export declare function isMonotonic(values: readonly number[]): boolean;
106
174
  /** Whether `cell` reads as a number. */
@@ -5,12 +5,13 @@ import type { ImageBudget } from "../components/image.js";
5
5
  import type { NativeNode } from "../native/node.js";
6
6
  import type { Component } from "../tui.js";
7
7
  /**
8
- * `smart`: a model picks the kind and columns of multi-series tables;
8
+ * `smart`: a model refines the local guess where meaning decides (kind,
9
+ * columns, row names, ordering, polarity, focus, title; see {@link shouldJudge});
9
10
  * `always`: the local best guess charts every table it reads as numeric;
10
11
  * `off`: tables stay tables.
11
12
  */
12
13
  export type TableChartMode = "smart" | "always" | "off";
13
- /** A multi-series table the host's planner picks a chart for. */
14
+ /** A table the host's planner picks a chart for ({@link shouldJudge}). */
14
15
  export interface TableChartRequest {
15
16
  /** The table's Markdown source. */
16
17
  readonly markdown: string;
@@ -1,3 +1,4 @@
1
+ import type { AnimationFrame, TranscriptPresentationTarget } from "../chrome/transcript-container.js";
1
2
  import { type Component, Container } from "../tui.js";
2
3
  import type { ImageContent } from "@oh-my-pi/pi-ai";
3
4
  import type { ReactionTarget } from "./reaction.js";
@@ -30,7 +31,7 @@ export declare function userBubbleColor(options?: UserBubbleOptions, tokenRegex?
30
31
  * (see {@link ReactionTarget}) drawn right-aligned in the bubble's top padding row;
31
32
  * a live-steered message carries a `*` marker left-aligned in the same row.
32
33
  */
33
- export declare class UserMessageComponent extends Container implements ReactionTarget {
34
+ export declare class UserMessageComponent extends Container implements ReactionTarget, TranscriptPresentationTarget {
34
35
  #private;
35
36
  constructor(text: string, options?: UserBubbleOptions);
36
37
  releaseRenderCaches(): void;
@@ -46,6 +47,16 @@ export declare class UserMessageComponent extends Container implements ReactionT
46
47
  describe(cx?: DescribeContext): NativeNode;
47
48
  /** Hover toolbar clicks: omp's own copy and rewind commands. */
48
49
  handleNativeEvent(event: NativeUiEvent): void;
50
+ /** Apply the transcript allocator's current viewport reservation. */
51
+ setTranscriptAllocation(rows: number, _frame?: AnimationFrame): void;
52
+ isTranscriptBlockFinalized(): boolean;
53
+ /**
54
+ * Single-row emergency representation for the overflow transcript layout:
55
+ * renders the first content line instead of the blank top padding row, and
56
+ * omits OSC 133 shell integration markers so moving live blocks across the
57
+ * viewport does not leave stale prompt marks on lines it no longer occupies.
58
+ */
59
+ renderTranscriptBlockEmergencyRow(width: number): string | undefined;
49
60
  render(width: number): readonly string[];
50
61
  }
51
62
  /**
@@ -1,5 +1,14 @@
1
1
  import type { TabBarTheme } from "../components/tab-bar.js";
2
2
  /** Compact single-line display text for statuses; unlike titles, collapse padding and trim. */
3
3
  export declare function sanitizeStatusText(text: string): string;
4
+ /**
5
+ * Like {@link sanitizeStatusText}, but keeps 7-bit SGR so extensions can colour
6
+ * `ctx.ui.setStatus` text. Every other escape (OSC links/titles, DCS/PM/APC,
7
+ * 7- and 8-bit CSI), C0/C1, lone surrogates, newlines and tabs are sanitized at
8
+ * least as strictly as in `sanitizeStatusText`; space runs collapse and edges
9
+ * trim (`String#trim` whitespace) by visible text, regardless of SGR placement.
10
+ * A styled result always ends with `\x1b[0m`.
11
+ */
12
+ export declare function sanitizeHookStatusText(input: string): string;
4
13
  /** Shared tab bar theme used by fullscreen overlays (settings, agent hub). */
5
14
  export declare function getTabBarTheme(): TabBarTheme;
@@ -63,6 +63,7 @@ export interface TranscriptViewportSpan {
63
63
  export declare class TranscriptContainer extends Container {
64
64
  #private;
65
65
  addChild(component: Component): void;
66
+ /** Removes `component` if {@link canRemoveBlock} allows it; otherwise does nothing. */
66
67
  removeChild(component: Component): void;
67
68
  clear(): void;
68
69
  setToolActivityVisible(visible: boolean): void;
@@ -149,6 +149,12 @@ export declare class Editor implements Component, Focusable {
149
149
  * cursor. The host styles it (ANSI allowed). Re-evaluated on every render, so hosts can derive
150
150
  * it from live state. */
151
151
  placeholder?: () => string | undefined;
152
+ /** Whole-message prediction for the next prompt, painted as ghost text after the cursor while
153
+ * the single-line buffer is a strict prefix of it, the cursor sits at its end, and no
154
+ * autocomplete is open. A shown prediction replaces the {@link placeholder}; Tab or Right inserts
155
+ * the rest without submitting. Must be a single line. Re-evaluated on every render, so hosts can
156
+ * derive it from live state. */
157
+ prediction?: () => string | undefined;
152
158
  constructor(theme: EditorTheme);
153
159
  /** Return bounded editor content, cursor, and transient child state for debug inspection. */
154
160
  debugState(): Record<string, unknown>;
@@ -44,6 +44,7 @@ export declare class FormField implements Component, Focusable, MouseRoutable {
44
44
  /** Label, description, details, preview, the control, error, summary, hint and footer as one column. */
45
45
  describe(_cx: DescribeContext): NativeNode;
46
46
  get focused(): boolean;
47
+ get hasError(): boolean;
47
48
  get debugChildren(): readonly Component[];
48
49
  set focused(value: boolean);
49
50
  setUseTerminalCursor(useTerminalCursor: boolean): void;
@@ -53,6 +53,13 @@ export declare function clearRenderCache(): void;
53
53
  export interface DefaultTextStyle {
54
54
  /** Foreground color function */
55
55
  color?: (text: string) => string;
56
+ /**
57
+ * The `color` foreground is prose-level: semantically-themed markdown spans
58
+ * (headings, links) keep their own `md*` theme colors instead of being
59
+ * painted over by it, while plain text runs still use `color`. Unset keeps
60
+ * the historical behavior where `color` paints every run.
61
+ */
62
+ proseFg?: boolean;
56
63
  /** Background color function */
57
64
  bgColor?: (text: string) => string;
58
65
  /** Bold text */
@@ -34,6 +34,13 @@ export interface FlattenTreeOptions<T, K extends TreeKey> {
34
34
  rootDepth?: number;
35
35
  /** Override the display depth inherited by direct children. */
36
36
  getChildDepth?: (item: T, row: TreeRow<T, K>, children: readonly T[]) => number;
37
+ /**
38
+ * Reuse the parent ancestor list when a child keeps the same display depth
39
+ * and the current item has no siblings (`siblingCount === 1`). Otherwise
40
+ * retain the current item as an ancestor so connector gutters remain
41
+ * accurate, including when the item has multiple children.
42
+ */
43
+ compactSameDepthAncestors?: boolean;
37
44
  /** Stop projection after this many structural items. Omit for no traversal cap. */
38
45
  maxItems?: number;
39
46
  }
@@ -30,8 +30,8 @@ export interface KittyGraphicsFeatures {
30
30
  * cannot use cursor-positioned placements because the outer terminal does not
31
31
  * know pane scroll/reflow state. An explicit `PI_FORCE_IMAGE_PROTOCOL=kitty`
32
32
  * opts into placeholders under any multiplexer — matching `timg -pk`.
33
- * Automatic Herdr fallback stays off because its pane marker does not prove
34
- * that the experimental Kitty renderer is enabled.
33
+ * Automatic fallback stays off inside multiplexers whose pane marker does not
34
+ * prove that a Kitty renderer is enabled (Herdr's is experimental).
35
35
  *
36
36
  * `PI_NO_KITTY_PLACEHOLDERS=1` and `PI_KITTY_PLACEHOLDERS=0` remain hard
37
37
  * opt-outs; `PI_KITTY_PLACEHOLDERS=1` explicitly opts in anywhere else.
@@ -21,13 +21,14 @@ interface LoopWatchdogTimer {
21
21
  cancel?(): void;
22
22
  }
23
23
  /**
24
- * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead of
25
- * a recorded deadline; a tick that fires `thresholdMs` past its deadline means
26
- * the loop was blocked that long. The overshoot is logged once on the rising
27
- * edge (one block ⇒ one line, deduped via `#wasBlocked`), tagged with the phase
28
- * active during the elapsed interval via {@link takeRecentLoopPhase} — which
29
- * survives the synchronous push/pop the instrumented hot paths do before this
30
- * delayed tick can run — so the stall names its cause instead of "unknown".
24
+ * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead,
25
+ * with that target recorded as its deadline; a tick that fires `thresholdMs`
26
+ * past its deadline means the loop was blocked that long. The overshoot is
27
+ * logged once on the rising edge (one block ⇒ one line, deduped via
28
+ * `#wasBlocked`). Attribution via
29
+ * {@link takeLoopPhaseAttribution} totals each innermost phase only after the tick's
30
+ * deadline. A phase is named, with `phaseMs`, only when its total outweighs all
31
+ * unlabeled time in that late window; otherwise the block remains "unknown".
31
32
  *
32
33
  * The handle is `unref`'d so the probe never keeps the process alive, and stop()
33
34
  * cancels the armed timer when the handle exposes `cancel` (the default
@@ -0,0 +1,16 @@
1
+ export declare const cmuxMultiplexer: {
2
+ readonly id: "cmux";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["CMUX_WORKSPACE_ID", "CMUX_SURFACE_ID", "CMUX_REMOTE_TRANSPORT"];
5
+ readonly ownsScreenGrid: true;
6
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
7
+ /**
8
+ * Workspace/socket state alone is not enough: only the injected surface UUID
9
+ * identifies the pane that should receive the notification. Without a valid
10
+ * surface, or without the binary, delivery falls through unchanged.
11
+ */
12
+ readonly notifier: {
13
+ readonly tier: "surface";
14
+ readonly send: ({ message, env }: import("./types.js").TerminalMultiplexerNotificationRequest) => boolean;
15
+ };
16
+ };
@@ -0,0 +1,26 @@
1
+ export declare const herdrMultiplexer: {
2
+ readonly id: "herdr";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["HERDR_ENV", "HERDR_PANE_ID", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID"];
5
+ readonly ownsScreenGrid: true;
6
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
7
+ /**
8
+ * Herdr multiplexes panes like tmux but swallows bare OSC 9 / OSC 99 and has
9
+ * no DCS passthrough envelope, and its bell relay does not flag a
10
+ * backgrounded tab — so without its CLI a backgrounded pane gets no signal
11
+ * at all that the agent finished or is waiting for input.
12
+ *
13
+ * `sound` maps the notification kind onto what Herdr offers: a question
14
+ * waiting on the user and a turn that stopped with an error both need the
15
+ * human and ring `request`, a settled turn rings `done`, anything else stays
16
+ * silent. The exact pane ID addresses the CLI call; without a valid one, or
17
+ * without the binary, delivery falls through.
18
+ */
19
+ readonly notifier: {
20
+ readonly tier: "pane";
21
+ readonly send: ({ message, env }: import("./types.js").TerminalMultiplexerNotificationRequest) => boolean;
22
+ };
23
+ readonly hyperlinks: "render";
24
+ readonly honorsSynchronizedOutput: true;
25
+ readonly imagesRequireOverride: true;
26
+ };
@@ -0,0 +1,13 @@
1
+ import type { TerminalNotification } from "../terminal-capabilities.js";
2
+ /** Title for out-of-band multiplexer notifications that carry none of their own. */
3
+ export declare const DEFAULT_NOTIFICATION_TITLE = "omp";
4
+ /** Title and body for an out-of-band multiplexer notification (cmux, Herdr). */
5
+ export declare function notificationTitleAndBody(message: string | TerminalNotification): {
6
+ title: string;
7
+ body: string;
8
+ };
9
+ /**
10
+ * Start a notifier CLI without waiting for or pinning it. Returns false when
11
+ * the binary cannot be spawned, which leaves delivery to the terminal fallback.
12
+ */
13
+ export declare function spawnNotifier(cmd: string[]): boolean;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Orca is an xterm.js-based IDE whose terminals expose paired pane/worktree IDs.
3
+ * It is recognized for launches, but it ranks below nested multiplexers and keeps
4
+ * the direct-terminal render path it used before it was recognized.
5
+ */
6
+ export declare const orcaMultiplexer: {
7
+ readonly id: "orca";
8
+ readonly precedence: "outerApp";
9
+ readonly sessionEnvKeys: readonly ["ORCA_PANE_KEY", "ORCA_WORKTREE_ID"];
10
+ readonly ownsScreenGrid: false;
11
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
12
+ };
@@ -0,0 +1,9 @@
1
+ export declare const screenMultiplexer: {
2
+ readonly id: "screen";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["STY"];
5
+ readonly termPrefix: "screen";
6
+ readonly ownsScreenGrid: true;
7
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
8
+ readonly hyperlinks: "drop";
9
+ };
@@ -0,0 +1,29 @@
1
+ /** Wrap a control sequence in tmux's DCS passthrough envelope. */
2
+ export declare function wrapTmuxPassthrough(payload: string): string;
3
+ export declare const tmuxMultiplexer: {
4
+ readonly id: "tmux";
5
+ readonly precedence: "session";
6
+ readonly sessionEnvKeys: readonly ["TMUX"];
7
+ readonly termPrefix: "tmux";
8
+ readonly ownsScreenGrid: true;
9
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
10
+ readonly notifier: {
11
+ readonly tier: "inBand";
12
+ readonly send: ({ sequence, write }: import("./types.js").TerminalMultiplexerNotificationRequest) => boolean;
13
+ };
14
+ readonly hyperlinks: {
15
+ readonly forward: {
16
+ readonly termProgram: "tmux";
17
+ readonly major: 3;
18
+ readonly minor: 4;
19
+ };
20
+ };
21
+ readonly backgroundColorCache: {
22
+ readonly passthrough: typeof wrapTmuxPassthrough;
23
+ readonly settleMs: 100;
24
+ };
25
+ readonly expandsRepPadding: true;
26
+ readonly expiresSynchronizedOutput: true;
27
+ readonly altRestoreEndsSynchronizedOutput: true;
28
+ readonly growsBeforeSigwinch: true;
29
+ };
@@ -0,0 +1,100 @@
1
+ import type { TerminalNotification } from "../terminal-capabilities.js";
2
+ /**
3
+ * Classification tier. `session` markers outrank the TERM fallback; `outerApp`
4
+ * hosts rank below it so a multiplexer nested inside them still wins.
5
+ */
6
+ export type TerminalMultiplexerPrecedence = "session" | "outerApp";
7
+ /**
8
+ * Notification delivery tier, tried in this order so the innermost target wins:
9
+ * a `pane` notifier addresses the exact pane through the multiplexer's CLI, a
10
+ * `surface` notifier addresses the containing app surface, and an `inBand`
11
+ * notifier rewrites the terminal's own notification sequence.
12
+ */
13
+ export type TerminalMultiplexerNotificationTier = "pane" | "surface" | "inBand";
14
+ /** One notification offered to a multiplexer notifier. */
15
+ export interface TerminalMultiplexerNotificationRequest {
16
+ readonly message: string | TerminalNotification;
17
+ readonly env: NodeJS.ProcessEnv;
18
+ /** The host terminal's formatted OSC notification, or `null` when it notifies with a bare BEL. */
19
+ readonly sequence: string | null;
20
+ /** Write a sequence through the active terminal. */
21
+ write(data: string): void;
22
+ }
23
+ export interface TerminalMultiplexerNotifier {
24
+ readonly tier: TerminalMultiplexerNotificationTier;
25
+ /** Deliver the notification. `false` leaves it to the next notifier and then the terminal fallback. */
26
+ send(request: TerminalMultiplexerNotificationRequest): boolean;
27
+ }
28
+ /**
29
+ * OSC 8 handling inside a multiplexer session:
30
+ * - `render`: the multiplexer draws and opens links itself, so links work
31
+ * whatever the outer terminal supports, unless another session with a
32
+ * hyperlink policy sits in the path.
33
+ * - `drop`: never forwards OSC 8; its session anywhere in the path disables links.
34
+ * - `forward`: forwards OSC 8 once it self-reports (`TERM_PROGRAM`/`TERM_PROGRAM_VERSION`)
35
+ * at least `major.minor`; older or unreported versions stay off.
36
+ */
37
+ export type TerminalMultiplexerHyperlinks = "render" | "drop" | {
38
+ readonly forward: {
39
+ /** Lowercase `TERM_PROGRAM` the multiplexer sets inside its panes. */
40
+ readonly termProgram: string;
41
+ readonly major: number;
42
+ readonly minor: number;
43
+ };
44
+ };
45
+ /** A multiplexer that answers OSC 11 from a cached copy of the outer terminal's background color. */
46
+ export interface TerminalMultiplexerBackgroundColorCache {
47
+ /** Wrap a query so it reaches the outer terminal and refreshes the cache. */
48
+ passthrough(query: string): string;
49
+ /** How long the multiplexer needs to store the outer reply before the cache is re-read. */
50
+ readonly settleMs: number;
51
+ }
52
+ /** Everything omp needs to recognize one terminal multiplexer and adapt to it. */
53
+ export interface TerminalMultiplexerModule<Id extends string = string> {
54
+ readonly id: Id;
55
+ readonly precedence: TerminalMultiplexerPrecedence;
56
+ /** Every environment variable `isInside()` reads. */
57
+ readonly sessionEnvKeys: readonly string[];
58
+ /** Lowercase TERM prefix that identifies the multiplexer when its session markers were stripped. */
59
+ readonly termPrefix?: string;
60
+ /**
61
+ * Whether the multiplexer owns the screen grid. When true, rendering takes the
62
+ * multiplexer path (no scrollback rebuild, anchored resize, skipped probes).
63
+ */
64
+ readonly ownsScreenGrid: boolean;
65
+ isInside(env?: NodeJS.ProcessEnv): boolean;
66
+ /** Routes notifications the multiplexer would otherwise swallow. */
67
+ readonly notifier?: TerminalMultiplexerNotifier;
68
+ /** OSC 8 policy inside a session; unset defers to the outer terminal. */
69
+ readonly hyperlinks?: TerminalMultiplexerHyperlinks;
70
+ /**
71
+ * The pane's VTE honors DEC 2026 even when DECRQM is unanswered or reports
72
+ * the mode unrecognized (status 0): synchronized output starts on inside a
73
+ * session, and an unrecognized report does not turn it off.
74
+ */
75
+ readonly honorsSynchronizedOutput?: boolean;
76
+ /**
77
+ * The session does not reveal whether its client renders graphics while
78
+ * outer-terminal identity variables leak in, so images and Kitty
79
+ * placeholders stay off unless the user forces them.
80
+ */
81
+ readonly imagesRequireOverride?: boolean;
82
+ /** Platforms where the multiplexer breaks OSC 11 passthrough, so host appearance is used instead. */
83
+ readonly backgroundQueryBrokenOn?: readonly NodeJS.Platform[];
84
+ /** Present when OSC 11 replies come from a cache an explicit refresh must update first. */
85
+ readonly backgroundColorCache?: TerminalMultiplexerBackgroundColorCache;
86
+ /** Expands REP into identical styled cells, so destructive replays REP-encode their padding. */
87
+ readonly expandsRepPadding?: boolean;
88
+ /** Expires a synchronized update after a deadline, so large rebuild replays renew DEC 2026 as they stream. */
89
+ readonly expiresSynchronizedOutput?: boolean;
90
+ /**
91
+ * Restoring the alternate buffer schedules a redraw that ends a synchronized
92
+ * update early, so a synchronized resize rebuild stays on the main buffer.
93
+ */
94
+ readonly altRestoreEndsSynchronizedOutput?: boolean;
95
+ /**
96
+ * May grow its grid before SIGWINCH reaches the app, so a rebuild-mode grow
97
+ * whose full viewport no longer reaches the bottom replays history.
98
+ */
99
+ readonly growsBeforeSigwinch?: boolean;
100
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * wmux is a Windows multiplexer (Electron + xterm.js) modeled on cmux/herdr that
3
+ * repaints its pane in place. WMUX_CLI / WMUX_PIPE are CLI path overrides and can
4
+ * be set outside a wmux terminal.
5
+ */
6
+ export declare const wmuxMultiplexer: {
7
+ readonly id: "wmux";
8
+ readonly precedence: "session";
9
+ readonly sessionEnvKeys: readonly ["WMUX", "WMUX_SURFACE_ID"];
10
+ readonly ownsScreenGrid: true;
11
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
12
+ };
@@ -0,0 +1,12 @@
1
+ export declare const zellijMultiplexer: {
2
+ readonly id: "zellij";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["ZELLIJ"];
5
+ readonly ownsScreenGrid: true;
6
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
7
+ readonly notifier: {
8
+ readonly tier: "inBand";
9
+ readonly send: ({ sequence, write }: import("./types.js").TerminalMultiplexerNotificationRequest) => boolean;
10
+ };
11
+ readonly backgroundQueryBrokenOn: readonly ["darwin"];
12
+ };
@@ -15,11 +15,12 @@ interface AgentTreeProjection<TRecord extends AgentRecordLike> {
15
15
  export declare const STATUS_ORDER: Record<AgentStatus, number>;
16
16
  /** Exact observer usage for one roster entry. */
17
17
  export declare function progressMetrics(observed: ObservableSession | undefined): AgentMetrics | undefined;
18
- /** Live session whose own messages back a row's metrics when no observer progress exists. */
18
+ /** Live session used to cache a row's direct assistant usage, including with observer progress. */
19
19
  export declare function hubFallbackStatsSession<TRecord extends AgentRecordLike>(ref: TRecord, observed: ObservableSession | undefined): NonNullable<TRecord["session"]> | undefined;
20
20
  /**
21
- * One roster row's usage: live observer progress, then persisted history, then
22
- * the cached fallback read of a live session (populated by {@link aggregateMetrics}).
21
+ * Preserve progress/history for non-cost metrics, overlaying cumulative direct-session cost.
22
+ * Persisted `history.metrics.cost` is a leaf-scoped snapshot, so it never overrides live
23
+ * progress; only the cumulative direct-cost sources do.
23
24
  */
24
25
  export declare function hubRowMetrics<TRecord extends AgentRecordLike>(ref: TRecord, observed: ObservableSession | undefined, sessionMetrics: WeakMap<object, {
25
26
  metrics: AgentMetrics | undefined;
@@ -28,6 +28,13 @@ export interface AgentHubSession {
28
28
  messages: AgentMessage[];
29
29
  };
30
30
  };
31
+ /** Existing session ledger includes off-transcript model usage and nested-task attribution. */
32
+ readonly sessionManager?: {
33
+ getUsageStatistics(): {
34
+ cost: number;
35
+ subagentCost: number;
36
+ };
37
+ };
31
38
  getSessionStats(): {
32
39
  tokens: {
33
40
  input: number;
@@ -64,6 +71,8 @@ export interface AgentRecordLike {
64
71
  resolvedModel?: string;
65
72
  resolvedModelIsFallback?: boolean;
66
73
  metrics?: AgentMetricsSummary;
74
+ /** Cumulative own-session assistant and model_usage cost, excluding nested task results. */
75
+ directCost?: number;
67
76
  readOnly?: boolean;
68
77
  outputPath?: string;
69
78
  patchPath?: string;
@@ -19,6 +19,8 @@ export interface CodeReviewOverlayCallbacks extends AnnotationOverlayCallbacks {
19
19
  export interface TextReviewOverlayCallbacks extends AnnotationOverlayCallbacks {
20
20
  onComplete(result: TextReviewOverlayResult | undefined): void;
21
21
  }
22
+ /** Splits a text source into the overlay's 1-based annotatable lines. */
23
+ export declare function splitTextLines(text: string): string[];
22
24
  /** A fullscreen, annotated diff picker that only relies on public OMP APIs. */
23
25
  export declare class AnnotationOverlay implements Focusable {
24
26
  #private;