@wrongstack/tui 1.0.3 → 1.0.5

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 (81) hide show
  1. package/README.md +3 -0
  2. package/dist/app-action-type.d.ts +29 -1
  3. package/dist/app-key-handler.d.ts +5 -120
  4. package/dist/app-settings-type.d.ts +3 -0
  5. package/dist/app-state.d.ts +21 -12
  6. package/dist/app-view-contract.d.ts +3 -0
  7. package/dist/app-view-sidebar.d.ts +9 -1
  8. package/dist/app.d.ts +3 -3
  9. package/dist/components/confirm-prompt.d.ts +1 -1
  10. package/dist/components/history/copy-geometry.d.ts +41 -0
  11. package/dist/components/history/copy-icon.d.ts +17 -0
  12. package/dist/components/history/copy-registry.d.ts +2 -0
  13. package/dist/components/history/entry-helpers.d.ts +7 -15
  14. package/dist/components/history/entry.d.ts +17 -1
  15. package/dist/components/history/index.d.ts +1 -1
  16. package/dist/components/history/sage-memory-block.d.ts +7 -9
  17. package/dist/components/history/scroll-controller-types.d.ts +16 -1
  18. package/dist/components/history/scrollbar-rail.d.ts +3 -4
  19. package/dist/components/history/stream-box.d.ts +7 -0
  20. package/dist/components/history/tool-card-geometry.d.ts +35 -0
  21. package/dist/components/history/tool-card.d.ts +3 -1
  22. package/dist/components/history/tool-entry.d.ts +3 -1
  23. package/dist/components/history/tool-group.d.ts +5 -3
  24. package/dist/components/history/tool-visual-types.d.ts +1 -1
  25. package/dist/components/history/types.d.ts +9 -0
  26. package/dist/components/history/use-history-controller.d.ts +2 -0
  27. package/dist/components/history/utils.d.ts +2 -2
  28. package/dist/components/history/visual-lines.d.ts +1 -1
  29. package/dist/components/history/wrap-geometry.d.ts +4 -4
  30. package/dist/components/history.d.ts +1 -1
  31. package/dist/components/inspect-overlay.d.ts +52 -0
  32. package/dist/components/powerline-rail.d.ts +4 -7
  33. package/dist/components/scrollable-history.d.ts +4 -4
  34. package/dist/components/settings-picker-constants.d.ts +11 -0
  35. package/dist/components/settings-picker-model.d.ts +8 -0
  36. package/dist/components/settings-picker.d.ts +4 -7
  37. package/dist/components/sidebar-panel-frame.d.ts +31 -0
  38. package/dist/components/sidebar-panels-workspace.d.ts +11 -2
  39. package/dist/components/sidebar-scrollbar.d.ts +36 -0
  40. package/dist/components/sidebar.d.ts +13 -1
  41. package/dist/components/status-bar-icons.d.ts +0 -7
  42. package/dist/history-entry.d.ts +4 -0
  43. package/dist/hit-test.d.ts +5 -5
  44. package/dist/hooks/subagent-history-format.d.ts +12 -0
  45. package/dist/hooks/tui-slash-command-options.d.ts +30 -0
  46. package/dist/hooks/use-app-environment.d.ts +136 -0
  47. package/dist/hooks/use-app-pipeline-builders.d.ts +1 -0
  48. package/dist/hooks/use-app-ref-spine.d.ts +74 -0
  49. package/dist/hooks/use-app-state.d.ts +20 -0
  50. package/dist/hooks/use-design-kit-slash-commands.d.ts +8 -0
  51. package/dist/hooks/use-next-steps-auto-submit.d.ts +1 -0
  52. package/dist/hooks/use-provider-slash-commands.d.ts +13 -0
  53. package/dist/hooks/use-resource-slash-commands.d.ts +11 -0
  54. package/dist/hooks/use-session-slash-commands.d.ts +17 -0
  55. package/dist/hooks/use-settings-slash-commands.d.ts +22 -0
  56. package/dist/hooks/use-theme-state.d.ts +3 -9
  57. package/dist/hooks/use-tui-slash-commands.d.ts +2 -29
  58. package/dist/index.js +8659 -7144
  59. package/dist/input-validation/action-types.d.ts +1 -1
  60. package/dist/key-handler-context.d.ts +142 -0
  61. package/dist/key-routes/key-route-busy.d.ts +23 -0
  62. package/dist/key-routes/key-route-composer.d.ts +33 -0
  63. package/dist/key-routes/key-route-overlay.d.ts +65 -0
  64. package/dist/key-routes/key-route-paste.d.ts +17 -0
  65. package/dist/key-routes/key-route-pointer.d.ts +18 -0
  66. package/dist/overlay-key-router.d.ts +2 -0
  67. package/dist/reducers/conversation.d.ts +1 -1
  68. package/dist/reducers/dialogs.d.ts +1 -1
  69. package/dist/reducers/helpers.d.ts +3 -1
  70. package/dist/reducers/workspace-panels.d.ts +11 -1
  71. package/dist/run-tui-exits.d.ts +46 -0
  72. package/dist/run-tui-launch.d.ts +23 -0
  73. package/dist/run-tui-mount.d.ts +28 -0
  74. package/dist/run-tui-session.d.ts +13 -0
  75. package/dist/run-tui.d.ts +1 -2
  76. package/dist/settings-contracts.d.ts +4 -0
  77. package/dist/sidebar-sizing.d.ts +2 -1
  78. package/dist/theme.d.ts +7 -0
  79. package/dist/tool-result-view-mode.d.ts +8 -0
  80. package/dist/ui-contracts.d.ts +17 -3
  81. package/package.json +7 -7
package/README.md CHANGED
@@ -69,6 +69,9 @@ process.exit(exitCode);
69
69
  The wordmark and route label render in the brand gradient (orange → pink).
70
70
 
71
71
  - **History pane** — assistant text, tool calls, tool results, errors, turn summaries
72
+ - **Tool-result density** — each committed tool card has copy / less / more controls;
73
+ choose the global `minimal`, `normal`, or bounded `full` default under
74
+ `/settings tool-result-view <mode>`
72
75
  - **Streaming text** — partial deltas render live; on abort, partial response is preserved
73
76
  - **Status bar** — model · provider · context-window % · YOLO chip · spinner
74
77
  - **Input box** — multi-line buffer with bracketed-paste detection, history (↑/↓), placeholder pills for attachments
@@ -9,7 +9,7 @@ import type { BrainLogEntry, BrainRiskLevel } from './brain-contracts.js';
9
9
  import type { BrainPanelSettings } from './brain-panel-model.js';
10
10
  import type { StatuslineItem } from './components/statusline-picker.js';
11
11
  import type { HistoryEntry } from './history-entry.js';
12
- import type { AuditLevel, CacheTtl, CompactorStrategy, ContextMode, LogLevel, ReasoningEffort, SettingsMode, SettingsPickerPatch, StatuslineMode } from './settings-contracts.js';
12
+ import type { AuditLevel, CacheTtl, CompactorStrategy, ContextMode, LogLevel, ReasoningEffort, SettingsMode, SettingsPickerPatch, StatuslineMode, ToolResultViewMode } from './settings-contracts.js';
13
13
  import type { AutonomyOption, ChipMeta, HelpEntry, LiveSessionEntry, McpPickerItem, ModeOption, PluginPickerItem, ProjectPickerItem, PromptPickEntry, ProviderOption, ResourceMenuAction, ResourceMenuSnapshot, ShadowState, ToolPickerItem, WorktreeRow } from './ui-contracts.js';
14
14
  export type Action = {
15
15
  type: 'addEntry';
@@ -74,6 +74,15 @@ export type Action = {
74
74
  type: 'copiedNotice';
75
75
  text: string;
76
76
  entryId: number | null;
77
+ } | {
78
+ type: 'inspectOverlayOpen';
79
+ entryId: number;
80
+ entryIds?: readonly number[] | undefined;
81
+ } | {
82
+ type: 'inspectOverlayClose';
83
+ } | {
84
+ type: 'inspectOverlayScroll';
85
+ delta: number;
77
86
  } | {
78
87
  type: 'brainStatus';
79
88
  state: State['brain']['state'];
@@ -390,6 +399,7 @@ export type Action = {
390
399
  breakerEnabled: boolean;
391
400
  breakerAutoKillResetMs: number;
392
401
  showModelReasoning: boolean;
402
+ toolResultViewMode: ToolResultViewMode;
393
403
  showAgentSwarmPanel: import('./app-settings-type.js').AgentSwarmPanelMode;
394
404
  showSidebar?: boolean | undefined;
395
405
  panelPositions: import('./ui-contracts.js').PanelPositionMap;
@@ -428,6 +438,10 @@ export type Action = {
428
438
  | {
429
439
  type: 'settingsValueSet';
430
440
  patch: SettingsPickerPatch;
441
+ } | {
442
+ type: 'toolResultViewSet';
443
+ entryIds: readonly number[];
444
+ mode: ToolResultViewMode;
431
445
  }
432
446
  /**
433
447
  * Update the live row-search filter. Empty string clears the filter.
@@ -767,6 +781,9 @@ export type Action = {
767
781
  info: State['confirmQueue'][0];
768
782
  } | {
769
783
  type: 'confirmClose';
784
+ } | {
785
+ type: 'confirmResolved';
786
+ toolUseId: string;
770
787
  } | {
771
788
  type: 'confirmClearAll';
772
789
  } | {
@@ -1181,6 +1198,17 @@ export type Action = {
1181
1198
  /** Reset sidebar scroll offset to 0. */
1182
1199
  | {
1183
1200
  type: 'sidebarScrollReset';
1201
+ }
1202
+ /** Jump the sidebar scroll to an absolute offset (scrollbar press/drag). */
1203
+ | {
1204
+ type: 'sidebarScrollSet';
1205
+ /** Absolute offset in rows from the content top; clamped to [0, max]. */
1206
+ offset: number;
1207
+ viewportHeight?: number | undefined;
1208
+ /** See the `sidebarScroll` twin field — identical clamp semantics. */
1209
+ sidebarTwinRowCount?: number | undefined;
1210
+ /** See the `sidebarScroll` twin field — identical clamp semantics. */
1211
+ effectiveSwarmOnSidebar?: boolean | undefined;
1184
1212
  } | {
1185
1213
  type: 'toggleKanbanPanel';
1186
1214
  } | {
@@ -1,123 +1,8 @@
1
- import type { Director } from '@wrongstack/core/coordination';
2
- import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
3
- import type { Action } from './app-action-type.js';
4
- import type { AppProps } from './app-props.js';
5
- import type { State } from './app-state.js';
6
- import { type KeyEvent } from './components/input.js';
7
- import type { HistoryScrollController } from './components/scrollable-history.js';
8
- import type { StatusBarClickMap } from './components/status-bar-types.js';
9
- import { type StatuslineItem } from './components/statusline-picker.js';
10
- import { type DOMElement } from './ink.js';
11
- import { type PasteAccumState } from './paste-accumulator.js';
12
- interface AppKeyHandlerOptions {
13
- state: State;
14
- dispatch: Dispatch<Action>;
15
- historyScrollRef: MutableRefObject<HistoryScrollController | null>;
16
- /** Resets the live-tail timeout after a user-driven history navigation. */
17
- onHistoryScrollActivity?: (() => void) | undefined;
18
- runInterruptLadder: () => void;
19
- enhanceCancelledRef: MutableRefObject<boolean>;
20
- enhanceAbortRef: MutableRefObject<AbortController | null>;
21
- inputGateRef: MutableRefObject<boolean>;
22
- lastEscAtRef: MutableRefObject<number>;
23
- pasteAccumRef: MutableRefObject<PasteAccumState>;
24
- pasteFlushTimerRef: MutableRefObject<ReturnType<typeof setTimeout> | null>;
25
- commitPaste: (full: string) => Promise<void>;
26
- tryPickerKey: (input: string, key: KeyEvent, isEnter: boolean) => boolean;
27
- dismissedEscAtRef: MutableRefObject<number>;
28
- streamingTextRef: MutableRefObject<string>;
29
- confirmExitRef: MutableRefObject<boolean>;
30
- activeCtrlRef: MutableRefObject<AbortController | null>;
31
- clearPendingConfirms: () => void;
32
- liveDirector: () => Director | null;
33
- openProjectPicker: () => Promise<void>;
34
- loadLiveSessions: () => Promise<void>;
35
- openStatuslinePicker: (field?: number) => void;
36
- statuslineHiddenItems: StatuslineItem[];
37
- getSddRun: AppProps['getSddRun'];
38
- onSddLifecycle: AppProps['onSddLifecycle'];
39
- getSettings: AppProps['getSettings'];
40
- saveSettings: AppProps['saveSettings'];
41
- lastEnterAtRef: MutableRefObject<number>;
42
- draftRef: MutableRefObject<{
43
- buffer: string;
44
- cursor: number;
45
- }>;
46
- setDraft: (buffer: string, cursor: number) => void;
47
- /** Submits the composer. `overrideRaw` bypasses the buffer (bash mode
48
- * forwards its draft through the `!` shell path this way). */
49
- submit: (overrideRaw?: string) => void;
50
- mouseMode: boolean;
51
- termRows: number;
52
- terminalColumns: number;
53
- terminalRows: number;
54
- /** Width of the main column (terminal width minus sidebar). When > 0,
55
- * scrollbar hit-tests use this instead of terminalColumns so the scrollbar
56
- * track is correctly positioned to the left of the sidebar. */
57
- mainColumnWidth: number;
58
- /**
59
- * Whether a full-width overlay owns the screen.
60
- *
61
- * Passed in rather than derived. The old proxy —
62
- * `mainColumnWidth >= stdout.columns` — is true whenever the sidebar is
63
- * ZERO wide, and `computeSidebarWidth` returns 0 for any terminal under
64
- * `SIDEBAR_MIN_TERMINAL` (64 columns). So on a narrow terminal the flag was
65
- * permanently true with nothing open, and the whole `if (!overlayOpen)`
66
- * block below — wheel scrolling, scrollbar drag, drag-select, copy icons,
67
- * every status-bar chip click, PageUp/PageDown — silently stopped working,
68
- * along with Shift+Tab, `?` help, and (via `routeInputKey`) up/down input
69
- * history. `app.tsx` already hands the real value to `useMouseTracking`.
70
- */
71
- overlayOpen: boolean;
72
- /**
73
- * Effective swarm-on-sidebar read: `panelPositions.fleet === 'sidebar'`
74
- * OR the legacy `showAgentSwarmPanel === 'sidebar'` flag from
75
- * `liveSettings`. The renderer at `app-view.tsx:897-899` reads
76
- * ONLY the legacy `showAgentSwarmPanel` field (not the panel-
77
- * position map) to decide whether to show the mission card; the
78
- * scroll-clamp reservation must match that source or a config-only
79
- * legacy 'sidebar' swarm mode (no recent picker open) will render
80
- * the mission card but the clamp will under-reserve, hiding the
81
- * bottom mission rows behind `RightSidebar`'s `overflowY="hidden"`
82
- * viewport. The OR with `panelPositions.fleet` is directionally
83
- * safe (over-reservation is harmless) and aligns with the field
84
- * that gates whether the swarm twin is mounted. See
85
- * {@link SidebarLayoutState.effectiveSwarmOnSidebar}. Threaded into
86
- * `sidebarScroll` dispatches so the reducer's mission-queue
87
- * reservation matches the actual render.
88
- */
89
- effectiveSwarmOnSidebar: boolean;
90
- /**
91
- * Approximate row count for routed sidebar twin panels mounted above
92
- * `SidebarContent`. Subtracted from `viewportHeight` by the reducer's
93
- * scroll clamp so the user can't scroll past the end into blank space.
94
- * See {@link SidebarLayoutState.sidebarTwinRowCount}.
95
- */
96
- sidebarTwinRowCount: number;
97
- statusBarWrapRef: MutableRefObject<DOMElement | null>;
98
- belowStatusBarRef: MutableRefObject<DOMElement | null>;
99
- /** Chip click map published by StatusBar on every render. */
100
- statusBarClickMapRef: MutableRefObject<StatusBarClickMap | null>;
101
- openModelPicker: () => Promise<void>;
102
- nextStepsAutoSubmitTimerRef: MutableRefObject<ReturnType<typeof setInterval> | undefined>;
103
- nextStepsAutoSubmitSuggestionRef: MutableRefObject<string | null>;
104
- nextStepsAutoSubmitLabel: string | null;
105
- setNextStepsAutoSubmitCountdown: Dispatch<SetStateAction<number | null>>;
106
- setNextStepsAutoSubmitLabel: Dispatch<SetStateAction<string | null>>;
107
- cancelNextStepsCountdown: () => void;
108
- pasteClipboardText: () => Promise<void>;
109
- pasteClipboardImage: () => Promise<void>;
110
- slashRegistry: AppProps['slashRegistry'];
111
- agent: AppProps['agent'];
112
- /**
113
- * Called with the copied entry's id after a chat card's copy icon is clicked
114
- * and its content was successfully written to the clipboard. Lets the host
115
- * surface a transient "Copied" confirmation and flash that card's icon. Not
116
- * called when the click missed or the write failed.
117
- */
118
- onHistoryCopy?: ((entryId: number) => void) | undefined;
119
- }
1
+ import type { KeyEvent } from './components/input.js';
2
+ import type { AppKeyHandlerOptions } from './key-handler-context.js';
3
+ export type { AppKeyHandlerOptions } from './key-handler-context.js';
4
+ /** Keyboard activity means the user has taken control of an armed automatic turn. */
5
+ export declare function stopNextStepsAutoSubmitOnKey(cancel: () => void): void;
120
6
  /** Creates the terminal key host around focused overlay/input routers. */
121
7
  export declare function createAppKeyHandler(options: AppKeyHandlerOptions): (input: string, key: KeyEvent) => Promise<void>;
122
- export {};
123
8
  //# sourceMappingURL=app-key-handler.d.ts.map
@@ -2,6 +2,7 @@ import type { FleetChatVerbosity, TokenSavingTier } from '@wrongstack/core/types
2
2
  import type { AgentSwarmPanelMode, CacheTtl, ContextMode, ReasoningEffort, StatuslineMode } from './settings-contracts.js';
3
3
  export type { PanelId, PanelPosition, PanelPositionMap } from './ui-contracts.js';
4
4
  export { coercePanelPositionMap, DEFAULT_PANEL_POSITIONS, PANEL_IDS, } from './ui-contracts.js';
5
+ import type { ToolResultViewMode } from './tool-result-view-mode.js';
5
6
  import type { PanelPositionMap } from './ui-contracts.js';
6
7
  export type { AgentSwarmPanelMode } from './settings-contracts.js';
7
8
  /**
@@ -72,6 +73,8 @@ export type Settings = {
72
73
  /** Single word shown in the TUI rainbow working-state chip. */
73
74
  thinkingWord: string;
74
75
  showModelReasoning: boolean;
76
+ /** Default presentation density for committed TUI tool results. */
77
+ toolResultViewMode?: ToolResultViewMode | undefined;
75
78
  /** Agent swarm panel placement: 'bottom' (lower region), 'sidebar' (right
76
79
  * sidebar, vertical), or 'off' (hidden). Default: 'bottom'.
77
80
  * Backward-compat: old boolean configs are coerced — true→'bottom', false→'off'. */
@@ -9,7 +9,7 @@ import type { BrainPanelSettings } from './brain-panel-model.js';
9
9
  import type { HistoryEntry } from './history-entry.js';
10
10
  import type { TuiHistoryBudget } from './history-retention.js';
11
11
  import type { ResumeLoadState } from './resume-load.js';
12
- import type { AuditLevel, CacheTtl, CompactorStrategy, ContextMode, LogLevel, ReasoningEffort, SettingsMode, StatuslineMode } from './settings-contracts.js';
12
+ import type { AuditLevel, CacheTtl, CompactorStrategy, ContextMode, LogLevel, ReasoningEffort, SettingsMode, StatuslineMode, ToolResultViewMode } from './settings-contracts.js';
13
13
  import type { AutonomyOption, ChipMeta, HelpEntry, LiveSessionEntry, McpPickerItem, ModeOption, PanelPositionMap, PluginPickerItem, ProjectPickerItem, PromptPickEntry, ProviderOption, RefineFailureDecision, RefineFailureModel, ResourceMenuAction, ResourceMenuSnapshot, SendMode, ShadowState, StatuslineItem, ToolPickerItem, WorktreeRow } from './ui-contracts.js';
14
14
  export type { Settings } from './app-settings-type.js';
15
15
  export type { DraftEntry, GoalSummary, QueueItem, ResumeSessionEntry, SlashCommandMatch, } from './app-state-core-types.js';
@@ -112,21 +112,28 @@ export type State = {
112
112
  partialAssistantText: string;
113
113
  } | null;
114
114
  hint: string;
115
+ /** Transient clipboard confirmation; separate from competing hint producers. */
116
+ copiedNotice: string;
117
+ /** Card whose copy icon should flash; null means no highlighted card. */
118
+ copiedEntryId: number | null;
115
119
  /**
116
- * Transient "Copied" confirmation shown when a chat card's copy icon is
117
- * clicked and its content lands on the clipboard. Separate from `hint` so it
118
- * takes precedence over the running-tools indicator (copying tool output
119
- * mid-run is a prime use case) and never races another hint producer. Empty
120
- * string = no notice. Auto-cleared by a host timer.
120
+ * Tool-result inspect overlay. Holds the clicked card's ids; the view
121
+ * resolves the full (untruncated) payload from live history / stream.
122
+ * Null when closed.
121
123
  */
122
- copiedNotice: string;
124
+ inspectOverlay: {
125
+ entryId: number;
126
+ entryIds?: readonly number[] | undefined;
127
+ scroll: number;
128
+ } | null;
123
129
  /**
124
- * Entry id of the card whose copy icon was just clicked, so ScrollableHistory
125
- * can flash that specific icon in the success color while the "Copied" notice
126
- * is active. `null` = no card highlighted. Set and cleared in lockstep with
127
- * `copiedNotice`.
130
+ * Per-card tool-result density overrides, keyed by immutable history id.
131
+ * These are presentation-only: the retained/canonical payload is untouched,
132
+ * so expanding after a compact render never loses data. Changing the global
133
+ * menu setting or clearing the session drops every local override.
134
+ * Group controls write the same override to each member id.
128
135
  */
129
- copiedEntryId: number | null;
136
+ toolResultViewOverrides: ReadonlyMap<number, ToolResultViewMode>;
130
137
  brain: {
131
138
  state: 'idle' | 'deciding' | 'answered' | 'ask_human' | 'denied';
132
139
  source?: string | undefined;
@@ -348,6 +355,8 @@ export type State = {
348
355
  * (API-level provisioning). Default: true.
349
356
  */
350
357
  showModelReasoning: boolean;
358
+ /** Global default for all committed tool-result cards. */
359
+ toolResultViewMode: ToolResultViewMode;
351
360
  /** Agent swarm panel placement: 'bottom' (lower region), 'sidebar' (right sidebar), or 'off'. Default: 'bottom'. */
352
361
  showAgentSwarmPanel: import('./app-settings-type.js').AgentSwarmPanelMode;
353
362
  /** Right sidebar visibility (mirrors Settings.showSidebar). Default: true. */
@@ -61,6 +61,8 @@ interface AppViewRuntime {
61
61
  statusBarRows: number;
62
62
  /** Chip click map published by StatusBar; consumed by the mouse hit-test. */
63
63
  statusBarClickMapRef: MutableRefObject<import('./components/status-bar-types.js').StatusBarClickMap | null>;
64
+ /** Actual inspect-modal title row used for Copy/Close hit-testing. */
65
+ inspectOverlayHeaderRef: MutableRefObject<DOMElement | null>;
64
66
  stableOnKey: (input: string, key: KeyEvent) => void;
65
67
  liveTodos: ReturnType<typeof useLiveTodos>;
66
68
  liveSettings: ReturnType<NonNullable<AppProps['getSettings']>> | undefined;
@@ -82,6 +84,7 @@ interface AppViewRuntime {
82
84
  enhanceCountdown: number | null;
83
85
  nextStepsAutoSubmitCountdown: number | null;
84
86
  nextStepsAutoSubmitLabel: string | null;
87
+ nextStepsAutoSubmitDeadlineMs: number | null;
85
88
  setDraft: (buffer: string, cursor: number) => void;
86
89
  focusedBoardId: string | null;
87
90
  getCronJobs: () => Promise<CronListResult>;
@@ -8,6 +8,14 @@ interface AppViewSidebarProps {
8
8
  runtime: AppViewProps['runtime'];
9
9
  sidebarWidth: number;
10
10
  sidebarContentWidth: number;
11
+ /** Current sidebar scroll offset (rows) — positions the scrollbar thumb. */
12
+ sidebarScrollOffset: number;
13
+ /**
14
+ * Estimated scrollable overflow (rows) from `estimateSidebarMaxScroll`,
15
+ * computed by `app-view.tsx` with the same inputs the wheel/press
16
+ * dispatchers thread. > 0 shows the one-column scrollbar in the rail.
17
+ */
18
+ sidebarMaxScroll: number;
11
19
  sidebarSlotVisible: (id: PanelId) => boolean;
12
20
  hiddenSidebarPanelCount: number;
13
21
  sidebarProcessData: ReturnType<typeof useSidebarProcessList>;
@@ -23,6 +31,6 @@ interface AppViewSidebarProps {
23
31
  * is bootstrapping or the daemon URL hasn't produced a response yet. */
24
32
  sidebarWrongProxyData: ReturnType<typeof useSidebarWrongProxy>;
25
33
  }
26
- export declare function AppViewSidebar({ host, runtime, sidebarWidth, sidebarContentWidth, sidebarSlotVisible, hiddenSidebarPanelCount, sidebarProcessData, sidebarConnectionsData, sidebarKanbanData, sidebarPlanData, sidebarWrongProxyEnabled, sidebarWrongProxyData, }: AppViewSidebarProps): React.ReactElement | null;
34
+ export declare function AppViewSidebar({ host, runtime, sidebarWidth, sidebarContentWidth, sidebarScrollOffset, sidebarMaxScroll, sidebarSlotVisible, hiddenSidebarPanelCount, sidebarProcessData, sidebarConnectionsData, sidebarKanbanData, sidebarPlanData, sidebarWrongProxyEnabled, sidebarWrongProxyData, }: AppViewSidebarProps): React.ReactElement | null;
27
35
  export {};
28
36
  //# sourceMappingURL=app-view-sidebar.d.ts.map
package/dist/app.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import React from 'react';
1
+ import type React from 'react';
2
+ export { buildGoalPreamble } from '@wrongstack/core/execution';
3
+ export type { AppProps } from './app-props.js';
2
4
  export { type Action, type FleetEntry, type QueueItem, type ResumeSessionEntry, reducer, type Settings, type SlashCommandMatch, type State, } from './app-reducer.js';
3
5
  export { nextInputWordStart, previousInputWordStart } from './input-editing.js';
4
6
  export { renderRunningTools } from './running-tools.js';
5
7
  export { selectedSlashCommandLine } from './slash-command-search.js';
6
- export { buildGoalPreamble } from '@wrongstack/core/execution';
7
- export type { AppProps } from './app-props.js';
8
8
  export { buildSteeringPreamble } from './steering-preamble.js';
9
9
  import type { AppProps } from './app-props.js';
10
10
  export declare function App(props: AppProps): React.ReactElement;
@@ -5,7 +5,7 @@ interface ConfirmPromptProps {
5
5
  input: unknown;
6
6
  suggestedPattern: string;
7
7
  onDecision: (decision: ConfirmDecision) => void;
8
- /** Enable YOLO mode (capital Y). Approves the current call. */
8
+ /** Enable YOLO mode (capital Y). Destructive current calls remain pending. */
9
9
  onEnableYolo: () => void;
10
10
  /** Whether this call was classified destructive. */
11
11
  destructive?: boolean;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Copy-hit geometry helpers — pure functions for resolving copy-icon targets
3
+ * in viewport coordinates.
4
+ *
5
+ * Extracted from scrollable-history.tsx.
6
+ */
7
+ import type { ToolResultViewMode } from '../../tool-result-view-mode.js';
1
8
  import type { HistoryEntry } from './index.js';
2
9
  /**
3
10
  * A clickable copy-icon target resolved in viewport coordinates during the
@@ -13,7 +20,30 @@ export interface CopyHit {
13
20
  startRow: number;
14
21
  endRow: number;
15
22
  iconCol: number;
23
+ /**
24
+ * Column of the inspect glyph when this card can open the detail overlay
25
+ * (tools and the live tool stream). Absent for non-tool cards.
26
+ */
27
+ inspectCol?: number | undefined;
28
+ /** Present only for committed tool cards/groups that expose −/+ controls. */
29
+ toolEntryIds?: readonly number[] | undefined;
30
+ toolViewMode?: ToolResultViewMode | undefined;
31
+ /** History-band columns for the spaced `▲ ▼` controls. */
32
+ lessCol?: number | undefined;
33
+ moreCol?: number | undefined;
16
34
  }
35
+ /**
36
+ * Resolve a click on a committed tool card's view-mode control.
37
+ *
38
+ * The control spans {@link VIEW_CONTROL_HIT_WIDTH} cells, not one: an exact
39
+ * single-column match on a one-row header is a target the user cannot
40
+ * reliably hit even when the column is right (and it was not — the columns
41
+ * used to be hard-coded one cell left of the glyphs).
42
+ */
43
+ export declare function findToolViewControl(hits: readonly CopyHit[], row: number, col: number): {
44
+ hit: CopyHit;
45
+ delta: -1 | 1;
46
+ } | null;
17
47
  /** Sentinel returned after copying the active, non-retained tool-stream box. */
18
48
  export declare const LIVE_TOOL_STREAM_COPY_ID = -1;
19
49
  /** Sentinel returned after copying a drag-select-then-right-click selection.
@@ -28,11 +58,22 @@ export declare const SELECTION_COPY_ID = -2;
28
58
  * the most recently rendered card. Exported-shape pure helper for unit tests.
29
59
  */
30
60
  export declare function findCopyHit(hits: readonly CopyHit[], row: number, col: number): CopyHit | null;
61
+ /** Resolve the inspect-icon target under a viewport cell, or null. */
62
+ export declare function findInspectHit(hits: readonly CopyHit[], row: number, col: number): CopyHit | null;
31
63
  /** Resolve a hit to the complete current clipboard payload without performing I/O. */
32
64
  export declare function resolveCopyPayload(hit: CopyHit, entriesById: ReadonlyMap<number, HistoryEntry>, liveToolText?: string | undefined): {
33
65
  entryId: number;
34
66
  text: string;
35
67
  } | null;
68
+ /** Resolve a hit to the inspect overlay payload (title + full body). */
69
+ export declare function resolveInspectPayload(hit: CopyHit, entriesById: ReadonlyMap<number, HistoryEntry>, liveTool?: {
70
+ name: string;
71
+ text: string;
72
+ } | undefined): {
73
+ entryId: number;
74
+ title: string;
75
+ body: string;
76
+ } | null;
36
77
  /**
37
78
  * Rows clipped from the top of the mounted history stack before it appears in
38
79
  * the viewport. Scrolled frames clip by the anchor's row offset; pinned frames
@@ -11,6 +11,17 @@ export declare const COPY_ICON = "\u274F";
11
11
  * the mouse handler treats the icon column plus this span as the target.
12
12
  */
13
13
  export declare const COPY_ICON_WIDTH = 1;
14
+ /**
15
+ * Inspect affordance (U+2315). Same single-cell contract as COPY_ICON.
16
+ * A blank column sits between copy and this glyph so the two hits are
17
+ * not adjacent.
18
+ */
19
+ export declare const INSPECT_ICON = "\u2315";
20
+ export declare const INSPECT_ICON_WIDTH = 1;
21
+ /** Blank cells between the copy glyph and the inspect glyph. */
22
+ export declare const INSPECT_ICON_GAP = 1;
23
+ /** History-band column of inspect relative to the copy column. */
24
+ export declare const INSPECT_COL_OFFSET: number;
14
25
  /**
15
26
  * Return the most useful lossless clipboard representation for any retained
16
27
  * history card. Text cards preserve their original text, pasted user cards use
@@ -22,4 +33,10 @@ export declare function copyableTextForEntry(entry: HistoryEntry): string;
22
33
  export declare function copyableTextForEntries(entries: readonly HistoryEntry[]): string;
23
34
  /** Every retained history entry has either natural text or a raw JSON fallback. */
24
35
  export declare function isCopyableEntry(_entry: HistoryEntry): boolean;
36
+ /** Tool cards (and live tool streams) expose the inspect overlay. */
37
+ export declare function isInspectableEntry(entry: HistoryEntry): boolean;
38
+ /** Full inspect document: status, input, and the canonical untruncated result. */
39
+ export declare function inspectTextForEntry(entry: HistoryEntry): string;
40
+ export declare function inspectTextForEntries(entries: readonly HistoryEntry[]): string;
41
+ export declare function inspectTitleForEntries(entries: readonly HistoryEntry[]): string;
25
42
  //# sourceMappingURL=copy-icon.d.ts.map
@@ -1,4 +1,5 @@
1
1
  import type { EntryHeightCache } from '../../height-cache.js';
2
+ import type { ToolResultViewMode } from '../../tool-result-view-mode.js';
2
3
  import type { CopyHit } from './copy-geometry.js';
3
4
  import { type RenderGroup } from './tool-group.js';
4
5
  interface CopyRegistry {
@@ -24,6 +25,7 @@ export declare function buildCopyRegistry(opts: {
24
25
  iconCol: number;
25
26
  showModelReasoning?: boolean | undefined;
26
27
  liveToolVisible: boolean;
28
+ viewModeForEntry?: ((entryId: number) => ToolResultViewMode) | undefined;
27
29
  }): CopyRegistry;
28
30
  export {};
29
31
  //# sourceMappingURL=copy-registry.d.ts.map
@@ -6,6 +6,12 @@ import type { HistoryEntry } from './types.js';
6
6
  * and count the rest; the side panel keeps the full set.
7
7
  */
8
8
  export declare const MAX_MEMORY_PROOF_ROWS = 4;
9
+ /** Shared open left-rail used by transcript cards (user, thinking, error, …). */
10
+ export declare function HistoryRail({ color, marginY, children, }: {
11
+ color: string;
12
+ marginY?: number;
13
+ children: React.ReactNode;
14
+ }): React.ReactElement;
9
15
  /**
10
16
  * Render the signed score contributions as `metadata 0.72×0.48 +0.34 │ …`.
11
17
  * Uses U+2212 for negatives so a minus is not mistaken for a hyphen inside
@@ -46,23 +52,9 @@ export declare function memoryLifecycleStyle(action: Extract<HistoryEntry, {
46
52
  }>['action']): {
47
53
  icon: string;
48
54
  color: string;
55
+ title: string;
49
56
  };
50
57
  export declare function brainRiskColor(risk: Extract<HistoryEntry, {
51
58
  kind: 'brain';
52
59
  }>['risk']): string;
53
- /**
54
- * Full-bordered notice card for `warn`/`error` entries. A rounded box with an
55
- * icon + label header in the accent color and the message body below, wrapped
56
- * to the terminal width and split across lines so multi-line diagnostics stay
57
- * readable instead of overflowing a single-line strip. Slash-command output
58
- * is normalized before it reaches Ink so cursor/control sequences can never
59
- * paint outside the notice card's measured geometry.
60
- */
61
- export declare function NoticeCard({ icon, label, color, text, termWidth, }: {
62
- icon: string;
63
- label: string;
64
- color: string;
65
- text: string;
66
- termWidth: number;
67
- }): React.ReactElement;
68
60
  //# sourceMappingURL=entry-helpers.d.ts.map
@@ -1,8 +1,19 @@
1
1
  import type { TodoItem } from '@wrongstack/core/agent';
2
2
  import React from 'react';
3
+ import type { ToolResultViewMode } from '../../tool-result-view-mode.js';
3
4
  import type { HistoryEntry } from './types.js';
4
5
  export { councilHeadline, councilSeatLine } from './entry-helpers.js';
5
- export declare const Entry: React.MemoExoticComponent<({ entry, termWidth, termHeight, setSuggestions, autonomyMode, multiDiffSummaryThreshold, todos, showModelReasoning, showSageMemoryInject, }: {
6
+ export declare const NEXT_STEP_SWEEP_DURATION_MS = 10000;
7
+ /** Number of completed glyphs in the final ten-second auto-submit sweep. */
8
+ export declare function nextStepSweepGlyphCount(glyphCount: number, deadlineMs: number, nowMs: number): number;
9
+ export declare function nextStepSweepParts(text: string, deadlineMs: number, nowMs: number): {
10
+ lit: string;
11
+ cursor: string;
12
+ pending: string;
13
+ };
14
+ /** Find the newest rendered panel that owns the armed suggestion label. */
15
+ export declare function findArmedNextStepsEntryId(entries: readonly HistoryEntry[], label: string | null | undefined): number | null;
16
+ export declare const Entry: React.MemoExoticComponent<({ entry, termWidth, termHeight, setSuggestions, autonomyMode, nextStepsAutoSubmitLabel, nextStepsAutoSubmitDeadlineMs, multiDiffSummaryThreshold, todos, showModelReasoning, showSageMemoryInject, toolResultViewMode, }: {
6
17
  entry: HistoryEntry;
7
18
  termWidth: number;
8
19
  /** Available managed-history rows; used by height-aware entries such as the banner. */
@@ -11,6 +22,10 @@ export declare const Entry: React.MemoExoticComponent<({ entry, termWidth, termH
11
22
  setSuggestions?: ((steps: string[]) => void) | undefined;
12
23
  /** Current autonomy mode — when 'auto', first step shows an auto marker. */
13
24
  autonomyMode?: string | undefined;
25
+ /** Exact armed suggestion label; identifies the row that will be submitted. */
26
+ nextStepsAutoSubmitLabel?: string | null | undefined;
27
+ /** Exact deadline; drives a character-by-character sweep during its final 10 seconds. */
28
+ nextStepsAutoSubmitDeadlineMs?: number | null | undefined;
14
29
  /** User-tunable cutoff for the multi-file diff summary footer. Passes
15
30
  * through to `formatMultiDiffSummary`; `undefined` means "use default". */
16
31
  multiDiffSummaryThreshold?: number | undefined;
@@ -29,5 +44,6 @@ export declare const Entry: React.MemoExoticComponent<({ entry, termWidth, termH
29
44
  * Default: false.
30
45
  */
31
46
  showSageMemoryInject?: boolean | undefined;
47
+ toolResultViewMode?: ToolResultViewMode | undefined;
32
48
  }) => React.ReactElement | null>;
33
49
  //# sourceMappingURL=entry.d.ts.map
@@ -16,5 +16,5 @@ export { AssistantStreamBox, assistantStreamBoxHeight, countLines, firstNonEmpty
16
16
  * primitives or stable reducer references, so default shallow
17
17
  * comparison is sufficient.
18
18
  */
19
- export declare const History: React.MemoExoticComponent<({ entries, generation, toolStream, setSuggestions, autonomyMode, multiDiffSummaryThreshold, todos, showModelReasoning, showSageMemoryInject, }: HistoryProps) => React.ReactElement>;
19
+ export declare const History: React.MemoExoticComponent<({ entries, generation, toolStream, setSuggestions, autonomyMode, nextStepsAutoSubmitLabel, nextStepsAutoSubmitDeadlineMs, multiDiffSummaryThreshold, todos, showModelReasoning, showSageMemoryInject, toolResultViewMode, toolResultViewOverrides, }: HistoryProps) => React.ReactElement>;
20
20
  //# sourceMappingURL=index.d.ts.map
@@ -1,17 +1,16 @@
1
1
  import type React from 'react';
2
+ /** Header the SAGE inject panel and its height estimator share. */
3
+ export declare function sageInjectTitle(toolName: string, count: number, stats?: string): string;
2
4
  /**
3
5
  * Compact magenta-bordered panel rendering SAGE memory-injection lines
4
6
  * appended to a tool result. Renders nothing when `sageLines` is empty.
5
7
  *
6
8
  * Layout:
7
9
  *
8
- * ┌ 🧠 SAGE MEMORY INJECTED · <tool> N memories ────────────────┐
9
- * │ [kind][importance] │
10
- * │ text body (wrapped to panel width) │
11
- * │ id: mem_… anchor: pkg/path relation: about_file │
12
- * │ tags: t1, t2, t3 │
13
- * │ [next memory …] │
14
- * └────────────────────────────────────────────────────────────────────┘
10
+ * │ 🧠 SAGE · <tool> N memories
11
+ * │ [kind][importance]
12
+ * │ text body
13
+ * │ id / anchor / relation / tags
15
14
  *
16
15
  * Each memory is parsed by `parseSageMemoryLine` and rendered as key/value
17
16
  * rows instead of the raw `[kind] <memory id="…">text</memory> (anchor) […]`
@@ -24,8 +23,7 @@ export declare function SageMemoryBlock({ sageLines, toolName, stats, compact, }
24
23
  stats?: string | undefined;
25
24
  /**
26
25
  * Compact (opt-in): one magenta chip with count + first preview.
27
- * Full (default): bordered panel with every memory row. The bordered form
28
- * is the canonical SAGE surface and is asserted by every render test in
26
+ * Full (default): left-rail panel with every memory row. Asserted by
29
27
  * `tests/glob-sage-render.test.tsx` and `tests/sage-memory-block-render.test.tsx`.
30
28
  */
31
29
  compact?: boolean | undefined;
@@ -34,6 +34,11 @@ export interface HistoryScrollController {
34
34
  * coordinate contract.
35
35
  */
36
36
  hasCopyTargetAt(row: number, col: number): boolean;
37
+ /**
38
+ * True when the viewport cell lands on a tool card's inspect icon.
39
+ * Same `row`/`col` contract as {@link hasCopyTargetAt}.
40
+ */
41
+ hasInspectTargetAt(row: number, col: number): boolean;
37
42
  /**
38
43
  * Handle a left-click inside the history viewport. `row` is 0-based from the
39
44
  * viewport top; `col` is 0-based from the LEFT EDGE OF THE HISTORY BAND, not
@@ -45,6 +50,16 @@ export interface HistoryScrollController {
45
50
  * entry id is returned; otherwise returns null.
46
51
  */
47
52
  copyAtViewportCell(row: number, col: number): Promise<number | null>;
53
+ /**
54
+ * Resolve the inspect target for the cell, or null on a miss.
55
+ * The host opens the overlay; the view loads the full payload from live state.
56
+ */
57
+ inspectAtViewportCell(row: number, col: number): {
58
+ entryId: number;
59
+ entryIds?: readonly number[];
60
+ } | null;
61
+ /** Activate a committed tool card's −/+ control. */
62
+ activateToolViewControlAt(row: number, col: number): boolean;
48
63
  /**
49
64
  * Begin a drag-to-select gesture at `row`,`col` (history-band viewport cell).
50
65
  * The cell must be inside a card's row range and inside the rendered band
@@ -66,7 +81,7 @@ export interface HistoryScrollController {
66
81
  * Copy contract (block-based): the selection rect only decides WHICH
67
82
  * blocks it touches — every touched block is copied in full via
68
83
  * copyableTextForEntry (the same payload the copy icon writes), so inline
69
- * chrome (card gutter, `👤 USER ` label, `ℹ ` icon) neither leaks into
84
+ * chrome (card gutter, `▸ USER ` label, `· ` icon) neither leaks into
70
85
  * nor offsets the payload. Columns still drive the GESTURE: band bounds
71
86
  * (`isOutOfBand`), the highlight band, and the drag-vs-click distinction
72
87
  * (a zero-size selection commits nothing).
@@ -1,8 +1,7 @@
1
1
  /**
2
- * Right-edge rail for the managed viewport. Its first column is the copy icon
3
- * (or blank), the second is a visual gap, and the third is the scrollbar track.
4
- * All three columns are always reserved, so copy affordances and scrollability
5
- * never reflow chat content.
2
+ * Right-edge rail for the managed viewport: copy, inspect, selection band,
3
+ * and scrollbar track. Columns are always reserved, so affordances never
4
+ * reflow chat content.
6
5
  *
7
6
  * The gap column doubles as the drag-selection highlight band: while a drag is
8
7
  * in progress, the subscribing component re-renders ONLY this rail (via