@orkestrel/terminal 0.0.8 → 0.0.10

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.
@@ -1,154 +1,71 @@
1
+ import { CheckboxField } from '@orkestrel/form';
2
+ import { ConfirmField } from '@orkestrel/form';
1
3
  import { DriverInterface } from '@orkestrel/database';
4
+ import { EditorField } from '@orkestrel/form';
2
5
  import { EmitterErrorHandler } from '@orkestrel/emitter';
3
6
  import { EmitterHooks } from '@orkestrel/emitter';
4
7
  import { EmitterInterface } from '@orkestrel/emitter';
8
+ import { FieldChoice } from '@orkestrel/form';
9
+ import { FieldError } from '@orkestrel/form';
10
+ import { FormInterface } from '@orkestrel/form';
11
+ import { FormSchema } from '@orkestrel/form';
12
+ import { FormValues } from '@orkestrel/form';
5
13
  import { Guard } from '@orkestrel/contract';
14
+ import { JSONRecord } from '@orkestrel/contract';
15
+ import { PasswordField } from '@orkestrel/form';
6
16
  import { Result } from '@orkestrel/contract';
17
+ import { SelectField } from '@orkestrel/form';
7
18
  import { Style } from '@orkestrel/console';
8
19
  import { StylerInterface } from '@orkestrel/console';
9
20
  import { TableInterface } from '@orkestrel/database';
21
+ import { TextField } from '@orkestrel/form';
10
22
 
11
23
  /** The `Accept` header value that opens the broker's SSE stream. */
12
24
  export declare const ACCEPT_EVENT_STREAM = "text/event-stream";
13
25
 
14
- /** Matches an alphanumeric string (letters and digits only). The `alphanumeric` rule tests against this. */
15
- export declare const ALPHANUMERIC_PATTERN: RegExp;
16
-
17
- /** The rejection reason a bare {@link PromptInterface.answer} returns — `'unknown'` (no such parked prompt) or `'rejected'` (failed validation / type-check). */
18
- export declare type AnswerError = 'unknown' | 'rejected';
19
-
20
26
  /**
21
- * Append a rule-backed {@link Validator} to `validators` when the rule is enabled — a `false` /
22
- * `undefined` `check` is skipped, a function `check` is added verbatim (the custom override), and
23
- * a primitive `check` is wrapped via {@link buildRuleValidator}. Mutates `validators` in place.
24
- */
25
- export declare function appendRule(validators: Validator[], rule: string, check: boolean | number | string | Validator | undefined): void;
27
+ * Why {@link PromptInterface.answer} refused. Names its axis with `reason`.
28
+ *
29
+ * @remarks
30
+ * - `unknown` — no form is parked under that id, or the one that was has already settled.
31
+ * - `rejected` — the authoritative form refused the values, and `errors` is exactly what it
32
+ * reported. A client seeds its local form with the values it sent, applies each failure through
33
+ * the form's `invalidate`, and asks again; the parked form stays parked until it accepts or
34
+ * expires. This is the retry loop that makes a server-side `custom` rule enforceable, because
35
+ * that rule never crossed the wire and the client could not have checked it.
36
+ */
37
+ export declare type AnswerError = {
38
+ readonly reason: 'unknown';
39
+ } | {
40
+ readonly reason: 'rejected';
41
+ readonly errors: readonly FieldError[];
42
+ };
26
43
 
27
44
  /** Backspace (BS, U+0008) — Ctrl+H / some terminals' Backspace. */
28
45
  export declare const BACKSPACE: string;
29
46
 
30
- /** Wrap a named rule + its primitive check into a {@link Validator} (returns `true` or the rule's message). */
31
- export declare function buildRuleValidator(rule: string, check: boolean | number | string): Validator;
32
-
33
- /** A choice item in a {@link CheckboxOptions} prompt — a {@link PromptChoice} plus an optional initial `checked` state. */
34
- export declare interface CheckboxChoice {
35
- readonly name: string;
36
- readonly value: string;
37
- readonly description?: string;
38
- readonly checked?: boolean;
39
- }
40
-
41
- /**
42
- * Options for a multi-selection {@link import('./helpers.js').checkboxReduce} prompt.
43
- *
44
- * @remarks
45
- * `choices` accepts bare strings or full {@link CheckboxChoice} objects (with an optional
46
- * initial `checked`); {@link import('./helpers.js').createCheckboxState} normalizes them.
47
- * `min` / `max` gate submission — a submit with fewer than `min` or more than `max` selected
48
- * is rejected (the prompt stays active with the reason in the view). `hint` adds a key hint after
49
- * the message (no computed default) and reaches the server driver's numbered fallback too.
50
- */
51
- export declare interface CheckboxOptions {
52
- readonly message: string;
53
- readonly choices: ReadonlyArray<string | CheckboxChoice>;
54
- readonly min?: number;
55
- readonly max?: number;
56
- readonly hint?: string;
57
- readonly styler?: StylerInterface;
58
- readonly theme?: PromptThemeOptions;
59
- }
60
-
61
47
  /**
62
48
  * Advance a checkbox prompt by one {@link KeyEvent} — the pure
63
49
  * `(state, key) → PromptStep<readonly string[]>` reducer. `up` / `down` (and `k` / `j`) move the
64
50
  * focus (wrapping); `space` toggles the focused index in the checked set; return submits the
65
- * checked values in CHOICE order — gated by `min` / `max` (an out-of-range count stays active with
66
- * the reason in the view); ctrl-c cancels.
67
- */
68
- export declare function checkboxReduce(state: CheckboxState, key: KeyEvent): PromptStep<readonly string[], CheckboxState>;
69
-
70
- /**
71
- * The immutable state of a checkbox prompt — the normalized choices, the optional hint, the
72
- * resolved styler + theme, the `focused` index, the set of `checked` indices, the optional
73
- * `min` / `max` gate, and the current `error`.
74
- *
75
- * @remarks
76
- * Built by {@link import('./helpers.js').createCheckboxState}; advanced by
77
- * {@link import('./helpers.js').checkboxReduce}. `up` / `down` move `focus` (wrapping); `space`
78
- * toggles the focused index in `checked`; `return` submits the checked values in choice order
79
- * (rejected, with `error`, when the count is outside `[min, max]`). `checked` is modelled as a
80
- * readonly index array (plain JSON data, copy-on-write — no `Set` to clone).
81
- */
82
- export declare interface CheckboxState {
83
- readonly message: string;
84
- readonly choices: readonly CheckboxChoice[];
85
- readonly hint?: string;
86
- readonly styler: StylerInterface;
87
- readonly theme: PromptTheme;
88
- readonly focused: number;
89
- readonly checked: readonly number[];
90
- readonly min?: number;
91
- readonly max?: number;
92
- readonly error?: string;
93
- }
94
-
95
- /** Render a {@link CheckboxState} as a MULTI-LINE styled view — the header (plus any hint), one box per choice (focused + checked marked), a count, and any error. */
96
- export declare function checkboxView(state: CheckboxState): string;
97
-
98
- /**
99
- * Compose several {@link Validator}s into ONE short-circuiting validator — it runs them in order
100
- * and returns the FIRST error message, or `true` when all pass. The empty composition always
101
- * passes.
51
+ * checked values in choice order; ctrl-c cancels. The form applies selection-count rules.
102
52
  */
103
- export declare function composeValidators(...validators: Validator[]): Validator;
53
+ export declare function checkboxReduce(state: ReturnType<typeof createCheckboxState>, key: KeyEvent): PromptStep<readonly string[], ReturnType<typeof createCheckboxState>>;
104
54
 
105
- /**
106
- * Options for a yes/no {@link import('./helpers.js').confirmReduce} confirmation prompt.
107
- *
108
- * @remarks
109
- * `hint` replaces the computed `(Y/n)` group in full, parentheses included, and is painted with
110
- * the theme's `hint` role. The accepted KEYS stay `y` / `n` / `enter` whatever the hint says, so a
111
- * hint that advertises other keys is the consumer's own contradiction.
112
- */
113
- export declare interface ConfirmOptions {
114
- readonly message: string;
115
- readonly default?: boolean;
116
- readonly hint?: string;
117
- readonly styler?: StylerInterface;
118
- readonly theme?: PromptThemeOptions;
119
- }
55
+ /** Render a checkbox-field key state as a multi-line styled view. */
56
+ export declare function checkboxView(state: ReturnType<typeof createCheckboxState>): string;
120
57
 
121
58
  /**
122
59
  * Advance a confirm prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<boolean>`
123
60
  * reducer. `y` / `Y` submits `true`, `n` / `N` submits `false`, return on an empty line submits
124
61
  * the `default`, ctrl-c cancels; any other key is ignored (stays active).
125
62
  */
126
- export declare function confirmReduce(state: ConfirmState, key: KeyEvent): PromptStep<boolean, ConfirmState>;
63
+ export declare function confirmReduce(state: ReturnType<typeof createConfirmState>, key: KeyEvent): PromptStep<boolean, ReturnType<typeof createConfirmState>>;
127
64
 
128
65
  /**
129
- * The immutable state of a confirm prompt — its message, the default answer, the optional hint
130
- * override, and the resolved styler + theme.
131
- *
132
- * @remarks
133
- * Built by {@link import('./helpers.js').createConfirmState}; advanced by
134
- * {@link import('./helpers.js').confirmReduce}. `y` / `Y` submits `true`, `n` / `N` submits
135
- * `false`, and `enter` takes the `default` (rendered with the active letter emphasized in the
136
- * `(Y/n)` hint, unless `hint` replaces that group).
137
- */
138
- export declare interface ConfirmState {
139
- readonly message: string;
140
- readonly default: boolean;
141
- readonly hint?: string;
142
- readonly styler: StylerInterface;
143
- readonly theme: PromptTheme;
144
- }
145
-
146
- /**
147
- * Render a {@link ConfirmState} as a styled view — the header plus the `(Y/n)` group with the
148
- * default letter painted by the `selected` role. A supplied `hint` replaces that whole group,
149
- * parentheses included, painted by the `hint` role.
66
+ * Render a confirm-field key state as a styled view. The selected role paints the default letter.
150
67
  */
151
- export declare function confirmView(state: ConfirmState): string;
68
+ export declare function confirmView(state: ReturnType<typeof createConfirmState>): string;
152
69
 
153
70
  /**
154
71
  * The single control BYTE → key descriptor table {@link import('./helpers.js').parseKey}
@@ -166,11 +83,37 @@ export declare const CONTROL_NAMES: Readonly<Record<string, {
166
83
  readonly ctrl: boolean;
167
84
  }>>;
168
85
 
169
- /** Build the initial {@link CheckboxState} from {@link CheckboxOptions} — normalizing choices, resolving the styler + theme, carrying any hint, seeding the checked set, carrying min/max. */
170
- export declare function createCheckboxState(options: CheckboxOptions): CheckboxState;
86
+ /**
87
+ * Build the initial checkbox-field key state.
88
+ *
89
+ * @param field - The checkbox field to render
90
+ * @param styler - The styler used to render the view
91
+ * @param theme - The optional terminal theme
92
+ * @returns The initial immutable key state
93
+ */
94
+ export declare function createCheckboxState(field: CheckboxField, styler?: StylerInterface, theme?: PromptThemeOptions): {
95
+ message: string;
96
+ choices: FieldChoice[];
97
+ styler: StylerInterface;
98
+ theme: PromptTheme;
99
+ focused: number;
100
+ checked: readonly number[];
101
+ };
171
102
 
172
- /** Build the initial {@link ConfirmState} from {@link ConfirmOptions} — defaulting the answer to `false`, resolving the styler + theme, carrying any hint override. */
173
- export declare function createConfirmState(options: ConfirmOptions): ConfirmState;
103
+ /**
104
+ * Build the initial confirm-field key state.
105
+ *
106
+ * @param field - The confirm field to render
107
+ * @param styler - The styler used to render the view
108
+ * @param theme - The optional terminal theme
109
+ * @returns The initial immutable key state
110
+ */
111
+ export declare function createConfirmState(field: ConfirmField, styler?: StylerInterface, theme?: PromptThemeOptions): {
112
+ message: string;
113
+ default: boolean;
114
+ styler: StylerInterface;
115
+ theme: PromptTheme;
116
+ };
174
117
 
175
118
  /**
176
119
  * Create a {@link TerminalStoreInterface} backed by one table of the `databases` layer — the
@@ -189,11 +132,38 @@ export declare function createConfirmState(options: ConfirmOptions): ConfirmStat
189
132
  */
190
133
  export declare function createDatabaseTerminalStore(driver?: DriverInterface): TerminalStoreInterface;
191
134
 
192
- /** Build the initial {@link EditorState} from {@link EditorOptions} — resolving the validator + styler + theme, carrying any hint override, seeding empty lines. */
193
- export declare function createEditorState(options: EditorOptions): EditorState;
135
+ /**
136
+ * Build the initial editor-field key state.
137
+ *
138
+ * @param field - The editor field to render
139
+ * @param styler - The styler used to render the view
140
+ * @param theme - The optional terminal theme
141
+ * @returns The initial immutable key state
142
+ */
143
+ export declare function createEditorState(field: EditorField, styler?: StylerInterface, theme?: PromptThemeOptions): {
144
+ message: string;
145
+ default: string;
146
+ styler: StylerInterface;
147
+ theme: PromptTheme;
148
+ lines: readonly string[];
149
+ current: string;
150
+ };
194
151
 
195
- /** Build the initial {@link InputState} from {@link InputOptions} — resolving the validator + styler + theme, seeding an empty value. */
196
- export declare function createInputState(options: InputOptions): InputState;
152
+ /**
153
+ * Build the initial text-field key state.
154
+ *
155
+ * @param field - The text field to render
156
+ * @param styler - The styler used to render the view
157
+ * @param theme - The optional terminal theme
158
+ * @returns The initial immutable key state
159
+ */
160
+ export declare function createInputState(field: TextField, styler?: StylerInterface, theme?: PromptThemeOptions): {
161
+ message: string;
162
+ default: string;
163
+ styler: StylerInterface;
164
+ theme: PromptTheme;
165
+ value: string;
166
+ };
197
167
 
198
168
  /**
199
169
  * Create the in-memory {@link TerminalStoreInterface} — a process-lifetime `Map` of endpoint
@@ -210,43 +180,51 @@ export declare function createInputState(options: InputOptions): InputState;
210
180
  */
211
181
  export declare function createMemoryTerminalStore(): TerminalStoreInterface;
212
182
 
213
- /** Build the initial {@link PasswordState} from {@link PasswordOptions} — resolving the validator + styler + theme + mask. */
214
- export declare function createPasswordState(options: PasswordOptions): PasswordState;
183
+ /**
184
+ * Build the initial password-field key state.
185
+ *
186
+ * @param field - The password field to render
187
+ * @param styler - The styler used to render the view
188
+ * @param theme - The optional terminal theme
189
+ * @returns The initial immutable key state
190
+ */
191
+ export declare function createPasswordState(field: PasswordField, styler?: StylerInterface, theme?: PromptThemeOptions): {
192
+ message: string;
193
+ mask: string;
194
+ styler: StylerInterface;
195
+ theme: PromptTheme;
196
+ value: string;
197
+ };
215
198
 
216
199
  /**
217
- * Create the headless prompt {@link PromptInterface} BROKER — it parks each prompt call as a
218
- * pending prompt and resolves it when {@link PromptInterface.answer} arrives (or rejects on
219
- * timeout). The tri-surface's headless arm: subscribe `emitter.on('pending', …)` to forward each
220
- * prompt to whoever can answer (an SSE transport, a remote terminal), then route the answer back
221
- * through `answer(id, value)`.
200
+ * Create the headless {@link PromptInterface} broker. It parks live forms and applies remote
201
+ * answers to the authoritative instances.
222
202
  *
223
203
  * @param options - See {@link PromptOptions}
224
204
  * @returns A {@link PromptInterface}
225
205
  *
226
206
  * @remarks
227
- * - **Park-as-Promise.** `await prompt.input({ message })` blocks until `answer(id, value)` accepts
228
- * a matching value; the value is validated (text forms) and type-checked to the form first.
229
- * - **Timeout → expire → reject (deterministic).** An unanswered prompt expires after
230
- * `options.timeout` (default {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}) and its
231
- * Promise rejects with a {@link import('./errors.js').TerminalError}; inject `options.timer` to
232
- * drive expiry without real time.
207
+ * The caller awaits the parked form's own `answer`. Timeout or teardown destroys the form, so that
208
+ * promise rejects with the Form package's `ABANDONED` error. Inject `options.timer` to drive
209
+ * expiry without real time.
233
210
  *
234
211
  * @example
235
212
  * ```ts
236
213
  * import { createPrompt } from '@src/core'
214
+ * import { createForm } from '@orkestrel/form'
237
215
  *
238
216
  * const prompt = createPrompt()
239
- * prompt.emitter.on('pending', (pending) => send(pending)) // forward to a remote client
240
- * const name = await prompt.input({ message: 'Your name', validate: { required: true } })
217
+ * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })
218
+ * const id = prompt.park(form)
219
+ * prompt.answer(id, { name: 'Ada' })
241
220
  * ```
242
221
  */
243
222
  export declare function createPrompt(options?: PromptOptions): PromptInterface;
244
223
 
245
224
  /**
246
225
  * Create the SSE prompt {@link PromptClientInterface} BRIDGE — it connects to a remote broker's SSE
247
- * endpoint, dispatches each received prompt to a LOCAL {@link import('./types.js').PromptFormInterface}
248
- * terminal (so a human at this machine answers a prompt parked elsewhere), and POSTs the answer
249
- * back. Universal — `fetch` / SSE are web-standard.
226
+ * endpoint, dispatches each received form to a local {@link import('./types.js').TerminalInterface},
227
+ * and POSTs the answer back. Universal — `fetch` / SSE are web-standard.
250
228
  *
251
229
  * @param options - See {@link PromptClientOptions} (`url` + `terminal` required)
252
230
  * @returns A {@link PromptClientInterface}
@@ -290,16 +268,29 @@ export declare function createPromptClient(options: PromptClientOptions): Prompt
290
268
  */
291
269
  export declare function createPromptTheme(options?: PromptThemeOptions): PromptTheme;
292
270
 
293
- /** Build the initial {@link SelectState} from {@link SelectOptions} — normalizing choices, resolving the styler + theme, carrying any hint, and pre-focusing the default. */
294
- export declare function createSelectState(options: SelectOptions): SelectState;
271
+ /**
272
+ * Build the initial select-field key state.
273
+ *
274
+ * @param field - The select field to render
275
+ * @param styler - The styler used to render the view
276
+ * @param theme - The optional terminal theme
277
+ * @returns The initial immutable key state
278
+ */
279
+ export declare function createSelectState(field: SelectField, styler?: StylerInterface, theme?: PromptThemeOptions): {
280
+ message: string;
281
+ choices: FieldChoice[];
282
+ styler: StylerInterface;
283
+ theme: PromptTheme;
284
+ focused: number;
285
+ };
295
286
 
296
287
  /**
297
- * Create the multi-endpoint {@link TerminalManagerInterface} — a named registry of
288
+ * Create the multi-endpoint {@link TerminalManager} — a named registry of
298
289
  * {@link PromptInterface} brokers so several parties can `ask` prompts of each other by name,
299
290
  * with a transitive DEADLOCK check across every in-flight ask.
300
291
  *
301
292
  * @param options - See {@link TerminalManagerOptions}
302
- * @returns A {@link TerminalManagerInterface}
293
+ * @returns A {@link TerminalManager}
303
294
  *
304
295
  * @example
305
296
  * ```ts
@@ -385,7 +376,7 @@ export declare class DatabaseTerminalStore implements TerminalStoreInterface {
385
376
  delete(id: string): Promise<void>;
386
377
  }
387
378
 
388
- /** The default mask glyph a {@link import('./types.js').PasswordState} renders each input character as — `*`. */
379
+ /** The default mask glyph {@link import('./helpers.js').createPasswordState} uses — `*`. */
389
380
  export declare const DEFAULT_MASK = "*";
390
381
 
391
382
  /**
@@ -423,26 +414,6 @@ export declare function defaultTimer(callback: () => void, ms: number): TimerCan
423
414
  /** Delete (DEL, U+007F) — the usual Backspace byte on a Unix TTY. */
424
415
  export declare const DELETE: string;
425
416
 
426
- /**
427
- * Dispatch a {@link PendingPrompt} to the matching {@link PromptFormInterface} method — the bridge
428
- * step a {@link PromptClient} runs to drive a LOCAL terminal with a prompt issued elsewhere.
429
- * Reconstructs typed options from the wire-safe {@link PendingPrompt.options} (every field
430
- * §14-narrowed, never an `as`; the validator rebuilt from rules via {@link reconstructValidationRules}),
431
- * then calls the matching prompt form and returns its resolved value.
432
- *
433
- * @remarks
434
- * `theme` and `hint` are plain data, so they cross the wire with the rest of the options bag and
435
- * are reconstructed here for every form that accepts them: the theme is narrowed by
436
- * {@link import('./validators.js').isPromptThemeOptions} (an off-shape theme is dropped, leaving
437
- * the defaults), then every glyph and hint is passed through {@link sanitizeDisplayText}. Choice
438
- * labels use the stream-safe {@link sanitizeChoiceLabels} treatment instead.
439
- *
440
- * @param terminal - The local {@link PromptFormInterface} to drive
441
- * @param pending - The decoded pending prompt to dispatch
442
- * @returns The prompt's resolved value (a `string` / `boolean` / `readonly string[]` per form)
443
- */
444
- export declare function dispatchPendingPrompt(terminal: PromptFormInterface, pending: PendingPrompt): Promise<string | boolean | readonly string[]>;
445
-
446
417
  /**
447
418
  * Apply a single line-editing {@link KeyEvent} to a text buffer — the editing shared by input /
448
419
  * password / editor. A printable key appends its character; `backspace` drops the last character;
@@ -451,98 +422,35 @@ export declare function dispatchPendingPrompt(terminal: PromptFormInterface, pen
451
422
  */
452
423
  export declare function editLine(value: string, key: KeyEvent): string | undefined;
453
424
 
454
- /**
455
- * Options for a multi-line {@link import('./helpers.js').editorReduce} editor prompt (terminated
456
- * by ctrl-d).
457
- *
458
- * @remarks
459
- * `hint` replaces the computed `(Ctrl+D to finish)` group in full, parentheses included. The
460
- * finish KEY stays ctrl-d whatever the hint says.
461
- */
462
- export declare interface EditorOptions {
463
- readonly message: string;
464
- readonly default?: string;
465
- readonly hint?: string;
466
- readonly validate?: Validator | ValidationRules;
467
- readonly styler?: StylerInterface;
468
- readonly theme?: PromptThemeOptions;
469
- }
470
-
471
425
  /**
472
426
  * Advance an editor prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
473
427
  * reducer. Printable characters extend the current line; backspace shrinks it; return commits the
474
428
  * current line and starts a fresh one; ctrl-d FINISHES (joining all lines, falling back to the
475
- * default when empty) through the validator; ctrl-c cancels. An invalid finish stays active with
476
- * the error.
429
+ * default when empty); ctrl-c cancels. The form validates the candidate after the driver fills it.
477
430
  */
478
- export declare function editorReduce(state: EditorState, key: KeyEvent): PromptStep<string, EditorState>;
479
-
480
- /**
481
- * The immutable state of an editor prompt — the committed `lines`, the in-progress `current`
482
- * line, plus the resolved validator + styler + theme, the default, the optional hint override,
483
- * and the current `error`.
484
- *
485
- * @remarks
486
- * Built by {@link import('./helpers.js').createEditorState}; advanced by
487
- * {@link import('./helpers.js').editorReduce}. A printable key appends to `current`; `return`
488
- * commits `current` to `lines` and starts a fresh line; ctrl-d finishes, submitting
489
- * `lines + current` joined by newlines (or `default` when empty). The whole text is validated
490
- * on finish.
491
- */
492
- export declare interface EditorState {
493
- readonly message: string;
494
- readonly default: string;
495
- readonly hint?: string;
496
- readonly validator: Validator;
497
- readonly styler: StylerInterface;
498
- readonly theme: PromptTheme;
499
- readonly lines: readonly string[];
500
- readonly current: string;
501
- readonly error?: string;
502
- }
431
+ export declare function editorReduce(state: ReturnType<typeof createEditorState>, key: KeyEvent): PromptStep<string, ReturnType<typeof createEditorState>>;
503
432
 
504
433
  /**
505
- * Render an {@link EditorState} as a MULTI-LINE styled view — the header with its finish hint, the
506
- * committed and in-progress lines painted by the `content` role, and any error. A supplied `hint`
507
- * replaces `(Ctrl+D to finish)` in full; the finish key stays ctrl-d.
434
+ * Render an editor-field key state as a multi-line styled view with its finish hint.
508
435
  */
509
- export declare function editorView(state: EditorState): string;
436
+ export declare function editorView(state: ReturnType<typeof createEditorState>): string;
510
437
 
511
- /** The styled error line (`✖ message`) — appended beneath a prompt view when the last submit failed validation, themed by the `error` role. */
438
+ /** The styled error line (`✖ message`) a form driver appends for a field failure. */
512
439
  export declare function errorLine(styler: StylerInterface, theme: PromptTheme, message: string): string;
513
440
 
514
441
  /** Escape (ESC, U+001B) — the lone byte, and the lead byte of every CSI / SS3 sequence. */
515
442
  export declare const ESCAPE: string;
516
443
 
517
444
  /**
518
- * Evaluate ONE built-in validation rule against `input`, returning its error message when the
519
- * rule fails or `undefined` when it passes. The atomic check {@link buildRuleValidator} wraps
520
- * into a {@link Validator}. Pure.
521
- *
522
- * @remarks
523
- * A function `check` is the custom-override path: it is called and its `true` ⇒ pass, a string
524
- * ⇒ that message, anything else ⇒ the generic {@link RULE_MESSAGES.invalid}. A primitive `check`
525
- * runs the named built-in: `required` (non-empty trimmed), `minimum` / `maximum` (length
526
- * bounds, the message interpolated with the count), `pattern` (a regex source), and the
527
- * `email` / `url` / `numeric` / `integer` / `alphanumeric` pattern tests.
528
- *
529
- * @param rule - The rule name (`'required'`, `'minimum'`, …)
530
- * @param check - The configured value (a primitive toggle / bound / pattern, or a custom {@link Validator})
531
- * @param input - The input string to test
532
- * @returns The error message when the rule fails, else `undefined`
533
- */
534
- export declare function evaluateRule(rule: string, check: boolean | number | string | Validator, input: string): string | undefined;
535
-
536
- /**
537
- * A minimal `fetch` — the subset of the global `fetch` the {@link PromptClient} uses (open the
538
- * SSE stream, POST an answer). Injected so a test drives the client with a controlled
539
- * `Response` (a scripted SSE `ReadableStream`) instead of a real network.
445
+ * A minimal `fetch` — the subset of the global `fetch` a {@link PromptClientInterface} uses: open
446
+ * the SSE stream, POST an answer. Injected so a test drives the client with a scripted `Response`
447
+ * instead of a real network.
540
448
  */
541
449
  export declare type FetchHandler = (input: string, init?: FetchInit) => Promise<Response>;
542
450
 
543
451
  /**
544
- * The request init the {@link PromptClient} passes to its {@link FetchHandler} — the `fetch`
545
- * `RequestInit` fields it actually sets (method / headers / body / abort signal).
452
+ * The request init a {@link PromptClientInterface} passes to its {@link FetchHandler} — the
453
+ * `RequestInit` fields it actually sets.
546
454
  */
547
455
  export declare interface FetchInit {
548
456
  readonly method?: string;
@@ -551,13 +459,10 @@ export declare interface FetchInit {
551
459
  readonly signal?: AbortSignal;
552
460
  }
553
461
 
554
- /** The min/max gate for a checkbox submit — the rejection message when `count` is out of range, else `undefined`. */
555
- export declare function gateSelection(count: number, min?: number, max?: number): string | undefined;
556
-
557
462
  /** The default {@link import('./types.js').FetchHandler} — the global `fetch`, adapted to the minimal injected shape the {@link import('./PromptClient.js').PromptClient} uses. */
558
463
  export declare function globalFetch(input: string, init?: FetchInit): Promise<Response>;
559
464
 
560
- /** The auth-token request header the {@link import('./types.js').PromptClient} sends when a `token` is configured. */
465
+ /** The auth-token request header the {@link import('./types.js').PromptClientInterface} sends. */
561
466
  export declare const HEADER_TOKEN = "x-orkestrel-token";
562
467
 
563
468
  /**
@@ -572,48 +477,15 @@ export declare const HEADER_TOKEN = "x-orkestrel-token";
572
477
  */
573
478
  export declare function hintedHeader(styler: StylerInterface, theme: PromptTheme, message: string, hint?: string): string;
574
479
 
575
- /** Options for a single-line text {@link import('./helpers.js').inputReduce} prompt. */
576
- export declare interface InputOptions {
577
- readonly message: string;
578
- readonly default?: string;
579
- readonly validate?: Validator | ValidationRules;
580
- readonly styler?: StylerInterface;
581
- readonly theme?: PromptThemeOptions;
582
- }
583
-
584
480
  /**
585
481
  * Advance an input prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
586
482
  * reducer. Printable characters extend the value; backspace shrinks it; ctrl-u clears it; ctrl-c
587
- * cancels; return submits (the empty line falls back to the default) through the validator — an
588
- * invalid submit stays active with the error in the view.
483
+ * cancels; return produces the candidate value, with an empty line falling back to the default.
589
484
  */
590
- export declare function inputReduce(state: InputState, key: KeyEvent): PromptStep<string, InputState>;
591
-
592
- /**
593
- * The immutable state of a text input prompt — its options, the resolved validator + styler +
594
- * theme, the accumulated `value`, and the current `error` (when the last submit failed validation).
595
- *
596
- * @remarks
597
- * Built by {@link import('./helpers.js').createInputState}; advanced by
598
- * {@link import('./helpers.js').inputReduce}. `value` accumulates printable characters and
599
- * shrinks on backspace; `error` holds the validation message shown in the view after a
600
- * rejected submit (cleared on the next keystroke).
601
- */
602
- export declare interface InputState {
603
- readonly message: string;
604
- readonly default: string;
605
- readonly validator: Validator;
606
- readonly styler: StylerInterface;
607
- readonly theme: PromptTheme;
608
- readonly value: string;
609
- readonly error?: string;
610
- }
611
-
612
- /** Render an {@link InputState} as a styled view — the header, the typed value painted by the `content` role (or the default painted as a hint), and any error. */
613
- export declare function inputView(state: InputState): string;
485
+ export declare function inputReduce(state: ReturnType<typeof createInputState>, key: KeyEvent): PromptStep<string, ReturnType<typeof createInputState>>;
614
486
 
615
- /** Matches an integer (optional sign). The `integer` rule tests against this. */
616
- export declare const INTEGER_PATTERN: RegExp;
487
+ /** Render a text-field key state as a styled view. */
488
+ export declare function inputView(state: ReturnType<typeof createInputState>): string;
617
489
 
618
490
  /**
619
491
  * Whether a caught value is an `AbortError` — the {@link import('./PromptClient.js').PromptClient}
@@ -622,15 +494,6 @@ export declare const INTEGER_PATTERN: RegExp;
622
494
  */
623
495
  export declare function isAbortError(error: unknown): boolean;
624
496
 
625
- /** Narrow an unknown wire payload to an answer POST body — a non-empty `id` string plus a `value` key (of any shape) present. */
626
- export declare function isAnswerPayload(value: unknown): value is {
627
- readonly id: string;
628
- readonly value: unknown;
629
- };
630
-
631
- /** Narrow an unknown value to a {@link CheckboxChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
632
- export declare function isCheckboxChoice(value: unknown): value is CheckboxChoice;
633
-
634
497
  /**
635
498
  * Whether `url` is an INSECURE remote endpoint — a plain `http://` URL whose host is NOT a
636
499
  * loopback address. Pure string parsing (no `URL` global), so it stays total on malformed input.
@@ -653,56 +516,23 @@ export declare function isCheckboxChoice(value: unknown): value is CheckboxChoic
653
516
  export declare function isInsecureRemote(url: string): boolean;
654
517
 
655
518
  /**
656
- * Narrow an unknown wire value to a {@link PendingPrompt} before dispatching it.
519
+ * Narrow an unknown wire value to a {@link PendingForm} envelope.
520
+ *
521
+ * @remarks
522
+ * This guard checks the transport record and proves only that `schema` is a record. The Form
523
+ * package's `parseForm` owns the schema payload and its semantic audit.
657
524
  *
658
525
  * @param value - The decoded wire value to inspect
659
- * @returns Whether `value` is a complete pending-prompt record
526
+ * @returns Whether the value is a complete pending-form envelope
660
527
  */
661
- export declare const isPendingPrompt: Guard<PendingPrompt>;
528
+ export declare function isPendingForm(value: unknown): value is PendingForm;
662
529
 
663
- /** Narrow an unknown value to a {@link PendingPromptStatus}. */
664
- export declare const isPendingPromptStatus: Guard<PendingPromptStatus>;
530
+ /** Narrow an unknown value to a {@link PendingFormStatus}. */
531
+ export declare const isPendingFormStatus: Guard<PendingFormStatus>;
665
532
 
666
533
  /** Whether a single character is a printable (non-control) character — used by {@link parseKey}'s char fallback. */
667
534
  export declare function isPrintable(character: string): boolean;
668
535
 
669
- /** Narrow an unknown value to a {@link PromptChoice} — the `recordOf` shape inlined so no non-exported member lingers (§5). */
670
- export declare function isPromptChoice(value: unknown): value is PromptChoice;
671
-
672
- /**
673
- * Narrow an unknown wire value to a {@link PromptThemeOptions} — the §14 guard a remote prompt's
674
- * `theme` option passes before it reaches a local prompt.
675
- *
676
- * @remarks
677
- * The shape is CLOSED at every level: an unknown icon slot, an unknown role, a non-string glyph, or
678
- * a role whose value is not a {@link isStyle}-shaped console style rejects the whole theme, so a
679
- * hostile payload degrades to the default theme rather than partially applying. Glyphs still carry
680
- * arbitrary text at this point — {@link import('./helpers.js').sanitizeThemeIcons} strips their
681
- * control bytes on dispatch.
682
- *
683
- * The icons and roles shapes are checked against their complete union-keyed guard records. A slot
684
- * added to either union without an entry here is a compile error rather than a wire theme that is
685
- * silently refused.
686
- */
687
- export declare const isPromptThemeOptions: Guard<PromptThemeOptions>;
688
-
689
- /** Narrow an unknown value to a {@link PromptType} — one of the six prompt forms. */
690
- export declare const isPromptType: Guard<PromptType>;
691
-
692
- /**
693
- * Narrow an unknown value to a console {@link Style} — the value every
694
- * {@link import('./types.js').PromptRole} carries, built from the console module's own
695
- * {@link import('@orkestrel/console').COLORS} + {@link import('@orkestrel/console').ATTRIBUTES} so
696
- * the style vocabulary has ONE source.
697
- *
698
- * @remarks
699
- * The record is CLOSED and matches the `Style` contract exactly: `foreground` and `background` are
700
- * optional and each must name a color (`default` included, as the console `Color` union has it),
701
- * `attributes` is REQUIRED and must be an array over the attribute set, and any other key rejects
702
- * the whole value.
703
- */
704
- export declare const isStyle: Guard<Style>;
705
-
706
536
  /**
707
537
  * Narrow an unknown caught value to a {@link TerminalError}.
708
538
  *
@@ -712,9 +542,11 @@ export declare const isStyle: Guard<Style>;
712
542
  * @example
713
543
  * ```ts
714
544
  * try {
715
- * const name = await prompt.input({ message: 'Your name' })
545
+ * await terminal.ask(form)
716
546
  * } catch (error) {
717
- * if (isTerminalError(error) && error.code === 'EXPIRE') retryLater()
547
+ * if (isTerminalError(error) && error.code === 'CANCEL') {
548
+ * // the person aborted
549
+ * }
718
550
  * }
719
551
  * ```
720
552
  */
@@ -723,6 +555,14 @@ export declare function isTerminalError(value: unknown): value is TerminalError;
723
555
  /** Narrow an unknown value to a {@link TerminalSnapshot}. */
724
556
  export declare const isTerminalSnapshot: Guard<TerminalSnapshot>;
725
557
 
558
+ /**
559
+ * Narrow an unknown value to a transport-neutral {@link WireEvent}.
560
+ *
561
+ * @param value - The candidate wire event
562
+ * @returns Whether the value carries an event name, serialized data, and an optional id
563
+ */
564
+ export declare const isWireEvent: Guard<WireEvent>;
565
+
726
566
  /**
727
567
  * The Control Sequence Introducer lead (`ESC[`) for the navigation keys — the prefix of the
728
568
  * arrow / home / end / delete sequences {@link SEQUENCE_NAMES} is keyed by. Named `KEY_CSI`
@@ -735,23 +575,21 @@ export declare const KEY_CSI: string;
735
575
  export declare const KEY_SS3: string;
736
576
 
737
577
  /**
738
- * One decoded keypress — the universal, TTY-agnostic representation of a single key, the
739
- * output of {@link import('./helpers.js').parseKey}. A reducer reads `name` (and the
740
- * modifier flags) to decide its transition; the raw `sequence` is preserved so a printable
741
- * character round-trips and an unknown escape is never lost.
578
+ * One decoded keypress — the TTY-agnostic representation of a single key, the output of
579
+ * {@link import('./helpers.js').parseKey}. A driver reads `name` and the modifier flags to decide
580
+ * its transition; `sequence` is preserved so a printable character round-trips and an unknown
581
+ * escape is never lost.
742
582
  *
743
583
  * @remarks
744
- * - `name` — the canonical key name: a control / navigation key (`return`, `backspace`,
745
- * `tab`, `escape`, `up` / `down` / `left` / `right`, `space`, `home`, `end`, `delete`),
746
- * a named ctrl combo (`c` with `ctrl` true for ctrl-c, likewise `d` / `u` / `a` / `e`),
747
- * or the printable character itself (`'a'`, `'7'`, `'?'`). An UNRECOGNIZED sequence
748
- * yields `name: ''` (empty) — never a throw (the decoder is total).
749
- * - `sequence` — the exact input bytes as a string (a `Uint8Array` is decoded UTF-8). The
750
- * driver writes this verbatim for a printable key; a reducer that needs the literal char
751
- * reads it.
752
- * - `ctrl` / `meta` / `shift` — the modifier flags. `ctrl` is `true` for a C0 control byte
753
- * (ctrl-c / ctrl-d / ctrl-u / ctrl-a / ctrl-e and the like); `meta` is `true` for an
754
- * ESC-prefixed (Alt) sequence; `shift` is `true` for an uppercase-letter printable.
584
+ * - `name` — the canonical key name: a control or navigation key (`return`, `backspace`, `tab`,
585
+ * `escape`, `up` / `down` / `left` / `right`, `space`, `home`, `end`, `delete`), a named ctrl
586
+ * combo (`c` with `ctrl` true for ctrl-c, likewise `d` / `u` / `a` / `e`), or the printable
587
+ * character itself (`'a'`, `'7'`, `'?'`). An unrecognized sequence yields `name: ''`; the decoder
588
+ * is total and never throws.
589
+ * - `sequence` — the exact input bytes as a string (a `Uint8Array` is decoded UTF-8). The driver
590
+ * writes this verbatim for a printable key.
591
+ * - `ctrl` / `meta` / `shift` — the modifier flags. `ctrl` is true for a C0 control byte, `meta`
592
+ * for an ESC-prefixed (Alt) sequence, `shift` for an uppercase-letter printable.
755
593
  */
756
594
  export declare interface KeyEvent {
757
595
  readonly name: string;
@@ -803,43 +641,31 @@ export declare class MemoryTerminalStore implements TerminalStoreInterface {
803
641
  /** Line feed (`\n`, U+000A) — Enter on some terminals / pasted input. */
804
642
  export declare const NEWLINE: string;
805
643
 
806
- /** Normalize a checkbox choice input into a full {@link CheckboxChoice} (a bare string becomes both name and value). */
807
- export declare function normalizeCheckboxChoice(choice: string | CheckboxChoice): CheckboxChoice;
808
-
809
- /** Normalize a select choice input into a full {@link PromptChoice} (a bare string becomes both name and value). */
810
- export declare function normalizeChoice(choice: string | PromptChoice): PromptChoice;
811
-
812
- /** Matches a numeric value (integer or decimal, optional sign). The `numeric` rule tests against this. */
813
- export declare const NUMERIC_PATTERN: RegExp;
814
-
815
644
  /**
816
- * One parked prompt's runtime state inside the broker — the wire-safe {@link PendingPrompt} record
817
- * it exposes, plus the live machinery that settles that prompt's Promise. `respond` is the per-form
818
- * gate-and-resolve closure: it validates + type-checks an answer and (on accept) resolves the parked
819
- * Promise, returning whether it accepted; it closes over the form's precisely-typed `resolve`, so no
820
- * per-form generic leaks into `answer`. `expire` rejects the parked Promise; `cancel` clears the
821
- * injected expiry timer ({@link TimerCancel}).
645
+ * One parked form's runtime state inside the broker — the live form, the wire-safe record the
646
+ * broker exposes, and the cancel for its expiry timer.
647
+ *
648
+ * @remarks
649
+ * `form` is the AUTHORITATIVE form the caller parked, not a copy: an answer fills and submits this
650
+ * one, so a `custom` rule that never crossed the wire still decides. Expiry destroys it, which
651
+ * abandons it and settles the caller's promise through the form's own lifecycle. `pending` is the
652
+ * wire record, whose `status` tracks the ticket.
822
653
  */
823
654
  export declare interface Parked {
824
- readonly prompt: PendingPrompt;
825
- readonly respond: (value: unknown) => unknown;
826
- readonly expire: () => void;
655
+ readonly form: FormInterface;
656
+ readonly pending: PendingForm;
827
657
  readonly cancel: TimerCancel;
828
658
  }
829
659
 
830
660
  /**
831
- * The request to {@link PromptInterface.park} a prompt directly — the general form the six
832
- * `PromptFormInterface` methods each wrap via a private `gateFor(form, options)` dispatch.
661
+ * The parking envelope — everything the broker needs about a park that the form itself does not
662
+ * say.
833
663
  *
834
664
  * @remarks
835
- * - `form` — which {@link PromptType} to park.
836
- * - `options` — that form's options bag ({@link PromptFormOptions}).
837
- * - `from` / `to` — set ONLY by a {@link TerminalManagerInterface} (the attribution edge); a
838
- * direct broker caller leaves both `undefined`.
665
+ * `from` and `to` are the attribution edge, set ONLY by a {@link TerminalManagerInterface}: which
666
+ * endpoint asked, which endpoint must answer. A direct broker caller passes no request at all.
839
667
  */
840
668
  export declare interface ParkRequest {
841
- readonly form: PromptType;
842
- readonly options: PromptFormOptions;
843
669
  readonly from?: string;
844
670
  readonly to?: string;
845
671
  }
@@ -877,122 +703,70 @@ export declare interface ParkRequest {
877
703
  */
878
704
  export declare function parseKey(input: string | Uint8Array): KeyEvent;
879
705
 
880
- /** The always-passing {@link Validator} — the resolved validator when no rules were supplied. */
881
- export declare function passing(_input: string): true;
882
-
883
- /** Options for a masked password {@link import('./helpers.js').passwordReduce} prompt. */
884
- export declare interface PasswordOptions {
885
- readonly message: string;
886
- readonly mask?: string;
887
- readonly validate?: Validator | ValidationRules;
888
- readonly styler?: StylerInterface;
889
- readonly theme?: PromptThemeOptions;
890
- }
891
-
892
706
  /**
893
707
  * Advance a password prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
894
708
  * reducer. Identical line-editing to {@link inputReduce} (printable extends, backspace shrinks,
895
- * ctrl-u clears, ctrl-c cancels) but the view masks the value; return submits through the
896
- * validator (no default fallback — a password has no echoed default).
709
+ * ctrl-u clears, ctrl-c cancels) but the view masks the value. Return produces the candidate value.
897
710
  */
898
- export declare function passwordReduce(state: PasswordState, key: KeyEvent): PromptStep<string, PasswordState>;
711
+ export declare function passwordReduce(state: ReturnType<typeof createPasswordState>, key: KeyEvent): PromptStep<string, ReturnType<typeof createPasswordState>>;
899
712
 
900
- /**
901
- * The immutable state of a password prompt — like {@link InputState} but with a `mask`
902
- * character the view renders in place of each input character.
903
- *
904
- * @remarks
905
- * Built by {@link import('./helpers.js').createPasswordState}; advanced by
906
- * {@link import('./helpers.js').passwordReduce}. The `value` is the real (unmasked) input;
907
- * the view shows `mask` repeated `value.length` times.
908
- */
909
- export declare interface PasswordState {
910
- readonly message: string;
911
- readonly mask: string;
912
- readonly validator: Validator;
913
- readonly styler: StylerInterface;
914
- readonly theme: PromptTheme;
915
- readonly value: string;
916
- readonly error?: string;
917
- }
918
-
919
- /** Render a {@link PasswordState} as a styled view — the header, the value masked to `mask` repeated and painted by the `content` role, and any error. */
920
- export declare function passwordView(state: PasswordState): string;
713
+ /** Render a password-field key state as a styled view. */
714
+ export declare function passwordView(state: ReturnType<typeof createPasswordState>): string;
921
715
 
922
716
  /**
923
- * One prompt PARKED by the broker — an id-keyed, wire-safe record of a {@link PromptFormInterface}
924
- * call awaiting a remote answer. The value a `pending` listener receives and the broker serializes
925
- * over SSE to a {@link PromptClient}.
717
+ * One form PARKED by the broker — an id-keyed, wire-safe record of a live form awaiting a remote
718
+ * answer. The value a `pending` listener receives and the broker serializes over SSE to a
719
+ * {@link PromptClientInterface}.
926
720
  *
927
721
  * @remarks
928
- * - `id` — the unique id (minted via `crypto.randomUUID()`); the key for {@link PromptInterface.answer}.
929
- * - `form` — which prompt form was called ({@link PromptType}); the discriminant a client
930
- * dispatches on (named for its axis — the prompt form — never `kind` / `type`).
931
- * - `message` — the prompt's question (lifted out of `options` for direct display).
932
- * - `options` — the WIRE-SAFE options (a `validate` FUNCTION dropped; the declarative
933
- * {@link ValidationRules} data + `choices` / `default` / `mask` / `hint` / `theme` kept — see
934
- * {@link import('./helpers.js').serializePromptOptions}). A client reconstructs the validator
935
- * from the rules via {@link resolveValidation}.
936
- * - `status` — the current {@link PendingPromptStatus}.
937
- * - `time` — the creation timestamp (ms since epoch).
938
- * - `from` / `to` — the OPTIONAL attribution edge a {@link TerminalManagerInterface} stamps on a
939
- * parked prompt (which endpoint asked, which endpoint must answer); absent for a bare broker
940
- * {@link PromptInterface} used directly (no manager attribution).
941
- */
942
- export declare interface PendingPrompt {
722
+ * - `id` — the unique id, minted with `crypto.randomUUID()`; the key for
723
+ * {@link PromptInterface.answer}.
724
+ * - `schema` — the parked form's schema projected to JSON by the dependency's own `serializeForm`.
725
+ * Every `custom` validator is dropped on the way out, so an authoritative rule the wire cannot
726
+ * carry stays server-side and is enforced when the answer comes back.
727
+ * - `status` — the ticket's {@link PendingFormStatus}.
728
+ * - `time` — the creation timestamp, ms since epoch.
729
+ * - `from` / `to` — the attribution edge a {@link TerminalManagerInterface} stamps on a parked
730
+ * form: which endpoint asked, which endpoint must answer. Both absent for a bare broker used
731
+ * directly.
732
+ */
733
+ export declare interface PendingForm {
943
734
  readonly id: string;
944
- readonly form: PromptType;
945
- readonly message: string;
946
- readonly options: Readonly<Record<string, unknown>>;
947
- readonly status: PendingPromptStatus;
735
+ readonly schema: JSONRecord;
736
+ readonly status: PendingFormStatus;
948
737
  readonly time: number;
949
738
  readonly from?: string;
950
739
  readonly to?: string;
951
740
  }
952
741
 
953
742
  /**
954
- * The lifecycle status of a parked {@link PendingPrompt} — where a brokered prompt stands.
955
- * Names its axis (the pending prompt's progression), never `kind` (AGENTS §4.4).
743
+ * The lifecycle status of a parked {@link PendingForm} — where the TICKET stands, which is not
744
+ * where the form stands. A ticket is `pending` until somebody answers it; the form it carries has
745
+ * its own status, and the two are separate facts about separate entities.
956
746
  *
957
747
  * @remarks
958
- * - `pending` — parked, awaiting an {@link PromptInterface.answer} (the Promise is unresolved).
959
- * - `answered` — answered and accepted (the Promise resolved with the validated value).
960
- * - `expired` — timed out (or torn down by `destroy`) before an answer (the Promise rejected).
748
+ * - `pending` — parked, awaiting {@link PromptInterface.answer}.
749
+ * - `answered` — answered and accepted, so the parked form settled.
750
+ * - `expired` — timed out, or torn down by `destroy`, before an answer.
961
751
  */
962
- export declare type PendingPromptStatus = 'pending' | 'answered' | 'expired';
752
+ export declare type PendingFormStatus = 'pending' | 'answered' | 'expired';
963
753
 
964
754
  /**
965
- * The headless prompt BROKER (observable §13) — parks each {@link PromptInterface} call as a
966
- * pending prompt and returns a Promise that resolves when the prompt is {@link answer}ed (or
967
- * rejects on timeout / teardown). The tri-surface's headless arm: there is no terminal here, so a
968
- * transport forwards each `pending` event to whoever can answer, and {@link answer} resolves the
969
- * parked Promise — for environments where direct user access is unavailable (a headless server on
970
- * stdio, a browser with no TTY, a prompt issued on one machine answered on another).
755
+ * The headless form broker. It parks live forms, exposes their serialized schemas, and applies
756
+ * remote answers to the authoritative form.
971
757
  *
972
758
  * @remarks
973
- * - **Park-as-Promise.** Each `input` / `password` / `confirm` / `select` / `checkbox` / `editor`
974
- * mints an id (`crypto.randomUUID()`), parks a wire-safe {@link PendingPrompt}, emits `pending`,
975
- * and returns an unresolved Promise. The prompt's options are serialized
976
- * ({@link serializePromptOptions}) so a transport can forward them as-is.
977
- * - **Answer validates + type-checks.** {@link answer} runs the prompt's per-form gate: it
978
- * type-checks `value` to the form (string / boolean / string[]) AND, for the text forms, runs
979
- * the validator resolved from the original `validate` rules. A rejected answer returns a failure
980
- * `Result` with error `'rejected'` and the prompt stays `pending`; an accepted answer resolves the
981
- * Promise, emits `answer`, and removes the prompt.
982
- * - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms (via the
983
- * INJECTED timer): `expire` fires and the parked Promise rejects with a {@link TerminalError}
984
- * (`code: 'EXPIRE'`). {@link destroy} expires every still-pending prompt the same way.
985
- * - **Deterministic.** The timer is injectable ({@link import('./types.js').TimerHandler}); a test
986
- * supplies one that fires on demand, so expiry is driven without real time.
759
+ * A parked record carries one call to `serializeForm`. A failed fill or submit leaves the record
760
+ * parked for another answer. A successful submit settles the form once, emits `answer`, and removes
761
+ * the record. Timeout and teardown abandon every unsettled form through its own `destroy` method.
987
762
  *
988
763
  * @example
989
764
  * ```ts
990
- * const prompt = createPrompt({ timeout: 60_000 })
991
- * prompt.emitter.on('pending', (pending) => broadcast(pending)) // forward to a client
992
- *
993
- * const name = await prompt.input({ message: 'Your name' }) // parks; resolves on answer()
994
- * // ...elsewhere, a client POSTs the answer back:
995
- * prompt.answer(id, 'Ada') // resolves the awaited input() above
765
+ * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })
766
+ * const prompt = createPrompt()
767
+ * const id = prompt.park(form)
768
+ * prompt.answer(id, { name: 'Ada' })
769
+ * await form.answer // { name: 'Ada' }
996
770
  * ```
997
771
  */
998
772
  export declare class Prompt implements PromptInterface {
@@ -1000,16 +774,10 @@ export declare class Prompt implements PromptInterface {
1000
774
  constructor(options?: PromptOptions);
1001
775
  get emitter(): EmitterInterface<PromptEventMap>;
1002
776
  get count(): number;
1003
- pending(): readonly PendingPrompt[];
1004
- pending(id: string): PendingPrompt | undefined;
1005
- answer(id: string, value: unknown): Result<unknown, AnswerError>;
1006
- park(request: ParkRequest): Ticket;
1007
- input(options: InputOptions): Promise<string>;
1008
- password(options: PasswordOptions): Promise<string>;
1009
- confirm(options: ConfirmOptions): Promise<boolean>;
1010
- select(options: SelectOptions): Promise<string>;
1011
- checkbox(options: CheckboxOptions): Promise<readonly string[]>;
1012
- editor(options: EditorOptions): Promise<string>;
777
+ pending(): readonly PendingForm[];
778
+ pending(id: string): PendingForm | undefined;
779
+ park(form: FormInterface, request?: ParkRequest): string;
780
+ answer(id: string, values: FormValues): Result<FormValues, AnswerError>;
1013
781
  destroy(): void;
1014
782
  }
1015
783
 
@@ -1039,49 +807,32 @@ export declare const PROMPT_ICONS: Readonly<{
1039
807
  */
1040
808
  export declare const PROMPT_ROLES: readonly PromptRole[];
1041
809
 
1042
- /** A choice item in a {@link SelectOptions} prompt — its displayed `name`, its resolved `value`, and an optional one-line `description`. */
1043
- export declare interface PromptChoice {
1044
- readonly name: string;
1045
- readonly value: string;
1046
- readonly description?: string;
1047
- }
1048
-
1049
810
  /**
1050
- * The SSE prompt BRIDGE (observable §13) — the client-side counterpart to
1051
- * {@link import('./Prompt.js').Prompt}. Connects to a remote broker's SSE endpoint, receives
1052
- * serialized pending prompts, dispatches EACH to a LOCAL {@link PromptFormInterface} terminal (so
1053
- * a human at THIS machine answers a prompt parked elsewhere), and POSTs the answer back.
1054
- * Universal — `fetch` + SSE are web-standard, so it runs in a browser or on a server; the
1055
- * injected `fetch` / timer make it fully deterministic in tests.
811
+ * The SSE form bridge. It ingests serialized forms from a remote broker, renders them through a
812
+ * local terminal, and posts answers back without blocking the event stream.
1056
813
  *
1057
814
  * @remarks
1058
- * - **Connect + reconnect.** {@link connect} opens the SSE stream (via the injected `fetch` + the
1059
- * core `SSEParser`) and loops, reconnecting after the stream drops with the `delay` backoff
1060
- * (driven by the injected timer) — unless `reconnect` is `false`, the client was
1061
- * {@link destroy}ed, or the drop was a deliberate {@link disconnect} (an abort).
1062
- * - **Dispatch + answer.** Each decoded `pending` event is narrowed to a `PendingPrompt` (§14 —
1063
- * never an `as`), dispatched to `terminal`, and its resolved value POSTed back to `url`.
1064
- * Dispatch is strictly SERIAL: the read loop drives the terminal for ONE prompt at a time and
1065
- * only reads/dispatches the next event after the current prompt fully settles (its answer
1066
- * POSTed). A prompt id redelivered (e.g. after a reconnect) WHILE its prior dispatch is still
1067
- * in flight is skipped (a `#seen` set tracks in-flight ids); once that dispatch settles (or the
1068
- * broker sends `expire` for the id) the id is dropped from `#seen`, so a later redelivery of the
1069
- * SAME id after completion is dispatched again — the client does not dedupe across completion.
1070
- * - **Server signals.** An `expire` event (the broker dropped a parked prompt) emits `expire`; a
1071
- * `shutdown` event calls {@link disconnect} (not {@link destroy}) — the client stops streaming
1072
- * without auto-reconnect but stays reusable; a later {@link connect} recovers it.
1073
- * - **Lean events (§13).** `connect` / `disconnect` / `expire` / `error` — errors are `unknown`.
1074
- * `disconnect` fires exactly once per connected-to-disconnected transition, whether triggered by
1075
- * the server ending the stream cleanly or by a deliberate {@link disconnect} / {@link destroy}.
815
+ * - **Connect + reconnect.** {@link connect} opens the SSE stream and reconnects after a transport
816
+ * drop with the injected backoff unless reconnect is disabled, the client was destroyed, or
817
+ * {@link disconnect} deliberately stopped it.
818
+ * - **Ingest + render.** Each `pending` envelope passes through `isPendingForm`, Form's `parseForm`,
819
+ * and terminal's `sanitizeSchema`, then enters a serial render queue. The SSE reader never awaits
820
+ * that queue, so `expire` and `shutdown` remain live while a person is answering.
821
+ * - **Safe local form.** The rendering copy omits every wire `pattern`, because Form compiles a
822
+ * pattern during local evaluation. The broker's parked form retains it and remains authoritative.
823
+ * - **Refusal retry.** A structured `rejected` response seeds a new rendering form with the values
824
+ * just submitted, applies every {@link FieldError} through `invalidate`, and asks again. No retry
825
+ * counter truncates the loop; acceptance, expiry, and shutdown are its bounds.
826
+ * - **Replay safety.** A replayed id is skipped while it is queued, rendering, or posting. Once an
827
+ * attempt ends, a later delivery of that id may be rendered again.
1076
828
  *
1077
829
  * @example
1078
830
  * ```ts
1079
831
  * const client = createPromptClient({
1080
832
  * url: 'http://localhost:3001/prompts',
1081
- * terminal: createTerminal(), // a local PromptFormInterface (T-c)
1082
- * on: { connect: () => log('connected') },
833
+ * terminal: createTerminal(),
1083
834
  * })
1084
- * await client.connect() // streams remote prompts to the local terminal, POSTs answers back
835
+ * await client.connect()
1085
836
  * ```
1086
837
  */
1087
838
  export declare class PromptClient implements PromptClientInterface {
@@ -1096,13 +847,15 @@ export declare class PromptClient implements PromptClientInterface {
1096
847
  }
1097
848
 
1098
849
  /**
1099
- * The client's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
850
+ * The client's event map — lean, errors `unknown`, no listener-error event.
1100
851
  *
1101
852
  * @remarks
1102
853
  * - `connect` — the SSE stream opened.
1103
- * - `disconnect` — the SSE stream closed (the server ended it, or {@link PromptClientInterface.disconnect}).
1104
- * - `expire` — the remote broker signalled a parked prompt expired (carries its `id`).
1105
- * - `error` — a connection / dispatch / POST fault (errors are `unknown`).
854
+ * - `disconnect` — the SSE stream closed, by the server or by
855
+ * {@link PromptClientInterface.disconnect}.
856
+ * - `expire` — the remote broker signalled that a parked form expired, carrying its id. The client
857
+ * destroys the local form rendering it, or drops it from the queue if it has not started.
858
+ * - `error` — a connection, render, or POST fault.
1106
859
  */
1107
860
  export declare type PromptClientEventMap = {
1108
861
  readonly connect: readonly [];
@@ -1112,17 +865,22 @@ export declare type PromptClientEventMap = {
1112
865
  };
1113
866
 
1114
867
  /**
1115
- * The SSE prompt BRIDGE (observable §13) — the client-side counterpart to {@link PromptInterface}.
1116
- * Connects to a remote broker's SSE endpoint, receives serialized {@link PendingPrompt}s,
1117
- * dispatches EACH to a local {@link PromptFormInterface} terminal, and POSTs the answer back —
1118
- * so a human at this machine answers prompts a broker parked elsewhere.
868
+ * The SSE form BRIDGE — the client-side counterpart to {@link PromptInterface}. It receives
869
+ * serialized {@link PendingForm} records from a remote broker, rebuilds each schema locally, drives
870
+ * it through a {@link TerminalInterface}, and POSTs the answer back, so a human at this machine
871
+ * answers forms a broker parked elsewhere.
1119
872
  *
1120
873
  * @remarks
1121
- * - **Connect.** {@link connect} opens the SSE stream (via the injected `fetch` + the core
1122
- * `SSEParser`) and resolves when the stream ends; it reconnects with the `delay` backoff
1123
- * unless `reconnect` is `false` or the client was {@link destroy}ed.
1124
- * - **Dispatch + answer.** Each decoded prompt is narrowed (§14) and dispatched to `terminal`;
1125
- * the resolved value POSTs back to `url`.
874
+ * - **Connect.** {@link connect} opens the SSE stream through the injected `fetch` and resolves
875
+ * when the stream ends. It reconnects on the `delay` backoff unless `reconnect` is false or the
876
+ * client was destroyed.
877
+ * - **Ingest, then render.** Ingestion never waits on a render. Each decoded record is narrowed,
878
+ * its schema parsed and sanitized, and queued; one form is driven at a time while the stream
879
+ * keeps reading, so an unanswered form never starves the connection.
880
+ * - **Never trust the wire.** Every rendered string is sanitized, and a `pattern` that arrived over
881
+ * the wire is never executed locally. The authoritative form decides.
882
+ * - **Refusal retries.** A rejected answer comes back with the parked form's own errors; the client
883
+ * applies them to the local form and asks again until the answer is accepted or the form expires.
1126
884
  * - **`connected`** reflects whether the stream is currently open.
1127
885
  */
1128
886
  export declare interface PromptClientInterface {
@@ -1135,27 +893,27 @@ export declare interface PromptClientInterface {
1135
893
  }
1136
894
 
1137
895
  /**
1138
- * Options for {@link import('./factories.js').createPromptClient} / the {@link PromptClientInterface}.
896
+ * Options for {@link import('./factories.js').createPromptClient} and the
897
+ * {@link PromptClientInterface}.
1139
898
  *
1140
899
  * @remarks
1141
- * - `url` — the remote broker's SSE endpoint (GET opens the stream; answers POST back to it).
1142
- * - `terminal` — the LOCAL {@link PromptFormInterface} each remote prompt is dispatched to, so a
1143
- * human at THIS machine answers a prompt issued elsewhere.
1144
- * - `token` — an optional auth token, sent as the
1145
- * {@link import('./constants.js').HEADER_TOKEN} header on every request.
1146
- * - `reconnect` — whether to reconnect after the stream drops (default `true`).
900
+ * - `url` — the remote broker's SSE endpoint. A GET opens the stream; answers POST back to it.
901
+ * - `terminal` — the LOCAL {@link TerminalInterface} each remote form is driven through, so a human
902
+ * at THIS machine answers a form parked elsewhere.
903
+ * - `token` — an optional auth token, sent as the {@link import('./constants.js').HEADER_TOKEN}
904
+ * header on every request.
905
+ * - `reconnect` — whether to reconnect after the stream drops, default true.
1147
906
  * - `delay` — ms to wait before each reconnect attempt (default
1148
907
  * {@link import('./constants.js').DEFAULT_RECONNECT_DELAY_MS}).
1149
- * - `on` — initial {@link PromptClientEventMap} listeners (AGENTS §8/§13).
1150
- * - `error` — the emitter's listener-error handler (AGENTS §13).
1151
- * - `fetch` — the injected {@link FetchHandler} (default the global `fetch`); supply a scripted
1152
- * fetch to drive the client deterministically in tests.
1153
- * - `timer` — the injected {@link TimerHandler} for the reconnect backoff (default the host
1154
- * `setTimeout`); supply a deterministic timer to drive reconnection without real time.
908
+ * - `on` — initial {@link PromptClientEventMap} listeners.
909
+ * - `error` — the emitter's listener-error handler.
910
+ * - `fetch` — the injected {@link FetchHandler}, default the global `fetch`.
911
+ * - `timer` — the injected {@link TimerHandler} for the reconnect backoff, default the host
912
+ * `setTimeout`.
1155
913
  */
1156
914
  export declare interface PromptClientOptions {
1157
915
  readonly url: string;
1158
- readonly terminal: PromptFormInterface;
916
+ readonly terminal: TerminalInterface;
1159
917
  readonly token?: string;
1160
918
  readonly reconnect?: boolean;
1161
919
  readonly delay?: number;
@@ -1166,111 +924,77 @@ export declare interface PromptClientOptions {
1166
924
  }
1167
925
 
1168
926
  /**
1169
- * The broker's event map (AGENTS §13) — lean, errors `unknown`, no listener-error event.
927
+ * The broker's event map — lean, errors `unknown`, no listener-error event.
1170
928
  *
1171
929
  * @remarks
1172
- * - `pending` — a prompt was parked (carries the wire-safe {@link PendingPrompt}); a transport
1173
- * forwards it to remote clients.
1174
- * - `answer` — a parked prompt was answered (carries its `id` + the accepted `value`).
1175
- * - `expire` — a parked prompt timed out (or was torn down) unanswered (carries its `id`).
930
+ * - `pending` — a form was parked; a transport forwards the wire record to remote clients.
931
+ * - `answer` — a parked form was answered and accepted, carrying its id and the settled values.
932
+ * - `expire` — a parked form timed out or was torn down unanswered, carrying its id.
1176
933
  */
1177
934
  export declare type PromptEventMap = {
1178
- readonly pending: readonly [prompt: PendingPrompt];
1179
- readonly answer: readonly [id: string, value: unknown];
935
+ readonly pending: readonly [form: PendingForm];
936
+ readonly answer: readonly [id: string, values: FormValues];
1180
937
  readonly expire: readonly [id: string];
1181
938
  };
1182
939
 
1183
- /**
1184
- * The shared ASYNC prompt contract — the six prompt forms as Promise-returning methods. The
1185
- * ONE vocabulary BOTH the headless {@link PromptInterface} broker (this module) and the server
1186
- * `Terminal` driver (T-c) implement, so a prompt issued through this surface resolves the same
1187
- * way on every surface (local TTY, headless broker, remote bridge).
1188
- *
1189
- * @remarks
1190
- * Each method takes the prompt's `*Options` and resolves to that form's value type:
1191
- * - `input` / `password` / `select` / `editor` → a `string`
1192
- * - `confirm` → a `boolean`
1193
- * - `checkbox` → a `readonly string[]` (the checked values, in choice order)
1194
- *
1195
- * This is a behavioral CONTRACT (the method surface), not an observable entity — the broker and
1196
- * the driver add their own emitter / lifecycle on top. The {@link PromptClient} dispatches a
1197
- * remote {@link PendingPrompt} to a LOCAL `PromptFormInterface` (so a human at this machine
1198
- * answers a prompt parked elsewhere).
1199
- */
1200
- export declare interface PromptFormInterface {
1201
- input(options: InputOptions): Promise<string>;
1202
- password(options: PasswordOptions): Promise<string>;
1203
- confirm(options: ConfirmOptions): Promise<boolean>;
1204
- select(options: SelectOptions): Promise<string>;
1205
- checkbox(options: CheckboxOptions): Promise<readonly string[]>;
1206
- editor(options: EditorOptions): Promise<string>;
1207
- }
1208
-
1209
- /**
1210
- * The union of every prompt form's options bag — the `options` a {@link ParkRequest} carries,
1211
- * narrowed at the call site by the paired {@link PromptType}.
1212
- */
1213
- export declare type PromptFormOptions = InputOptions | PasswordOptions | ConfirmOptions | SelectOptions | CheckboxOptions | EditorOptions;
1214
-
1215
940
  /** The styled prompt-message header (`? message`) — the leading line every active prompt view shares, themed by the `question` + `message` roles. */
1216
941
  export declare function promptHeader(styler: StylerInterface, theme: PromptTheme, message: string): string;
1217
942
 
1218
943
  /**
1219
- * One glyph slot a prompt view renders — the icon axis of a {@link PromptTheme}. A named value
1220
- * set (which marks a view can draw), not a toggle, so it stays a union.
944
+ * One glyph slot a rendered field draws — the icon axis of a {@link PromptTheme}. A named value
945
+ * set, not a toggle, so it stays a union.
1221
946
  *
1222
947
  * @remarks
1223
- * - `question` — the leading mark on a prompt's message line.
1224
- * - `pointer` — the cursor before the input / the focused choice row.
1225
- * - `dot` / `selected` — an unfocused / focused row marker in a select list.
1226
- * - `checked` / `unchecked` — a checked / unchecked box in a checkbox list.
1227
- * - `success` / `error` — the mark on a resolved prompt's submit line / its validation error line.
948
+ * - `question` — the leading mark on a field's label line.
949
+ * - `pointer` — the cursor before the input or the focused choice row.
950
+ * - `dot` / `selected` — an unfocused / focused row marker in a choice list.
951
+ * - `checked` / `unchecked` — a checked / unchecked box in a multi-choice list.
952
+ * - `success` / `error` — the mark on a settled field's line / on its failure line.
1228
953
  */
1229
954
  export declare type PromptIcon = 'question' | 'pointer' | 'dot' | 'selected' | 'checked' | 'unchecked' | 'success' | 'error';
1230
955
 
1231
956
  /**
1232
- * The headless prompt BROKER (observable §13) — implements {@link PromptFormInterface} by
1233
- * PARKING each call as a {@link PendingPrompt} and returning a Promise that resolves when the
1234
- * prompt is {@link answer}ed (or rejects on timeout). The local-TTY / headless / remote
1235
- * tri-surface's headless arm: no terminal here — a transport forwards each `pending` to whoever
1236
- * can answer, and {@link answer} resolves the parked Promise.
957
+ * The headless form BROKER — parks a live form until somebody elsewhere answers it. The headless
958
+ * arm of the local-TTY / headless / remote trio: there is no terminal here, so a transport forwards
959
+ * each `pending` record to whoever can answer, and {@link answer} drives the parked form to
960
+ * settlement.
1237
961
  *
1238
962
  * @remarks
1239
- * - **Park-as-Promise.** Each `input` / `password` / … call mints an id, parks a
1240
- * {@link PendingPrompt}, emits `pending`, and returns an unresolved Promise. {@link park} is the
1241
- * general entry point the six form methods wrap.
1242
- * - **Answer validates.** {@link answer} validates `value` against the prompt's resolved
1243
- * validator AND type-checks it to the prompt form before accepting; a bad answer is rejected
1244
- * (a {@link Result}<unknown, {@link AnswerError}> failure, the prompt stays `pending`).
1245
- * - **Timeout → expire → reject.** An unanswered prompt expires after `timeout` ms — `expire`
1246
- * fires and the parked Promise rejects (a {@link import('./errors.js').TerminalError}). The
1247
- * timer is injectable for deterministic tests.
1248
- * - **Accessors (§9.1).** `pending()` lists the parked prompts; `pending(id)` looks one up.
1249
- */
1250
- export declare interface PromptInterface extends PromptFormInterface {
963
+ * - **The form is the unit.** {@link park} takes a live form, mints an id, emits `pending`, and
964
+ * returns the id. It wraps no promise, because the caller already holds one: the form's own
965
+ * `answer`.
966
+ * - **The parked form is authoritative.** {@link answer} fills and submits THAT form, so every rule
967
+ * it carries decides, including a `custom` validator the wire dropped. A refusal returns the
968
+ * form's own errors and leaves the form parked.
969
+ * - **Timeout abandons.** An unanswered form is destroyed after `timeout` ms; `expire` fires and
970
+ * the caller's promise rejects on the form's own lifecycle. The timer is injectable.
971
+ * - **Accessors.** `pending()` lists the parked records; `pending(id)` looks one up.
972
+ */
973
+ export declare interface PromptInterface {
1251
974
  readonly emitter: EmitterInterface<PromptEventMap>;
1252
975
  readonly count: number;
1253
- park(request: ParkRequest): Ticket;
1254
- pending(): readonly PendingPrompt[];
1255
- pending(id: string): PendingPrompt | undefined;
1256
- answer(id: string, value: unknown): Result<unknown, AnswerError>;
976
+ park(form: FormInterface, request?: ParkRequest): string;
977
+ pending(): readonly PendingForm[];
978
+ pending(id: string): PendingForm | undefined;
979
+ answer(id: string, values: FormValues): Result<FormValues, AnswerError>;
1257
980
  destroy(): void;
1258
981
  }
1259
982
 
1260
983
  /**
1261
- * Options for {@link import('./factories.js').createPrompt} / the {@link PromptInterface} broker.
984
+ * Options for {@link import('./factories.js').createPrompt} and every {@link PromptInterface}
985
+ * broker, including one a {@link TerminalManagerInterface} mounts per endpoint.
1262
986
  *
1263
987
  * @remarks
1264
- * - `on` — initial {@link PromptEventMap} listeners (AGENTS §8/§13).
1265
- * - `error` — the emitter's listener-error handler (AGENTS §13).
1266
- * - `timeout` — ms a parked prompt waits before it expires + its Promise rejects (default
988
+ * - `on` — initial {@link PromptEventMap} listeners.
989
+ * - `error` — the emitter's listener-error handler.
990
+ * - `timeout` — ms a parked form waits before it expires and is abandoned (default
1267
991
  * {@link import('./constants.js').DEFAULT_PROMPT_TIMEOUT_MS}).
1268
- * - `timer` — the injected {@link TimerHandler} (default the host `setTimeout`); supply a
992
+ * - `timer` — the injected {@link TimerHandler}, default the host `setTimeout`; supply a
1269
993
  * deterministic timer to drive expiry in tests without real time.
1270
- * - `cap` — the maximum number of prompts this broker will hold PARKED at once (default
1271
- * UNBOUNDED — no cap unless supplied). Once `count` reaches `cap`, a new park is rejected with
1272
- * a {@link import('./errors.js').TerminalError} (`code: 'LIMIT'`) WITHOUT parking, minting an
1273
- * id, emitting `pending`, or arming the expiry timer — the runaway-asker memory ceiling.
994
+ * - `cap` — the maximum number of forms this broker holds parked at once, default unbounded. Once
995
+ * `count` reaches `cap` a new park is refused with a
996
+ * {@link import('./errors.js').TerminalError} coded `LIMIT`, without parking, minting an id,
997
+ * emitting `pending`, or arming a timer. The runaway-asker memory ceiling.
1274
998
  */
1275
999
  export declare interface PromptOptions {
1276
1000
  readonly on?: EmitterHooks<PromptEventMap>;
@@ -1281,54 +1005,50 @@ export declare interface PromptOptions {
1281
1005
  }
1282
1006
 
1283
1007
  /**
1284
- * One styling slot a prompt view renders through — the SEMANTIC axis of a {@link PromptTheme}.
1285
- * A role says what a fragment MEANS; the theme decides what that meaning looks like, so a
1286
- * consumer re-maps the styled fragments by naming roles rather than reimplementing a view.
1008
+ * One styling slot a rendered field paints through — the semantic axis of a {@link PromptTheme}. A
1009
+ * role says what a fragment MEANS; the theme decides what that meaning looks like, so a consumer
1010
+ * re-maps styled output by naming roles rather than reimplementing a renderer.
1287
1011
  *
1288
1012
  * @remarks
1289
- * - `question` — the leading question mark on an active prompt's line.
1290
- * - `pointer` — the cursor before the input / the focused choice row.
1291
- * - `message` — the prompt's own question text.
1292
- * - `content` — the prompt's primary content: the typed value, the mask run, an unfocused choice
1293
- * label, a committed editor line, an in-progress editor line, or a fallback choice name. Its
1294
- * default is the EMPTY style, so an unthemed prompt renders this content as bare text.
1295
- * - `success` / `error` — a resolved prompt's mark / a validation-error mark and its message.
1296
- * - `selected` — a chosen value: the checked box, the focused select marker, the confirm default letter.
1013
+ * - `question` — the leading mark on an active field's line.
1014
+ * - `pointer` — the cursor before the input or the focused choice row.
1015
+ * - `message` — the field's own label text.
1016
+ * - `content` — the field's primary content: the typed value, the mask run, an unfocused choice
1017
+ * label, or a committed editor line. Its default is the EMPTY style, so unthemed content renders
1018
+ * as bare text.
1019
+ * - `success` / `error` — a settled field's mark / a failure mark and its message.
1020
+ * - `selected` — a chosen value: the checked box, the focused choice marker, the confirm default
1021
+ * letter.
1297
1022
  * - `focus` — the label of the row the cursor is on.
1298
- * - `hint` — dim supplementary text: a default value, a key hint, a selection count, a committed answer.
1299
- * - `muted` — a dim off-state mark: an unfocused select marker, an unchecked box, a fallback index.
1300
- * - `description` — a choice's one-line description.
1023
+ * - `hint` — dim supplementary text: a default value, a key hint, a selection count, a committed
1024
+ * answer.
1025
+ * - `muted` — a dim off-state mark: an unfocused choice marker, an unchecked box, a fallback index.
1026
+ * - `description` — a choice's one-line help text.
1301
1027
  */
1302
1028
  export declare type PromptRole = 'question' | 'pointer' | 'message' | 'content' | 'success' | 'error' | 'selected' | 'focus' | 'hint' | 'muted' | 'description';
1303
1029
 
1304
1030
  /**
1305
- * The discriminant of a {@link PromptStep} — where the prompt stands after a key. `active`:
1306
- * keep prompting (the input was consumed, or rejected by validation). `submit`: the prompt
1307
- * resolved with its `value`. `cancel`: the user aborted (ctrl-c). Names its axis (the prompt's
1308
- * progression), never `kind` (AGENTS §4.4).
1031
+ * Where one field's reducer stands after a key. `active`: keep asking, because the key was
1032
+ * consumed or the answer was refused. `submit`: the field resolved with its `value`. `cancel`: the
1033
+ * user aborted with ctrl-c. Names its axis, never `kind`.
1309
1034
  */
1310
1035
  export declare type PromptStatus = 'active' | 'submit' | 'cancel';
1311
1036
 
1312
1037
  /**
1313
- * The result of one reducer step — the next `state`, the rendered `view`, the `status`, and,
1314
- * on `submit`, the resolved `value`. The whole contract between a pure prompt reducer and the
1315
- * impure driver: the driver applies the next `state`, writes the `view`, and — when `status`
1316
- * is `submit` — reads `value`.
1038
+ * The result of one reducer step — the next `state`, the rendered `view`, the `status`, and, on
1039
+ * `submit`, the resolved `value`. The whole contract between a pure reducer and the impure driver:
1040
+ * the driver applies the next `state`, writes the `view`, and reads `value` on `submit`.
1317
1041
  *
1318
- * @typeParam T - The prompt's resolved value type (`string` for input / password / select /
1319
- * editor, `boolean` for confirm, `readonly string[]` for checkbox).
1320
- * @typeParam S - The prompt's concrete state shape ({@link InputState}, {@link SelectState}, …) —
1321
- * carried directly so `state` stays precisely typed with no union narrowing or assertion.
1042
+ * @typeParam T - The value this field resolves to, as its control admits it.
1043
+ * @typeParam S - The reducer's concrete state shape, carried directly so `state` stays precisely
1044
+ * typed with no union narrowing and no assertion.
1322
1045
  *
1323
1046
  * @remarks
1324
- * - `state` — the next immutable prompt state (feed it to the next reduce call). On `submit` /
1325
- * `cancel` it is the final state.
1326
- * - `view` — the styled string to render NOW (possibly MULTI-LINE for `select` / `checkbox`),
1327
- * built through the state's {@link StylerInterface}. On an invalid `submit` it carries the
1328
- * error; the driver re-renders it each step.
1329
- * - `status` — the {@link PromptStatus}: `active` to continue, `submit` when resolved, `cancel`
1330
- * on abort.
1331
- * - `value` — present ONLY on a `submit` step, carrying the prompt's resolved value.
1047
+ * - `state` — the next immutable state; feed it to the next reduce call. On `submit` or `cancel` it
1048
+ * is the final state.
1049
+ * - `view` — the styled string to render now, possibly multi-line. On a refused `submit` it carries
1050
+ * the failure; the driver re-renders it each step.
1051
+ * - `value` — present ONLY on a `submit` step.
1332
1052
  */
1333
1053
  export declare interface PromptStep<T, S> {
1334
1054
  readonly state: S;
@@ -1338,17 +1058,16 @@ export declare interface PromptStep<T, S> {
1338
1058
  }
1339
1059
 
1340
1060
  /**
1341
- * A prompt's resolved PRESENTATION — the glyph for every {@link PromptIcon} and the console
1342
- * {@link Style} for every {@link PromptRole}. Plain JSON data (no functions), so it crosses the wire
1343
- * with the prompt it decorates. Built by {@link import('./helpers.js').createPromptTheme}; carried
1344
- * resolved on every prompt state, so a view never reads a hardcoded glyph or color.
1061
+ * A resolved PRESENTATION — the glyph for every {@link PromptIcon} and the console {@link Style}
1062
+ * for every {@link PromptRole}. Plain JSON data with no functions, so it crosses the wire with the
1063
+ * form it decorates. Built by {@link import('./helpers.js').createPromptTheme}.
1345
1064
  *
1346
1065
  * @remarks
1347
1066
  * A role's value is the console module's own {@link Style} — the one style model the whole console
1348
- * / terminal system shares — so a view paints a role through
1349
- * {@link StylerInterface.render} and this package holds no second style vocabulary. A `Style`
1350
- * carries a `foreground`, a `background`, and an attribute list, so a role can express a background
1351
- * color, which the styler's accessor chain cannot name.
1067
+ * and terminal system shares — so a renderer paints a role through
1068
+ * {@link import('@orkestrel/console').StylerInterface} and this package holds no second style
1069
+ * vocabulary. A `Style` carries a foreground, a background, and an attribute list, so a role can
1070
+ * express a background color, which a styler's accessor chain cannot name.
1352
1071
  */
1353
1072
  export declare interface PromptTheme {
1354
1073
  readonly icons: Readonly<Record<PromptIcon, string>>;
@@ -1356,8 +1075,8 @@ export declare interface PromptTheme {
1356
1075
  }
1357
1076
 
1358
1077
  /**
1359
- * The PARTIAL {@link PromptTheme} a prompt option bag carries — every icon and every role is
1360
- * optional, and {@link import('./helpers.js').createPromptTheme} merges what is supplied over
1078
+ * The PARTIAL {@link PromptTheme} an option bag carries — every icon and every role is optional,
1079
+ * and {@link import('./helpers.js').createPromptTheme} merges what is supplied over
1361
1080
  * {@link import('./constants.js').DEFAULT_PROMPT_THEME} leaf by leaf. Supplying one icon or one
1362
1081
  * role leaves every other slot at its default.
1363
1082
  */
@@ -1366,105 +1085,44 @@ export declare interface PromptThemeOptions {
1366
1085
  readonly roles?: Readonly<Partial<Record<PromptRole, Style>>>;
1367
1086
  }
1368
1087
 
1369
- /**
1370
- * The six prompt KINDS this core provides — the value family a higher-level broker (T-b)
1371
- * dispatches on. Names the prompt axis; a named value set (not a toggle), so it stays a union.
1372
- */
1373
- export declare type PromptType = 'input' | 'password' | 'confirm' | 'select' | 'checkbox' | 'editor';
1374
-
1375
- /**
1376
- * The union of a resolved prompt's value shapes — a `string` (`input` / `password` / `select` /
1377
- * `editor`), a `boolean` (`confirm`), or a `readonly string[]` (`checkbox`, the checked values in
1378
- * choice order). The type a {@link Ticket}'s `value` Promise resolves to.
1379
- */
1380
- export declare type PromptValue = string | boolean | readonly string[];
1381
-
1382
- /**
1383
- * Rebuild a wire-decoded `validate` payload into a {@link ValidationRules} bag — the inverse of
1384
- * {@link serializeValidationRules}. Keeps only the primitive rule values (`boolean` / `number` /
1385
- * `string`) a serialized prompt could carry; an empty / non-record / all-dropped payload yields
1386
- * `undefined` (no rules to apply). The client feeds the result back through {@link resolveValidation}.
1387
- */
1388
- export declare function reconstructValidationRules(value: unknown): ValidationRules | undefined;
1389
-
1390
- /** Read a `choices` option as a list of bare strings / full choices — each element narrowed by `guard`, off-shape elements stringified. */
1391
- export declare function resolveChoices<TChoice extends PromptChoice | CheckboxChoice>(options: Readonly<Record<string, unknown>>, guard: Guard<TChoice>): ReadonlyArray<string | TChoice>;
1392
-
1393
- /** Read one option by key, narrowed by `guard` — `undefined` when absent or off-shape (§14, never an `as`). */
1394
- export declare function resolveOption<T>(options: Readonly<Record<string, unknown>>, key: string, guard: Guard<T>): T | undefined;
1088
+ /** Carriage return (`\r`, U+000D) — Enter on most terminals. */
1089
+ export declare const RETURN: string;
1395
1090
 
1396
1091
  /**
1397
- * Compile a {@link Validator} or declarative {@link ValidationRules} (or nothing) into ONE
1398
- * composed {@link Validator}. A bare validator passes through; rules are appended in the fixed
1399
- * order (required → minimum → maximum → pattern → email → url → numeric → integer → alphanumeric
1400
- * → custom) and composed; absent / empty input yields an always-passing validator. Pure.
1401
- *
1402
- * @remarks
1403
- * Unlike a prior variant (which returned `Validator | undefined`), this ALWAYS returns a
1404
- * `Validator` — an absent or empty rule set yields a validator that returns `true` for every
1405
- * input. That keeps a prompt's state unconditional (it always holds a real validator to apply on
1406
- * submit), with no `undefined` branch at the call site.
1092
+ * Sanitize text for one single-line display slot. Composes console's ANSI {@link strip} and C0
1093
+ * {@link stripControls} passes with removal of tab, line feed, and carriage return.
1407
1094
  *
1408
- * @param validate - A custom {@link Validator}, a {@link ValidationRules} bag, or `undefined`
1409
- * @returns The composed {@link Validator} (always-passing when nothing was supplied)
1095
+ * @param text - The text to sanitize for a glyph or hint slot
1096
+ * @returns The text with ANSI sequences, every C0 control character, and DEL removed
1410
1097
  *
1411
1098
  * @example
1412
1099
  * ```ts
1413
- * const v = resolveValidation({ required: true, minimum: 3 })
1414
- * v('') // 'This field is required'
1415
- * v('ab') // 'Must be at least 3 characters'
1416
- * v('abc') // true
1100
+ * sanitizeDisplayText('Q\rOVERWRITE\nNEXT\tX') // 'QOVERWRITENEXTX'
1417
1101
  * ```
1418
1102
  */
1419
- export declare function resolveValidation(validate?: Validator | ValidationRules): Validator;
1420
-
1421
- /** Carriage return (`\r`, U+000D) — Enter on most terminals. */
1422
- export declare const RETURN: string;
1423
-
1424
- /**
1425
- * Each built-in validation rule's default error message — what the composed {@link
1426
- * import('./types.js').Validator} returns when that rule fails (the `minimum` / `maximum`
1427
- * messages are interpolated with the configured length at build time). Frozen; the source of
1428
- * truth for the rule-failure copy.
1429
- */
1430
- export declare const RULE_MESSAGES: Readonly<{
1431
- required: "This field is required";
1432
- minimum: "Must be at least {count} characters";
1433
- maximum: "Must be at most {count} characters";
1434
- pattern: "Must match pattern: {pattern}";
1435
- email: "Must be a valid email address";
1436
- url: "Must be a valid URL";
1437
- numeric: "Must be a numeric value";
1438
- integer: "Must be an integer";
1439
- alphanumeric: "Must contain only letters and digits";
1440
- invalid: "Invalid input";
1441
- }>;
1103
+ export declare function sanitizeDisplayText(text: string): string;
1442
1104
 
1443
1105
  /**
1444
- * Sanitize a list of resolved choices' human-readable labels (`name` + `description`) with
1445
- * {@link stripControls} — shared by the `select` and `checkbox` branches of
1446
- * {@link dispatchPendingPrompt} so a remote-supplied choice can never inject raw control bytes
1447
- * into the local terminal's rendered view.
1106
+ * Sanitize every terminal-readable string in a parsed form schema.
1448
1107
  *
1449
- * @param choices - The resolved choices (bare strings or full {@link PromptChoice} /
1450
- * {@link CheckboxChoice} objects) as returned by {@link resolveChoices}
1451
- * @returns The same choices with every `name` / `description` control-stripped
1452
- */
1453
- export declare function sanitizeChoiceLabels<TChoice extends PromptChoice | CheckboxChoice>(choices: ReadonlyArray<string | TChoice>): ReadonlyArray<string | TChoice>;
1454
-
1455
- /**
1456
- * Sanitize text for one single-line display slot. Composes console's {@link stripControls} pass
1457
- * with removal of tab, line feed, and carriage return, so no C0 control or DEL survives.
1108
+ * @remarks
1109
+ * Display strings pass through {@link sanitizeDisplayText}: labels, help, placeholders, masks,
1110
+ * choice labels and help, file accept entries, and pattern sources. Identity and answer strings
1111
+ * stay verbatim: schema, group, and field names, group references, choice values, and defaults.
1112
+ * Rewriting those would sever the rendering copy from the authoritative form. Field metadata is
1113
+ * removed because terminal neither renders nor interprets it. Pattern sources are sanitized as
1114
+ * text only and are never compiled or executed here.
1458
1115
  *
1459
- * @param text - The text to sanitize for a glyph or hint slot
1460
- * @returns The text with every C0 control character and DEL removed
1116
+ * @param schema - A schema already accepted by the Form package's `parseForm`
1117
+ * @returns A new schema with terminal-readable strings sanitized and field metadata omitted
1461
1118
  *
1462
1119
  * @example
1463
1120
  * ```ts
1464
- * sanitizeDisplayText('Q\rOVERWRITE\nNEXT\tX') // 'QOVERWRITENEXTX'
1121
+ * sanitizeSchema({ fields: [{ control: 'text', name: 'na\u001bme', label: 'N\u0000ame' }] })
1122
+ * // { fields: [{ control: 'text', name: 'na\u001bme', label: 'Name' }] }
1465
1123
  * ```
1466
1124
  */
1467
- export declare function sanitizeDisplayText(text: string): string;
1125
+ export declare function sanitizeSchema(schema: FormSchema): FormSchema;
1468
1126
 
1469
1127
  /**
1470
1128
  * Sanitize every glyph a wire-supplied {@link PromptThemeOptions} carries for a single-line display
@@ -1476,53 +1134,16 @@ export declare function sanitizeDisplayText(text: string): string;
1476
1134
  */
1477
1135
  export declare function sanitizeThemeIcons(theme: PromptThemeOptions): PromptThemeOptions;
1478
1136
 
1479
- /**
1480
- * Options for a single-selection {@link import('./helpers.js').selectReduce} prompt.
1481
- *
1482
- * @remarks
1483
- * `choices` accepts bare strings (used as both `name` and `value`) or full {@link PromptChoice}
1484
- * objects; {@link import('./helpers.js').createSelectState} normalizes them. `default` pre-focuses
1485
- * the choice whose `value` matches it. `hint` adds a key hint after the message — the list is
1486
- * self-explanatory at a TTY, so there is no computed default; the server driver shows it on the
1487
- * numbered fallback too.
1488
- */
1489
- export declare interface SelectOptions {
1490
- readonly message: string;
1491
- readonly choices: ReadonlyArray<string | PromptChoice>;
1492
- readonly default?: string;
1493
- readonly hint?: string;
1494
- readonly styler?: StylerInterface;
1495
- readonly theme?: PromptThemeOptions;
1496
- }
1497
-
1498
1137
  /**
1499
1138
  * Advance a select prompt by one {@link KeyEvent} — the pure `(state, key) → PromptStep<string>`
1500
1139
  * reducer. `up` / `down` (and `k` / `j`) move the focus, WRAPPING at the ends; return submits the
1501
1140
  * focused choice's `value`; ctrl-c cancels. An empty choice list can never submit (a higher layer
1502
1141
  * guards against it); any other key is ignored.
1503
1142
  */
1504
- export declare function selectReduce(state: SelectState, key: KeyEvent): PromptStep<string, SelectState>;
1505
-
1506
- /**
1507
- * The immutable state of a select prompt — the normalized choices, the optional hint, the
1508
- * resolved styler + theme, and the `focused` index (the highlighted row).
1509
- *
1510
- * @remarks
1511
- * Built by {@link import('./helpers.js').createSelectState}; advanced by
1512
- * {@link import('./helpers.js').selectReduce}. `up` / `down` move `focused` (wrapping at the
1513
- * ends); `return` submits the focused choice's `value`.
1514
- */
1515
- export declare interface SelectState {
1516
- readonly message: string;
1517
- readonly choices: readonly PromptChoice[];
1518
- readonly hint?: string;
1519
- readonly styler: StylerInterface;
1520
- readonly theme: PromptTheme;
1521
- readonly focused: number;
1522
- }
1143
+ export declare function selectReduce(state: ReturnType<typeof createSelectState>, key: KeyEvent): PromptStep<string, ReturnType<typeof createSelectState>>;
1523
1144
 
1524
- /** Render a {@link SelectState} as a MULTI-LINE styled view — the header (plus any hint) followed by one row per choice, the focused row marked. */
1525
- export declare function selectView(state: SelectState): string;
1145
+ /** Render a select-field key state as a multi-line styled view. */
1146
+ export declare function selectView(state: ReturnType<typeof createSelectState>): string;
1526
1147
 
1527
1148
  /**
1528
1149
  * The exact escape SEQUENCE → canonical key NAME table {@link import('./helpers.js').parseKey}
@@ -1538,54 +1159,20 @@ export declare function selectView(state: SelectState): string;
1538
1159
  */
1539
1160
  export declare const SEQUENCE_NAMES: Readonly<Record<string, string>>;
1540
1161
 
1541
- /** Strip functions from a `choices` option — each choice keeps its plain fields; a bare string passes through. */
1542
- export declare function serializeChoices(choices: unknown): readonly unknown[];
1543
-
1544
1162
  /** Serialize a parked prompt's expiry into a {@link WireEvent} — event `'expire'`, `data` the JSON-stringified `{ id }` payload. */
1545
1163
  export declare function serializeExpire(id: string): WireEvent;
1546
1164
 
1547
- /** 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). */
1548
- export declare function serializePending(prompt: PendingPrompt): WireEvent;
1549
-
1550
- /**
1551
- * Produce the WIRE-SAFE form of a prompt's options — the {@link PendingPrompt.options} a broker
1552
- * serializes over SSE. Drops everything that cannot cross the wire (a `styler`, and any
1553
- * function-valued `validate` rule), while KEEPING the declarative data a remote client needs to
1554
- * reconstruct the prompt: the {@link ValidationRules} as data, plus `message` / `choices` /
1555
- * `default` / `mask` / `min` / `max` / `hint` / `theme`. This is WHY T-a's validation is
1556
- * rules-as-data, and why a theme is data too: a `Validator`
1557
- * FUNCTION can't be serialized, but its declarative rules can — the client rebuilds the validator
1558
- * from them via {@link resolveValidation}.
1559
- *
1560
- * @remarks
1561
- * - **Functions are dropped.** A top-level function value (e.g. a bare-`Validator` `validate`) is
1562
- * omitted entirely; the `styler` (a fluent function-bearing object) is dropped by key.
1563
- * - **`validate` rules are flattened.** A {@link ValidationRules} bag is copied with each
1564
- * function rule replaced by `true` (the rule's INTENT survives as the built-in check; its
1565
- * custom function does not). A bare-function `validate` is dropped (no declarative form to keep).
1566
- * - **`choices` are function-stripped.** Each choice keeps its plain fields (`name` / `value` /
1567
- * `description` / `checked`); a bare string passes through.
1568
- *
1569
- * @param options - The raw prompt options bag (may hold functions / a styler)
1570
- * @returns A JSON-safe options record — only serializable, declarative data
1571
- */
1572
- export declare function serializePromptOptions(options: object): Readonly<Record<string, unknown>>;
1165
+ /** Serialize a parked {@link PendingForm} into a {@link WireEvent}. */
1166
+ export declare function serializePending(form: PendingForm): WireEvent;
1573
1167
 
1574
1168
  /** The {@link WireEvent} a broker/manager sends when it is going away — event `'shutdown'`, no payload. */
1575
1169
  export declare function serializeShutdown(): WireEvent;
1576
1170
 
1577
- /**
1578
- * Flatten a `validate` option to its wire-safe {@link ValidationRules} DATA — a function rule
1579
- * becomes `true` (its built-in check survives; its body cannot cross the wire). A bare-function
1580
- * `validate` (no rules object) has no declarative form, so it yields `undefined` (dropped).
1581
- */
1582
- export declare function serializeValidationRules(validate: unknown): Readonly<Record<string, unknown>> | undefined;
1583
-
1584
1171
  /** Space (U+0020). */
1585
1172
  export declare const SPACE = " ";
1586
1173
 
1587
1174
  /**
1588
- * The maximum number of characters the {@link import('./types.js').PromptClient} lets its
1175
+ * The maximum number of characters the {@link import('./types.js').PromptClientInterface} lets its
1589
1176
  * SSE parser buffer before treating the stream as hostile — 1 MiB, comfortably above any
1590
1177
  * legitimate prompt payload. Passed as the `limit` to `createSSEParser` so an unterminated
1591
1178
  * or oversized `data:` field cannot grow the buffer without bound (a memory-exhaustion guard).
@@ -1593,11 +1180,11 @@ export declare const SPACE = " ";
1593
1180
  export declare const SSE_BUFFER_LIMIT = 1048576;
1594
1181
 
1595
1182
  /**
1596
- * The SSE `event:` names the broker emits and the {@link import('./types.js').PromptClient}
1183
+ * The SSE `event:` names the broker emits and the {@link import('./types.js').PromptClientInterface}
1597
1184
  * dispatches on. Frozen; the source of truth for the wire event vocabulary.
1598
1185
  *
1599
1186
  * @remarks
1600
- * - `pending` — a serialized {@link import('./types.js').PendingPrompt} to dispatch + answer.
1187
+ * - `pending` — a serialized {@link import('./types.js').PendingForm} to dispatch and answer.
1601
1188
  * - `expire` — an `{ id }` payload: the broker expired a parked prompt (the client drops it).
1602
1189
  * - `shutdown` — the broker is going away; the client disconnects (no auto-reconnect) but stays reusable.
1603
1190
  */
@@ -1613,47 +1200,81 @@ export declare function submitHeader(styler: StylerInterface, theme: PromptTheme
1613
1200
  /** Tab (`\t`, U+0009). */
1614
1201
  export declare const TAB: string;
1615
1202
 
1616
- /** The rejection reason a {@link TerminalManagerInterface.answer} call returns — an {@link AnswerError}, plus `'terminal'` (no such endpoint). */
1617
- export declare type TerminalAnswerError = AnswerError | 'terminal';
1203
+ /**
1204
+ * Why a {@link TerminalManagerInterface.answer} call refused — an {@link AnswerError} from the
1205
+ * endpoint's own broker, or `terminal` when no endpoint is mounted under that name. One
1206
+ * discriminant, `reason`, across both.
1207
+ */
1208
+ export declare type TerminalAnswerError = AnswerError | {
1209
+ readonly reason: 'terminal';
1210
+ };
1618
1211
 
1619
1212
  /**
1620
- * An error a {@link import('./Prompt.js').Prompt} broker rejects a parked prompt's Promise with.
1213
+ * The error the terminal surfaces for its own refusals: parking on a destroyed or full broker, an
1214
+ * unusable driver stream, a manager routing fault, or a ctrl-c cancellation. A parked form's own
1215
+ * lifecycle failures reject through the form's `answer` with the form package's error, never with
1216
+ * this one.
1621
1217
  *
1622
1218
  * @remarks
1623
- * Carries a {@link TerminalErrorCode} and an optional `context` bag (the prompt `id`). Thrown —
1624
- * as a Promise rejection on the awaited prompt call — when a parked prompt is not answered before
1625
- * its `timeout`, or when the broker is `destroy`ed while the prompt is still `pending` (both
1626
- * `EXPIRE`); or when the user aborts an interactive server-`Terminal` prompt with ctrl-c
1627
- * (`CANCEL`). Narrow a caught value with {@link isTerminalError} and branch on `error.code`.
1219
+ * Carries a {@link TerminalErrorCode} and an optional `context` bag naming the offending values:
1220
+ * `{ cap }` on `LIMIT`, `{ to, known }` on `TARGET`, and `{ from, to, path }` on `DEADLOCK`. Narrow
1221
+ * a caught value with {@link isTerminalError} and branch on `error.code`.
1628
1222
  */
1629
1223
  export declare class TerminalError extends Error {
1630
1224
  /** The machine-readable condition — see {@link TerminalErrorCode}. */
1631
1225
  readonly code: TerminalErrorCode;
1632
- /** An optional context bag naming the offending prompt id. */
1226
+ /** An optional context bag naming the offending values — see the class {@link TerminalError remarks}. */
1633
1227
  readonly context?: Readonly<Record<string, unknown>>;
1634
1228
  constructor(code: TerminalErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
1635
1229
  }
1636
1230
 
1637
1231
  /**
1638
1232
  * The machine-readable condition carried by a {@link import('./errors.js').TerminalError} — the
1639
- * axis a `catch` branches on. Names its axis (the failure condition), never `kind` (AGENTS §4.4).
1233
+ * axis a `catch` branches on. Names its axis (the failure condition), never `kind`.
1640
1234
  *
1641
1235
  * @remarks
1642
- * - `EXPIRE` — a parked broker prompt was not answered before its `timeout` (or the broker was
1643
- * `destroy`ed while it was still pending); the prompt's Promise rejects with this.
1644
- * - `CANCEL` — the user aborted an interactive prompt (ctrl-c) at the server `Terminal` (T-c)
1645
- * driver; the awaited prompt call rejects with this so a caller can branch on `error.code`.
1646
- * - `LIMIT` — a broker's optional `cap` on concurrently-parked prompts was already reached; the
1647
- * new call is rejected WITHOUT parking (never counted, never emitted, no timer armed).
1648
- * - `DESTROYED` — a call reached an already-`destroy`ed {@link TerminalManagerInterface} (`add`
1649
- * at entry, or `open` whose `store` read resolved after a `destroy` happened in the gap).
1236
+ * Every code here is terminal's own. A refusal that belongs to the form — a malformed schema, a
1237
+ * value a control cannot hold, a write to a settled form — arrives as the dependency's own
1238
+ * `FormError` and is never re-coded.
1239
+ *
1240
+ * - `EXPIRE` — `park` was called on an already-`destroy`ed broker: the given form is destroyed and
1241
+ * the call throws before minting an id. A parked form that times out is abandoned instead, not
1242
+ * coded `EXPIRE` — the broker destroys it and the caller's `answer` promise rejects on the
1243
+ * form's own lifecycle with the Form package's `ABANDONED` error.
1244
+ * - `CANCEL` — the user aborted at the server `Terminal` driver with ctrl-c.
1245
+ * - `DRIVER` — the driver could not read the terminal it was given.
1246
+ * - `DEADLOCK` — an endpoint was asked to answer its own question.
1247
+ * - `TARGET` — the named endpoint cannot be reached.
1248
+ * - `LIMIT` — the broker's optional `cap` on concurrently parked forms was already reached. The
1249
+ * new call is refused WITHOUT parking: no id, no `pending` event, no timer.
1250
+ * - `DESTROYED` — a call reached an already-destroyed {@link TerminalManagerInterface}.
1650
1251
  */
1651
1252
  export declare type TerminalErrorCode = 'EXPIRE' | 'CANCEL' | 'DRIVER' | 'DEADLOCK' | 'TARGET' | 'LIMIT' | 'DESTROYED';
1652
1253
 
1254
+ /**
1255
+ * The contract for asking a form of a human at a keyboard — one method, because a form is one
1256
+ * question however many fields it holds. The server `Terminal` implements it against a real TTY;
1257
+ * a {@link PromptClientInterface} holds one to answer forms parked elsewhere.
1258
+ *
1259
+ * @remarks
1260
+ * `ask` drives the form the caller passes: it walks the schema's fields in order, binds each
1261
+ * keystroke through the form's own `fill`, submits, and returns the settled values. The returned
1262
+ * promise is the form's `answer`, so a caller holding the form can await either one. Ctrl-c at an
1263
+ * interactive driver is the one exception: it rejects THIS promise with a `TerminalError` coded
1264
+ * `CANCEL` and leaves the form `editing`, so the form's own `answer` stays pending for its owner.
1265
+ *
1266
+ * A driver never owns the form's lifetime. To interrupt an active walk, destroy the form: it
1267
+ * abandons, `answer` rejects, and the driver stops rendering on the form's `abandon` event. That
1268
+ * is the only cancellation channel, which is why this contract needs no second method.
1269
+ */
1270
+ export declare interface TerminalInterface {
1271
+ ask(form: FormInterface): Promise<FormValues>;
1272
+ }
1273
+
1653
1274
  /**
1654
1275
  * The multi-endpoint terminal MANAGER — a registry of named {@link PromptInterface} brokers (one
1655
- * per endpoint), so several parties can `ask` prompts of each other by NAME with a `from` → `to`
1656
- * attribution edge on every parked prompt, and a transitive DEADLOCK check across all in-flight
1276
+ * per endpoint), so several parties can `ask` forms of each other by NAME with a `from` → `to`
1277
+ * attribution edge on every parked form, and a transitive DEADLOCK check across all in-flight
1657
1278
  * asks.
1658
1279
  *
1659
1280
  * @remarks
@@ -1664,22 +1285,23 @@ export declare type TerminalErrorCode = 'EXPIRE' | 'CANCEL' | 'DRIVER' | 'DEADLO
1664
1285
  * - **`ask`.** The target must already be mounted via {@link add} — `ask` never auto-adds it;
1665
1286
  * rejects `TARGET` for an unknown `to` (listing the known names). Rejects `DEADLOCK` when parking
1666
1287
  * `from → to` would close a cycle over the CURRENT in-flight edge set (walked transitively);
1667
- * otherwise parks through the target's broker and resolves with the ORIGINAL ticket Promise (edge
1668
- * cleanup is attached via `.then`, never altering the value/rejection the caller observes).
1288
+ * otherwise parks the caller's live form through the target's broker and returns that form's own
1289
+ * `answer` promise. Edge cleanup never alters the value or rejection the caller observes.
1669
1290
  * - **Durable open / save.** `open(name)` restores an EMPTY broker from the `store` (parked
1670
1291
  * Promises are process-bound and never resurrected); `save(name)` persists the endpoint's
1671
1292
  * configured `timeout`.
1672
1293
  * - **Removal.** `remove` drops one endpoint, a batch (§9.2, array overload FIRST), or every
1673
- * endpoint when called without an argument. It destroys each broker, which expires every prompt
1294
+ * endpoint when called without an argument. It destroys each broker, which expires every form
1674
1295
  * still parked on it. `destroy` is idempotent.
1675
1296
  *
1676
1297
  * @example
1677
1298
  * ```ts
1299
+ * const form = createForm({ fields: [{ control: 'text', name: 'name' }] })
1678
1300
  * const manager = new TerminalManager()
1679
1301
  * manager.add('agent')
1680
- * const name = manager.ask('user', 'agent', 'input', { message: 'Your name' })
1681
- * manager.answer('agent', manager.pending('agent')[0].id, 'Ada')
1682
- * await name // 'Ada'
1302
+ * const answer = manager.ask('user', 'agent', form)
1303
+ * manager.answer('agent', manager.pending('agent')[0].id, { name: 'Ada' })
1304
+ * await answer // { name: 'Ada' }
1683
1305
  * ```
1684
1306
  */
1685
1307
  export declare class TerminalManager implements TerminalManagerInterface {
@@ -1689,14 +1311,11 @@ export declare class TerminalManager implements TerminalManagerInterface {
1689
1311
  get count(): number;
1690
1312
  terminal(name: string): PromptInterface | undefined;
1691
1313
  terminals(): readonly string[];
1692
- add(name: string, options?: TerminalOptions): PromptInterface;
1693
- ask(from: string, to: string, form: 'input' | 'password' | 'editor', options: InputOptions | PasswordOptions | EditorOptions): Promise<string>;
1694
- ask(from: string, to: string, form: 'confirm', options: ConfirmOptions): Promise<boolean>;
1695
- ask(from: string, to: string, form: 'select', options: SelectOptions): Promise<string>;
1696
- ask(from: string, to: string, form: 'checkbox', options: CheckboxOptions): Promise<readonly string[]>;
1697
- pending(): readonly PendingPrompt[];
1698
- pending(to: string): readonly PendingPrompt[];
1699
- answer(to: string, id: string, value: unknown): Result<unknown, TerminalAnswerError>;
1314
+ add(name: string, options?: PromptOptions): PromptInterface;
1315
+ ask(from: string, to: string, form: FormInterface): Promise<FormValues>;
1316
+ pending(): readonly PendingForm[];
1317
+ pending(to: string): readonly PendingForm[];
1318
+ answer(to: string, id: string, values: FormValues): Result<FormValues, TerminalAnswerError>;
1700
1319
  open(name: string): Promise<PromptInterface | undefined>;
1701
1320
  save(name: string): Promise<boolean>;
1702
1321
  remove(names: readonly string[]): boolean;
@@ -1706,40 +1325,39 @@ export declare class TerminalManager implements TerminalManagerInterface {
1706
1325
  }
1707
1326
 
1708
1327
  /**
1709
- * The manager's event map (AGENTS §13) — the name-attributed re-emission of every mounted
1710
- * broker's events, so a caller subscribes once for ALL endpoints instead of per-broker.
1328
+ * The manager's event map — the name-attributed re-emission of every mounted broker's events, so a
1329
+ * caller subscribes once for ALL endpoints instead of once per broker.
1711
1330
  *
1712
1331
  * @remarks
1713
- * - `pending` — an endpoint parked a prompt (carries the {@link PendingPrompt}, itself carrying
1714
- * `from` / `to`).
1715
- * - `answer` — an endpoint's parked prompt was answered (`to` names the endpoint).
1716
- * - `expire` — an endpoint's parked prompt timed out (`to` names the endpoint).
1332
+ * - `pending` — an endpoint parked a form; the record itself carries `from` and `to`.
1333
+ * - `answer` — an endpoint's parked form was answered; `to` names the endpoint.
1334
+ * - `expire` — an endpoint's parked form expired; `to` names the endpoint.
1717
1335
  */
1718
1336
  export declare type TerminalManagerEventMap = {
1719
- readonly pending: readonly [prompt: PendingPrompt];
1720
- readonly answer: readonly [to: string, id: string, value: unknown];
1337
+ readonly pending: readonly [form: PendingForm];
1338
+ readonly answer: readonly [to: string, id: string, values: FormValues];
1721
1339
  readonly expire: readonly [to: string, id: string];
1722
1340
  };
1723
1341
 
1724
1342
  /**
1725
- * The multi-endpoint terminal MANAGER (§9.1/§9.2) — a registry of named {@link PromptInterface}
1726
- * brokers (one per endpoint), so several parties (agents, tools, humans) can `ask` prompts of
1727
- * each other by NAME, attributed with a `from` → `to` edge on every parked {@link PendingPrompt}.
1343
+ * The multi-endpoint terminal MANAGER — a registry of named {@link PromptInterface} brokers, one
1344
+ * per endpoint, so several parties (agents, tools, humans) can ask forms of each other BY NAME,
1345
+ * attributed with a `from` → `to` edge on every parked record.
1728
1346
  *
1729
1347
  * @remarks
1730
- * - **Accessors (§9.1).** `terminal(name)` looks up one endpoint's broker; `terminals()` lists
1731
- * every mounted endpoint name.
1732
- * - **`add`** mints (or returns the existing) broker for `name` — idempotent, never clobbers a
1733
- * live endpoint.
1734
- * - **`ask`** is the attributed convenience: parks a prompt from `from` to `to` (auto-`add`ing
1735
- * `to` if absent) and resolves with the typed value, precisely overloaded per {@link PromptType}.
1736
- * - **`pending()`** lists every endpoint's parked prompts; `pending(to)` scopes to one endpoint.
1348
+ * - **Accessors.** `terminal(name)` looks up one endpoint's broker; `terminals()` lists every
1349
+ * mounted endpoint name.
1350
+ * - **`add`** mints, or returns, the broker for `name`. Idempotent; it never clobbers a live
1351
+ * endpoint.
1352
+ * - **`ask`** is the attributed convenience: it parks `form` from `from` to `to`, adding `to` if it
1353
+ * is absent, and resolves with the settled values.
1354
+ * - **`pending()`** lists every endpoint's parked records; `pending(to)` scopes to one endpoint.
1737
1355
  * - **`answer`** routes to the named endpoint's broker.
1738
- * - **`open`** restores (or returns the live) broker for `name` from the `store`.
1739
- * - **`save`** persists an endpoint's config snapshot to the `store` (`false` when there is no
1740
- * store, or `name` is unknown).
1741
- * - **Batch `remove` (§9.2).** The array overload is declared FIRST — `remove(names)` removes each
1742
- * listed endpoint (`true` when any named terminal was removed); `remove(name)` removes one;
1356
+ * - **`open`** restores, or returns the live, broker for `name` from the `store`.
1357
+ * - **`save`** persists an endpoint's config snapshot; false when there is no store, or `name` is
1358
+ * unknown.
1359
+ * - **Batch `remove`.** The array overload is declared FIRST: `remove(names)` removes each listed
1360
+ * endpoint and reports whether any named terminal was removed; `remove(name)` removes one;
1743
1361
  * `remove()` removes every endpoint without destroying the manager.
1744
1362
  * - **`destroy`** tears down every broker, then the manager's own emitter.
1745
1363
  */
@@ -1748,14 +1366,11 @@ export declare interface TerminalManagerInterface {
1748
1366
  readonly count: number;
1749
1367
  terminal(name: string): PromptInterface | undefined;
1750
1368
  terminals(): readonly string[];
1751
- add(name: string, options?: TerminalOptions): PromptInterface;
1752
- ask(from: string, to: string, form: 'input' | 'password' | 'editor', options: InputOptions | PasswordOptions | EditorOptions): Promise<string>;
1753
- ask(from: string, to: string, form: 'confirm', options: ConfirmOptions): Promise<boolean>;
1754
- ask(from: string, to: string, form: 'select', options: SelectOptions): Promise<string>;
1755
- ask(from: string, to: string, form: 'checkbox', options: CheckboxOptions): Promise<readonly string[]>;
1756
- pending(): readonly PendingPrompt[];
1757
- pending(to: string): readonly PendingPrompt[];
1758
- answer(to: string, id: string, value: unknown): Result<unknown, TerminalAnswerError>;
1369
+ add(name: string, options?: PromptOptions): PromptInterface;
1370
+ ask(from: string, to: string, form: FormInterface): Promise<FormValues>;
1371
+ pending(): readonly PendingForm[];
1372
+ pending(to: string): readonly PendingForm[];
1373
+ answer(to: string, id: string, values: FormValues): Result<FormValues, TerminalAnswerError>;
1759
1374
  open(name: string): Promise<PromptInterface | undefined>;
1760
1375
  save(name: string): Promise<boolean>;
1761
1376
  remove(names: readonly string[]): boolean;
@@ -1765,15 +1380,14 @@ export declare interface TerminalManagerInterface {
1765
1380
  }
1766
1381
 
1767
1382
  /**
1768
- * Options for {@link import('./factories.js').createTerminalManager} / the
1383
+ * Options for {@link import('./factories.js').createTerminalManager} and the
1769
1384
  * {@link TerminalManagerInterface}.
1770
1385
  *
1771
1386
  * @remarks
1772
- * - `store` — the optional {@link TerminalStoreInterface} backing `open` / `save`.
1773
- * - `timeout` / `timer` / `cap` — the manager-wide default for each endpoint's broker (overridable
1774
- * per {@link TerminalManagerInterface.add} call via {@link TerminalOptions}, exactly like
1775
- * `timeout` / `timer`).
1776
- * - `on` / `error` — the manager's {@link EmitterHooks} + {@link EmitterErrorHandler} (AGENTS §13).
1387
+ * - `store` — the optional {@link TerminalStoreInterface} backing `open` and `save`.
1388
+ * - `timeout` / `timer` / `cap` — the manager-wide default for each endpoint's broker, overridable
1389
+ * per {@link TerminalManagerInterface.add} call.
1390
+ * - `on` / `error` — the manager's own emitter hooks and listener-error handler.
1777
1391
  */
1778
1392
  export declare interface TerminalManagerOptions {
1779
1393
  readonly store?: TerminalStoreInterface;
@@ -1784,28 +1398,29 @@ export declare interface TerminalManagerOptions {
1784
1398
  readonly error?: EmitterErrorHandler;
1785
1399
  }
1786
1400
 
1787
- /** 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. */
1788
- export declare interface TerminalOptions {
1789
- readonly timeout?: number;
1790
- readonly timer?: TimerHandler_2;
1791
- readonly cap?: number;
1792
- }
1793
-
1794
- /** 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. */
1401
+ /**
1402
+ * One endpoint's persisted CONFIG snapshot — `id` is the endpoint name and `timeout` its configured
1403
+ * default. Parked forms are process-bound and are never resurrected, so `open` always restores an
1404
+ * EMPTY broker.
1405
+ */
1795
1406
  export declare interface TerminalSnapshot {
1796
1407
  readonly id: string;
1797
1408
  readonly timeout?: number;
1798
1409
  }
1799
1410
 
1800
- /** One opaque persisted row — the shape a `TableInterface<TerminalSnapshotRow>`-backed store reads/writes; `snapshot` is narrowed with {@link import('./validators.js').isTerminalSnapshot} on read. */
1411
+ /**
1412
+ * One opaque persisted row — the shape a `TableInterface<TerminalSnapshotRow>`-backed store reads
1413
+ * and writes. `snapshot` is narrowed with {@link import('./validators.js').isTerminalSnapshot} on
1414
+ * read.
1415
+ */
1801
1416
  export declare interface TerminalSnapshotRow {
1802
1417
  readonly id: string;
1803
1418
  readonly snapshot: unknown;
1804
1419
  }
1805
1420
 
1806
1421
  /**
1807
- * The point-access persistence seam (AGENTS §5 — Stores) for a {@link TerminalManagerInterface}'s
1808
- * endpoint configs. Every primitive is async; `delete` of an absent id is a no-op.
1422
+ * The point-access persistence seam for a {@link TerminalManagerInterface}'s endpoint configs.
1423
+ * Every primitive is async; deleting an absent id is a no-op.
1809
1424
  */
1810
1425
  export declare interface TerminalStoreInterface {
1811
1426
  get(id: string): Promise<TerminalSnapshot | undefined>;
@@ -1813,20 +1428,14 @@ export declare interface TerminalStoreInterface {
1813
1428
  delete(id: string): Promise<void>;
1814
1429
  }
1815
1430
 
1816
- /** The handle {@link PromptInterface.park} returns — the parked prompt's `id` plus the Promise that resolves (or rejects) with its {@link PromptValue}. */
1817
- export declare interface Ticket {
1818
- readonly id: string;
1819
- readonly value: Promise<PromptValue>;
1820
- }
1821
-
1822
1431
  /** Cancel a pending {@link TimerHandler} deadline — idempotent, safe to call after the timer fired. */
1823
1432
  export declare type TimerCancel = () => void;
1824
1433
 
1825
1434
  /**
1826
1435
  * One injected timer — arms a deadline `callback` to fire after `ms`, returning a
1827
1436
  * {@link TimerCancel} that cancels it. The broker's timeout seam: the default wraps the host
1828
- * `setTimeout` / `clearTimeout`; a test injects a deterministic timer that captures the callback
1829
- * and fires it on demand (no real time, no global fake-timer patching).
1437
+ * `setTimeout` and `clearTimeout`; a test injects a deterministic timer that captures the callback
1438
+ * and fires it on demand, with no real time and no global patching.
1830
1439
  */
1831
1440
  declare type TimerHandler_2 = (callback: () => void, ms: number) => TimerCancel;
1832
1441
  export { TimerHandler_2 as TimerHandler }
@@ -1835,61 +1444,11 @@ export { TimerHandler_2 as TimerHandler }
1835
1444
  export declare function toggleIndex(indices: readonly number[], index: number): readonly number[];
1836
1445
 
1837
1446
  /**
1838
- * Matches an HTTP(S) URL. The `url` rule tests against this. Kept locally because it is
1839
- * narrower than `FORMAT_PATTERNS.uri`, which accepts other URI schemes.
1840
- */
1841
- export declare const URL_PATTERN: RegExp;
1842
-
1843
- /**
1844
- * Declarative validation rules for a text prompt — each key toggles (or overrides) one
1845
- * built-in check. {@link import('./helpers.js').resolveValidation} compiles a `ValidationRules`
1846
- * (or a bare {@link Validator}) into ONE composed {@link Validator}.
1847
- *
1848
- * @remarks
1849
- * Each rule is EITHER a primitive that turns on the built-in check, OR a {@link Validator}
1850
- * that replaces it with custom logic:
1851
- *
1852
- * - `required` — `true` ⇒ input must be non-empty after trimming.
1853
- * - `minimum` — a number ⇒ input must be at least that many characters.
1854
- * - `maximum` — a number ⇒ input must be at most that many characters.
1855
- * - `pattern` — a string ⇒ input must match that regex source.
1856
- * - `email` — `true` ⇒ input must look like an email address.
1857
- * - `url` — `true` ⇒ input must look like an HTTP(S) URL.
1858
- * - `numeric` — `true` ⇒ input must be a number (integer or decimal).
1859
- * - `integer` — `true` ⇒ input must be an integer.
1860
- * - `alphanumeric` — `true` ⇒ input must contain only letters and digits.
1861
- * - `custom` — an arbitrary {@link Validator} escape hatch.
1862
- *
1863
- * Rules compose in the fixed order above; the FIRST failing rule short-circuits and its
1864
- * message is returned. A `false` / `undefined` rule is skipped.
1865
- */
1866
- export declare interface ValidationRules {
1867
- readonly required?: boolean | Validator;
1868
- readonly minimum?: number | Validator;
1869
- readonly maximum?: number | Validator;
1870
- readonly pattern?: string | Validator;
1871
- readonly email?: boolean | Validator;
1872
- readonly url?: boolean | Validator;
1873
- readonly numeric?: boolean | Validator;
1874
- readonly integer?: boolean | Validator;
1875
- readonly alphanumeric?: boolean | Validator;
1876
- readonly custom?: Validator;
1877
- }
1878
-
1879
- /**
1880
- * A single input validator — given the current input string, returns `true` when the input
1881
- * is valid, or an error MESSAGE string when it is not. The atomic unit the validation engine
1882
- * composes and the prompts apply on submit.
1883
- *
1884
- * @remarks
1885
- * A `Validator` is total and pure — it never throws and never mutates. The `true | string`
1886
- * shape (not `boolean`) is deliberate: an invalid result CARRIES its message, so a prompt
1887
- * can render the exact reason. {@link import('./helpers.js').composeValidators} runs several
1888
- * in order and returns the FIRST error (short-circuiting), so the most specific rule wins.
1447
+ * One SSE-shaped wire frame — the `event` name, its already-stringified `data` payload, and an
1448
+ * optional `id`. The transport-neutral shape {@link import('./helpers.js').serializePending},
1449
+ * {@link import('./helpers.js').serializeExpire}, and
1450
+ * {@link import('./helpers.js').serializeShutdown} build, with no `http` dependency.
1889
1451
  */
1890
- export declare type Validator = (input: string) => true | string;
1891
-
1892
- /** 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. */
1893
1452
  export declare interface WireEvent {
1894
1453
  readonly event: string;
1895
1454
  readonly data: string;