@wrongstack/tui 0.289.0 → 0.291.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-initial-state.d.ts +5 -1
- package/dist/app-initial-state.d.ts.map +1 -1
- package/dist/app-props.d.ts +6 -0
- package/dist/app-props.d.ts.map +1 -1
- package/dist/app-reducer.d.ts.map +1 -1
- package/dist/app-state.d.ts +50 -0
- package/dist/app-state.d.ts.map +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.d.ts.map +1 -1
- package/dist/components/agents-monitor.d.ts.map +1 -1
- package/dist/components/animation-style.d.ts +29 -0
- package/dist/components/animation-style.d.ts.map +1 -1
- package/dist/components/clear-confirm-panel.d.ts +35 -0
- package/dist/components/clear-confirm-panel.d.ts.map +1 -0
- package/dist/components/confirm-prompt.d.ts +2 -1
- package/dist/components/confirm-prompt.d.ts.map +1 -1
- package/dist/components/exit-confirm-panel.d.ts +28 -0
- package/dist/components/exit-confirm-panel.d.ts.map +1 -0
- package/dist/components/fleet-panel.d.ts +1 -0
- package/dist/components/fleet-panel.d.ts.map +1 -1
- package/dist/components/history/banner.d.ts.map +1 -1
- package/dist/components/history/index.d.ts.map +1 -1
- package/dist/components/history/types.d.ts +6 -0
- package/dist/components/history/types.d.ts.map +1 -1
- package/dist/components/kanban-panel.d.ts +72 -1
- package/dist/components/kanban-panel.d.ts.map +1 -1
- package/dist/components/phase-panel.d.ts.map +1 -1
- package/dist/components/plugin-picker.d.ts.map +1 -1
- package/dist/components/scrollable-history.d.ts.map +1 -1
- package/dist/components/settings-picker.d.ts +1 -1
- package/dist/components/settings-picker.d.ts.map +1 -1
- package/dist/components/slash-confirm-panel.d.ts +14 -0
- package/dist/components/slash-confirm-panel.d.ts.map +1 -0
- package/dist/components/status-bar-chips.d.ts.map +1 -1
- package/dist/components/status-bar.d.ts +5 -1
- package/dist/components/status-bar.d.ts.map +1 -1
- package/dist/components/statusline-picker.d.ts +6 -4
- package/dist/components/statusline-picker.d.ts.map +1 -1
- package/dist/components/tools-picker.d.ts.map +1 -1
- package/dist/exit-slash.d.ts +40 -0
- package/dist/exit-slash.d.ts.map +1 -0
- package/dist/f-key-panels.d.ts +3 -1
- package/dist/f-key-panels.d.ts.map +1 -1
- package/dist/heap-watchdog.d.ts.map +1 -1
- package/dist/height-cache.d.ts +5 -0
- package/dist/height-cache.d.ts.map +1 -1
- package/dist/history-retention.d.ts +20 -0
- package/dist/history-retention.d.ts.map +1 -0
- package/dist/hooks/use-auth-panel.d.ts +2 -0
- package/dist/hooks/use-auth-panel.d.ts.map +1 -1
- package/dist/hooks/use-director-fleet-bridge.d.ts +0 -4
- package/dist/hooks/use-director-fleet-bridge.d.ts.map +1 -1
- package/dist/hooks/use-exit-command.d.ts +55 -0
- package/dist/hooks/use-exit-command.d.ts.map +1 -0
- package/dist/hooks/use-session-interrupt-controller.d.ts +7 -0
- package/dist/hooks/use-session-interrupt-controller.d.ts.map +1 -1
- package/dist/hooks/use-subagent-events.d.ts +0 -3
- package/dist/hooks/use-subagent-events.d.ts.map +1 -1
- package/dist/hooks/use-tui-activity.d.ts +2 -0
- package/dist/hooks/use-tui-activity.d.ts.map +1 -1
- package/dist/hooks/use-tui-event-bridge.d.ts +1 -0
- package/dist/hooks/use-tui-event-bridge.d.ts.map +1 -1
- package/dist/index.js +4660 -1935
- package/dist/index.js.map +4 -4
- package/dist/input-validation.d.ts +263 -0
- package/dist/input-validation.d.ts.map +1 -0
- package/dist/kanban-audit.d.ts +116 -0
- package/dist/kanban-audit.d.ts.map +1 -0
- package/dist/kanban-slash.d.ts +178 -0
- package/dist/kanban-slash.d.ts.map +1 -0
- package/dist/run-tui.d.ts +11 -0
- package/dist/run-tui.d.ts.map +1 -1
- package/dist/submit-history.d.ts +9 -6
- package/dist/submit-history.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central input validation for the TUI.
|
|
3
|
+
*
|
|
4
|
+
* EVERY external data source that crosses a trust boundary passes through
|
|
5
|
+
* this module. Uses an **allow-list** approach: define the exact allowed
|
|
6
|
+
* shape, types, ranges, and formats for each input; reject everything else.
|
|
7
|
+
*
|
|
8
|
+
* ── Trust boundaries ─────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* Boundary | Source | Validator
|
|
11
|
+
* ------------------|---------------------|--------------------------
|
|
12
|
+
* stdin | terminal emulator | validateStdinFragment()
|
|
13
|
+
* paste | bracketed-paste | validatePasteContent()
|
|
14
|
+
* AppProps | CLI host | validateAppProps()
|
|
15
|
+
* Reducer Action | handleKey dispatch | validateAction()
|
|
16
|
+
* Host callbacks | CLI/plugin return | validateCallbackReturn()
|
|
17
|
+
* Mouse events | SGR protocol | validateMouseEvent()
|
|
18
|
+
* Fleet telemetry | Director/FleetBus | validateFleetEntry()
|
|
19
|
+
* Restore data | session JSONL | validateRestoreEntry()
|
|
20
|
+
*
|
|
21
|
+
* ── Rejection over coercion ──────────────────────────────────────────
|
|
22
|
+
*
|
|
23
|
+
* This module **rejects** invalid input rather than coercing it, for
|
|
24
|
+
* three reasons:
|
|
25
|
+
*
|
|
26
|
+
* 1. **Coercion masks injection.** Trimming whitespace from a string
|
|
27
|
+
* that contains an ANSI escape sequence leaves the sequence intact.
|
|
28
|
+
* 2. **Semantic gap.** Coercing a value (e.g. clamping a PID to a valid
|
|
29
|
+
* range) creates a gap between what was validated and what the
|
|
30
|
+
* application actually uses — a future code path may use the raw
|
|
31
|
+
* value, not the clamped one.
|
|
32
|
+
* 3. **Predictable failure.** Silent data transformation makes security
|
|
33
|
+
* debugging harder — rejection produces a clear error at the point
|
|
34
|
+
* of violation; coercion produces subtly wrong behavior downstream.
|
|
35
|
+
*
|
|
36
|
+
* The single exception is **normalization before comparison**: trimming
|
|
37
|
+
* whitespace from an enum label before comparing it to the allow-list
|
|
38
|
+
* prevents trivial bypasses (e.g. `" off "` vs `"off"`). This is
|
|
39
|
+
* NOT the same as coercing the input — the normalized comparison only
|
|
40
|
+
* determines allow/deny; the caller receives the original value.
|
|
41
|
+
*
|
|
42
|
+
* ── Conventions ──────────────────────────────────────────────────────
|
|
43
|
+
*
|
|
44
|
+
* - Every `validate*` function returns `ValidationResult<T>`:
|
|
45
|
+
* `{ valid: true; value: T }` — the validated, ready-to-use value
|
|
46
|
+
* `{ valid: false; error: string }` — a specific, actionable message
|
|
47
|
+
* - Every error message follows the pattern:
|
|
48
|
+
* `"{path}": {what went wrong} ({detail}).`
|
|
49
|
+
* - Size limits are defined as module-level constants with doc comments.
|
|
50
|
+
* - Enum/union allow-lists are defined as `const` arrays and kept in
|
|
51
|
+
* ONE place so the allowed set is auditable.
|
|
52
|
+
*/
|
|
53
|
+
export interface ValidationOk<T> {
|
|
54
|
+
valid: true;
|
|
55
|
+
value: T;
|
|
56
|
+
}
|
|
57
|
+
export interface ValidationError {
|
|
58
|
+
valid: false;
|
|
59
|
+
error: string;
|
|
60
|
+
}
|
|
61
|
+
export type ValidationResult<T> = ValidationOk<T> | ValidationError;
|
|
62
|
+
/** Maximum length of a single input line from stdin (before Enter). */
|
|
63
|
+
export declare const MAX_INPUT_BUFFER_CHARS = 100000;
|
|
64
|
+
/** Maximum size of a single paste operation (prevents OOM from 10MB paste). */
|
|
65
|
+
export declare const MAX_PASTE_CHARS = 50000;
|
|
66
|
+
/** Maximum length of a single history entry text payload. */
|
|
67
|
+
export declare const MAX_ENTRY_TEXT_CHARS = 500000;
|
|
68
|
+
/** Maximum entries in the history array (matches history-retention.ts). */
|
|
69
|
+
export declare const MAX_HISTORY_ENTRIES = 400;
|
|
70
|
+
/** Maximum actions in a fleetBatch dispatch (prevents render-thrashing). */
|
|
71
|
+
export declare const MAX_BATCHED_ACTIONS = 500;
|
|
72
|
+
/** Maximum length of a single action payload string field. */
|
|
73
|
+
export declare const MAX_ACTION_STRING_FIELD = 10000;
|
|
74
|
+
/** Maximum subagents tracked in fleet state. */
|
|
75
|
+
export declare const MAX_FLEET_ENTRIES = 200;
|
|
76
|
+
/** Maximum tools tracked per-subagent. */
|
|
77
|
+
export declare const MAX_RECENT_TOOLS = 100;
|
|
78
|
+
/** Maximum messages tracked per-subagent. */
|
|
79
|
+
export declare const MAX_RECENT_MESSAGES = 50;
|
|
80
|
+
/** Maximum depth of nested action payload objects. */
|
|
81
|
+
export declare const MAX_ACTION_DEPTH = 8;
|
|
82
|
+
/** Maximum characters in a paste fragment (single stdin chunk). */
|
|
83
|
+
export declare const MAX_PASTE_FRAGMENT_CHARS = 100000;
|
|
84
|
+
/** Maximum length for any host-provided string (model, provider, etc). */
|
|
85
|
+
export declare const MAX_HOST_STRING_FIELD = 500;
|
|
86
|
+
/** Maximum entries in a picker matches list. */
|
|
87
|
+
export declare const MAX_PICKER_MATCHES = 500;
|
|
88
|
+
/** Known reducer action types — the authoritative allow-list. */
|
|
89
|
+
export declare const ALLOWED_ACTION_TYPES: readonly ['addEntry', 'compactHistory', 'setBuffer', 'clearInput', 'clearHistory', 'replaceHistory', 'replaceEntry', 'historyRoll', 'setHistoryIndex', 'setHistoryDraft', 'scrollBy', 'scrollTo', 'scrollPage', 'scrollToBottom', 'scrollToTop', 'setMeasuredLines', 'setViewportRows', 'pickerOpen', 'pickerMove', 'pickerClose', 'pickerQuery', 'pickerSetMatches', 'slashPickerOpen', 'slashPickerMove', 'slashPickerClose', 'slashPickerQuery', 'slashPickerSetMatches', 'modelPickerOpen', 'modelPickerMove', 'modelPickerClose', 'modelPickerSelect', 'modelPickerSetProviders', 'autonomyPickerOpen', 'autonomyPickerMove', 'autonomyPickerClose', 'autonomyPickerSelect', 'cancelSend', 'submit', 'afterSubmit', 'streamStart', 'streamToken', 'streamToolUse', 'streamToolResult', 'streamEnd', 'setMode', 'toggleHelp', 'toggleMonitor', 'toggleAgentsMonitor', 'toggleTodosMonitor', 'toggleQueuePanel', 'toggleProcessList', 'toggleCronMonitor', 'toggleAuditPanel', 'togglePlanPanel', 'closeAllPanels', 'toggleKanbanPanel', 'toggleGoalPanel', 'toggleGoalKanbanPanel', 'toggleContextPanel', 'toggleSessionsPanel', 'sessionsPanelSet', 'sessionsPanelMove', 'sessionsPanelBusy', 'sessionResumeConfirmSet', 'sessionResumeConfirmClear', 'settingsPickerOpen', 'settingsPickerMove', 'settingsPickerClose', 'settingsPick', 'statuslinePickerOpen', 'statuslinePickerMove', 'statuslinePickerClose', 'pluginPickerOpen', 'pluginPickerMove', 'pluginPickerClose', 'mcpPickerOpen', 'mcpPickerMove', 'mcpPickerClose', 'toolsPickerOpen', 'toolsPickerMove', 'toolsPickerClose', 'projectPickerOpen', 'projectPickerMove', 'projectPickerClose', 'projectPickerSelect', 'projectPickerSetItems', 'fKeyPickerOpen', 'fKeyPickerMove', 'fKeyPickerClose', 'confirmPromptOpen', 'confirmPromptClose', 'clearConfirmOpen', 'clearConfirmClose', 'clearConfirmSetValue', 'exitConfirmOpen', 'exitConfirmClose', 'slashConfirmOpen', 'slashConfirmClose', 'escConfirmOpen', 'escConfirmClose', 'sendModePickerOpen', 'sendModePickerMove', 'sendModePickerClose', 'resetContextChip', 'fleetSeed', 'fleetSpawn', 'fleetToolStart', 'fleetToolEnd', 'fleetStart', 'fleetDelta', 'fleetMessage', 'fleetTool', 'fleetUsage', 'fleetDone', 'fleetRemove', 'fleetBudgetWarning', 'fleetBudgetExtended', 'fleetCtxPct', 'fleetCost', 'fleetConcurrency', 'fleetBatch', 'leaderIterStart', 'leaderIterEnd', 'leaderToolStart', 'leaderToolEnd', 'leaderCtxPct', 'setFleetChat', 'enhanceOpen', 'enhanceClose', 'enhanceBusy', 'enhanceBusyDone', 'enhanceResult', 'enhanceCancel', 'enhanceDismiss', 'refineFailureOpen', 'refineFailureClose', 'continueConfirmOpen', 'continueConfirmClose', 'checkpointReceived', 'rewindOverlayOpen', 'rewindOverlayClose', 'rewindOverlayMove', 'setDeadline', 'setLiveProvider', 'setLiveModel', 'setCapability', 'setDesignKit', 'setAnimationStyle', 'setEffectiveMaxContext', 'setStreamingText', 'setToolStream', 'toolStreamClear', 'setThinkingWord', 'setFleetVerbosity', 'setAutonomyMode', 'setSendMode', 'setSteering', 'setSteeringPending', 'clearSteering', 'setGoalText', 'setGoalPanelGoal', 'showGoal', 'goalSummary', 'goalRunInit', 'goalRunPhaseUpdate', 'goalRunRunningPhases', 'goalRunElapsed', 'goalRunTaskActive', 'goalRunMonitorToggle', 'goalRunReset', 'sddBoardSnapshot', 'toggleSddBoardMonitor', 'sddBoardFocusNext', 'sddBoardFocusPrev', 'worktreeUpsert', 'worktreeRemove', 'toggleWorktreeMonitor', 'collabBugFound', 'collabPlanEmitted', 'collabEvalComplete', 'collabSessionDone', 'collabSubagentSpawned', 'debugStreamStats', 'debugStreamStatsClear', 'countdownTick', 'countdownEnded', 'coordinatorEvent', 'toggleCoordinatorMonitor', 'setEnhancedPrompt', 'enhanceConfirmOpen', 'enhanceConfirmClose', 'setShowModelReasoning'];
|
|
90
|
+
export type AllowedActionType = (typeof ALLOWED_ACTION_TYPES)[number];
|
|
91
|
+
/** History entry kinds that carry text (must be non-empty to be stored). */
|
|
92
|
+
export declare const TEXT_BEARING_ENTRY_KINDS: Set<string>;
|
|
93
|
+
/** All valid history entry kinds. */
|
|
94
|
+
export declare const ALLOWED_ENTRY_KINDS: Set<string>;
|
|
95
|
+
/** Fleet subagent status values. */
|
|
96
|
+
export declare const ALLOWED_FLEET_STATUSES: Set<string>;
|
|
97
|
+
/** Mouse event kinds */
|
|
98
|
+
export declare const ALLOWED_MOUSE_KINDS: Set<string>;
|
|
99
|
+
/** Mouse buttons */
|
|
100
|
+
export declare const ALLOWED_MOUSE_BUTTONS: Set<string>;
|
|
101
|
+
/** Keyboard modifier keys. */
|
|
102
|
+
export declare const ALLOWED_KEY_EVENT_FIELDS: Set<string>;
|
|
103
|
+
/** Scroll direction for scrollPage. */
|
|
104
|
+
export declare const ALLOWED_SCROLL_DIRS: Set<string>;
|
|
105
|
+
/** Autonomy mode values. */
|
|
106
|
+
export declare const ALLOWED_AUTONOMY_MODES: Set<string>;
|
|
107
|
+
/** Known send modes. */
|
|
108
|
+
export declare const ALLOWED_SEND_MODES: Set<string>;
|
|
109
|
+
/** FleetChat verbosity modes. */
|
|
110
|
+
export declare const ALLOWED_FLEET_CHAT_MODES: Set<string>;
|
|
111
|
+
/** SDD lifecycle ops. */
|
|
112
|
+
export declare const ALLOWED_SDD_OPS: Set<string>;
|
|
113
|
+
/** Project picker item kinds. */
|
|
114
|
+
export declare const ALLOWED_PICKER_KINDS: Set<string>;
|
|
115
|
+
/** Collaboration verdicts. */
|
|
116
|
+
export declare const ALLOWED_COLLAB_VERDICTS: Set<string>;
|
|
117
|
+
/** Known terminal capability keys. */
|
|
118
|
+
export declare const ALLOWED_CAPABILITY_FIELDS: Set<string>;
|
|
119
|
+
/**
|
|
120
|
+
* Validate a keyboard input fragment from stdin.
|
|
121
|
+
*
|
|
122
|
+
* This catches raw bytes from the terminal BEFORE they enter the input
|
|
123
|
+
* buffer. The allow-list is: known control sequences, printable ASCII,
|
|
124
|
+
* and common Unicode (which terminals emit as normal keystrokes).
|
|
125
|
+
*
|
|
126
|
+
* Rejects:
|
|
127
|
+
* - Raw escape sequences that don't match known patterns
|
|
128
|
+
* - Fragments containing C0 control chars other than tab/newline (0x09, 0x0a)
|
|
129
|
+
* - Fragments longer than MAX_PASTE_FRAGMENT_CHARS (likely a flood)
|
|
130
|
+
*/
|
|
131
|
+
export declare function validateStdinFragment(data: Buffer | string): ValidationResult<string>;
|
|
132
|
+
/**
|
|
133
|
+
* Validate a fully-assembled paste payload AFTER the terminal markers
|
|
134
|
+
* have been stripped by paste-accumulator.ts.
|
|
135
|
+
*
|
|
136
|
+
* Rejects:
|
|
137
|
+
* - Paste > MAX_PASTE_CHARS
|
|
138
|
+
* - Paste containing C0 control chars (except \n, \t)
|
|
139
|
+
* - Paste that looks binary (>30% non-printable)
|
|
140
|
+
* - Paste with unbalanced surrogate pairs
|
|
141
|
+
*/
|
|
142
|
+
export declare function validatePasteContent(content: string): ValidationResult<string>;
|
|
143
|
+
/**
|
|
144
|
+
* Validate a parsed mouse event from the SGR protocol.
|
|
145
|
+
*
|
|
146
|
+
* Rejects:
|
|
147
|
+
* - Unknown mouse event kinds
|
|
148
|
+
* - Unknown button values
|
|
149
|
+
* - Coordinates outside reasonable terminal bounds (0 < x < 5000, 0 < y < 5000)
|
|
150
|
+
* - Wheel values other than -1, 0, 1
|
|
151
|
+
*/
|
|
152
|
+
export declare function validateMouseEvent(event: {
|
|
153
|
+
kind: string;
|
|
154
|
+
button: string;
|
|
155
|
+
x: number;
|
|
156
|
+
y: number;
|
|
157
|
+
wheel: number;
|
|
158
|
+
shift?: boolean;
|
|
159
|
+
meta?: boolean;
|
|
160
|
+
ctrl?: boolean;
|
|
161
|
+
motion?: boolean;
|
|
162
|
+
}): ValidationResult<{
|
|
163
|
+
kind: string;
|
|
164
|
+
button: string;
|
|
165
|
+
x: number;
|
|
166
|
+
y: number;
|
|
167
|
+
wheel: number;
|
|
168
|
+
shift: boolean;
|
|
169
|
+
meta: boolean;
|
|
170
|
+
ctrl: boolean;
|
|
171
|
+
motion: boolean;
|
|
172
|
+
}>;
|
|
173
|
+
/**
|
|
174
|
+
* Validate a KeyEvent from stdin against the known allow-list.
|
|
175
|
+
*
|
|
176
|
+
* Every boolean field in a KeyEvent must be exactly `true` or `false`.
|
|
177
|
+
* `fn` must be 1–12 when present. `wheelDeltaY` must be an integer.
|
|
178
|
+
*
|
|
179
|
+
* Rejects:
|
|
180
|
+
* - Unknown fields on the key event object
|
|
181
|
+
* - Non-boolean values for boolean fields
|
|
182
|
+
* - fn outside 1–12
|
|
183
|
+
* - wheelDeltaY that is not an integer
|
|
184
|
+
*/
|
|
185
|
+
export declare function validateKeyEventFields(key: Record<string, unknown>): ValidationResult<Record<string, unknown>>;
|
|
186
|
+
/**
|
|
187
|
+
* Validate a reducer Action before dispatch.
|
|
188
|
+
*
|
|
189
|
+
* This is the central gate for all state mutations. EVERY dispatch from
|
|
190
|
+
* external sources (keyboard, host callbacks, fleet events, restore data)
|
|
191
|
+
* should run through this before reaching the reducer.
|
|
192
|
+
*
|
|
193
|
+
* Rejects:
|
|
194
|
+
* - Unknown action types
|
|
195
|
+
* - Malformed payload types
|
|
196
|
+
* - Oversized strings/arrays
|
|
197
|
+
* - Out-of-range numeric values
|
|
198
|
+
* - Deeply nested payloads (prototype poison / DoS)
|
|
199
|
+
*/
|
|
200
|
+
export declare function validateAction(action: {
|
|
201
|
+
type: string;
|
|
202
|
+
[key: string]: any;
|
|
203
|
+
}): ValidationResult<Record<string, unknown>>;
|
|
204
|
+
/**
|
|
205
|
+
* Validate a fleet subagent entry from telemetry.
|
|
206
|
+
*
|
|
207
|
+
* Rejects:
|
|
208
|
+
* - Unknown status
|
|
209
|
+
* - Oversized streamingText
|
|
210
|
+
* - Malformed recent tools/messages arrays
|
|
211
|
+
* - Non-finite cost
|
|
212
|
+
*/
|
|
213
|
+
export declare function validateFleetEntry(entry: {
|
|
214
|
+
id: string;
|
|
215
|
+
name: string;
|
|
216
|
+
status: string;
|
|
217
|
+
streamingText?: string;
|
|
218
|
+
iterations?: number;
|
|
219
|
+
toolCalls?: number;
|
|
220
|
+
recentTools?: unknown[];
|
|
221
|
+
recentMessages?: unknown[];
|
|
222
|
+
cost?: number;
|
|
223
|
+
}): ValidationResult<Record<string, unknown>>;
|
|
224
|
+
/**
|
|
225
|
+
* Validate a restored session entry (from JSONL).
|
|
226
|
+
*
|
|
227
|
+
* Rejects:
|
|
228
|
+
* - Non-object entries
|
|
229
|
+
* - Unknown entry kinds
|
|
230
|
+
* - Oversized payloads
|
|
231
|
+
*/
|
|
232
|
+
export declare function validateRestoreEntry(entry: unknown, index: number): ValidationResult<Record<string, unknown>>;
|
|
233
|
+
/**
|
|
234
|
+
* Validate that an action dispatch is safe for the reducer.
|
|
235
|
+
* This is a convenience wrapper used at the dispatch boundary.
|
|
236
|
+
* Returns the original action untouched if valid, or throws with the
|
|
237
|
+
* specific error message if invalid.
|
|
238
|
+
*
|
|
239
|
+
* In production, the caller can log the error and drop the action
|
|
240
|
+
* instead of crashing — but for security boundaries, throwing is the
|
|
241
|
+
* safe default because it prevents any partially-valid state from
|
|
242
|
+
* reaching the reducer.
|
|
243
|
+
*/
|
|
244
|
+
export declare function ensureValidAction(action: {
|
|
245
|
+
type: string;
|
|
246
|
+
[key: string]: any;
|
|
247
|
+
}): Record<string, unknown>;
|
|
248
|
+
/**
|
|
249
|
+
* Safe dispatch wrapper: validates the action, then calls dispatch
|
|
250
|
+
* only if validation passes. Returns true if dispatched, false if rejected
|
|
251
|
+
* (with the error logged).
|
|
252
|
+
*/
|
|
253
|
+
export declare function safeDispatch(action: {
|
|
254
|
+
type: string;
|
|
255
|
+
[key: string]: any;
|
|
256
|
+
}, dispatch: (action: Record<string, unknown>) => void): boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Normalize a string by trimming whitespace.
|
|
259
|
+
* This is the ONLY normalization we do — and it's only used for
|
|
260
|
+
* allow-list comparison, never for the returned value.
|
|
261
|
+
*/
|
|
262
|
+
export declare function normalizedEquals(a: string, b: string): boolean;
|
|
263
|
+
//# sourceMappingURL=input-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-validation.d.ts","sourceRoot":"","sources":["../src/input-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `kanban-audit.ts` — Pure, dependency-free Kanban Cleaner audit for the TUI.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the WebUI's `packages/webui/src/lib/kanban-cleaner.ts` but
|
|
5
|
+
* lives in the TUI package so we don't have to thread `@wrongstack/webui`
|
|
6
|
+
* (which depends on React) into a server-side TUI pipeline. The two
|
|
7
|
+
* implementations intentionally share the same audit-code vocabulary
|
|
8
|
+
* (10 codes) and severity levels (`error`, `warning`) so a board audited
|
|
9
|
+
* from either surface produces the same findings.
|
|
10
|
+
*
|
|
11
|
+
* The output of `auditKanbanBoard()` feeds the TUI panel header badge so
|
|
12
|
+
* users see Cleaner warnings inline — mirroring the WebUI's
|
|
13
|
+
* `KanbanCleanerAlert` component, but inline-pinned to the project root.
|
|
14
|
+
*/
|
|
15
|
+
import type { KanbanBoard } from '@wrongstack/kanban';
|
|
16
|
+
export type KanbanAuditSeverity = 'error' | 'warning';
|
|
17
|
+
export type KanbanAuditIssueCode = 'missing-description' | 'missing-assignee' | 'missing-due-date' | 'missing-labels' | 'missing-subtasks' | 'missing-success-criteria' | 'skipped-lifecycle-state' | 'abandoned-running-task' | 'stale-running-task' | 'stale-review';
|
|
18
|
+
export interface KanbanAuditIssue {
|
|
19
|
+
id: string;
|
|
20
|
+
taskId: string;
|
|
21
|
+
taskTitle: string;
|
|
22
|
+
code: KanbanAuditIssueCode;
|
|
23
|
+
severity: KanbanAuditSeverity;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
export interface KanbanAuditSummary {
|
|
27
|
+
/** Wall-clock time the audit ran (ISO-8601). Used by the renderer to
|
|
28
|
+
* label the report and by tests to pin the audit's age. */
|
|
29
|
+
generatedAt: string;
|
|
30
|
+
/** IDs of every board this summary aggregated across. */
|
|
31
|
+
boardIds: readonly string[];
|
|
32
|
+
/** Aggregate severity counters. */
|
|
33
|
+
counts: Readonly<Record<KanbanAuditSeverity, number>>;
|
|
34
|
+
/** Detail buckets surfaced by the WebUI Cleaner `KanbanCleanerAlert`.
|
|
35
|
+
* Each entry lists the tasks that triggered the signal. */
|
|
36
|
+
dependencyBlocked: {
|
|
37
|
+
count: number;
|
|
38
|
+
tasks: ReadonlyArray<{
|
|
39
|
+
board: {
|
|
40
|
+
title: string;
|
|
41
|
+
};
|
|
42
|
+
task: {
|
|
43
|
+
title: string;
|
|
44
|
+
};
|
|
45
|
+
}>;
|
|
46
|
+
};
|
|
47
|
+
staleAssignments: {
|
|
48
|
+
count: number;
|
|
49
|
+
tasks: ReadonlyArray<{
|
|
50
|
+
board: {
|
|
51
|
+
title: string;
|
|
52
|
+
};
|
|
53
|
+
task: {
|
|
54
|
+
title: string;
|
|
55
|
+
};
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
58
|
+
failedRetryable: {
|
|
59
|
+
count: number;
|
|
60
|
+
tasks: ReadonlyArray<{
|
|
61
|
+
board: {
|
|
62
|
+
title: string;
|
|
63
|
+
};
|
|
64
|
+
task: {
|
|
65
|
+
title: string;
|
|
66
|
+
};
|
|
67
|
+
}>;
|
|
68
|
+
};
|
|
69
|
+
heartbeatDue: {
|
|
70
|
+
count: number;
|
|
71
|
+
tasks: ReadonlyArray<{
|
|
72
|
+
board: {
|
|
73
|
+
title: string;
|
|
74
|
+
};
|
|
75
|
+
task: {
|
|
76
|
+
title: string;
|
|
77
|
+
};
|
|
78
|
+
}>;
|
|
79
|
+
};
|
|
80
|
+
/** Activity stamps for last dispatch / last recovery. */
|
|
81
|
+
lastDispatchedAt: string | undefined;
|
|
82
|
+
lastStaleRecoveredAt: string | undefined;
|
|
83
|
+
/** All issues raised by the audit, sorted error-first. */
|
|
84
|
+
issues: readonly KanbanAuditIssue[];
|
|
85
|
+
/** Distinct task IDs across `issues`. */
|
|
86
|
+
affectedTaskCount: number;
|
|
87
|
+
}
|
|
88
|
+
export interface KanbanAuditOptions {
|
|
89
|
+
/** Explicit time input keeps the audit pure, deterministic, and unit-testable. */
|
|
90
|
+
now: number | Date;
|
|
91
|
+
/** IDs or names of agents currently reported as running by the WebUI fleet roster. */
|
|
92
|
+
liveAgentIdentities?: ReadonlySet<string> | readonly string[] | undefined;
|
|
93
|
+
/** Enable only when the shared Kanban task contract exposes a due date. */
|
|
94
|
+
requireDueDate?: boolean | undefined;
|
|
95
|
+
/** Used when the board has no review-age policy. */
|
|
96
|
+
defaultReviewStaleAfterMs?: number | undefined;
|
|
97
|
+
}
|
|
98
|
+
export declare function auditKanbanBoard(board: KanbanBoard, options: KanbanAuditOptions): KanbanAuditSummary;
|
|
99
|
+
/**
|
|
100
|
+
* Short headline string used by the panel header badge. Returns null when
|
|
101
|
+
* the summary is empty so callers can hide the badge entirely instead of
|
|
102
|
+
* rendering a noisy "0 warnings" chip.
|
|
103
|
+
*/
|
|
104
|
+
export declare function summarizeAuditHeadline(summary: KanbanAuditSummary): string | null;
|
|
105
|
+
/**
|
|
106
|
+
* Top-N issues, biased toward `error` severity first. The TUI panel
|
|
107
|
+
* surfaces these inline; the full list is still available via
|
|
108
|
+
* `summary.issues` for tooling.
|
|
109
|
+
*
|
|
110
|
+
* We sort defensively (rather than rely on `auditKanbanBoard`'s in-place
|
|
111
|
+
* order) so callers that hand us a manually-built summary — for example
|
|
112
|
+
* a test fixture, or a future caller that aggregates issues from
|
|
113
|
+
* multiple boards — still get the error-first ordering.
|
|
114
|
+
*/
|
|
115
|
+
export declare function topAuditIssues(summary: KanbanAuditSummary, limit?: number): readonly KanbanAuditIssue[];
|
|
116
|
+
//# sourceMappingURL=kanban-audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-audit.d.ts","sourceRoot":"","sources":["../src/kanban-audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/kanban` — Open the project kanban panel and perform common board/task
|
|
3
|
+
* operations from the TUI composer.
|
|
4
|
+
*
|
|
5
|
+
* Subcommands (no subcommand → opens the kanban panel):
|
|
6
|
+
*
|
|
7
|
+
* /kanban — open the project kanban panel
|
|
8
|
+
* /kanban create <title> — create a new board, then open the panel
|
|
9
|
+
* /kanban add <title> [--column X] — add a task to the active board
|
|
10
|
+
* /kanban use <boardId|title|tag> — open the kanban panel on a specific board
|
|
11
|
+
* /kanban boards — list every board in the project
|
|
12
|
+
* /kanban health — show kanban queue health summary
|
|
13
|
+
* /kanban audit [boardId|all] — show kanban cleaner audit (KanbanAuditSummary)
|
|
14
|
+
* /kanban help — show usage
|
|
15
|
+
*
|
|
16
|
+
* The slash command intentionally mirrors the WebUI `kanban` surface so
|
|
17
|
+
* users can perform the same operations in either surface. The TUI panel
|
|
18
|
+
* remains the rich interactive view (navigate, move, assign); this command
|
|
19
|
+
* is the text-first equivalent for when the panel is closed.
|
|
20
|
+
*/
|
|
21
|
+
import type { SlashCommand } from '@wrongstack/core';
|
|
22
|
+
import type { KanbanBoundaryAccess, KanbanBoundaryPolicy, KanbanBoundarySelectorKind, KanbanBoardSummary, KanbanQueueHealth } from '@wrongstack/kanban';
|
|
23
|
+
import { type KanbanAuditSummary } from './kanban-audit.js';
|
|
24
|
+
/**
|
|
25
|
+
* Result of resolving the board that an `add` operation should target.
|
|
26
|
+
*
|
|
27
|
+
* `KanbanPanel` resolves the active board from the session tag
|
|
28
|
+
* (`session:<sessionId>`) and falls back to the most-recently-updated board.
|
|
29
|
+
* We mirror that exact precedence so a `/kanban add` issued while the panel
|
|
30
|
+
* is open lands on the same board the user is looking at.
|
|
31
|
+
*/
|
|
32
|
+
export interface KanbanAddTarget {
|
|
33
|
+
boardId: string;
|
|
34
|
+
boardTitle: string;
|
|
35
|
+
/** Column that the task will be placed in. Defaults to the first column. */
|
|
36
|
+
columnId: string;
|
|
37
|
+
}
|
|
38
|
+
export interface KanbanSlashDeps {
|
|
39
|
+
/**
|
|
40
|
+
* Absolute project root used by the kanban file store. Must match the
|
|
41
|
+
* value `KanbanPanel` reads from `agent.ctx.projectRoot` — using
|
|
42
|
+
* `agent.ctx.cwd` would diverge when the user launches the TUI from a
|
|
43
|
+
* subdirectory of the project.
|
|
44
|
+
*/
|
|
45
|
+
projectRoot: string;
|
|
46
|
+
/** Optional session id; when present, prefer a `session:<id>`-tagged board. */
|
|
47
|
+
sessionId?: string | null | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Panel-open bridge installed by `App`. Calling `onPanelOpen.current(...)`
|
|
50
|
+
* with `'toggleKanbanPanel'` opens the kanban panel — same mechanism the
|
|
51
|
+
* other slash commands use. When absent, slash command returns text-only.
|
|
52
|
+
*/
|
|
53
|
+
onPanelOpen?: {
|
|
54
|
+
current: ((action: string) => boolean) | null;
|
|
55
|
+
} | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Optional hook that selects a specific board in the kanban panel. Used
|
|
58
|
+
* by `/kanban use <boardId|title|tag>` and the Goal → Kanban bridge.
|
|
59
|
+
* The callback receives a board id and returns true on success.
|
|
60
|
+
*/
|
|
61
|
+
onBoardFocus?: {
|
|
62
|
+
current: ((boardId: string) => boolean) | null;
|
|
63
|
+
} | undefined;
|
|
64
|
+
/** Terminal width (cols) for help / boards table rendering. */
|
|
65
|
+
terminalWidth?: number | undefined;
|
|
66
|
+
}
|
|
67
|
+
export declare function createKanbanSlashCommand(deps: KanbanSlashDeps): SlashCommand;
|
|
68
|
+
export type ParsedKanbanArgs = {
|
|
69
|
+
kind: 'open';
|
|
70
|
+
} | {
|
|
71
|
+
kind: 'help';
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'boards';
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'create';
|
|
76
|
+
title: string;
|
|
77
|
+
} | {
|
|
78
|
+
kind: 'add';
|
|
79
|
+
title: string;
|
|
80
|
+
column: string | null;
|
|
81
|
+
} | {
|
|
82
|
+
kind: 'use';
|
|
83
|
+
query: string;
|
|
84
|
+
} | {
|
|
85
|
+
kind: 'health';
|
|
86
|
+
} | {
|
|
87
|
+
kind: 'audit';
|
|
88
|
+
boardQuery: string;
|
|
89
|
+
} | {
|
|
90
|
+
kind: 'boundary';
|
|
91
|
+
boardQuery: string;
|
|
92
|
+
action: 'show' | 'allow' | 'deny' | 'clear';
|
|
93
|
+
taskQuery?: string | undefined;
|
|
94
|
+
selectorKind?: KanbanBoundarySelectorKind | undefined;
|
|
95
|
+
access?: KanbanBoundaryAccess | undefined;
|
|
96
|
+
path?: string | undefined;
|
|
97
|
+
enforcement?: KanbanBoundaryPolicy['enforcement'] | undefined;
|
|
98
|
+
shellAccess?: KanbanBoundaryPolicy['shellAccess'] | undefined;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Parse the raw `args` string (everything after `/kanban`) into a tagged
|
|
102
|
+
* union. Exported so tests can exercise the grammar in isolation.
|
|
103
|
+
*
|
|
104
|
+
* Grammar (whitespace-separated; quoted titles supported for the `create`
|
|
105
|
+
* subcommand):
|
|
106
|
+
*
|
|
107
|
+
* args ::= ε | "help" | "boards" | "create" title
|
|
108
|
+
* | "add" title ("--" "column" columnId)?
|
|
109
|
+
* title ::= token+
|
|
110
|
+
* columnId ::= token
|
|
111
|
+
*/
|
|
112
|
+
export declare function parseKanbanArgs(raw: string): ParsedKanbanArgs;
|
|
113
|
+
/**
|
|
114
|
+
* Resolve the board that an `/kanban add` should target, matching the
|
|
115
|
+
* precedence used by `KanbanPanel`:
|
|
116
|
+
*
|
|
117
|
+
* 1. The board tagged with `session:<sessionId>` (when a session id is known).
|
|
118
|
+
* 2. The most-recently-updated board (sort by `updatedAt` desc).
|
|
119
|
+
*
|
|
120
|
+
* If `column` is given, prefer a column whose id or title matches. The match is
|
|
121
|
+
* case-insensitive and falls back to the first column when nothing matches.
|
|
122
|
+
*
|
|
123
|
+
* Exported so tests can validate the precedence rules against fixture boards.
|
|
124
|
+
*/
|
|
125
|
+
export declare function resolveAddTarget(deps: Pick<KanbanSlashDeps, 'projectRoot' | 'sessionId'>, column: string | null): Promise<KanbanAddTarget | null>;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve a user-supplied board query to a specific board. Three match
|
|
128
|
+
* strategies are tried in order:
|
|
129
|
+
*
|
|
130
|
+
* 1. **Exact id match** — fastest, never ambiguous.
|
|
131
|
+
* 2. **Exact title match** (case-insensitive) — what `/kanban use Sprint 2`
|
|
132
|
+
* should hit when the user types the title verbatim.
|
|
133
|
+
* 3. **Tag match** — for `goal:<text>` or `session:<id>` tags so the
|
|
134
|
+
* Goal → Kanban bridge can navigate by tag without knowing the id.
|
|
135
|
+
*
|
|
136
|
+
* Returns null when no summary matches. The caller can then surface a
|
|
137
|
+
* helpful error pointing at `/kanban boards`.
|
|
138
|
+
*/
|
|
139
|
+
export declare function resolveBoardByQuery(deps: Pick<KanbanSlashDeps, 'projectRoot'>, query: string): Promise<KanbanBoardSummary | null>;
|
|
140
|
+
/**
|
|
141
|
+
* Render the kanban queue health report as a compact markdown block.
|
|
142
|
+
* Surfaces the same Sprint-2 fields the WebUI's `KanbanView` shows
|
|
143
|
+
* (dependencyBlocked, staleAssignments, failedRetryable, heartbeatDue)
|
|
144
|
+
* plus per-status counts and the last dispatch / last recovery stamps.
|
|
145
|
+
*/
|
|
146
|
+
export declare function renderHealthReport(health: KanbanQueueHealth): string;
|
|
147
|
+
/**
|
|
148
|
+
* Render the Kanban Cleaner audit across the project. Mirrors the WebUI
|
|
149
|
+
* `KanbanCleanerAlert` vocabulary so a user running either surface gets
|
|
150
|
+
* the same findings.
|
|
151
|
+
*
|
|
152
|
+
* Two modes:
|
|
153
|
+
* - `boardQuery` empty → audit every board (per-board table).
|
|
154
|
+
* - `boardQuery` present → audit the single matching board, or report
|
|
155
|
+
* a clear "not found" error.
|
|
156
|
+
*
|
|
157
|
+
* Each board's row shows the issue counts (error · warning) and the
|
|
158
|
+
* top-3 issues, biasing toward error severity first.
|
|
159
|
+
*/
|
|
160
|
+
export declare function renderProjectAudit(deps: Pick<KanbanSlashDeps, 'projectRoot'>, boardQuery: string): Promise<string>;
|
|
161
|
+
/**
|
|
162
|
+
* Render a board-row table from a list of audited boards. The output
|
|
163
|
+
* is a compact markdown block suitable for the slash composer.
|
|
164
|
+
*
|
|
165
|
+
* When `multi` is true (audit-all mode) each row is prefixed with the
|
|
166
|
+
* board title; in single-board mode the title appears once as a header.
|
|
167
|
+
*/
|
|
168
|
+
export declare function renderAuditReport(rows: ReadonlyArray<{
|
|
169
|
+
title: string;
|
|
170
|
+
summary: KanbanAuditSummary;
|
|
171
|
+
error?: string;
|
|
172
|
+
}>, multi: boolean): string;
|
|
173
|
+
/**
|
|
174
|
+
* Render a compact boards table. Width-aware so a 60-col terminal still gets
|
|
175
|
+
* useful output (column titles truncated, counts abbreviated).
|
|
176
|
+
*/
|
|
177
|
+
export declare function renderBoardsList(boards: readonly KanbanBoardSummary[], width?: number): string;
|
|
178
|
+
//# sourceMappingURL=kanban-slash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-slash.d.ts","sourceRoot":"","sources":["../src/kanban-slash.ts"],"names":[],"mappings":"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"}
|
package/dist/run-tui.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface RunTuiOptions {
|
|
|
15
15
|
/** Host-owned mutable bridge for slash commands that need masked input. */
|
|
16
16
|
secretInputController?: {
|
|
17
17
|
readSecret(prompt: string): Promise<string>;
|
|
18
|
+
readText?(prompt: string): Promise<string>;
|
|
18
19
|
} | undefined;
|
|
19
20
|
attachments: AttachmentStore;
|
|
20
21
|
events: EventBus;
|
|
@@ -85,6 +86,8 @@ export interface RunTuiOptions {
|
|
|
85
86
|
family?: string | undefined;
|
|
86
87
|
/** Last 3 chars of the active API key — shown in the banner for visual key-pick verification. */
|
|
87
88
|
keyTail?: string | undefined;
|
|
89
|
+
/** Active fallback profile name, shown in the banner (e.g. "default"). */
|
|
90
|
+
profile?: string | undefined;
|
|
88
91
|
/** Background autonomy agents to display in the banner (Brain, Shadow,
|
|
89
92
|
* Kanban, Mailbox, Memory, etc.). Read from the mailbox at boot. */
|
|
90
93
|
autonomyAgents?: import('./components/history/types.js').AutonomyAgentStatus[] | undefined;
|
|
@@ -166,6 +169,7 @@ export interface RunTuiOptions {
|
|
|
166
169
|
onClearHistory?: ((dispatch: React.Dispatch<{
|
|
167
170
|
type: 'clearHistory';
|
|
168
171
|
model?: string | undefined;
|
|
172
|
+
provider?: string | undefined;
|
|
169
173
|
} | {
|
|
170
174
|
type: 'resetContextChip';
|
|
171
175
|
} | {
|
|
@@ -224,6 +228,11 @@ export interface RunTuiOptions {
|
|
|
224
228
|
interruptController?: {
|
|
225
229
|
abortLeader: () => boolean;
|
|
226
230
|
isRunning?: (() => boolean) | undefined;
|
|
231
|
+
confirmClear?: ((info: {
|
|
232
|
+
leaderActive: boolean;
|
|
233
|
+
subagentCount: number;
|
|
234
|
+
}) => Promise<boolean>) | undefined;
|
|
235
|
+
confirmSlash?: ((question: string, defaultYes: boolean) => Promise<boolean | null>) | undefined;
|
|
227
236
|
resetSession?: (() => void) | undefined;
|
|
228
237
|
waitForIdle?: (() => Promise<void>) | undefined;
|
|
229
238
|
} | undefined;
|
|
@@ -243,6 +252,8 @@ export interface RunTuiOptions {
|
|
|
243
252
|
buildEnhancerProvider?: ((providerId: string, modelId: string) => Promise<import('@wrongstack/core').Provider | undefined>) | undefined;
|
|
244
253
|
/** Resolve the one-key "retry with another model" fallback ref on a refine failure. */
|
|
245
254
|
getEnhanceFallbackRef?: (() => string | undefined) | undefined;
|
|
255
|
+
/** Resolve the dedicated refiner target for the initial refinement attempt. */
|
|
256
|
+
getConfiguredRefinerRef?: (() => string | undefined) | undefined;
|
|
246
257
|
/**
|
|
247
258
|
* Controller for status bar hidden items. App installs a dispatch-backed
|
|
248
259
|
* setter on mount so the /statusline slash command can update the TUI's
|