@orkestrel/terminal 0.0.3 → 0.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.
@@ -0,0 +1,1693 @@
1
+ import { DriverInterface } from '@orkestrel/database';
2
+ import { EmitterErrorHandler } from '@orkestrel/emitter';
3
+ import { EmitterHooks } from '@orkestrel/emitter';
4
+ import { EmitterInterface } from '@orkestrel/emitter';
5
+ import { Guard } from '@orkestrel/contract';
6
+ import { StylerInterface } from '@orkestrel/console';
7
+ import { TableInterface } from '@orkestrel/database';
8
+
9
+ /** The `Accept` header value that opens the broker's SSE stream. */
10
+ export declare const ACCEPT_EVENT_STREAM = "text/event-stream";
11
+
12
+ /** Matches an alphanumeric string (letters and digits only). The `alphanumeric` rule tests against this. */
13
+ export declare const ALPHANUMERIC_PATTERN: RegExp;
14
+
15
+ /** The rejection reason a bare {@link PromptInterface.answer} returns — `'unknown'` (no such parked prompt) or `'rejected'` (failed validation / type-check). */
16
+ export declare type AnswerError = 'unknown' | 'rejected';
17
+
18
+ /** The outcome of a bare {@link PromptInterface.answer} call — the accepted `value` on success, else the {@link AnswerError}. */
19
+ export declare type AnswerResult = {
20
+ readonly success: true;
21
+ readonly value: unknown;
22
+ } | {
23
+ readonly success: false;
24
+ readonly error: AnswerError;
25
+ };
26
+
27
+ /**
28
+ * Append a rule-backed {@link Validator} to `validators` when the rule is enabled — a `false` /
29
+ * `undefined` `check` is skipped, a function `check` is added verbatim (the custom override), and
30
+ * a primitive `check` is wrapped via {@link buildRuleValidator}. Mutates `validators` in place.
31
+ */
32
+ export declare function appendRule(validators: Validator[], rule: string, check: boolean | number | string | Validator | undefined): void;
33
+
34
+ /** Backspace (BS, U+0008) — Ctrl+H / some terminals' Backspace. */
35
+ export declare const BACKSPACE: string;
36
+
37
+ /** Wrap a named rule + its primitive check into a {@link Validator} (returns `true` or the rule's message). */
38
+ export declare function buildRuleValidator(rule: string, check: boolean | number | string): Validator;
39
+
40
+ /** A choice item in a {@link CheckboxOptions} prompt — a {@link PromptChoice} plus an optional initial `checked` state. */
41
+ export declare interface CheckboxChoice {
42
+ readonly name: string;
43
+ readonly value: string;
44
+ readonly description?: string;
45
+ readonly checked?: boolean;
46
+ }
47
+
48
+ /**
49
+ * Options for a multi-selection {@link import('./helpers.js').checkboxReduce} prompt.
50
+ *
51
+ * @remarks
52
+ * `choices` accepts bare strings or full {@link CheckboxChoice} objects (with an optional
53
+ * initial `checked`); {@link import('./helpers.js').createCheckboxState} normalizes them.
54
+ * `min` / `max` gate submission — a submit with fewer than `min` or more than `max` selected
55
+ * is rejected (the prompt stays active with the reason in the view).
56
+ */
57
+ export declare interface CheckboxOptions {
58
+ readonly message: string;
59
+ readonly choices: readonly (string | CheckboxChoice)[];
60
+ readonly min?: number;
61
+ readonly max?: number;
62
+ readonly styler?: StylerInterface;
63
+ }
64
+
65
+ /**
66
+ * Advance a checkbox prompt by one {@link KeyEvent} — the pure
67
+ * `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the
68
+ * focus (wrapping); `space` toggles the focused index in the checked set; return submits the
69
+ * checked values in CHOICE order — gated by `min` / `max` (an out-of-range count stays active with
70
+ * the reason in the view); ctrl-c cancels.
71
+ */
72
+ export declare function checkboxReduce(state: CheckboxState, key: KeyEvent): PromptStep<readonly string[], CheckboxState>;
73
+
74
+ /**
75
+ * The immutable state of a checkbox prompt — the normalized choices, the styler, the `focused`
76
+ * index, the set of `checked` indices, the optional `min` / `max` gate, and the current `error`.
77
+ *
78
+ * @remarks
79
+ * Built by {@link import('./helpers.js').createCheckboxState}; advanced by
80
+ * {@link import('./helpers.js').checkboxReduce}. `up` / `down` move `focus` (wrapping); `space`
81
+ * toggles the focused index in `checked`; `return` submits the checked values in choice order
82
+ * (rejected, with `error`, when the count is outside `[min, max]`). `checked` is modelled as a
83
+ * readonly index array (plain JSON data, copy-on-write — no `Set` to clone).
84
+ */
85
+ export declare interface CheckboxState {
86
+ readonly message: string;
87
+ readonly choices: readonly CheckboxChoice[];
88
+ readonly styler: StylerInterface;
89
+ readonly focused: number;
90
+ readonly checked: readonly number[];
91
+ readonly min?: number;
92
+ readonly max?: number;
93
+ readonly error?: string;
94
+ }
95
+
96
+ /** Render a {@link CheckboxState} as a MULTI-LINE styled view — the header, one box per choice (focused + checked marked), a count, and any error. */
97
+ export declare function checkboxView(state: CheckboxState): string;
98
+
99
+ /**
100
+ * Compose several {@link Validator}s into ONE short-circuiting validator — it runs them in order
101
+ * and returns the FIRST error message, or `true` when all pass. The empty composition always
102
+ * passes.
103
+ */
104
+ export declare function composeValidators(...validators: Validator[]): Validator;
105
+
106
+ /** Options for a yes/no {@link import('./helpers.js').confirmReduce} confirmation prompt. */
107
+ export declare interface ConfirmOptions {
108
+ readonly message: string;
109
+ readonly default?: boolean;
110
+ readonly styler?: StylerInterface;
111
+ }
112
+
113
+ /**
114
+ * Advance a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`
115
+ * reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits
116
+ * the `default`, ctrl-c cancels; any other key is ignored (stays active).
117
+ */
118
+ export declare function confirmReduce(state: ConfirmState, key: KeyEvent): PromptStep<boolean, ConfirmState>;
119
+
120
+ /**
121
+ * The immutable state of a confirm prompt — its message, the default answer, and the styler.
122
+ *
123
+ * @remarks
124
+ * Built by {@link import('./helpers.js').createConfirmState}; advanced by
125
+ * {@link import('./helpers.js').confirmReduce}. `y` / `Y` submits `true`, `n` / `N` submits
126
+ * `false`, and `enter` takes the `default` (rendered with the active letter capitalized in the
127
+ * `(Y/n)` hint).
128
+ */
129
+ export declare interface ConfirmState {
130
+ readonly message: string;
131
+ readonly default: boolean;
132
+ readonly styler: StylerInterface;
133
+ }
134
+
135
+ /** Render a {@link ConfirmState} as a styled view — the header plus a `(Y/n)` hint with the default letter emphasized. */
136
+ export declare function confirmView(state: ConfirmState): string;
137
+
138
+ /**
139
+ * The single control BYTE → key descriptor table {@link import('./helpers.js').parseKey}
140
+ * consults for the one-byte keys. Each entry carries the canonical `name` and whether it is a
141
+ * `ctrl` combination. The source of truth for the single-byte key decode; frozen.
142
+ *
143
+ * @remarks
144
+ * `return` / `newline` map to `return` (one canonical Enter name); `delete` / `backspace` both
145
+ * map to `backspace` (the two Backspace bytes); the Ctrl combos (`c` / `d` / `u` / `a` / `e`)
146
+ * carry `ctrl: true` so a reducer can match `key.ctrl && key.name === 'c'`. `escape` / `tab` /
147
+ * `space` are plain named keys.
148
+ */
149
+ export declare const CONTROL_NAMES: Readonly<Record<string, {
150
+ readonly name: string;
151
+ readonly ctrl: boolean;
152
+ }>>;
153
+
154
+ /** Build the initial {@link CheckboxState} from {@link CheckboxOptions} — normalizing choices, seeding the checked set, carrying min/max. */
155
+ export declare function createCheckboxState(options: CheckboxOptions): CheckboxState;
156
+
157
+ /** Build the initial {@link ConfirmState} from {@link ConfirmOptions} — defaulting the answer to `false`. */
158
+ export declare function createConfirmState(options: ConfirmOptions): ConfirmState;
159
+
160
+ /**
161
+ * Create a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the
162
+ * driver-pluggable twin of {@link createMemoryTerminalStore}, storing each endpoint's config
163
+ * snapshot as one opaque JSON column.
164
+ *
165
+ * @param driver - The {@link DriverInterface} backing the table (default an in-memory driver)
166
+ * @returns A {@link TerminalStoreInterface}
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * import { createDatabaseTerminalStore } from '@src/core'
171
+ *
172
+ * const store = createDatabaseTerminalStore() // in-memory by default
173
+ * ```
174
+ */
175
+ export declare function createDatabaseTerminalStore(driver?: DriverInterface): TerminalStoreInterface;
176
+
177
+ /** Build the initial {@link EditorState} from {@link EditorOptions} — resolving the validator + styler, seeding empty lines. */
178
+ export declare function createEditorState(options: EditorOptions): EditorState;
179
+
180
+ /** Build the initial {@link InputState} from {@link InputOptions} — resolving the validator + styler, seeding an empty value. */
181
+ export declare function createInputState(options: InputOptions): InputState;
182
+
183
+ /**
184
+ * Create the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of endpoint
185
+ * config snapshots, the default store backing a {@link TerminalManagerInterface}'s `open` / `save`.
186
+ *
187
+ * @returns A {@link TerminalStoreInterface}
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * import { createMemoryTerminalStore } from '@src/core'
192
+ *
193
+ * const store = createMemoryTerminalStore()
194
+ * ```
195
+ */
196
+ export declare function createMemoryTerminalStore(): TerminalStoreInterface;
197
+
198
+ /** Build the initial {@link PasswordState} from {@link PasswordOptions} — resolving the validator + styler + mask. */
199
+ export declare function createPasswordState(options: PasswordOptions): PasswordState;
200
+
201
+ /**
202
+ * Create the headless prompt {@link PromptInterface} BROKER — it parks each prompt call as a
203
+ * pending prompt and resolves it when {@link PromptInterface.answer} arrives (or rejects on
204
+ * timeout). The tri-surface's headless arm: subscribe `emitter.on('pending', …)` to forward each
205
+ * prompt to whoever can answer (an SSE transport, a remote terminal), then route the answer back
206
+ * through `answer(id, value)`.
207
+ *
208
+ * @param options - See {@link PromptOptions}
209
+ * @returns A {@link PromptInterface}
210
+ *
211
+ * @remarks
212
+ * - **Park-as-Promise.** `await prompt.input({ message })` blocks until `answer(id, value)` accepts
213
+ * a matching value; the value is validated (text forms) and type-checked to the form first.
214
+ * - **Timeout → expire → reject (deterministic).** An unanswered prompt expires after
215
+ * `options.timeout` (default {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}) and its
216
+ * Promise rejects with a {@link import('./errors.js').TerminalError}; inject `options.timer` to
217
+ * drive expiry without real time.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * import { createPrompt } from '@src/core'
222
+ *
223
+ * const prompt = createPrompt()
224
+ * prompt.emitter.on('pending', (pending) => send(pending)) // forward to a remote client
225
+ * const name = await prompt.input({ message: 'Your name', validate: { required: true } })
226
+ * ```
227
+ */
228
+ export declare function createPrompt(options?: PromptOptions): PromptInterface;
229
+
230
+ /**
231
+ * Create the SSE prompt {@link PromptClientInterface} BRIDGE — it connects to a remote broker's SSE
232
+ * endpoint, dispatches each received prompt to a LOCAL {@link import('./types.js').PromptFormInterface}
233
+ * terminal (so a human at this machine answers a prompt parked elsewhere), and POSTs the answer
234
+ * back. Universal — `fetch` / SSE are web-standard.
235
+ *
236
+ * @param options - See {@link PromptClientOptions} (`url` + `terminal` required)
237
+ * @returns A {@link PromptClientInterface}
238
+ *
239
+ * @remarks
240
+ * - **Connect + reconnect.** `await client.connect()` streams remote prompts until the stream
241
+ * ends; it reconnects with the `delay` backoff unless `reconnect` is `false` / the client was
242
+ * `destroy`ed. Inject `options.fetch` (a scripted `fetch`) and `options.timer` to drive it
243
+ * deterministically in tests — no real network.
244
+ * - **§14 wire narrowing.** Every decoded prompt is guard-narrowed before dispatch (never an `as`).
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * import { createPromptClient } from '@src/core'
249
+ *
250
+ * const client = createPromptClient({ url: 'http://host/prompts', terminal })
251
+ * await client.connect()
252
+ * ```
253
+ */
254
+ export declare function createPromptClient(options: PromptClientOptions): PromptClientInterface;
255
+
256
+ /** Build the initial {@link SelectState} from {@link SelectOptions} — normalizing choices and pre-focusing the default. */
257
+ export declare function createSelectState(options: SelectOptions): SelectState;
258
+
259
+ /**
260
+ * Create the multi-endpoint {@link TerminalManagerInterface} — a named registry of
261
+ * {@link PromptInterface} brokers so several parties can `ask` prompts of each other by name,
262
+ * with a transitive DEADLOCK check across every in-flight ask.
263
+ *
264
+ * @param options - See {@link TerminalManagerOptions}
265
+ * @returns A {@link TerminalManagerInterface}
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * import { createTerminalManager } from '@src/core'
270
+ *
271
+ * const manager = createTerminalManager()
272
+ * manager.add('agent')
273
+ * ```
274
+ */
275
+ export declare function createTerminalManager(options?: TerminalManagerOptions): TerminalManagerInterface;
276
+
277
+ /** Ctrl+A (SOH, U+0001) — move to start of line. */
278
+ export declare const CTRL_A: string;
279
+
280
+ /** Ctrl+C (ETX, U+0003) — interrupt / cancel. */
281
+ export declare const CTRL_C: string;
282
+
283
+ /** Ctrl+D (EOT, U+0004) — end-of-transmission / finish (the editor's commit key). */
284
+ export declare const CTRL_D: string;
285
+
286
+ /** Ctrl+E (ENQ, U+0005) — move to end of line. */
287
+ export declare const CTRL_E: string;
288
+
289
+ /** Ctrl+U (NAK, U+0015) — clear the current line. */
290
+ export declare const CTRL_U: string;
291
+
292
+ /**
293
+ * A {@link TerminalStoreInterface} backed by one table of the `databases` layer — an endpoint's
294
+ * durable CONFIG state IS a row, so persistence reduces to keyed point-access (`get` / `set` /
295
+ * `delete`) over a {@link TableInterface}, the driver-pluggable twin of the plain-`Map`
296
+ * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}.
297
+ *
298
+ * @remarks
299
+ * The store is driver-agnostic: it holds a single {@link TableInterface} whose backend (memory,
300
+ * JSON, SQLite, IndexedDB) is chosen by whoever builds it (the factories), so a JSON / SQLite /
301
+ * IndexedDB backend swaps in WITHOUT touching the manager — the same seam as
302
+ * {@link import('./MemoryTerminalStore.js').MemoryTerminalStore}. The driver defaults to memory
303
+ * ({@link import('./factories.js').createDatabaseTerminalStore} passes `createMemoryDriver()`), so
304
+ * it ALSO works in memory out of the box; you opt into the durable plumbing by passing a JSON /
305
+ * SQLite / IndexedDB driver.
306
+ *
307
+ * The {@link TerminalSnapshot} is stored as ONE OPAQUE JSON COLUMN — the table is a row of
308
+ * `{ id; snapshot }` ({@link TerminalSnapshotRow}). The snapshot is already a COMPLETE,
309
+ * self-contained, pure-JSON CONFIG payload (no live broker state), so storing it whole is lossless
310
+ * AND keeps the row type flat (`snapshot` reads back as `unknown`).
311
+ *
312
+ * - **`set(snapshot)` upserts under the snapshot's OWN `id`** (no separate id param) — it writes
313
+ * the row `{ id: snapshot.id, snapshot }`.
314
+ * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
315
+ * a {@link TerminalSnapshot} ({@link import('./validators.js').isTerminalSnapshot} — the AGENTS §14
316
+ * boundary narrow for an untrusted storage read), or `undefined` if none is stored.
317
+ * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
318
+ *
319
+ * There is NO idle-TTL / eviction — a persisted config lives until an explicit `delete`. The public
320
+ * surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method bijection with
321
+ * {@link TerminalStoreInterface}). Hydration stays a caller concern: `open` always restores an EMPTY
322
+ * broker — parked Promises are process-bound and never resurrected.
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * import { createDatabaseTerminalStore, createMemoryDriver } from '@src/core'
327
+ *
328
+ * const store = createDatabaseTerminalStore(createMemoryDriver()) // a durable driver swaps in here
329
+ * await store.set({ id: 'shell', timeout: 5000 }) // persist the config (one JSON column)
330
+ * const snapshot = await store.get('shell')
331
+ * await store.delete('shell') // drop it
332
+ * ```
333
+ */
334
+ export declare class DatabaseTerminalStore implements TerminalStoreInterface {
335
+ #private;
336
+ /**
337
+ * Wrap a table as a terminal store.
338
+ *
339
+ * @param table - The {@link TableInterface} holding the snapshots — its row is the
340
+ * {@link TerminalSnapshotRow} `{ id; snapshot }` shape (the snapshot one opaque JSON column)
341
+ */
342
+ constructor(table: TableInterface<TerminalSnapshotRow>);
343
+ /** Resolve the persisted snapshot for `id`, narrowing the opaque JSON column back to a `TerminalSnapshot`. */
344
+ get(id: string): Promise<TerminalSnapshot | undefined>;
345
+ /** Insert or replace under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
346
+ set(snapshot: TerminalSnapshot): Promise<void>;
347
+ /** Drop a snapshot by id; an absent id is a no-op (no throw). */
348
+ delete(id: string): Promise<void>;
349
+ }
350
+
351
+ /** The default mask glyph a {@link import('./types.js').PasswordState} renders each input character as — `*`. */
352
+ export declare const DEFAULT_MASK = "*";
353
+
354
+ /** How long (ms) the {@link import('./types.js').PromptInterface} broker parks an unanswered prompt before it expires — 5 minutes. */
355
+ export declare const DEFAULT_PROMPT_TIMEOUT_MS = 300000;
356
+
357
+ /** How long (ms) the {@link import('./types.js').PromptClientInterface} waits before each reconnect attempt — 2 seconds. */
358
+ export declare const DEFAULT_RECONNECT_DELAY_MS = 2000;
359
+
360
+ /**
361
+ * The default {@link import('./types.js').TimerHandler} — a thin host `setTimeout` / `clearTimeout`
362
+ * wrapper that arms `callback` after `ms` and returns a {@link TimerCancel}. The deadline seam
363
+ * behind both the {@link import('./Prompt.js').Prompt} broker (its expiry) and the
364
+ * {@link import('./PromptClient.js').PromptClient} (its reconnect backoff); a test injects a
365
+ * deterministic timer instead, so neither entity touches real time.
366
+ */
367
+ export declare function defaultTimer(callback: () => void, ms: number): TimerCancel;
368
+
369
+ /** Delete (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */
370
+ export declare const DELETE: string;
371
+
372
+ /**
373
+ * Dispatch a {@link PendingPrompt} to the matching {@link PromptFormInterface} method — the bridge
374
+ * step a {@link PromptClient} runs to drive a LOCAL terminal with a prompt issued elsewhere.
375
+ * Reconstructs typed options from the wire-safe {@link PendingPrompt.options} (every field
376
+ * §14-narrowed, never an `as`; the validator rebuilt from rules via {@link reconstructValidationRules}),
377
+ * then calls the matching prompt form and returns its resolved value.
378
+ *
379
+ * @param terminal - The local {@link PromptFormInterface} to drive
380
+ * @param pending - The decoded pending prompt to dispatch
381
+ * @returns The prompt's resolved value (a `string` / `boolean` / `readonly string[]` per form)
382
+ */
383
+ export declare function dispatchPendingPrompt(terminal: PromptFormInterface, pending: PendingPrompt): Promise<string | boolean | readonly string[]>;
384
+
385
+ /**
386
+ * Apply a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input /
387
+ * password / editor. A printable key appends its character; `backspace` drops the last character;
388
+ * `space` appends a space; ctrl-u clears the line. Returns the new buffer, or `undefined` when the
389
+ * key does not edit the line (so the caller can leave the state untouched).
390
+ */
391
+ export declare function editLine(value: string, key: KeyEvent): string | undefined;
392
+
393
+ /** Options for a multi-line {@link import('./helpers.js').editorReduce} editor prompt (terminated by ctrl-d). */
394
+ export declare interface EditorOptions {
395
+ readonly message: string;
396
+ readonly default?: string;
397
+ readonly validate?: Validator | ValidationRules;
398
+ readonly styler?: StylerInterface;
399
+ }
400
+
401
+ /**
402
+ * Advance an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
403
+ * reducer. Printable characters extend the current line; backspace shrinks it; return commits the
404
+ * current line and starts a fresh one; ctrl-d FINISHES (joining all lines, falling back to the
405
+ * default when empty) through the validator; ctrl-c cancels. An invalid finish stays active with
406
+ * the error.
407
+ */
408
+ export declare function editorReduce(state: EditorState, key: KeyEvent): PromptStep<string, EditorState>;
409
+
410
+ /**
411
+ * The immutable state of an editor prompt — the committed `lines`, the in-progress `current`
412
+ * line, plus the resolved validator + styler, the default, and the current `error`.
413
+ *
414
+ * @remarks
415
+ * Built by {@link import('./helpers.js').createEditorState}; advanced by
416
+ * {@link import('./helpers.js').editorReduce}. A printable key appends to `current`; `return`
417
+ * commits `current` to `lines` and starts a fresh line; ctrl-d finishes, submitting
418
+ * `lines + current` joined by newlines (or `default` when empty). The whole text is validated
419
+ * on finish.
420
+ */
421
+ export declare interface EditorState {
422
+ readonly message: string;
423
+ readonly default: string;
424
+ readonly validator: Validator;
425
+ readonly styler: StylerInterface;
426
+ readonly lines: readonly string[];
427
+ readonly current: string;
428
+ readonly error?: string;
429
+ }
430
+
431
+ /** Render an {@link EditorState} as a MULTI-LINE styled view — the header (with a Ctrl+D hint), the committed lines, the in-progress line, and any error. */
432
+ export declare function editorView(state: EditorState): string;
433
+
434
+ /** Matches an email address — a non-trivial `local@domain.tld` shape. The `email` rule tests against this. */
435
+ export declare const EMAIL_PATTERN: RegExp;
436
+
437
+ /** The styled error line (`✖ message`) — appended beneath a prompt view when the last submit failed validation. */
438
+ export declare function errorLine(styler: StylerInterface, message: string): string;
439
+
440
+ /** Escape (ESC, U+001B) — the lone byte, and the lead byte of every CSI / SS3 sequence. */
441
+ export declare const ESCAPE: string;
442
+
443
+ /**
444
+ * Evaluate ONE built-in validation rule against `input`, returning its error message when the
445
+ * rule fails or `undefined` when it passes. The atomic check {@link buildRuleValidator} wraps
446
+ * into a {@link Validator}. Pure.
447
+ *
448
+ * @remarks
449
+ * A function `check` is the custom-override path: it is called and its `true` ⇒ pass, a string
450
+ * ⇒ that message, anything else ⇒ the generic {@link RULE_MESSAGES.invalid}. A primitive `check`
451
+ * runs the named built-in: `required` (non-empty trimmed), `minimum` / `maximum` (length
452
+ * bounds, the message interpolated with the count), `pattern` (a regex source), and the
453
+ * `email` / `url` / `numeric` / `integer` / `alphanumeric` pattern tests.
454
+ *
455
+ * @param rule - The rule name (`'required'`, `'minimum'`, …)
456
+ * @param check - The configured value (a primitive toggle / bound / pattern, or a custom {@link Validator})
457
+ * @param input - The input string to test
458
+ * @returns The error message when the rule fails, else `undefined`
459
+ */
460
+ export declare function evaluateRule(rule: string, check: boolean | number | string | Validator, input: string): string | undefined;
461
+
462
+ /**
463
+ * A minimal `fetch` — the subset of the global `fetch` the {@link PromptClient} uses (open the
464
+ * SSE stream, POST an answer). Injected so a test drives the client with a controlled
465
+ * `Response` (a scripted SSE `ReadableStream`) instead of a real network.
466
+ */
467
+ export declare type FetchHandler = (input: string, init?: FetchInit) => Promise<Response>;
468
+
469
+ /**
470
+ * The request init the {@link PromptClient} passes to its {@link FetchHandler} — the `fetch`
471
+ * `RequestInit` fields it actually sets (method / headers / body / abort signal).
472
+ */
473
+ export declare interface FetchInit {
474
+ readonly method?: string;
475
+ readonly headers?: Readonly<Record<string, string>>;
476
+ readonly body?: string;
477
+ readonly signal?: AbortSignal;
478
+ }
479
+
480
+ /** The min/max gate for a checkbox submit — the rejection message when `count` is out of range, else `undefined`. */
481
+ export declare function gateSelection(count: number, min?: number, max?: number): string | undefined;
482
+
483
+ /** The default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses. */
484
+ export declare function globalFetch(input: string, init?: FetchInit): Promise<Response>;
485
+
486
+ /** The auth-token request header the {@link import('./types.js').PromptClient} sends when a `token` is configured. */
487
+ export declare const HEADER_TOKEN = "x-orkestrel-token";
488
+
489
+ /** Options for a single-line text {@link import('./helpers.js').inputReduce} prompt. */
490
+ export declare interface InputOptions {
491
+ readonly message: string;
492
+ readonly default?: string;
493
+ readonly validate?: Validator | ValidationRules;
494
+ readonly styler?: StylerInterface;
495
+ }
496
+
497
+ /**
498
+ * Advance an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
499
+ * reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c
500
+ * cancels; return submits (the empty line falls back to the default) through the validator — an
501
+ * invalid submit stays active with the error in the view.
502
+ */
503
+ export declare function inputReduce(state: InputState, key: KeyEvent): PromptStep<string, InputState>;
504
+
505
+ /**
506
+ * The immutable state of a text input prompt — its options, the resolved validator + styler,
507
+ * the accumulated `value`, and the current `error` (when the last submit failed validation).
508
+ *
509
+ * @remarks
510
+ * Built by {@link import('./helpers.js').createInputState}; advanced by
511
+ * {@link import('./helpers.js').inputReduce}. `value` accumulates printable characters and
512
+ * shrinks on backspace; `error` holds the validation message shown in the view after a
513
+ * rejected submit (cleared on the next keystroke).
514
+ */
515
+ export declare interface InputState {
516
+ readonly message: string;
517
+ readonly default: string;
518
+ readonly validator: Validator;
519
+ readonly styler: StylerInterface;
520
+ readonly value: string;
521
+ readonly error?: string;
522
+ }
523
+
524
+ /** Render an {@link InputState} as a styled view — the header, the typed value (or dimmed default hint), and any error. */
525
+ export declare function inputView(state: InputState): string;
526
+
527
+ /** Matches an integer (optional sign). The `integer` rule tests against this. */
528
+ export declare const INTEGER_PATTERN: RegExp;
529
+
530
+ /**
531
+ * Whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}
532
+ * distinguishes a deliberate `disconnect` / teardown (an aborted `fetch`) from a real fault, so it
533
+ * exits its connect loop quietly instead of emitting `error` / reconnecting.
534
+ */
535
+ export declare function isAbortError(error: unknown): boolean;
536
+
537
+ /** Narrow an unknown wire payload to an answer POST body — a non-empty `id` string plus a `value` key (of any shape) present. */
538
+ export declare function isAnswerPayload(value: unknown): value is {
539
+ readonly id: string;
540
+ readonly value: unknown;
541
+ };
542
+
543
+ /** Narrow an unknown value to a {@link CheckboxChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
544
+ export declare function isCheckboxChoice(value: unknown): value is CheckboxChoice;
545
+
546
+ /**
547
+ * Whether `url` is an INSECURE remote endpoint — a plain `http://` URL whose host is NOT a
548
+ * loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input.
549
+ *
550
+ * @remarks
551
+ * A loopback host (`localhost`, `127.0.0.1`, `[::1]`) over `http://` is exempt (local
552
+ * development has no network hop to eavesdrop on); every other `http://` host is insecure.
553
+ * An `https://` URL (or any non-`http://` scheme) is never flagged.
554
+ *
555
+ * @param url - The candidate endpoint URL
556
+ * @returns `true` when `url` is a non-loopback `http://` endpoint
557
+ *
558
+ * @example
559
+ * ```ts
560
+ * isInsecureRemote('http://example.com') // true
561
+ * isInsecureRemote('http://localhost:3000') // false
562
+ * isInsecureRemote('https://example.com') // false
563
+ * ```
564
+ */
565
+ export declare function isInsecureRemote(url: string): boolean;
566
+
567
+ /**
568
+ * Narrow an unknown wire value to a {@link PendingPrompt} before dispatching it.
569
+ *
570
+ * @param value - The decoded wire value to inspect
571
+ * @returns Whether `value` is a complete pending-prompt record
572
+ */
573
+ export declare const isPendingPrompt: Guard<PendingPrompt>;
574
+
575
+ /** Narrow an unknown value to a {@link PendingPromptStatus}. */
576
+ export declare const isPendingPromptStatus: Guard<PendingPromptStatus>;
577
+
578
+ /** Whether a single character is a printable (non-control) character — used by {@link parseKey}'s char fallback. */
579
+ export declare function isPrintable(character: string): boolean;
580
+
581
+ /** Narrow an unknown value to a {@link PromptChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
582
+ export declare function isPromptChoice(value: unknown): value is PromptChoice;
583
+
584
+ /** Narrow an unknown value to a {@link PromptType} — one of the six prompt forms. */
585
+ export declare const isPromptType: Guard<PromptType>;
586
+
587
+ /**
588
+ * Narrow an unknown caught value to a {@link TerminalError}.
589
+ *
590
+ * @param value - The value to test (typically a `catch` binding or a rejected prompt call)
591
+ * @returns `true` when `value` is a {@link TerminalError}
592
+ *
593
+ * @example
594
+ * ```ts
595
+ * try {
596
+ * const name = await prompt.input({ message: 'Your name' })
597
+ * } catch (error) {
598
+ * if (isTerminalError(error) && error.code === 'EXPIRE') retryLater()
599
+ * }
600
+ * ```
601
+ */
602
+ export declare function isTerminalError(value: unknown): value is TerminalError;
603
+
604
+ /** Narrow an unknown value to a {@link TerminalSnapshot}. */
605
+ export declare const isTerminalSnapshot: Guard<TerminalSnapshot>;
606
+
607
+ /**
608
+ * The Control Sequence Introducer lead (`ESC[`) for the navigation keys — the prefix of the
609
+ * arrow / home / end / delete sequences {@link SEQUENCE_NAMES} is keyed by. Named `KEY_CSI`
610
+ * (not `CSI`) so it never collides with the console module's SGR `CSI` (both barrel through
611
+ * `@src/core`).
612
+ */
613
+ export declare const KEY_CSI: string;
614
+
615
+ /** The Single Shift Three lead (`ESCO`) — the alternate arrow-key prefix some terminals emit (`ESC O A`). */
616
+ export declare const KEY_SS3: string;
617
+
618
+ /**
619
+ * One decoded keypress — the universal, TTY-agnostic representation of a single key, the
620
+ * output of {@link import('./helpers.js').parseKey}. A reducer reads `name` (and the
621
+ * modifier flags) to decide its transition; the raw `sequence` is preserved so a printable
622
+ * character round-trips and an unknown escape is never lost.
623
+ *
624
+ * @remarks
625
+ * - `name` — the canonical key name: a control / navigation key (`return`, `backspace`,
626
+ * `tab`, `escape`, `up` / `down` / `left` / `right`, `space`, `home`, `end`, `delete`),
627
+ * a named ctrl combo (`c` with `ctrl` true for ctrl-c, likewise `d` / `u` / `a` / `e`),
628
+ * or the printable character itself (`'a'`, `'7'`, `'?'`). An UNRECOGNIZED sequence
629
+ * yields `name: ''` (empty) — never a throw (the decoder is total).
630
+ * - `sequence` — the exact input bytes as a string (a `Uint8Array` is decoded UTF-8). The
631
+ * driver writes this verbatim for a printable key; a reducer that needs the literal char
632
+ * reads it.
633
+ * - `ctrl` / `meta` / `shift` — the modifier flags. `ctrl` is `true` for a C0 control byte
634
+ * (ctrl-c / ctrl-d / ctrl-u / ctrl-a / ctrl-e and the like); `meta` is `true` for an
635
+ * ESC-prefixed (Alt) sequence; `shift` is `true` for an uppercase-letter printable.
636
+ */
637
+ export declare interface KeyEvent {
638
+ readonly name: string;
639
+ readonly sequence: string;
640
+ readonly ctrl: boolean;
641
+ readonly meta: boolean;
642
+ readonly shift: boolean;
643
+ }
644
+
645
+ /**
646
+ * The in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of
647
+ * {@link TerminalSnapshot}s keyed by endpoint id, the DEFAULT store
648
+ * {@link import('./factories.js').createMemoryTerminalStore} builds. The EXACT twin of
649
+ * {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore}.
650
+ *
651
+ * @remarks
652
+ * A plain `Map<string, TerminalSnapshot>` (AGENTS §21 — the snapshot is already pure, self-contained
653
+ * CONFIG-only JSON, so no encoding is needed for the memory tier). There is NO idle-TTL and NO
654
+ * eviction: a persisted config lives until an explicit `delete`. A durable backend (JSON / SQLite /
655
+ * IndexedDB) swaps in through the SAME interface without touching the manager — its
656
+ * driver-pluggable twin is {@link import('./DatabaseTerminalStore.js').DatabaseTerminalStore} (the
657
+ * snapshot as one opaque JSON column).
658
+ *
659
+ * - **`get` resolves the persisted snapshot for an id**, or `undefined` if none is stored.
660
+ * - **`set` inserts / replaces under the snapshot's OWN `id`** (no separate id param).
661
+ * - **`delete` drops a snapshot by id**; an absent id is a no-op (no throw).
662
+ *
663
+ * The public surface is EXACTLY `get` / `set` / `delete` — no extra members (the §22 method
664
+ * bijection with {@link TerminalStoreInterface}). Hydration is a caller concern: `open` always
665
+ * restores an EMPTY broker — parked Promises are process-bound and never resurrected.
666
+ *
667
+ * @example
668
+ * ```ts
669
+ * import { createMemoryTerminalStore } from '@src/core'
670
+ *
671
+ * const store = createMemoryTerminalStore()
672
+ * await store.set({ id: 'shell', timeout: 5000 }) // persist a config
673
+ * const snapshot = await store.get('shell')
674
+ * await store.delete('shell') // drop it
675
+ * ```
676
+ */
677
+ export declare class MemoryTerminalStore implements TerminalStoreInterface {
678
+ #private;
679
+ get(id: string): Promise<TerminalSnapshot | undefined>;
680
+ set(snapshot: TerminalSnapshot): Promise<void>;
681
+ delete(id: string): Promise<void>;
682
+ }
683
+
684
+ /** Line feed (`\n`, U+000A) — Enter on some terminals / pasted input. */
685
+ export declare const NEWLINE: string;
686
+
687
+ /** Normalize a checkbox choice input into a full {@link CheckboxChoice} (a bare string becomes both name and value). */
688
+ export declare function normalizeCheckboxChoice(choice: string | CheckboxChoice): CheckboxChoice;
689
+
690
+ /** Normalize a select choice input into a full {@link PromptChoice} (a bare string becomes both name and value). */
691
+ export declare function normalizeChoice(choice: string | PromptChoice): PromptChoice;
692
+
693
+ /** Matches a numeric value (integer or decimal, optional sign). The `numeric` rule tests against this. */
694
+ export declare const NUMERIC_PATTERN: RegExp;
695
+
696
+ /**
697
+ * One parked prompt's runtime state inside the broker — the wire-safe {@link PendingPrompt} record
698
+ * it exposes, plus the live machinery that settles that prompt's Promise. `respond` is the per-form
699
+ * gate-and-resolve closure: it validates + type-checks an answer and (on accept) resolves the parked
700
+ * Promise, returning whether it accepted; it closes over the form's precisely-typed `resolve`, so no
701
+ * per-form generic leaks into `answer`. `expire` rejects the parked Promise; `cancel` clears the
702
+ * injected expiry timer ({@link TimerCancel}).
703
+ */
704
+ export declare interface Parked {
705
+ readonly prompt: PendingPrompt;
706
+ readonly respond: (value: unknown) => unknown;
707
+ readonly expire: () => void;
708
+ readonly cancel: TimerCancel;
709
+ }
710
+
711
+ /**
712
+ * The request to {@link PromptInterface.park} a prompt directly — the general form the six
713
+ * `PromptFormInterface` methods each wrap via a private `gateFor(form, options)` dispatch.
714
+ *
715
+ * @remarks
716
+ * - `form` — which {@link PromptType} to park.
717
+ * - `options` — that form's options bag ({@link PromptFormOptions}).
718
+ * - `from` / `to` — set ONLY by a {@link TerminalManagerInterface} (the attribution edge); a
719
+ * direct broker caller leaves both `undefined`.
720
+ */
721
+ export declare interface ParkRequest {
722
+ readonly form: PromptType;
723
+ readonly options: PromptFormOptions;
724
+ readonly from?: string;
725
+ readonly to?: string;
726
+ }
727
+
728
+ /**
729
+ * Decode one keypress's bytes into a {@link KeyEvent} — total, never throws. A `Uint8Array` is
730
+ * read as UTF-8; the resulting string is matched against the known control bytes
731
+ * ({@link CONTROL_NAMES}) and escape sequences ({@link SEQUENCE_NAMES}), falling back to a
732
+ * single printable character. An unrecognized sequence yields `name: ''` with the raw `sequence`
733
+ * preserved.
734
+ *
735
+ * @remarks
736
+ * - **Single control byte.** A one-character control input (`return` / `backspace` / `tab` /
737
+ * `escape` / `space`, or a Ctrl combo `c` / `d` / `u` / `a` / `e`) is looked up in
738
+ * {@link CONTROL_NAMES}, carrying its `ctrl` flag.
739
+ * - **Escape sequence.** A multi-byte ESC sequence (`up` / `down` / `left` / `right` in BOTH the
740
+ * `ESC[A` and `ESCOA` forms, plus `home` / `end` / `delete`) is looked up in
741
+ * {@link SEQUENCE_NAMES} and flagged `meta`.
742
+ * - **Printable character.** A single printable character becomes `name` = that character, with
743
+ * `shift` set when it is an uppercase letter. A multi-code-point printable (an emoji, a pasted
744
+ * run) keeps its first code point as the name and the whole input as `sequence`.
745
+ * - **Unknown.** Anything else (an unrecognized escape, an empty input) yields `name: ''` —
746
+ * total, so the driver never crashes on a stray byte.
747
+ *
748
+ * @param input - The raw keypress bytes, as a string or `Uint8Array`
749
+ * @returns The decoded {@link KeyEvent}
750
+ *
751
+ * @example
752
+ * ```ts
753
+ * parseKey('\r') // { name: 'return', sequence: '\r', ctrl: false, meta: false, shift: false }
754
+ * parseKey('\x1b[A') // { name: 'up', sequence: '\x1b[A', ctrl: false, meta: true, shift: false }
755
+ * parseKey('A') // { name: 'A', sequence: 'A', ctrl: false, meta: false, shift: true }
756
+ * parseKey('\x03') // { name: 'c', sequence: '\x03', ctrl: true, meta: false, shift: false }
757
+ * ```
758
+ */
759
+ export declare function parseKey(input: string | Uint8Array): KeyEvent;
760
+
761
+ /**
762
+ * Parse a JSON wire string TOTAL — a malformed / empty payload yields `undefined` (the caller's
763
+ * guard then rejects it), never a throw. The {@link import('./PromptClient.js').PromptClient}
764
+ * decodes every SSE `data` field through this before §14-narrowing it.
765
+ */
766
+ export declare function parseWireJSON(text: string): unknown;
767
+
768
+ /** The always-passing {@link Validator} — the resolved validator when no rules were supplied. */
769
+ export declare function passing(_input: string): true;
770
+
771
+ /** Options for a masked password {@link import('./helpers.js').passwordReduce} prompt. */
772
+ export declare interface PasswordOptions {
773
+ readonly message: string;
774
+ readonly mask?: string;
775
+ readonly validate?: Validator | ValidationRules;
776
+ readonly styler?: StylerInterface;
777
+ }
778
+
779
+ /**
780
+ * Advance a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
781
+ * reducer. Identical line-editing to {@link inputReduce} (printable extends, backspace shrinks,
782
+ * ctrl-u clears, ctrl-c cancels) but the view masks the value; return submits through the
783
+ * validator (no default fallback — a password has no echoed default).
784
+ */
785
+ export declare function passwordReduce(state: PasswordState, key: KeyEvent): PromptStep<string, PasswordState>;
786
+
787
+ /**
788
+ * The immutable state of a password prompt — like {@link InputState} but with a `mask`
789
+ * character the view renders in place of each input character.
790
+ *
791
+ * @remarks
792
+ * Built by {@link import('./helpers.js').createPasswordState}; advanced by
793
+ * {@link import('./helpers.js').passwordReduce}. The `value` is the real (unmasked) input;
794
+ * the view shows `mask` repeated `value.length` times.
795
+ */
796
+ export declare interface PasswordState {
797
+ readonly message: string;
798
+ readonly mask: string;
799
+ readonly validator: Validator;
800
+ readonly styler: StylerInterface;
801
+ readonly value: string;
802
+ readonly error?: string;
803
+ }
804
+
805
+ /** Render a {@link PasswordState} as a styled view — the header, the value masked to `mask` repeated, and any error. */
806
+ export declare function passwordView(state: PasswordState): string;
807
+
808
+ /**
809
+ * One prompt PARKED by the broker — an id-keyed, wire-safe record of a {@link PromptFormInterface}
810
+ * call awaiting a remote answer. The value a `pending` listener receives and the broker serializes
811
+ * over SSE to a {@link PromptClient}.
812
+ *
813
+ * @remarks
814
+ * - `id` — the unique id (minted via `crypto.randomUUID()`); the key for {@link PromptInterface.answer}.
815
+ * - `form` — which prompt form was called ({@link PromptType}); the discriminant a client
816
+ * dispatches on (named for its axis — the prompt form — never `kind` / `type`).
817
+ * - `message` — the prompt's question (lifted out of `options` for direct display).
818
+ * - `options` — the WIRE-SAFE options (a `validate` FUNCTION dropped; the declarative
819
+ * {@link ValidationRules} data + `choices` / `default` / `mask` kept — see
820
+ * {@link import('./helpers.js').serializePromptOptions}). A client reconstructs the validator
821
+ * from the rules via {@link resolveValidation}.
822
+ * - `status` — the current {@link PendingPromptStatus}.
823
+ * - `time` — the creation timestamp (ms since epoch).
824
+ * - `from` / `to` — the OPTIONAL attribution edge a {@link TerminalManagerInterface} stamps on a
825
+ * parked prompt (which endpoint asked, which endpoint must answer); absent for a bare broker
826
+ * {@link PromptInterface} used directly (no manager attribution).
827
+ */
828
+ export declare interface PendingPrompt {
829
+ readonly id: string;
830
+ readonly form: PromptType;
831
+ readonly message: string;
832
+ readonly options: Readonly<Record<string, unknown>>;
833
+ readonly status: PendingPromptStatus;
834
+ readonly time: number;
835
+ readonly from?: string;
836
+ readonly to?: string;
837
+ }
838
+
839
+ /**
840
+ * The lifecycle status of a parked {@link PendingPrompt} — where a brokered prompt stands.
841
+ * Names its axis (the pending prompt's progression), never `kind` (AGENTS §4.4).
842
+ *
843
+ * @remarks
844
+ * - `pending` — parked, awaiting an {@link PromptInterface.answer} (the Promise is unresolved).
845
+ * - `answered` — answered and accepted (the Promise resolved with the validated value).
846
+ * - `expired` — timed out (or torn down by `destroy`) before an answer (the Promise rejected).
847
+ */
848
+ export declare type PendingPromptStatus = 'pending' | 'answered' | 'expired';
849
+
850
+ /**
851
+ * The headless prompt BROKER (observable §13) — parks each {@link PromptInterface} call as a
852
+ * pending prompt and returns a Promise that resolves when the prompt is {@link answer}ed (or
853
+ * rejects on timeout / teardown). The tri-surface's headless arm: there is no terminal here, so a
854
+ * transport forwards each `pending` event to whoever can answer, and {@link answer} resolves the
855
+ * parked Promise — for environments where direct user access is unavailable (a headless server on
856
+ * stdio, a browser with no TTY, a prompt issued on one machine answered on another).
857
+ *
858
+ * @remarks
859
+ * - **Park-as-Promise.** Each `input` / `password` / `confirm` / `select` / `checkbox` / `editor`
860
+ * mints an id (`crypto.randomUUID()`), parks a wire-safe {@link PendingPrompt}, emits `pending`,
861
+ * and returns an unresolved Promise. The prompt's options are serialized
862
+ * ({@link serializePromptOptions}) so a transport can forward them as-is.
863
+ * - **Answer validates + type-checks.** {@link answer} runs the prompt's per-form gate: it
864
+ * type-checks `value` to the form (string / boolean / string[]) AND, for the text forms, runs
865
+ * the validator resolved from the original `validate` rules. A rejected answer returns `false`
866
+ * and the prompt stays `pending`; an accepted answer resolves the Promise, emits `answer`, and
867
+ * removes the prompt.
868
+ * - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms (via the
869
+ * INJECTED timer): `expire` fires and the parked Promise rejects with a {@link TerminalError}
870
+ * (`code: 'EXPIRE'`). {@link destroy} expires every still-pending prompt the same way.
871
+ * - **Deterministic.** The timer is injectable ({@link import('./types.js').TimerHandler}); a test
872
+ * supplies one that fires on demand, so expiry is driven without real time.
873
+ *
874
+ * @example
875
+ * ```ts
876
+ * const prompt = createPrompt({ timeout: 60_000 })
877
+ * prompt.emitter.on('pending', (pending) => broadcast(pending)) // forward to a client
878
+ *
879
+ * const name = await prompt.input({ message: 'Your name' }) // parks; resolves on answer()
880
+ * // ...elsewhere, a client POSTs the answer back:
881
+ * prompt.answer(id, 'Ada') // resolves the awaited input() above
882
+ * ```
883
+ */
884
+ export declare class Prompt implements PromptInterface {
885
+ #private;
886
+ constructor(options?: PromptOptions);
887
+ get emitter(): EmitterInterface<PromptEventMap>;
888
+ get count(): number;
889
+ pending(): readonly PendingPrompt[];
890
+ pending(id: string): PendingPrompt | undefined;
891
+ answer(id: string, value: unknown): AnswerResult;
892
+ park(request: ParkRequest): Ticket;
893
+ input(options: InputOptions): Promise<string>;
894
+ password(options: PasswordOptions): Promise<string>;
895
+ confirm(options: ConfirmOptions): Promise<boolean>;
896
+ select(options: SelectOptions): Promise<string>;
897
+ checkbox(options: CheckboxOptions): Promise<readonly string[]>;
898
+ editor(options: EditorOptions): Promise<string>;
899
+ destroy(): void;
900
+ }
901
+
902
+ /**
903
+ * The prompt-view icon glyphs the reducers render the prompt line and choice rows with. PLAIN
904
+ * glyphs — color is applied by the {@link import('./types.js').PromptState}'s
905
+ * {@link import('@orkestrel/console').StylerInterface} at render time, never baked into the
906
+ * constant (AGENTS — styling orthogonal to data; the rework fixes the terminal's ANSI-in-the-icon).
907
+ * Frozen.
908
+ *
909
+ * @remarks
910
+ * - `question` — the leading mark on a prompt's message line.
911
+ * - `pointer` — the cursor before the input / the focused choice row.
912
+ * - `dot` / `selected` — an unfocused / focused row marker in a select list.
913
+ * - `checked` / `unchecked` — a checked / unchecked box in a checkbox list.
914
+ */
915
+ export declare const PROMPT_ICONS: Readonly<{
916
+ question: "?";
917
+ pointer: "›";
918
+ dot: "○";
919
+ selected: "●";
920
+ checked: "☑";
921
+ unchecked: "☐";
922
+ }>;
923
+
924
+ /** A choice item in a {@link SelectOptions} prompt — its displayed `name`, its resolved `value`, and an optional one-line `description`. */
925
+ export declare interface PromptChoice {
926
+ readonly name: string;
927
+ readonly value: string;
928
+ readonly description?: string;
929
+ }
930
+
931
+ /**
932
+ * The SSE prompt BRIDGE (observable §13) — the client-side counterpart to
933
+ * {@link import('./Prompt.js').Prompt}. Connects to a remote broker's SSE endpoint, receives
934
+ * serialized pending prompts, dispatches EACH to a LOCAL {@link PromptFormInterface} terminal (so
935
+ * a human at THIS machine answers a prompt parked elsewhere), and POSTs the answer back.
936
+ * Universal — `fetch` + SSE are web-standard, so it runs in a browser or on a server; the
937
+ * injected `fetch` / timer make it fully deterministic in tests.
938
+ *
939
+ * @remarks
940
+ * - **Connect + reconnect.** {@link connect} opens the SSE stream (via the injected `fetch` + the
941
+ * core `SSEParser`) and loops, reconnecting after the stream drops with the `delay` backoff
942
+ * (driven by the injected timer) — unless `reconnect` is `false`, the client was
943
+ * {@link destroy}ed, or the drop was a deliberate {@link disconnect} (an abort).
944
+ * - **Dispatch + answer.** Each decoded `pending` event is narrowed to a `PendingPrompt` (§14 —
945
+ * never an `as`), dispatched to `terminal`, and its resolved value POSTed back to `url`.
946
+ * Dispatch is strictly SERIAL: the read loop drives the terminal for ONE prompt at a time and
947
+ * only reads/dispatches the next event after the current prompt fully settles (its answer
948
+ * POSTed). A prompt id redelivered (e.g. after a reconnect) WHILE its prior dispatch is still
949
+ * in flight is skipped (a `#seen` set tracks in-flight ids); once that dispatch settles (or the
950
+ * broker sends `expire` for the id) the id is dropped from `#seen`, so a later redelivery of the
951
+ * SAME id after completion is dispatched again — the client does not dedupe across completion.
952
+ * - **Server signals.** An `expire` event (the broker dropped a parked prompt) emits `expire`; a
953
+ * `shutdown` event calls {@link disconnect} (not {@link destroy}) — the client stops streaming
954
+ * without auto-reconnect but stays reusable; a later {@link connect} recovers it.
955
+ * - **Lean events (§13).** `connect` / `disconnect` / `expire` / `error` — errors are `unknown`.
956
+ * `disconnect` fires exactly once per connected-to-disconnected transition, whether triggered by
957
+ * the server ending the stream cleanly or by a deliberate {@link disconnect} / {@link destroy}.
958
+ *
959
+ * @example
960
+ * ```ts
961
+ * const client = createPromptClient({
962
+ * url: 'http://localhost:3001/prompts',
963
+ * terminal: createTerminal(), // a local PromptFormInterface (T-c)
964
+ * on: { connect: () => log('connected') },
965
+ * })
966
+ * await client.connect() // streams remote prompts to the local terminal, POSTs answers back
967
+ * ```
968
+ */
969
+ export declare class PromptClient implements PromptClientInterface {
970
+ #private;
971
+ readonly url: string;
972
+ constructor(options: PromptClientOptions);
973
+ get emitter(): EmitterInterface<PromptClientEventMap>;
974
+ get connected(): boolean;
975
+ connect(): Promise<void>;
976
+ disconnect(): void;
977
+ destroy(): void;
978
+ }
979
+
980
+ /**
981
+ * The client's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
982
+ *
983
+ * @remarks
984
+ * - `connect` — the SSE stream opened.
985
+ * - `disconnect` — the SSE stream closed (the server ended it, or {@link PromptClientInterface.disconnect}).
986
+ * - `expire` — the remote broker signalled a parked prompt expired (carries its `id`).
987
+ * - `error` — a connection / dispatch / POST fault (errors are `unknown`).
988
+ */
989
+ export declare type PromptClientEventMap = {
990
+ readonly connect: readonly [];
991
+ readonly disconnect: readonly [];
992
+ readonly expire: readonly [id: string];
993
+ readonly error: readonly [error: unknown];
994
+ };
995
+
996
+ /**
997
+ * The SSE prompt BRIDGE (observable §13) — the client-side counterpart to {@link PromptInterface}.
998
+ * Connects to a remote broker's SSE endpoint, receives serialized {@link PendingPrompt}s,
999
+ * dispatches EACH to a local {@link PromptFormInterface} terminal, and POSTs the answer back —
1000
+ * so a human at this machine answers prompts a broker parked elsewhere.
1001
+ *
1002
+ * @remarks
1003
+ * - **Connect.** {@link connect} opens the SSE stream (via the injected `fetch` + the core
1004
+ * `SSEParser`) and resolves when the stream ends; it reconnects with the `delay` backoff
1005
+ * unless `reconnect` is `false` or the client was {@link destroy}ed.
1006
+ * - **Dispatch + answer.** Each decoded prompt is narrowed (§14) and dispatched to `terminal`;
1007
+ * the resolved value POSTs back to `url`.
1008
+ * - **`connected`** reflects whether the stream is currently open.
1009
+ */
1010
+ export declare interface PromptClientInterface {
1011
+ readonly emitter: EmitterInterface<PromptClientEventMap>;
1012
+ readonly url: string;
1013
+ readonly connected: boolean;
1014
+ connect(): Promise<void>;
1015
+ disconnect(): void;
1016
+ destroy(): void;
1017
+ }
1018
+
1019
+ /**
1020
+ * Options for {@link import('./factories.js').createPromptClient} / the {@link PromptClientInterface}.
1021
+ *
1022
+ * @remarks
1023
+ * - `url` — the remote broker's SSE endpoint (GET opens the stream; answers POST back to it).
1024
+ * - `terminal` — the LOCAL {@link PromptFormInterface} each remote prompt is dispatched to, so a
1025
+ * human at THIS machine answers a prompt issued elsewhere.
1026
+ * - `token` — an optional auth token, sent as the
1027
+ * {@link import('./constants.js').HEADER_TOKEN} header on every request.
1028
+ * - `reconnect` — whether to reconnect after the stream drops (default `true`).
1029
+ * - `delay` — ms to wait before each reconnect attempt (default
1030
+ * {@link import('./constants.js').DEFAULT_RECONNECT_DELAY_MS}).
1031
+ * - `on` — initial {@link PromptClientEventMap} listeners (AGENTS §8/§13).
1032
+ * - `error` — the emitter's listener-error handler (AGENTS §13).
1033
+ * - `fetch` — the injected {@link FetchHandler} (default the global `fetch`); supply a scripted
1034
+ * fetch to drive the client deterministically in tests.
1035
+ * - `timer` — the injected {@link TimerHandler} for the reconnect backoff (default the host
1036
+ * `setTimeout`); supply a deterministic timer to drive reconnection without real time.
1037
+ */
1038
+ export declare interface PromptClientOptions {
1039
+ readonly url: string;
1040
+ readonly terminal: PromptFormInterface;
1041
+ readonly token?: string;
1042
+ readonly reconnect?: boolean;
1043
+ readonly delay?: number;
1044
+ readonly on?: EmitterHooks<PromptClientEventMap>;
1045
+ readonly error?: EmitterErrorHandler;
1046
+ readonly fetch?: FetchHandler;
1047
+ readonly timer?: TimerHandler_2;
1048
+ }
1049
+
1050
+ /**
1051
+ * The broker's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
1052
+ *
1053
+ * @remarks
1054
+ * - `pending` — a prompt was parked (carries the wire-safe {@link PendingPrompt}); a transport
1055
+ * forwards it to remote clients.
1056
+ * - `answer` — a parked prompt was answered (carries its `id` + the accepted `value`).
1057
+ * - `expire` — a parked prompt timed out (or was torn down) unanswered (carries its `id`).
1058
+ */
1059
+ export declare type PromptEventMap = {
1060
+ readonly pending: readonly [prompt: PendingPrompt];
1061
+ readonly answer: readonly [id: string, value: unknown];
1062
+ readonly expire: readonly [id: string];
1063
+ };
1064
+
1065
+ /**
1066
+ * The shared ASYNC prompt contract — the six prompt forms as Promise-returning methods. The
1067
+ * ONE vocabulary BOTH the headless {@link PromptInterface} broker (this module) and the server
1068
+ * `Terminal` driver (T-c) implement, so a prompt issued through this surface resolves the same
1069
+ * way on every surface (local TTY, headless broker, remote bridge).
1070
+ *
1071
+ * @remarks
1072
+ * Each method takes the prompt's `*Options` and resolves to that form's value type:
1073
+ * - `input` / `password` / `select` / `editor` → a `string`
1074
+ * - `confirm` → a `boolean`
1075
+ * - `checkbox` → a `readonly string[]` (the checked values, in choice order)
1076
+ *
1077
+ * This is a behavioral CONTRACT (the method surface), not an observable entity — the broker and
1078
+ * the driver add their own emitter / lifecycle on top. The {@link PromptClient} dispatches a
1079
+ * remote {@link PendingPrompt} to a LOCAL `PromptFormInterface` (so a human at this machine
1080
+ * answers a prompt parked elsewhere).
1081
+ */
1082
+ export declare interface PromptFormInterface {
1083
+ input(options: InputOptions): Promise<string>;
1084
+ password(options: PasswordOptions): Promise<string>;
1085
+ confirm(options: ConfirmOptions): Promise<boolean>;
1086
+ select(options: SelectOptions): Promise<string>;
1087
+ checkbox(options: CheckboxOptions): Promise<readonly string[]>;
1088
+ editor(options: EditorOptions): Promise<string>;
1089
+ }
1090
+
1091
+ /**
1092
+ * The union of every prompt form's options bag — the `options` a {@link ParkRequest} carries,
1093
+ * narrowed at the call site by the paired {@link PromptType}.
1094
+ */
1095
+ export declare type PromptFormOptions = InputOptions | PasswordOptions | ConfirmOptions | SelectOptions | CheckboxOptions | EditorOptions;
1096
+
1097
+ /** The styled prompt-message header (`? message`) — the leading line every active prompt view shares. */
1098
+ export declare function promptHeader(styler: StylerInterface, message: string): string;
1099
+
1100
+ /**
1101
+ * The headless prompt BROKER (observable §13) — implements {@link PromptFormInterface} by
1102
+ * PARKING each call as a {@link PendingPrompt} and returning a Promise that resolves when the
1103
+ * prompt is {@link answer}ed (or rejects on timeout). The local-TTY / headless / remote
1104
+ * tri-surface's headless arm: no terminal here — a transport forwards each `pending` to whoever
1105
+ * can answer, and {@link answer} resolves the parked Promise.
1106
+ *
1107
+ * @remarks
1108
+ * - **Park-as-Promise.** Each `input` / `password` / … call mints an id, parks a
1109
+ * {@link PendingPrompt}, emits `pending`, and returns an unresolved Promise. {@link park} is the
1110
+ * general entry point the six form methods wrap.
1111
+ * - **Answer validates.** {@link answer} validates `value` against the prompt's resolved
1112
+ * validator AND type-checks it to the prompt form before accepting; a bad answer is rejected
1113
+ * (an {@link AnswerResult} failure, the prompt stays `pending`).
1114
+ * - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms — `expire`
1115
+ * fires and the parked Promise rejects (a {@link import('./errors.js').TerminalError}). The
1116
+ * timer is injectable for deterministic tests.
1117
+ * - **Accessors (§9.1).** `pending()` lists the parked prompts; `pending(id)` looks one up.
1118
+ */
1119
+ export declare interface PromptInterface extends PromptFormInterface {
1120
+ readonly emitter: EmitterInterface<PromptEventMap>;
1121
+ readonly count: number;
1122
+ park(request: ParkRequest): Ticket;
1123
+ pending(): readonly PendingPrompt[];
1124
+ pending(id: string): PendingPrompt | undefined;
1125
+ answer(id: string, value: unknown): AnswerResult;
1126
+ destroy(): void;
1127
+ }
1128
+
1129
+ /**
1130
+ * Options for {@link import('./factories.js').createPrompt} / the {@link PromptInterface} broker.
1131
+ *
1132
+ * @remarks
1133
+ * - `on` — initial {@link PromptEventMap} listeners (AGENTS §8/§13).
1134
+ * - `error` — the emitter's listener-error handler (AGENTS §13).
1135
+ * - `timeout` — ms a parked prompt waits before it expires + its Promise rejects (default
1136
+ * {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}).
1137
+ * - `timer` — the injected {@link TimerHandler} (default the host `setTimeout`); supply a
1138
+ * deterministic timer to drive expiry in tests without real time.
1139
+ * - `cap` — the maximum number of prompts this broker will hold PARKED at once (default
1140
+ * UNBOUNDED — no cap unless supplied). Once `count` reaches `cap`, a new park is rejected with
1141
+ * a {@link import('./errors.js').TerminalError} (`code: 'LIMIT'`) WITHOUT parking, minting an
1142
+ * id, emitting `pending`, or arming the expiry timer — the runaway-asker memory ceiling.
1143
+ */
1144
+ export declare interface PromptOptions {
1145
+ readonly on?: EmitterHooks<PromptEventMap>;
1146
+ readonly error?: EmitterErrorHandler;
1147
+ readonly timeout?: number;
1148
+ readonly timer?: TimerHandler_2;
1149
+ readonly cap?: number;
1150
+ }
1151
+
1152
+ /**
1153
+ * The discriminant of a {@link PromptStep} — where the prompt stands after a key. `active`:
1154
+ * keep prompting (the input was consumed, or rejected by validation). `submit`: the prompt
1155
+ * resolved with its `value`. `cancel`: the user aborted (ctrl-c). Names its axis (the prompt's
1156
+ * progression), never `kind` (AGENTS §4.4).
1157
+ */
1158
+ export declare type PromptStatus = 'active' | 'submit' | 'cancel';
1159
+
1160
+ /**
1161
+ * The result of one reducer step — the next `state`, the rendered `view`, the `status`, and,
1162
+ * on `submit`, the resolved `value`. The whole contract between a pure prompt reducer and the
1163
+ * impure driver: the driver applies the next `state`, writes the `view`, and — when `status`
1164
+ * is `submit` — reads `value`.
1165
+ *
1166
+ * @typeParam T - The prompt's resolved value type (`string` for input / password / select /
1167
+ * editor, `boolean` for confirm, `readonly string[]` for checkbox).
1168
+ * @typeParam S - The prompt's concrete state shape ({@link InputState}, {@link SelectState}, …) —
1169
+ * carried directly so `state` stays precisely typed with no union narrowing or assertion.
1170
+ *
1171
+ * @remarks
1172
+ * - `state` — the next immutable prompt state (feed it to the next reduce call). On `submit` /
1173
+ * `cancel` it is the final state.
1174
+ * - `view` — the styled string to render NOW (possibly MULTI-LINE for `select` / `checkbox`),
1175
+ * built through the state's {@link StylerInterface}. On an invalid `submit` it carries the
1176
+ * error; the driver re-renders it each step.
1177
+ * - `status` — the {@link PromptStatus}: `active` to continue, `submit` when resolved, `cancel`
1178
+ * on abort.
1179
+ * - `value` — present ONLY on a `submit` step, carrying the prompt's resolved value.
1180
+ */
1181
+ export declare interface PromptStep<T, S> {
1182
+ readonly state: S;
1183
+ readonly view: string;
1184
+ readonly status: PromptStatus;
1185
+ readonly value?: T;
1186
+ }
1187
+
1188
+ /**
1189
+ * The six prompt KINDS this core provides — the value family a higher-level broker (T-b)
1190
+ * dispatches on. Names the prompt axis; a named value set (not a toggle), so it stays a union.
1191
+ */
1192
+ export declare type PromptType = 'input' | 'password' | 'confirm' | 'select' | 'checkbox' | 'editor';
1193
+
1194
+ /**
1195
+ * The union of a resolved prompt's value shapes — a `string` (`input` / `password` / `select` /
1196
+ * `editor`), a `boolean` (`confirm`), or a `readonly string[]` (`checkbox`, the checked values in
1197
+ * choice order). The type a {@link Ticket}'s `value` Promise resolves to.
1198
+ */
1199
+ export declare type PromptValue = string | boolean | readonly string[];
1200
+
1201
+ /**
1202
+ * Rebuild a wire-decoded `validate` payload into a {@link ValidationRules} bag — the inverse of
1203
+ * {@link serializeValidationRules}. Keeps only the primitive rule values (`boolean` / `number` /
1204
+ * `string`) a serialized prompt could carry; an empty / non-record / all-dropped payload yields
1205
+ * `undefined` (no rules to apply). The client feeds the result back through {@link resolveValidation}.
1206
+ */
1207
+ export declare function reconstructValidationRules(value: unknown): ValidationRules | undefined;
1208
+
1209
+ /** Read a `choices` option as a list of bare strings / full choices — each element narrowed by `guard`, off-shape elements stringified. */
1210
+ export declare function resolveChoices<TChoice extends PromptChoice | CheckboxChoice>(options: Readonly<Record<string, unknown>>, guard: Guard<TChoice>): readonly (string | TChoice)[];
1211
+
1212
+ /** Read one option by key, narrowed by `guard` — `undefined` when absent or off-shape (§14, never an `as`). */
1213
+ export declare function resolveOption<T>(options: Readonly<Record<string, unknown>>, key: string, guard: Guard<T>): T | undefined;
1214
+
1215
+ /**
1216
+ * Compile a {@link Validator} or declarative {@link ValidationRules} (or nothing) into ONE
1217
+ * composed {@link Validator}. A bare validator passes through; rules are appended in the fixed
1218
+ * order (required → minimum → maximum → pattern → email → url → numeric → integer → alphanumeric
1219
+ * → custom) and composed; absent / empty input yields an always-passing validator. Pure.
1220
+ *
1221
+ * @remarks
1222
+ * Unlike a prior variant (which returned `Validator | undefined`), this ALWAYS returns a
1223
+ * `Validator` — an absent or empty rule set yields a validator that returns `true` for every
1224
+ * input. That keeps a prompt's state unconditional (it always holds a real validator to apply on
1225
+ * submit), with no `undefined` branch at the call site.
1226
+ *
1227
+ * @param validate - A custom {@link Validator}, a {@link ValidationRules} bag, or `undefined`
1228
+ * @returns The composed {@link Validator} (always-passing when nothing was supplied)
1229
+ *
1230
+ * @example
1231
+ * ```ts
1232
+ * const v = resolveValidation({ required: true, minimum: 3 })
1233
+ * v('') // 'This field is required'
1234
+ * v('ab') // 'Must be at least 3 characters'
1235
+ * v('abc') // true
1236
+ * ```
1237
+ */
1238
+ export declare function resolveValidation(validate?: Validator | ValidationRules): Validator;
1239
+
1240
+ /** Carriage return (`\r`, U+000D) — Enter on most terminals. */
1241
+ export declare const RETURN: string;
1242
+
1243
+ /**
1244
+ * Each built-in validation rule's default error message — what the composed {@link
1245
+ * import('./types.js').Validator} returns when that rule fails (the `minimum` / `maximum`
1246
+ * messages are interpolated with the configured length at build time). Frozen; the source of
1247
+ * truth for the rule-failure copy.
1248
+ */
1249
+ export declare const RULE_MESSAGES: Readonly<{
1250
+ required: "This field is required";
1251
+ minimum: "Must be at least {count} characters";
1252
+ maximum: "Must be at most {count} characters";
1253
+ pattern: "Must match pattern: {pattern}";
1254
+ email: "Must be a valid email address";
1255
+ url: "Must be a valid URL";
1256
+ numeric: "Must be a numeric value";
1257
+ integer: "Must be an integer";
1258
+ alphanumeric: "Must contain only letters and digits";
1259
+ invalid: "Invalid input";
1260
+ }>;
1261
+
1262
+ /**
1263
+ * Sanitize a list of resolved choices' human-readable labels (`name` + `description`) with
1264
+ * {@link stripControls} — shared by the `select` and `checkbox` branches of
1265
+ * {@link dispatchPendingPrompt} so a remote-supplied choice can never inject raw control bytes
1266
+ * into the local terminal's rendered view.
1267
+ *
1268
+ * @param choices - The resolved choices (bare strings or full {@link PromptChoice} /
1269
+ * {@link CheckboxChoice} objects) as returned by {@link resolveChoices}
1270
+ * @returns The same choices with every `name` / `description` control-stripped
1271
+ */
1272
+ export declare function sanitizeChoiceLabels<TChoice extends PromptChoice | CheckboxChoice>(choices: readonly (string | TChoice)[]): readonly (string | TChoice)[];
1273
+
1274
+ /**
1275
+ * Options for a single-selection {@link import('./helpers.js').selectReduce} prompt.
1276
+ *
1277
+ * @remarks
1278
+ * `choices` accepts bare strings (used as both `name` and `value`) or full {@link PromptChoice}
1279
+ * objects; {@link import('./helpers.js').createSelectState} normalizes them. `default` pre-focuses
1280
+ * the choice whose `value` matches it.
1281
+ */
1282
+ export declare interface SelectOptions {
1283
+ readonly message: string;
1284
+ readonly choices: readonly (string | PromptChoice)[];
1285
+ readonly default?: string;
1286
+ readonly styler?: StylerInterface;
1287
+ }
1288
+
1289
+ /**
1290
+ * Advance a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
1291
+ * reducer. `up` / `down` (and `k` / `j`) move the focus, WRAPPING at the ends; return submits the
1292
+ * focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer
1293
+ * guards against it); any other key is ignored.
1294
+ */
1295
+ export declare function selectReduce(state: SelectState, key: KeyEvent): PromptStep<string, SelectState>;
1296
+
1297
+ /**
1298
+ * The immutable state of a select prompt — the normalized choices, the styler, and the
1299
+ * `focused` index (the highlighted row).
1300
+ *
1301
+ * @remarks
1302
+ * Built by {@link import('./helpers.js').createSelectState}; advanced by
1303
+ * {@link import('./helpers.js').selectReduce}. `up` / `down` move `focused` (wrapping at the
1304
+ * ends); `return` submits the focused choice's `value`.
1305
+ */
1306
+ export declare interface SelectState {
1307
+ readonly message: string;
1308
+ readonly choices: readonly PromptChoice[];
1309
+ readonly styler: StylerInterface;
1310
+ readonly focused: number;
1311
+ }
1312
+
1313
+ /** Render a {@link SelectState} as a MULTI-LINE styled view — the header followed by one row per choice, the focused row marked. */
1314
+ export declare function selectView(state: SelectState): string;
1315
+
1316
+ /**
1317
+ * The exact escape SEQUENCE → canonical key NAME table {@link import('./helpers.js').parseKey}
1318
+ * consults for the navigation / editing keys. Covers BOTH the CSI form (`ESC[A`…) and the SS3
1319
+ * form (`ESCOA`…) of the four arrows, plus the `home` / `end` / `delete` CSI sequences (with
1320
+ * their numeric-tilde variants). The source of truth for the multi-byte key decode; frozen.
1321
+ *
1322
+ * @remarks
1323
+ * Terminals disagree on these: a cursor key is `ESC[A` (normal) or `ESCOA` (application mode),
1324
+ * and Home / End / Delete each have a letter form (`ESC[H` / `ESC[F`) and a numeric form
1325
+ * (`ESC[1~` / `ESC[4~` / `ESC[3~`). Every accepted spelling maps to one name so a reducer never
1326
+ * sees the wire encoding.
1327
+ */
1328
+ export declare const SEQUENCE_NAMES: Readonly<Record<string, string>>;
1329
+
1330
+ /** Strip functions from a `choices` option — each choice keeps its plain fields; a bare string passes through. */
1331
+ export declare function serializeChoices(choices: unknown): readonly unknown[];
1332
+
1333
+ /** Serialize a parked prompt's expiry into a {@link WireEvent} — event `'expire'`, `data` the JSON-stringified `{ id }` payload. */
1334
+ export declare function serializeExpire(id: string): WireEvent;
1335
+
1336
+ /** Serialize a parked {@link PendingPrompt} into a {@link WireEvent} — event `'pending'`, `data` the JSON-stringified prompt, `id` the prompt's own id (an SSE bridge sets its frame `id:` from this). */
1337
+ export declare function serializePending(prompt: PendingPrompt): WireEvent;
1338
+
1339
+ /**
1340
+ * Produce the WIRE-SAFE form of a prompt's options — the {@link PendingPrompt.options} a broker
1341
+ * serializes over SSE. Drops everything that cannot cross the wire (a `styler`, and any
1342
+ * function-valued `validate` rule), while KEEPING the declarative data a remote client needs to
1343
+ * reconstruct the prompt: the {@link ValidationRules} as data, plus `message` / `choices` /
1344
+ * `default` / `mask` / `min` / `max`. This is WHY T-a's validation is rules-as-data: a `Validator`
1345
+ * FUNCTION can't be serialized, but its declarative rules can — the client rebuilds the validator
1346
+ * from them via {@link resolveValidation}.
1347
+ *
1348
+ * @remarks
1349
+ * - **Functions are dropped.** A top-level function value (e.g. a bare-`Validator` `validate`) is
1350
+ * omitted entirely; the `styler` (a fluent function-bearing object) is dropped by key.
1351
+ * - **`validate` rules are flattened.** A {@link ValidationRules} bag is copied with each
1352
+ * function rule replaced by `true` (the rule's INTENT survives as the built-in check; its
1353
+ * custom function does not). A bare-function `validate` is dropped (no declarative form to keep).
1354
+ * - **`choices` are function-stripped.** Each choice keeps its plain fields (`name` / `value` /
1355
+ * `description` / `checked`); a bare string passes through.
1356
+ *
1357
+ * @param options - The raw prompt options bag (may hold functions / a styler)
1358
+ * @returns A JSON-safe options record — only serializable, declarative data
1359
+ */
1360
+ export declare function serializePromptOptions(options: object): Readonly<Record<string, unknown>>;
1361
+
1362
+ /** The {@link WireEvent} a broker/manager sends when it is going away — event `'shutdown'`, no payload. */
1363
+ export declare function serializeShutdown(): WireEvent;
1364
+
1365
+ /**
1366
+ * Flatten a `validate` option to its wire-safe {@link ValidationRules} DATA — a function rule
1367
+ * becomes `true` (its built-in check survives; its body cannot cross the wire). A bare-function
1368
+ * `validate` (no rules object) has no declarative form, so it yields `undefined` (dropped).
1369
+ */
1370
+ export declare function serializeValidationRules(validate: unknown): Readonly<Record<string, unknown>> | undefined;
1371
+
1372
+ /** Space (U+0020). */
1373
+ export declare const SPACE = " ";
1374
+
1375
+ /**
1376
+ * The maximum number of characters the {@link import('./types.js').PromptClient} lets its
1377
+ * SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any
1378
+ * legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated
1379
+ * or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).
1380
+ */
1381
+ export declare const SSE_BUFFER_LIMIT = 1048576;
1382
+
1383
+ /**
1384
+ * The SSE `event:` names the broker emits and the {@link import('./types.js').PromptClient}
1385
+ * dispatches on. Frozen; the source of truth for the wire event vocabulary.
1386
+ *
1387
+ * @remarks
1388
+ * - `pending` — a serialized {@link import('./types.js').PendingPrompt} to dispatch + answer.
1389
+ * - `expire` — an `{ id }` payload: the broker expired a parked prompt (the client drops it).
1390
+ * - `shutdown` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.
1391
+ */
1392
+ export declare const SSE_EVENTS: Readonly<{
1393
+ pending: "pending";
1394
+ expire: "expire";
1395
+ shutdown: "shutdown";
1396
+ }>;
1397
+
1398
+ /** The styled submit line (`✔ message`) — the committed header an interactive prompt shows once resolved. */
1399
+ export declare function submitHeader(styler: StylerInterface, message: string): string;
1400
+
1401
+ /** Tab (`\t`, U+0009). */
1402
+ export declare const TAB: string;
1403
+
1404
+ /** The rejection reason a {@link TerminalManagerInterface.answer} call returns — an {@link AnswerError}, plus `'terminal'` (no such endpoint). */
1405
+ export declare type TerminalAnswerError = AnswerError | 'terminal';
1406
+
1407
+ /** The outcome of a {@link TerminalManagerInterface.answer} call — the accepted `value` on success, else the {@link TerminalAnswerError}. */
1408
+ export declare type TerminalAnswerResult = {
1409
+ readonly success: true;
1410
+ readonly value: unknown;
1411
+ } | {
1412
+ readonly success: false;
1413
+ readonly error: TerminalAnswerError;
1414
+ };
1415
+
1416
+ /**
1417
+ * An error a {@link import('./Prompt.js').Prompt} broker rejects a parked prompt's Promise with.
1418
+ *
1419
+ * @remarks
1420
+ * Carries a {@link TerminalErrorCode} and an optional `context` bag (the prompt `id`). Thrown —
1421
+ * as a Promise rejection on the awaited prompt call — when a parked prompt is not answered before
1422
+ * its `timeout`, or when the broker is `destroy`ed while the prompt is still `pending` (both
1423
+ * `EXPIRE`); or when the user aborts an interactive server-`Terminal` prompt with ctrl-c
1424
+ * (`CANCEL`). Narrow a caught value with {@link isTerminalError} and branch on `error.code`.
1425
+ */
1426
+ export declare class TerminalError extends Error {
1427
+ /** The machine-readable condition — see {@link TerminalErrorCode}. */
1428
+ readonly code: TerminalErrorCode;
1429
+ /** An optional context bag naming the offending prompt id. */
1430
+ readonly context?: Readonly<Record<string, unknown>>;
1431
+ constructor(code: TerminalErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
1432
+ }
1433
+
1434
+ /**
1435
+ * The machine-readable condition carried by a {@link import('./errors.js').TerminalError} — the
1436
+ * axis a `catch` branches on. Names its axis (the failure condition), never `kind` (AGENTS §4.4).
1437
+ *
1438
+ * @remarks
1439
+ * - `EXPIRE` — a parked broker prompt was not answered before its `timeout` (or the broker was
1440
+ * `destroy`ed while it was still pending); the prompt's Promise rejects with this.
1441
+ * - `CANCEL` — the user aborted an interactive prompt (ctrl-c) at the server `Terminal` (T-c)
1442
+ * driver; the awaited prompt call rejects with this so a caller can branch on `error.code`.
1443
+ * - `LIMIT` — a broker's optional `cap` on concurrently-parked prompts was already reached; the
1444
+ * new call is rejected WITHOUT parking (never counted, never emitted, no timer armed).
1445
+ * - `DESTROYED` — a call reached an already-`destroy`ed {@link TerminalManagerInterface} (`add`
1446
+ * at entry, or `open` whose `store` read resolved after a `destroy` happened in the gap).
1447
+ */
1448
+ export declare type TerminalErrorCode = 'EXPIRE' | 'CANCEL' | 'DRIVER' | 'DEADLOCK' | 'TARGET' | 'LIMIT' | 'DESTROYED';
1449
+
1450
+ /**
1451
+ * The multi-endpoint terminal MANAGER — a registry of named {@link PromptInterface} brokers (one
1452
+ * per endpoint), so several parties can `ask` prompts of each other by NAME with a `from` → `to`
1453
+ * attribution edge on every parked prompt, and a transitive DEADLOCK check across all in-flight
1454
+ * asks.
1455
+ *
1456
+ * @remarks
1457
+ * - **Registry.** `add(name, options?)` mints (or, if `name` is already mounted, returns the
1458
+ * EXISTING broker UNCHANGED — idempotent, never clobbers a live/parked endpoint). Every mounted
1459
+ * broker's `pending` / `answer` / `expire` events are re-emitted on the manager, attributed by
1460
+ * `name`.
1461
+ * - **`ask`.** The target must already be mounted via {@link add} — `ask` never auto-adds it;
1462
+ * rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking
1463
+ * `from → to` would close a cycle over the CURRENT in-flight edge set (walked transitively);
1464
+ * otherwise parks through the target's broker and resolves with the ORIGINAL ticket Promise (edge
1465
+ * cleanup is attached via `.then`, never altering the value/rejection the caller observes).
1466
+ * - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked
1467
+ * Promises are process-bound and never resurrected); `save(name)` persists the endpoint's
1468
+ * configured `timeout`.
1469
+ * - **Removal.** `remove` drops one endpoint or a batch (§9.2, array overload FIRST), destroying
1470
+ * each broker (which expires every prompt still parked on it). `clear` drops all; `destroy` is
1471
+ * idempotent.
1472
+ *
1473
+ * @example
1474
+ * ```ts
1475
+ * const manager = new TerminalManager()
1476
+ * manager.add('agent')
1477
+ * const name = manager.ask('user', 'agent', 'input', { message: 'Your name' })
1478
+ * manager.answer('agent', manager.pending('agent')[0].id, 'Ada')
1479
+ * await name // 'Ada'
1480
+ * ```
1481
+ */
1482
+ export declare class TerminalManager implements TerminalManagerInterface {
1483
+ #private;
1484
+ constructor(options?: TerminalManagerOptions);
1485
+ get emitter(): EmitterInterface<TerminalManagerEventMap>;
1486
+ get count(): number;
1487
+ terminal(name: string): PromptInterface | undefined;
1488
+ terminals(): readonly string[];
1489
+ add(name: string, options?: TerminalOptions): PromptInterface;
1490
+ ask(from: string, to: string, form: 'input' | 'password' | 'editor', options: InputOptions | PasswordOptions | EditorOptions): Promise<string>;
1491
+ ask(from: string, to: string, form: 'confirm', options: ConfirmOptions): Promise<boolean>;
1492
+ ask(from: string, to: string, form: 'select', options: SelectOptions): Promise<string>;
1493
+ ask(from: string, to: string, form: 'checkbox', options: CheckboxOptions): Promise<readonly string[]>;
1494
+ pending(): readonly PendingPrompt[];
1495
+ pending(to: string): readonly PendingPrompt[];
1496
+ answer(to: string, id: string, value: unknown): TerminalAnswerResult;
1497
+ open(name: string): Promise<PromptInterface | undefined>;
1498
+ save(name: string): Promise<boolean>;
1499
+ remove(names: readonly string[]): boolean;
1500
+ remove(name: string): boolean;
1501
+ clear(): void;
1502
+ destroy(): void;
1503
+ }
1504
+
1505
+ /**
1506
+ * The manager's event map (AGENTS §13) — the name-attributed re-emission of every mounted
1507
+ * broker's events, so a caller subscribes once for ALL endpoints instead of per-broker.
1508
+ *
1509
+ * @remarks
1510
+ * - `pending` — an endpoint parked a prompt (carries the {@link PendingPrompt}, itself carrying
1511
+ * `from` / `to`).
1512
+ * - `answer` — an endpoint's parked prompt was answered (`to` names the endpoint).
1513
+ * - `expire` — an endpoint's parked prompt timed out (`to` names the endpoint).
1514
+ */
1515
+ export declare type TerminalManagerEventMap = {
1516
+ readonly pending: readonly [prompt: PendingPrompt];
1517
+ readonly answer: readonly [to: string, id: string, value: unknown];
1518
+ readonly expire: readonly [to: string, id: string];
1519
+ };
1520
+
1521
+ /**
1522
+ * The multi-endpoint terminal MANAGER (§9.1/§9.2) — a registry of named {@link PromptInterface}
1523
+ * brokers (one per endpoint), so several parties (agents, tools, humans) can `ask` prompts of
1524
+ * each other by NAME, attributed with a `from` → `to` edge on every parked {@link PendingPrompt}.
1525
+ *
1526
+ * @remarks
1527
+ * - **Accessors (§9.1).** `terminal(name)` looks up one endpoint's broker; `terminals()` lists
1528
+ * every mounted endpoint name.
1529
+ * - **`add`** mints (or returns the existing) broker for `name` — idempotent, never clobbers a
1530
+ * live endpoint.
1531
+ * - **`ask`** is the attributed convenience: parks a prompt from `from` to `to` (auto-`add`ing
1532
+ * `to` if absent) and resolves with the typed value, precisely overloaded per {@link PromptType}.
1533
+ * - **`pending()`** lists every endpoint's parked prompts; `pending(to)` scopes to one endpoint.
1534
+ * - **`answer`** routes to the named endpoint's broker.
1535
+ * - **`open`** restores (or returns the live) broker for `name` from the `store`.
1536
+ * - **`save`** persists an endpoint's config snapshot to the `store` (`false` when there is no
1537
+ * store, or `name` is unknown).
1538
+ * - **Batch `remove` (§9.2).** The array overload is declared FIRST — `remove(names)` removes each
1539
+ * listed endpoint (`true` when any of the named terminals was removed); `remove(name)` removes one.
1540
+ * - **`clear`** removes every endpoint without destroying the manager; **`destroy`** tears down
1541
+ * every broker, then the manager's own emitter.
1542
+ */
1543
+ export declare interface TerminalManagerInterface {
1544
+ readonly emitter: EmitterInterface<TerminalManagerEventMap>;
1545
+ readonly count: number;
1546
+ terminal(name: string): PromptInterface | undefined;
1547
+ terminals(): readonly string[];
1548
+ add(name: string, options?: TerminalOptions): PromptInterface;
1549
+ ask(from: string, to: string, form: 'input' | 'password' | 'editor', options: InputOptions | PasswordOptions | EditorOptions): Promise<string>;
1550
+ ask(from: string, to: string, form: 'confirm', options: ConfirmOptions): Promise<boolean>;
1551
+ ask(from: string, to: string, form: 'select', options: SelectOptions): Promise<string>;
1552
+ ask(from: string, to: string, form: 'checkbox', options: CheckboxOptions): Promise<readonly string[]>;
1553
+ pending(): readonly PendingPrompt[];
1554
+ pending(to: string): readonly PendingPrompt[];
1555
+ answer(to: string, id: string, value: unknown): TerminalAnswerResult;
1556
+ open(name: string): Promise<PromptInterface | undefined>;
1557
+ save(name: string): Promise<boolean>;
1558
+ remove(names: readonly string[]): boolean;
1559
+ remove(name: string): boolean;
1560
+ clear(): void;
1561
+ destroy(): void;
1562
+ }
1563
+
1564
+ /**
1565
+ * Options for {@link import('./factories.js').createTerminalManager} / the
1566
+ * {@link TerminalManagerInterface}.
1567
+ *
1568
+ * @remarks
1569
+ * - `store` — the optional {@link TerminalStoreInterface} backing `open` / `save`.
1570
+ * - `timeout` / `timer` / `cap` — the manager-wide default for each endpoint's broker (overridable
1571
+ * per {@link TerminalManagerInterface.add} call via {@link TerminalOptions}, exactly like
1572
+ * `timeout` / `timer`).
1573
+ * - `on` / `error` — the manager's {@link EmitterHooks} + {@link EmitterErrorHandler} (AGENTS §13).
1574
+ */
1575
+ export declare interface TerminalManagerOptions {
1576
+ readonly store?: TerminalStoreInterface;
1577
+ readonly timeout?: number;
1578
+ readonly timer?: TimerHandler_2;
1579
+ readonly cap?: number;
1580
+ readonly on?: EmitterHooks<TerminalManagerEventMap>;
1581
+ readonly error?: EmitterErrorHandler;
1582
+ }
1583
+
1584
+ /** Options for {@link import('./factories.js').createTerminal} / a manager-owned {@link PromptInterface} broker — the per-endpoint `timeout` + injected `timer` + optional `cap` on concurrently-parked prompts. */
1585
+ export declare interface TerminalOptions {
1586
+ readonly timeout?: number;
1587
+ readonly timer?: TimerHandler_2;
1588
+ readonly cap?: number;
1589
+ }
1590
+
1591
+ /** One endpoint's persisted CONFIG snapshot — `id` is the endpoint name; `timeout` its configured default. Parked Promises are process-bound and are never resurrected — `open` always restores an EMPTY broker. */
1592
+ export declare interface TerminalSnapshot {
1593
+ readonly id: string;
1594
+ readonly timeout?: number;
1595
+ }
1596
+
1597
+ /** One opaque persisted row — the shape a `TableInterface<TerminalSnapshotRow>`-backed store reads/writes; `snapshot` is narrowed with {@link import('./validators.js').isTerminalSnapshot} on read. */
1598
+ export declare interface TerminalSnapshotRow {
1599
+ readonly id: string;
1600
+ readonly snapshot: unknown;
1601
+ }
1602
+
1603
+ /**
1604
+ * The point-access persistence seam (AGENTS §5 — Stores) for a {@link TerminalManagerInterface}'s
1605
+ * endpoint configs. Every primitive is async; `delete` of an absent id is a no-op.
1606
+ */
1607
+ export declare interface TerminalStoreInterface {
1608
+ get(id: string): Promise<TerminalSnapshot | undefined>;
1609
+ set(snapshot: TerminalSnapshot): Promise<void>;
1610
+ delete(id: string): Promise<void>;
1611
+ }
1612
+
1613
+ /** The handle {@link PromptInterface.park} returns — the parked prompt's `id` plus the Promise that resolves (or rejects) with its {@link PromptValue}. */
1614
+ export declare interface Ticket {
1615
+ readonly id: string;
1616
+ readonly value: Promise<PromptValue>;
1617
+ }
1618
+
1619
+ /** Cancel a pending {@link TimerHandler} deadline — idempotent, safe to call after the timer fired. */
1620
+ export declare type TimerCancel = () => void;
1621
+
1622
+ /**
1623
+ * One injected timer — arms a deadline `callback` to fire after `ms`, returning a
1624
+ * {@link TimerCancel} that cancels it. The broker's timeout seam: the default wraps the host
1625
+ * `setTimeout` / `clearTimeout`; a test injects a deterministic timer that captures the callback
1626
+ * and fires it on demand (no real time, no global fake-timer patching).
1627
+ */
1628
+ declare type TimerHandler_2 = (callback: () => void, ms: number) => TimerCancel;
1629
+ export { TimerHandler_2 as TimerHandler }
1630
+
1631
+ /** Toggle `index` in a readonly index list — copy-on-write, returning the new sorted-by-insertion list. */
1632
+ export declare function toggleIndex(indices: readonly number[], index: number): readonly number[];
1633
+
1634
+ /** Matches an HTTP(S) URL. The `url` rule tests against this. */
1635
+ export declare const URL_PATTERN: RegExp;
1636
+
1637
+ /**
1638
+ * Declarative validation rules for a text prompt — each key toggles (or overrides) one
1639
+ * built-in check. {@link import('./helpers.js').resolveValidation} compiles a `ValidationRules`
1640
+ * (or a bare {@link Validator}) into ONE composed {@link Validator}.
1641
+ *
1642
+ * @remarks
1643
+ * Each rule is EITHER a primitive that turns on the built-in check, OR a {@link Validator}
1644
+ * that replaces it with custom logic:
1645
+ *
1646
+ * - `required` — `true` ⇒ input must be non-empty after trimming.
1647
+ * - `minimum` — a number ⇒ input must be at least that many characters.
1648
+ * - `maximum` — a number ⇒ input must be at most that many characters.
1649
+ * - `pattern` — a string ⇒ input must match that regex source.
1650
+ * - `email` — `true` ⇒ input must look like an email address.
1651
+ * - `url` — `true` ⇒ input must look like an HTTP(S) URL.
1652
+ * - `numeric` — `true` ⇒ input must be a number (integer or decimal).
1653
+ * - `integer` — `true` ⇒ input must be an integer.
1654
+ * - `alphanumeric` — `true` ⇒ input must contain only letters and digits.
1655
+ * - `custom` — an arbitrary {@link Validator} escape hatch.
1656
+ *
1657
+ * Rules compose in the fixed order above; the FIRST failing rule short-circuits and its
1658
+ * message is returned. A `false` / `undefined` rule is skipped.
1659
+ */
1660
+ export declare interface ValidationRules {
1661
+ readonly required?: boolean | Validator;
1662
+ readonly minimum?: number | Validator;
1663
+ readonly maximum?: number | Validator;
1664
+ readonly pattern?: string | Validator;
1665
+ readonly email?: boolean | Validator;
1666
+ readonly url?: boolean | Validator;
1667
+ readonly numeric?: boolean | Validator;
1668
+ readonly integer?: boolean | Validator;
1669
+ readonly alphanumeric?: boolean | Validator;
1670
+ readonly custom?: Validator;
1671
+ }
1672
+
1673
+ /**
1674
+ * A single input validator — given the current input string, returns `true` when the input
1675
+ * is valid, or an error MESSAGE string when it is not. The atomic unit the validation engine
1676
+ * composes and the prompts apply on submit.
1677
+ *
1678
+ * @remarks
1679
+ * A `Validator` is total and pure — it never throws and never mutates. The `true | string`
1680
+ * shape (not `boolean`) is deliberate: an invalid result CARRIES its message, so a prompt
1681
+ * can render the exact reason. {@link import('./helpers.js').composeValidators} runs several
1682
+ * in order and returns the FIRST error (short-circuiting), so the most specific rule wins.
1683
+ */
1684
+ export declare type Validator = (input: string) => true | string;
1685
+
1686
+ /** One SSE-shaped wire frame — the `event` name, its `data` payload (already JSON-stringified), and an optional `id`. The transport-neutral shape {@link import('./helpers.js').serializePending} / {@link import('./helpers.js').serializeExpire} / {@link import('./helpers.js').serializeShutdown} build, with no `http` dependency. */
1687
+ export declare interface WireEvent {
1688
+ readonly event: string;
1689
+ readonly data: string;
1690
+ readonly id?: string;
1691
+ }
1692
+
1693
+ export { }