@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
@@ -1,5 +1,5 @@
1
1
  import type { Model } from "@oh-my-pi/pi-ai";
2
- import type { ModelKind } from "@oh-my-pi/pi-catalog/types";
2
+ import { type ModelKind } from "@oh-my-pi/pi-catalog/types";
3
3
  import type { Component } from "../tui.js";
4
4
  import type { SgrMouseEvent } from "../mouse.js";
5
5
  import { type ConfiguredThinkingLevel } from "../thinking.js";
@@ -103,6 +103,14 @@ export type RoleAssignments = Record<string, RoleAssignment | undefined>;
103
103
  * the alt+p session picker.
104
104
  */
105
105
  export declare function resolveRoleAssignments(settings: ModelBrowserSource, allModels: ReadonlyArray<Model>, autoCandidates: ReadonlyArray<Model>): RoleAssignments;
106
+ /**
107
+ * Role-resolution pool for a `--models`/`enabledModels` scope: the scoped
108
+ * models plus every available non-chat runner (judge, search, image, …). The
109
+ * scope only filters chat models (it feeds Ctrl+P cycling) and runtime role
110
+ * resolution ignores it for other kinds, so displayed role assignments must too.
111
+ * Shared by the /models hub and the session picker.
112
+ */
113
+ export declare function scopedRolePool(scoped: ReadonlyArray<Model>, registry: ModelBrowserRegistry): Model[];
106
114
  /** Wrap raw models into browser items. */
107
115
  export declare function buildBrowserItems(models: ReadonlyArray<Model>): ModelBrowserItem[];
108
116
  /** Options for {@link sortModelItems}. */
@@ -44,6 +44,7 @@ export type AnyUiMetadata = UiBase & {
44
44
  options?: ReadonlyArray<SubmenuOption> | "runtime";
45
45
  secret?: boolean;
46
46
  ordered?: boolean;
47
+ editor?: "json";
47
48
  };
48
49
  /** Structural schema entries supplied by the application host. */
49
50
  export interface SettingsDisplayEntry {
@@ -1,4 +1,4 @@
1
- import type { UsageLimit, UsageReport, UsageResetCredits } from "@oh-my-pi/pi-ai";
1
+ import type { UsageLimit, UsageReport, UsageResetCreditDetail, UsageResetCredits } from "@oh-my-pi/pi-ai";
2
2
  /** Include the usage tier in a limit title unless its label already names it. */
3
3
  export declare function formatLimitTitle(limit: UsageLimit): string;
4
4
  /** Human-readable quota scope for a normalized saved-reset window ID. */
@@ -10,6 +10,8 @@ export interface UsageResetSummary {
10
10
  soonestExpiry?: string;
11
11
  unavailableReason?: string;
12
12
  }
13
+ /** Expiry (epoch ms) of a credit that still banks a reset, whether or not it can be spent now. */
14
+ export declare function bankedResetCreditExpiryMs(credit: UsageResetCreditDetail): number | undefined;
13
15
  /**
14
16
  * Normalize old count-only and current provider reset metadata for display.
15
17
  * `availableCount` is banked inventory; `redeemableCount` is the subset that
@@ -28,6 +28,7 @@ export interface SetupHost extends SetupUiHost {
28
28
  readonly disabledProviders: readonly string[];
29
29
  readonly authStorage: AuthStorage;
30
30
  readonly modelSource: ModelBrowserSource;
31
+ /** Available and catalog models of every kind; role chips resolve over all of them, rows list chat models. */
31
32
  getModels(): {
32
33
  available: Model[];
33
34
  all: Model[];
@@ -111,6 +111,8 @@ export declare class StatusLineComponent<TSession extends StatusLineSession = St
111
111
  setRecording(recording: boolean): void;
112
112
  /** Set the callback that presents detected Codex reset celebrations, or clear it with `undefined`. */
113
113
  setCodexResetFireworksHandler(handler: ((event: CodexResetFireworksEvent) => void) | undefined): void;
114
+ /** Set the callback that presents the first pool-wide saved-reset expiry warning, or clear it with `undefined`. */
115
+ setResetExpiryNoticeHandler(handler: ((notice: string) => void) | undefined): void;
114
116
  setHookStatus(key: string, text: string | undefined): void;
115
117
  watchBranch(onBranchChange: () => void): void;
116
118
  dispose(): void;
@@ -2,7 +2,7 @@ import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core";
2
2
  import type { Tool, UsageLimit, UsageReport } from "@oh-my-pi/pi-ai";
3
3
  import type { Model } from "@oh-my-pi/pi-catalog/types";
4
4
  import type { CompactionBoundaries } from "./context-usage.js";
5
- import type { StatusLineSettings } from "./types.js";
5
+ import type { StatusLineSettings, StatusResetExpiry } from "./types.js";
6
6
  export interface StatusAccountIdentity {
7
7
  accountId?: string;
8
8
  email?: string;
@@ -126,6 +126,9 @@ export interface StatusLineHost<TSession extends StatusLineSession = StatusLineS
126
126
  }>;
127
127
  calculateTokensPerSecond(messages: readonly AgentMessage[], isStreaming: boolean): number | null;
128
128
  limitMatchesActiveAccount(report: UsageReport, limit: UsageLimit, identity: StatusAccountIdentity): boolean;
129
+ classifyResetExpiry?(report: UsageReport, nowMs: number): StatusResetExpiry | undefined;
130
+ /** One-line warning for saved resets anywhere in the pool that expire within 24 hours. */
131
+ resetExpiryNotice?(reports: readonly UsageReport[], nowMs: number): string | undefined;
129
132
  computeCompactionBoundaries(session: TSession, contextWindow: number, model?: Model): CompactionBoundaries | null;
130
133
  }
131
134
  export interface FooterHost {
@@ -223,9 +223,18 @@ export interface SegmentContext {
223
223
  expiryHours?: number;
224
224
  expired?: boolean;
225
225
  unavailableReason?: string;
226
+ expiring?: StatusResetExpiry;
226
227
  };
227
228
  } | null;
228
229
  }
230
+ /** How close an account's saved resets are to expiring, when it is worth a warning. */
231
+ export interface StatusResetExpiry {
232
+ /** `soon`: within 7 days; `imminent`: within 24 hours. */
233
+ tier: "soon" | "imminent";
234
+ count: number;
235
+ /** Expiry of the reset the warning is about (epoch ms); it can be later than the soonest banked one. */
236
+ expiresAtMs: number;
237
+ }
229
238
  export interface RenderedSegment {
230
239
  content: string;
231
240
  visible: boolean;
@@ -245,8 +254,8 @@ export interface SegmentView {
245
254
  }
246
255
  /**
247
256
  * The status line's facts for the native composer. A TSP terminal shows no
248
- * status strip: the tab title carries the session, the pane header the path
249
- * and branch, and the composer the rest.
257
+ * status strip: the tab title carries the session, the pane header the path,
258
+ * and the composer the rest (the git branch among its facts).
250
259
  */
251
260
  export interface ComposerFacts {
252
261
  /**
@@ -1,6 +1,6 @@
1
1
  import type { HangulCompatibilityJamoWidth } from "./utils.js";
2
2
  export * from "./terminal-multiplexer.js";
3
- export { isInsideTmux, wrapTmuxPassthrough } from "./tmux.js";
3
+ export { wrapTmuxPassthrough } from "./tmux.js";
4
4
  export declare enum ImageProtocol {
5
5
  Kitty = "\u001B_G",
6
6
  Iterm2 = "\u001B]1337;File=",
@@ -55,12 +55,6 @@ export declare class TerminalInfo {
55
55
  formatNotification(message: string | TerminalNotification): string;
56
56
  sendNotification(message: string | TerminalNotification): void;
57
57
  }
58
- /**
59
- * Whether the agent process is running inside a Zellij session. Read fresh on
60
- * each call (like {@link isInsideTmux}) so a session attached/detached mid-run
61
- * is observed and tests can toggle `Bun.env.ZELLIJ` per case.
62
- */
63
- export declare function isInsideZellij(env?: NodeJS.ProcessEnv): boolean;
64
58
  /**
65
59
  * Whether the agent process runs in an SSH session, so the terminal emulator
66
60
  * is remote and host-local input/keyboard modes cannot be assumed.
@@ -96,12 +90,12 @@ export declare function synchronizedOutputUserOverride(env?: NodeJS.ProcessEnv):
96
90
  * 2. Positive `TERM_FEATURES` advertisement (`Sy`) — survives SSH/mux wrapping.
97
91
  * 3. Windows Terminal (1.24+) via `WT_SESSION`, on native win32 and the
98
92
  * WSL/SSH-fronted host alike.
99
- * 4. Herdr panes. Herdr is otherwise treated as a multiplexer so leaked
100
- * kitty/ghostty identities cannot enable placeholder graphics, but its
101
- * pane VTE is libghostty and already suppresses compositing while DEC 2026
102
- * is set. Leaving sync off lets CUP-diff paints and split write(2) chunks
103
- * composite as dirty-row patches — the live viewport tears, with the top
104
- * frozen while only the bottom refreshes.
93
+ * 4. A multiplexer session whose pane VTE honors DEC 2026 regardless of its
94
+ * DECRQM reply (Herdr's libghostty pane). Such a multiplexer still owns
95
+ * the grid, so leaked kitty/ghostty identities cannot enable placeholder
96
+ * graphics, but leaving sync off lets CUP-diff paints and split write(2)
97
+ * chunks composite as dirty-row patches — the live viewport tears, with
98
+ * the top frozen while only the bottom refreshes.
105
99
  * 5. Known direct terminals with confirmed support. SSH does *not* disable —
106
100
  * DEC 2026 passes through SSH when the outer terminal honors it.
107
101
  * 6. Everything else starts off, including risky multiplexers; the runtime
@@ -159,27 +153,25 @@ export declare function hyperlinksUserOverride(env?: NodeJS.ProcessEnv): boolean
159
153
  * Policy (highest precedence first):
160
154
  * 1. Explicit user override (`PI_NO_HYPERLINKS=1` off, `PI_FORCE_HYPERLINKS=1`
161
155
  * on). Opt-out wins ties.
162
- * 2. Herdr pane with no nested screen/tmux: on. Herdr hides the outer
163
- * terminal (`TERM=xterm-256color`, no `TERM_PROGRAM`), but it renders
164
- * OSC 8 in its own grid and opens links itself on Ctrl+click, so the
165
- * outer terminal's support does not matter.
156
+ * 2. Every multiplexer session with a hyperlink policy renders OSC 8 itself
157
+ * (`render`, e.g. Herdr): on. Herdr hides the outer terminal
158
+ * (`TERM=xterm-256color`, no `TERM_PROGRAM`), so the outer terminal's
159
+ * support does not matter.
166
160
  * 3. Static terminal capability — terminals whose {@link TerminalInfo} marks
167
161
  * `hyperlinks: false` (e.g. `base`) stay off unless the user forced on.
168
- * 4. GNU screen's explicit session marker (`STY`) always off, even if tmux is
169
- * also present: a screen layer anywhere in the path cannot forward OSC 8.
170
- * 5. tmux session (`TMUX` set): enabled when tmux self-reports >= 3.4 via
171
- * `TERM_PROGRAM_VERSION` (tmux 3.4 stores OSC 8 as a cell attribute and
172
- * forwards it to outer terminals whose `terminal-features` include
173
- * `hyperlinks`). Older or unknown versions stay off; on outer terminals
174
- * without the feature configured, tmux silently drops the sequence —
175
- * identical to today. Checked before the screen-family TERM heuristic
176
- * because tmux's historical `default-terminal` is `screen-256color`, so
177
- * `TERM=screen*` inside a tmux session must NOT short-circuit to off.
178
- * 6. screen-family TERM without `TMUX` always off: screen never gained OSC 8
179
- * support.
180
- * 7. tmux-family TERM without `TMUX` env — unusual (e.g. inspection scripts);
181
- * no version available, so off.
182
- * 8. Otherwise honor the static terminal capability.
162
+ * 4. A session that never forwards OSC 8 (`drop`, e.g. GNU screen's `STY`)
163
+ * always off, even when a forwarding multiplexer is also present.
164
+ * 5. Forwarding sessions (`forward`, e.g. `TMUX`): enabled when every one
165
+ * self-reports at least its forwarding version via `TERM_PROGRAM` /
166
+ * `TERM_PROGRAM_VERSION` (tmux 3.4). Older or unknown versions stay off;
167
+ * on outer terminals without the feature configured, tmux silently drops
168
+ * the sequence. Checked before the TERM heuristic because tmux's
169
+ * historical `default-terminal` is `screen-256color`, so `TERM=screen*`
170
+ * inside a tmux session must NOT short-circuit to off.
171
+ * 6. TERM naming a multiplexer whose session markers were stripped: off,
172
+ * since no session proves the multiplexer forwards OSC 8 (screen never
173
+ * does; tmux has no version to check).
174
+ * 7. Otherwise honor the static terminal capability.
183
175
  */
184
176
  export declare function shouldEnableHyperlinksByDefault(env?: NodeJS.ProcessEnv, terminalId?: TerminalId): boolean;
185
177
  /**
@@ -1,20 +1,133 @@
1
- /** True when this process is running inside a Herdr pane. */
2
- export declare function isInsideHerdr(env?: NodeJS.ProcessEnv): boolean;
3
- /** Terminal multiplexers omp recognizes as owning the screen grid. */
4
- export type TerminalMultiplexer = "herdr" | "tmux" | "screen" | "zellij" | "cmux" | "wmux";
1
+ import type { TerminalMultiplexerModule, TerminalMultiplexerNotificationRequest } from "./multiplexers/types.js";
5
2
  /**
6
- * Classify which terminal multiplexer owns the current screen grid, or `null`
7
- * for a direct terminal. Single source of truth for both the render-path gate
3
+ * Every multiplexer omp recognizes, in classification order within each
4
+ * precedence tier. Adding a multiplexer means adding its module here.
5
+ */
6
+ declare const registry: readonly [{
7
+ readonly id: "herdr";
8
+ readonly precedence: "session";
9
+ readonly sessionEnvKeys: readonly ["HERDR_ENV", "HERDR_PANE_ID", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID"];
10
+ readonly ownsScreenGrid: true;
11
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
12
+ readonly notifier: {
13
+ readonly tier: "pane";
14
+ readonly send: ({ message, env }: TerminalMultiplexerNotificationRequest) => boolean;
15
+ };
16
+ readonly hyperlinks: "render";
17
+ readonly honorsSynchronizedOutput: true;
18
+ readonly imagesRequireOverride: true;
19
+ }, {
20
+ readonly id: "tmux";
21
+ readonly precedence: "session";
22
+ readonly sessionEnvKeys: readonly ["TMUX"];
23
+ readonly termPrefix: "tmux";
24
+ readonly ownsScreenGrid: true;
25
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
26
+ readonly notifier: {
27
+ readonly tier: "inBand";
28
+ readonly send: ({ sequence, write }: TerminalMultiplexerNotificationRequest) => boolean;
29
+ };
30
+ readonly hyperlinks: {
31
+ readonly forward: {
32
+ readonly termProgram: "tmux";
33
+ readonly major: 3;
34
+ readonly minor: 4;
35
+ };
36
+ };
37
+ readonly backgroundColorCache: {
38
+ readonly passthrough: typeof import("./tmux.js").wrapTmuxPassthrough;
39
+ readonly settleMs: 100;
40
+ };
41
+ readonly expandsRepPadding: true;
42
+ readonly expiresSynchronizedOutput: true;
43
+ readonly altRestoreEndsSynchronizedOutput: true;
44
+ readonly growsBeforeSigwinch: true;
45
+ }, {
46
+ readonly id: "screen";
47
+ readonly precedence: "session";
48
+ readonly sessionEnvKeys: readonly ["STY"];
49
+ readonly termPrefix: "screen";
50
+ readonly ownsScreenGrid: true;
51
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
52
+ readonly hyperlinks: "drop";
53
+ }, {
54
+ readonly id: "zellij";
55
+ readonly precedence: "session";
56
+ readonly sessionEnvKeys: readonly ["ZELLIJ"];
57
+ readonly ownsScreenGrid: true;
58
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
59
+ readonly notifier: {
60
+ readonly tier: "inBand";
61
+ readonly send: ({ sequence, write }: TerminalMultiplexerNotificationRequest) => boolean;
62
+ };
63
+ readonly backgroundQueryBrokenOn: readonly ["darwin"];
64
+ }, {
65
+ readonly id: "cmux";
66
+ readonly precedence: "session";
67
+ readonly sessionEnvKeys: readonly ["CMUX_WORKSPACE_ID", "CMUX_SURFACE_ID", "CMUX_REMOTE_TRANSPORT"];
68
+ readonly ownsScreenGrid: true;
69
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
70
+ readonly notifier: {
71
+ readonly tier: "surface";
72
+ readonly send: ({ message, env }: TerminalMultiplexerNotificationRequest) => boolean;
73
+ };
74
+ }, {
75
+ readonly id: "wmux";
76
+ readonly precedence: "session";
77
+ readonly sessionEnvKeys: readonly ["WMUX", "WMUX_SURFACE_ID"];
78
+ readonly ownsScreenGrid: true;
79
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
80
+ }, {
81
+ readonly id: "orca";
82
+ readonly precedence: "outerApp";
83
+ readonly sessionEnvKeys: readonly ["ORCA_PANE_KEY", "ORCA_WORKTREE_ID"];
84
+ readonly ownsScreenGrid: false;
85
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
86
+ }];
87
+ /** Terminal multiplexers omp recognizes. */
88
+ export type TerminalMultiplexer = (typeof registry)[number]["id"];
89
+ /** Every environment variable multiplexer classification reads, including the TERM fallback. */
90
+ export declare const TERMINAL_MULTIPLEXER_ENV_KEYS: readonly string[];
91
+ /**
92
+ * Whether an explicit session marker identifies the current provider.
93
+ *
94
+ * TERM is intentionally excluded: it is a classification fallback, not proof
95
+ * that a particular multiplexer session owns the current grid.
96
+ */
97
+ export declare function hasTerminalMultiplexerSession(multiplexer: TerminalMultiplexer, env?: NodeJS.ProcessEnv): boolean;
98
+ /**
99
+ * Every multiplexer whose explicit session markers are present, in registry
100
+ * order. Nested sessions all appear; TERM is not consulted.
101
+ */
102
+ export declare function terminalMultiplexerSessions(env?: NodeJS.ProcessEnv): TerminalMultiplexerModule<TerminalMultiplexer>[];
103
+ /**
104
+ * The multiplexer TERM names, whether or not its session markers survived.
105
+ * This is classification's TERM fallback; session markers are not consulted.
106
+ */
107
+ export declare function terminalMultiplexerForTerm(env?: NodeJS.ProcessEnv): TerminalMultiplexerModule<TerminalMultiplexer> | undefined;
108
+ /**
109
+ * Registry entry for the multiplexer hosting the current process, or
110
+ * `undefined` for a direct terminal. Same precedence as
111
+ * {@link classifyTerminalMultiplexer}.
112
+ */
113
+ export declare function classifyTerminalMultiplexerModule(env?: NodeJS.ProcessEnv): TerminalMultiplexerModule<TerminalMultiplexer> | undefined;
114
+ /**
115
+ * Classify which terminal multiplexer hosts the current process, or `null` for
116
+ * a direct terminal. Single source of truth for both the render-path gate
8
117
  * (`isInsideTerminalMultiplexer`) and the debug snapshot label.
9
118
  *
10
- * TMUX/STY/ZELLIJ, Herdr, and the CMUX/WMUX workspace/surface/remote-transport
11
- * markers are authoritative session signals. TERM can also survive when those
12
- * are stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh). Do not
13
- * use CMUX_SOCKET_PATH / WMUX_CLI / WMUX_PIPE here: they are CLI socket/path
14
- * overrides and can be set outside a CMUX/WMUX terminal. wmux is a Windows
15
- * multiplexer (Electron + xterm.js) modeled on cmux/herdr that repaints its
16
- * pane in place and exports WMUX=1 plus a native WMUX_SURFACE_ID.
119
+ * Session markers are authoritative. TERM can also survive when those markers
120
+ * are stripped (`sudo` without -E, `su`, env-sanitizing launchers/ssh), so a
121
+ * `tmux`/`screen` TERM prefix comes next. Outer applications such as Orca rank
122
+ * last so a multiplexer running inside them wins.
17
123
  */
18
124
  export declare function classifyTerminalMultiplexer(env?: NodeJS.ProcessEnv): TerminalMultiplexer | null;
19
- /** True when a terminal multiplexer owns the current screen grid. */
125
+ /** True when the classified multiplexer owns the current screen grid. */
20
126
  export declare function isInsideTerminalMultiplexer(env?: NodeJS.ProcessEnv): boolean;
127
+ /**
128
+ * Offer a notification to every active session's notifier, innermost tier
129
+ * first. Returns whether one delivered it; otherwise the terminal fallback
130
+ * applies unchanged.
131
+ */
132
+ export declare function routeTerminalMultiplexerNotification(request: TerminalMultiplexerNotificationRequest): boolean;
133
+ export {};
@@ -230,6 +230,12 @@ export interface Terminal {
230
230
  * `status` is the DECRPM value when the terminal answered DECRQM.
231
231
  */
232
232
  onPrivateModeReport?(callback: PrivateModeReportHandler): void;
233
+ /**
234
+ * Report whether DA1 advertises SIXEL (attribute 4), replaying the latest
235
+ * advertisement to late subscribers. A missing attribute does not rule out
236
+ * support discovered through another graphics query.
237
+ */
238
+ onSixelSupport?(callback: (supported: boolean) => void): void;
233
239
  /**
234
240
  * Register a callback fired once the startup Glyph Protocol handshake
235
241
  * resolves (see {@link GlyphProtocolReportHandler}). A subscriber that
@@ -298,10 +304,11 @@ export declare class ProcessTerminal implements Terminal {
298
304
  onAppearanceReport(callback: (appearance: TerminalAppearance, requestToken?: TerminalAppearanceRequestToken) => void): () => void;
299
305
  /**
300
306
  * Re-query the terminal background through the startup DA1-sentinel FIFO,
301
- * pending/queued gating, parsing, dedup, and appearance callbacks. Inside
302
- * tmux, only this explicit path first passes an OSC 11 query to the outer
303
- * terminal, waits briefly for tmux to consume the response into its cache,
304
- * then reads that cache with a direct query. The outer query deliberately has
307
+ * pending/queued gating, parsing, dedup, and appearance callbacks. Inside a
308
+ * multiplexer that answers OSC 11 from a cache (tmux), only this explicit
309
+ * path first passes an OSC 11 query to the outer terminal, waits briefly for
310
+ * the multiplexer to consume the response into its cache, then reads that
311
+ * cache with a direct query. The outer query deliberately has
305
312
  * no DA1 sentinel: multiplexers can decode a fragmented DA1 response as a key
306
313
  * sequence and leak the remaining bytes into the editor. Startup and Mode 2031
307
314
  * probes remain direct. Suppressed while inactive, headless, or after teardown.
@@ -309,6 +316,7 @@ export declare class ProcessTerminal implements Terminal {
309
316
  refreshAppearance(requestToken?: TerminalAppearanceRequestToken): TerminalAppearanceRequestToken | void;
310
317
  onPrivateModeReport(callback: PrivateModeReportHandler): void;
311
318
  onGlyphProtocolReport(callback: GlyphProtocolReportHandler): void;
319
+ onSixelSupport(callback: (supported: boolean) => void): void;
312
320
  onTspHello(callback: TspHelloHandler): void;
313
321
  get tspProbePending(): boolean;
314
322
  get tspExpected(): boolean;
@@ -1,10 +1,19 @@
1
1
  import type { SpinnerFramesOverride } from "./symbols.js";
2
2
  export type ColorValue = string | number;
3
+ /**
4
+ * Tokens a theme is allowed to omit. They resolve to the terminal default
5
+ * foreground, so `Theme.fg()` stays total for callers that paint a token a theme
6
+ * never declared (e.g. a `modelTags.<role>.color` reference) while
7
+ * `Theme.hasColor()` still reports them as unset.
8
+ */
9
+ export declare const OPTIONAL_THEME_COLOR_RECORD: {
10
+ assistantMessageText: true;
11
+ };
3
12
  export interface ThemeJson {
4
13
  $schema?: string;
5
14
  name: string;
6
15
  vars?: Record<string, ColorValue>;
7
- colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax"> & {
16
+ colors: Omit<Record<ThemeColor | ThemeBg, ColorValue>, "thinkingMax" | keyof typeof OPTIONAL_THEME_COLOR_RECORD> & Partial<Record<keyof typeof OPTIONAL_THEME_COLOR_RECORD, ColorValue>> & {
8
17
  thinkingMax?: ColorValue;
9
18
  };
10
19
  export?: {
@@ -36,7 +45,7 @@ export interface ThemeJson {
36
45
  spinnerFrames?: SpinnerFramesOverride;
37
46
  };
38
47
  }
39
- export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
48
+ export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "assistantMessageText" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "thinkingMax" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
40
49
  /** Check if a string is a valid ThemeColor value */
41
50
  export declare function isValidThemeColor(color: string): color is ThemeColor;
42
51
  export type ThemeBg = "selectedBg" | "userMessageBg" | "customMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg" | "statusLineBg";
@@ -54,6 +54,13 @@ export declare class Theme {
54
54
  * against. Built once per instance and frozen; callers share the object.
55
55
  */
56
56
  get sessionAccentInputs(): SessionAccentTheme;
57
+ /**
58
+ * True when the theme paints `color` with a real foreground — i.e. the token
59
+ * exists and is not the empty string (terminal default, `\x1b[39m`). Optional
60
+ * tokens absent from the theme JSON are also false. Callers use this to keep
61
+ * terminal-default behavior byte-for-byte when an optional token is unset.
62
+ */
63
+ hasColor(color: ThemeColor): boolean;
57
64
  fg(color: ThemeColor, text: string): string;
58
65
  /** Apply a foreground, replacing terminal-default tokens with the theme's contrast-safe fallback. */
59
66
  fgResolved(color: ThemeColor, text: string): string;
@@ -1,7 +1,4 @@
1
- /** Whether the process is running inside a tmux session. */
2
- export declare function isInsideTmux(env?: NodeJS.ProcessEnv): boolean;
3
- /** Wrap a control sequence in tmux's DCS passthrough envelope. */
4
- export declare function wrapTmuxPassthrough(payload: string): string;
1
+ export { wrapTmuxPassthrough } from "./multiplexers/tmux.js";
5
2
  /** Pass a control sequence through tmux, leaving direct-terminal output unchanged. */
6
3
  export declare function wrapTmuxPassthroughIfNeeded(payload: string, env?: NodeJS.ProcessEnv): string;
7
4
  /**
@@ -24,6 +24,8 @@ export interface EvalCellResult {
24
24
  durationMs?: number;
25
25
  exitCode?: number;
26
26
  statusEvents?: EvalStatusEvent[];
27
+ /** Discrete events dropped from the front of `statusEvents` (see {@link recordStatusEvent}). */
28
+ statusEventsElided?: number;
27
29
  hasMarkdown?: boolean;
28
30
  }
29
31
  /** Tool result detail object surfaced to the UI/transcript. */
@@ -32,6 +34,8 @@ export interface EvalToolDetails {
32
34
  jsonOutputs?: unknown[];
33
35
  images?: ImageContent[];
34
36
  statusEvents?: EvalStatusEvent[];
37
+ /** Discrete events dropped from the front of `statusEvents` (see {@link recordStatusEvent}). */
38
+ statusEventsElided?: number;
35
39
  isError?: boolean;
36
40
  meta?: OutputMeta;
37
41
  /** First backend that produced cells. Kept for transcript compatibility. */
@@ -74,13 +78,28 @@ export declare function statusEventKey(event: {
74
78
  op: string;
75
79
  [key: string]: unknown;
76
80
  }): string | undefined;
81
+ /**
82
+ * Discrete status events a log keeps. A cell polling a helper in a loop emits
83
+ * one event per call (hundreds of thousands), and every live update and the
84
+ * persisted tool result would otherwise carry them all.
85
+ */
86
+ export declare const MAX_STATUS_EVENTS = 200;
87
+ /** A status event list plus the count of discrete events dropped from its front. */
88
+ export type StatusEventLog = Pick<EvalCellResult, "statusEvents" | "statusEventsElided">;
77
89
  /**
78
90
  * Append or replace a status event. Progress snapshots (see
79
91
  * {@link statusEventKey}) coalesce in place, preserving first-seen order; every
80
- * other op is a discrete action and simply appends. Keeps the persisted event
81
- * list bounded even when a subagent or batch emits hundreds of progress ticks.
92
+ * other op is a discrete action and simply appends.
82
93
  */
83
94
  export declare function upsertStatusEvent(events: EvalStatusEvent[], event: EvalStatusEvent): void;
95
+ /**
96
+ * {@link upsertStatusEvent} into a bounded log. Past {@link MAX_STATUS_EVENTS},
97
+ * the oldest discrete event is dropped and counted. Snapshots stay, since agent
98
+ * cards read them, and so does the newest committed `todo` result, which the
99
+ * session todo panel refreshes from. Renderers already show only the newest
100
+ * events behind an "… N earlier" row.
101
+ */
102
+ export declare function recordStatusEvent(log: StatusEventLog, event: EvalStatusEvent): void;
84
103
  /** Render eval code cells, structured display output, and progress events. */
85
104
  export declare const evalToolRenderer: {
86
105
  animatedPendingPreview: true;
@@ -84,12 +84,22 @@ export interface LimitsMeta {
84
84
  reached: number;
85
85
  suggestion: number;
86
86
  };
87
- /** `unit` may be absent in sessions persisted before it was recorded. */
87
+ /**
88
+ * `unit` may be absent in sessions persisted before it was recorded. `lines` is
89
+ * the first-to-last range of cut lines, and `selectorBase` the target a
90
+ * `:raw:<line>-<line>` selector reads one of them whole from (an artifact's
91
+ * `artifact://<id>` stands in when absent).
92
+ */
88
93
  columnTruncated?: {
89
94
  maxColumn: number;
90
95
  unit?: "bytes" | "chars";
91
96
  artifactId?: string;
92
97
  artifactElidedBytes?: number;
98
+ lines?: {
99
+ first: number;
100
+ last: number;
101
+ };
102
+ selectorBase?: string;
93
103
  };
94
104
  }
95
105
  /**
@@ -31,6 +31,15 @@ export interface OutputSummary {
31
31
  columnDroppedBytes?: number;
32
32
  /** Number of distinct lines that hit the per-line column cap. */
33
33
  columnTruncatedLines?: number;
34
+ /**
35
+ * 1-indexed first and last lines the column cap cut, numbered as in the
36
+ * artifact. Absent when the numbering cannot match the artifact (an inline
37
+ * substitute was capped, or `replace()` rewrote the buffer).
38
+ */
39
+ columnTruncatedRange?: {
40
+ first: number;
41
+ last: number;
42
+ };
34
43
  /** Configured per-line column cap in effect (UTF-8 bytes), when > 0. */
35
44
  columnMax?: number;
36
45
  /** Artifact ID for internal URL access (artifact://<id>) when truncated */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-tui",
4
- "version": "18.8.8",
4
+ "version": "18.9.0",
5
5
  "description": "Terminal User Interface library with differential rendering for efficient text-based applications",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": {
@@ -41,14 +41,14 @@
41
41
  "gen:glyphs": "bun scripts/gen-glyphs.ts"
42
42
  },
43
43
  "dependencies": {
44
- "@oh-my-pi/omptype": "18.8.8",
45
- "@oh-my-pi/pi-agent-core": "18.8.8",
46
- "@oh-my-pi/pi-ai": "18.8.8",
47
- "@oh-my-pi/pi-catalog": "18.8.8",
48
- "@oh-my-pi/pi-natives": "18.8.8",
49
- "@oh-my-pi/pi-utils": "18.8.8",
50
- "@oh-my-pi/pi-wire": "18.8.8",
51
- "@oh-my-pi/snapcompact": "18.8.8"
44
+ "@oh-my-pi/omptype": "18.9.0",
45
+ "@oh-my-pi/pi-agent-core": "18.9.0",
46
+ "@oh-my-pi/pi-ai": "18.9.0",
47
+ "@oh-my-pi/pi-catalog": "18.9.0",
48
+ "@oh-my-pi/pi-natives": "18.9.0",
49
+ "@oh-my-pi/pi-utils": "18.9.0",
50
+ "@oh-my-pi/pi-wire": "18.9.0",
51
+ "@oh-my-pi/snapcompact": "18.9.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "kitty-vt-wasm": "^0.2.0"