@fuaran-ui/ops 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,552 @@
1
+ import { Result, NodeId, NodeKind, JsonValue, Binding, SemanticStyle, StateBehaviour, Node, Cell, ColExpr, DataSource, Transform, EvalError, Table, TextSource, CellFormat, ColumnWidth, Orientation, ToneVariant, StyleWeight, Emphasis, HeadingVariant, BadgeVariant, IconSource } from '@fuaran-ui/schema';
2
+
3
+ /** Local JSON AST. Shape-for-shape port of the F# decoder's private `Json` DU. */
4
+ type JsonAst = {
5
+ readonly kind: 'JNull';
6
+ } | {
7
+ readonly kind: 'JBool';
8
+ readonly value: boolean;
9
+ } | {
10
+ readonly kind: 'JNumber';
11
+ readonly value: number;
12
+ } | {
13
+ readonly kind: 'JString';
14
+ readonly value: string;
15
+ } | {
16
+ readonly kind: 'JArray';
17
+ readonly items: readonly JsonAst[];
18
+ } | {
19
+ readonly kind: 'JObject';
20
+ readonly fields: ReadonlyMap<string, JsonAst>;
21
+ };
22
+ /** Structural parse failure carrying the byte offset where it was detected. */
23
+ interface ParseError {
24
+ readonly message: string;
25
+ readonly offset: number;
26
+ }
27
+ /**
28
+ * Parse a JSON document into the local AST. Mirrors the F# `tryParse`: empty /
29
+ * whitespace-only input is a structural error; otherwise a single top-level
30
+ * value is parsed (trailing content after the first value is not inspected,
31
+ * matching the F# parser).
32
+ */
33
+ declare const parse: (input: string) => Result<JsonAst, ParseError>;
34
+ /** Look up an object field by key (key-order tolerant per WIRE_FORMAT.md §2). */
35
+ declare const field: (fields: ReadonlyMap<string, JsonAst>, key: string) => JsonAst | undefined;
36
+
37
+ type TreeOp<TMsg> = {
38
+ readonly kind: 'EditNode';
39
+ readonly target: NodeId;
40
+ readonly newKind: NodeKind<TMsg>;
41
+ } | {
42
+ readonly kind: 'UpdateProp';
43
+ readonly target: NodeId;
44
+ readonly path: string;
45
+ readonly value: JsonValue;
46
+ } | {
47
+ readonly kind: 'ReplaceBinding';
48
+ readonly target: NodeId;
49
+ readonly slot: string;
50
+ readonly binding: Binding<unknown>;
51
+ } | {
52
+ readonly kind: 'UpdateStyle';
53
+ readonly target: NodeId;
54
+ readonly style: SemanticStyle;
55
+ } | {
56
+ readonly kind: 'UpdateState';
57
+ readonly target: NodeId;
58
+ readonly state: StateBehaviour<TMsg>;
59
+ } | {
60
+ readonly kind: 'InsertChild';
61
+ readonly parentId: NodeId;
62
+ readonly position: number;
63
+ readonly child: Node<TMsg>;
64
+ } | {
65
+ readonly kind: 'RemoveNode';
66
+ readonly target: NodeId;
67
+ } | {
68
+ readonly kind: 'MoveNode';
69
+ readonly target: NodeId;
70
+ readonly newParentId: NodeId;
71
+ readonly newPosition: number;
72
+ } | {
73
+ readonly kind: 'ReorderChildren';
74
+ readonly parentId: NodeId;
75
+ readonly newOrder: readonly NodeId[];
76
+ } | {
77
+ readonly kind: 'ReplaceRoot';
78
+ readonly node: Node<TMsg>;
79
+ } | {
80
+ readonly kind: 'Batch';
81
+ readonly ops: readonly TreeOp<TMsg>[];
82
+ };
83
+
84
+ /**
85
+ * Re-render a parsed JSON AST (parse.ts) to canonical wire bytes — Ordinal-
86
+ * sorted object keys (§2 rule 2), the §2 rule-5 number layout, the §2 rule-6
87
+ * string escapes — using the very primitives the encoder uses. For input that
88
+ * was already canonical, `renderAstCanonical(parse(x)) === x`. This is the
89
+ * §15 wire-versioning building block: it re-emits a preserved `Unknown`
90
+ * payload byte-for-byte (must-ignore-but-preserve, WIRE_FORMAT.md §15.3) and
91
+ * renders the `$profile` / `$payload` envelope. Mirrors F#
92
+ * `Fuaran.Core.Wire.Canon.render` composed over the same JVal shape.
93
+ */
94
+ declare const renderAstCanonical: (ast: JsonAst) => string;
95
+ /** Encode a `DataSource` to its canonical-JSON string (Compute layer). */
96
+ declare const encodeDataSource: (src: DataSource) => string;
97
+ /** Encode a literal `Cell` to its `$type`-tagged canonical-JSON string. */
98
+ declare const encodeCell: (c: Cell) => string;
99
+ /** Encode a `ColExpr` to its canonical-JSON string. */
100
+ declare const encodeColExpr: (e: ColExpr) => string;
101
+ /** Encode a transform `pipeline` (ordered steps) to its canonical-JSON array string. */
102
+ declare const encodePipeline: (pipeline: readonly Transform[]) => string;
103
+ /** Encode a `Node<TMsg>` to its canonical-JSON string (WIRE_FORMAT.md). */
104
+ declare const encodeNode: <TMsg>(n: Node<TMsg>) => string;
105
+ /** Encode a `TreeOp<TMsg>` to its canonical-JSON string (WIRE_FORMAT.md). */
106
+ declare const encodeOp: <TMsg>(op: TreeOp<TMsg>) => string;
107
+
108
+ type DR<T> = Result<T, EvalError>;
109
+ /** The canonical string of a cell (float via the shared `Canon` layout). */
110
+ declare const cellString: (c: Cell) => string;
111
+ /** Evaluate a `ColExpr` against one row. */
112
+ /** The evaluation environment (fuaran-core#77) — binds each `ColExpr.param` name to a `Cell`. */
113
+ type EvalEnv = Readonly<Record<string, Cell>>;
114
+ /** Resolve a `Ref` source to a concrete `Table` (default: reject every ref). */
115
+ type SourceResolver = (ref: string) => DR<Table>;
116
+ /** A `Ref`-rejecting resolver — the default for embedded-only pipelines. */
117
+ declare const noResolve: SourceResolver;
118
+ /** Evaluate a `DataSource` to a concrete `Table`, resolving a `Ref` through `resolve`. */
119
+ declare const evalSource: (resolve: SourceResolver, src: DataSource) => DR<Table>;
120
+ /**
121
+ * The reference evaluator: fold the pipeline over the input table, threading a
122
+ * `Frame`. `resolve` provides any `Ref` source a `Join` / `Union` names.
123
+ */
124
+ declare const evalPipelineWithInEnv: (resolve: SourceResolver, env: EvalEnv, pipeline: readonly Transform[], input: Table) => DR<Table>;
125
+ declare const evalPipelineWith: (resolve: SourceResolver, pipeline: readonly Transform[], input: Table) => DR<Table>;
126
+ /** The reference evaluator over embedded sources only (`Ref` ⇒ `UnresolvedSource`). */
127
+ declare const evalPipeline: (pipeline: readonly Transform[], input: Table) => DR<Table>;
128
+ /** The env-aware evaluator over embedded sources only (fuaran-core#77 / Phase 424). */
129
+ declare const evalPipelineInEnv: (env: EvalEnv, pipeline: readonly Transform[], input: Table) => DR<Table>;
130
+ /** Render an `EvalError` as a stable human string (mirrors F# `DataFrame.errorString`). */
131
+ declare const evalErrorString: (e: EvalError) => string;
132
+ /** Every `ColExpr.param` name a single step references (only `filter` / `derive` carry a ColExpr). */
133
+ declare const stepParams: (t: Transform) => readonly string[];
134
+ /** Every distinct param name a pipeline references — the TS mirror of Core `Transform.paramsOf`. */
135
+ declare const pipelineParams: (pipeline: readonly Transform[]) => readonly string[];
136
+
137
+ /** Stable AI-friendly discriminator for decode-time failures (WIRE_FORMAT.md §6). */
138
+ type DecodeErrorCode = 'INVALID_JSON' | 'MISSING_FIELD' | 'WRONG_TYPE' | 'UNKNOWN_DU_CASE' | 'WRONG_NODE_KIND' | 'EMPTY_NODE_ID';
139
+ /** AI-recoverable decode-time failure. Mirrors the F# `DecodeError` record. */
140
+ interface DecodeError {
141
+ readonly code: DecodeErrorCode;
142
+ readonly path: string;
143
+ readonly message: string;
144
+ readonly expectedShape?: string;
145
+ }
146
+ type R<T> = Result<T, DecodeError>;
147
+ /** Typed-value coercers for `TreeOp.UpdateProp` (used by the apply engine). */
148
+ declare const coerce: {
149
+ int: (v: JsonValue) => Result<number, string>;
150
+ float: (v: JsonValue) => Result<number, string>;
151
+ bool: (v: JsonValue) => Result<boolean, string>;
152
+ string: (v: JsonValue) => Result<string, string>;
153
+ textSource: (v: JsonValue) => Result<TextSource, string>;
154
+ bindingNumber: (v: JsonValue) => Result<Binding<number>, string>;
155
+ bindingInt: (v: JsonValue) => Result<Binding<number>, string>;
156
+ bindingBool: (v: JsonValue) => Result<Binding<boolean>, string>;
157
+ bindingString: (v: JsonValue) => Result<Binding<string>, string>;
158
+ cellFormat: (v: JsonValue) => Result<CellFormat, string>;
159
+ columnWidth: (v: JsonValue) => Result<ColumnWidth, string>;
160
+ orientation: (v: JsonValue) => Result<Orientation, string>;
161
+ tone: (v: JsonValue) => Result<ToneVariant, string>;
162
+ weight: (v: JsonValue) => Result<StyleWeight, string>;
163
+ emphasis: (v: JsonValue) => Result<Emphasis, string>;
164
+ /**
165
+ * The behavioural `emphasis` BOOL on Fact / LabelValueRow — the UpdateProp
166
+ * twin of `decodeEmphasisFlag`, so a TreeOp edit gets the same
167
+ * cross-vocabulary admission as a fresh decode (0.2.8, 2026-07-19 sweep).
168
+ */
169
+ emphasisFlag: (v: JsonValue) => Result<boolean, string>;
170
+ headingVariant: (v: JsonValue) => Result<HeadingVariant, string>;
171
+ badgeVariant: (v: JsonValue) => Result<BadgeVariant, string>;
172
+ iconSource: (v: JsonValue) => Result<IconSource, string>;
173
+ };
174
+ /** Decode a canonical-JSON `Node` payload into the storage-shape `Node<unknown>`. */
175
+ declare const decodeNode: (json: string) => R<Node<unknown>>;
176
+ /** Decode a canonical-JSON `TreeOp` payload into the storage-shape `TreeOp<unknown>`. */
177
+ declare const decodeOp: (json: string) => R<TreeOp<unknown>>;
178
+
179
+ type ApplyErrorCode = 'NodeNotFound' | 'ParentNotFound' | 'ChildlessKind' | 'PositionOutOfRange' | 'DuplicateNodeId' | 'FieldNotFound' | 'SlotNotFound' | 'KindMismatch' | 'PathInvalid' | 'PathNotSupportedYet' | 'OrderingMismatch' | 'BatchAborted';
180
+ interface ApplyError {
181
+ readonly code: ApplyErrorCode;
182
+ readonly message: string;
183
+ /** Inner-op index when `code === 'BatchAborted'`. */
184
+ readonly batchIndex?: number;
185
+ }
186
+ /** One record per applied leaf op (Phase 76 telemetry contract). */
187
+ interface OpApplyTelemetryRecord {
188
+ readonly op: TreeOp<unknown>['kind'];
189
+ readonly targetId: string;
190
+ }
191
+ type ApplyResult<TMsg> = Result<{
192
+ readonly newTree: Node<TMsg>;
193
+ readonly emittedTelemetry: readonly OpApplyTelemetryRecord[];
194
+ }, ApplyError>;
195
+ /**
196
+ * Apply a single tree-op against `tree`, returning either the updated tree plus
197
+ * emitted telemetry, or a structured `ApplyError`. Fold this across an op list
198
+ * to apply many; wrap in `TreeOp.Batch` for atomic all-or-nothing application.
199
+ */
200
+ declare const apply: <TMsg>(tree: Node<TMsg>, op: TreeOp<TMsg>) => ApplyResult<TMsg>;
201
+
202
+ /** The result envelope captured on a DAG record (closed shape). */
203
+ type DagResultEnvelope = {
204
+ readonly $type: 'Success';
205
+ } | {
206
+ readonly $type: 'Failure';
207
+ readonly code: string;
208
+ readonly message: string;
209
+ };
210
+ /**
211
+ * Content-addressed, multi-parent op-record — the branching-DAG generalisation
212
+ * of the linear `OpRecord`. `parents` is in author order (head = primary
213
+ * parent); `outcomeHash` is present only on a merge node (committed to the
214
+ * canonical encoding of the resulting tree). A linear chain is the degenerate
215
+ * single-parent case.
216
+ */
217
+ interface DagOpRecord<TMsg = unknown> {
218
+ readonly streamId: string;
219
+ readonly hash: string;
220
+ readonly parents: readonly string[];
221
+ readonly op: TreeOp<TMsg>;
222
+ readonly outcomeHash?: string;
223
+ readonly promptId?: string;
224
+ readonly userId: string;
225
+ /** Unix seconds. */
226
+ readonly timestamp: number;
227
+ readonly resultEnvelope: DagResultEnvelope;
228
+ readonly tombstoned: boolean;
229
+ }
230
+ /**
231
+ * Encode a `DagOpRecord` to its canonical JSON wire form. Keys in Ordinal order
232
+ * (hash < op < outcomeHash < parents < promptId < resultEnvelope < streamId <
233
+ * timestamp < tombstoned < userId); `outcomeHash` / `promptId` omitted when
234
+ * absent; `op` nests `encodeOp` verbatim. Byte-identical to the F#
235
+ * `DagWire.encodeRecord`.
236
+ */
237
+ declare const encodeDagRecord: <TMsg>(record: DagOpRecord<TMsg>) => string;
238
+ type DecodeResult<T> = {
239
+ readonly ok: true;
240
+ readonly value: T;
241
+ } | {
242
+ readonly ok: false;
243
+ readonly error: string;
244
+ };
245
+ /**
246
+ * Decode a canonical DAG-record envelope. The nested `op` AST is routed through
247
+ * the canonical `decodeOp` (the same structural decoder the linear wire path
248
+ * uses). Returns a structured error string on any wire-shape violation.
249
+ */
250
+ declare const decodeDagRecord: (json: string) => DecodeResult<DagOpRecord<unknown>>;
251
+
252
+ type N = Node<unknown>;
253
+ /** A `(nodeId, facet)` cell that could not be auto-merged. */
254
+ interface MergeConflict {
255
+ readonly nodeId: string;
256
+ readonly facet: string;
257
+ }
258
+ /** Outcome of a 3-way merge: the merged tree, or the conflicting cells. */
259
+ type MergeResult = {
260
+ readonly ok: true;
261
+ readonly tree: N;
262
+ } | {
263
+ readonly ok: false;
264
+ readonly conflicts: readonly MergeConflict[];
265
+ };
266
+ /**
267
+ * Author-agnostic facet 3-way merge of `a` and `b` over their common `base`
268
+ * (all three share the root id). Returns the merged tree on full auto-merge, or
269
+ * the conflicting cells. Deterministic + host-reproducible (NodeId-byte
270
+ * tie-break, no wall-clock) — byte-identical to the F# `TreeMerge.merge3Way`.
271
+ */
272
+ declare const merge3Way: (base: N, a: N, b: N) => MergeResult;
273
+
274
+ /**
275
+ * A wire profile id — `<name>@<major>.<minor>` (e.g. `core@1.0`). `name` is the
276
+ * capability namespace; `major` is the `/vN/` incompatibility boundary (a
277
+ * removal/rename mints a new major — old consumers cannot interpret it); `minor`
278
+ * is the additive capability counter (a new kind/case/field bumps the minor — an
279
+ * older consumer tolerates it via must-ignore-but-preserve).
280
+ */
281
+ interface Profile {
282
+ readonly name: string;
283
+ readonly major: number;
284
+ readonly minor: number;
285
+ }
286
+ /** The canonical string form: `<name>@<major>.<minor>`. */
287
+ declare const renderProfile: (p: Profile) => string;
288
+ /** The base `core` profile — `core@1.0` (the current wire). */
289
+ declare const coreV1: Profile;
290
+ /**
291
+ * Parse `<name>@<major>.<minor>`. Names a typed error on any malformed shape —
292
+ * the same envelope discipline as the decoder (no exceptions escape). Mirrors
293
+ * F# `Profile.tryParse`: split on the LAST `@`, require a `<major>.<minor>` of
294
+ * two non-negative integers.
295
+ */
296
+ declare const tryParseProfile: (s: string) => Result<Profile, string>;
297
+ /** The capability-negotiation outcome of a consumer reading an artifact's profile. */
298
+ type Compatibility =
299
+ /** Authored at-or-below the consumer's profile (same name + major) — decode fully. */
300
+ {
301
+ readonly kind: 'Current';
302
+ }
303
+ /**
304
+ * Authored *ahead* of the consumer (same name + major, higher minor) — the
305
+ * consumer may meet kinds it does not understand; it must tolerate (preserve +
306
+ * degrade), never crash.
307
+ */
308
+ | {
309
+ readonly kind: 'Behind';
310
+ readonly authored: Profile;
311
+ }
312
+ /**
313
+ * A different namespace or a different major — an incompatible `/vN/` boundary
314
+ * the consumer cannot interpret at all (hard-refuse, never silently mis-decode).
315
+ */
316
+ | {
317
+ readonly kind: 'Foreign';
318
+ readonly authored: Profile;
319
+ };
320
+ /**
321
+ * Negotiate a consumer's supported profile against an artifact's authored
322
+ * profile. Minor-ahead is `Behind` (tolerable); a different name or major is
323
+ * `Foreign` (refuse). Mirrors F# `Versioning.negotiate`.
324
+ */
325
+ declare const negotiate: (consumer: Profile, authored: Profile) => Compatibility;
326
+ /** The versioned-envelope payload key. `$`-prefixed so it sorts before data keys. */
327
+ declare const PAYLOAD_KEY = "$payload";
328
+ /** The versioned-envelope profile key. */
329
+ declare const PROFILE_KEY = "$profile";
330
+ /** The optional artifact-declares-its-required-profile key. */
331
+ declare const REQUIRED_PROFILE_KEY = "requiredProfile";
332
+ /**
333
+ * A versioning-layer failure. Extends the six canonical `DecodeErrorCode`s with
334
+ * two envelope-specific codes, kept OUT of the core `DecodeErrorCode` set (the
335
+ * six-code AI-recovery surface stays unpolluted — WIRE_FORMAT.md §6):
336
+ * - `FOREIGN_PROFILE` — negotiate returned `Foreign`; the artifact is refused.
337
+ * - `MALFORMED_PROFILE` — the `$profile` value is not a `<name>@<major>.<minor>`.
338
+ */
339
+ type EnvelopeErrorCode = DecodeErrorCode | 'FOREIGN_PROFILE' | 'MALFORMED_PROFILE';
340
+ interface EnvelopeError {
341
+ readonly code: EnvelopeErrorCode;
342
+ readonly path: string;
343
+ readonly message: string;
344
+ }
345
+ /**
346
+ * A versioned wire envelope: the producer's authored `profile` + the artifact
347
+ * `payload` (a `Node` / `TreeOp` as the verbatim parsed AST). `$profile` /
348
+ * `$payload` sort before any lower-case data key under the canonical key order,
349
+ * so an enveloped fixture is byte-stable. Mirrors F# `Versioning.Envelope`.
350
+ */
351
+ interface Envelope {
352
+ readonly profile: Profile;
353
+ readonly payload: JsonAst;
354
+ }
355
+ /** Render an envelope to canonical wire bytes. */
356
+ declare const encodeEnvelope: (env: Envelope) => string;
357
+ /**
358
+ * Decode an envelope from an already-parsed AST — reads `$profile` (parsed) +
359
+ * the verbatim `$payload`. A missing / non-string / malformed `$profile`, or a
360
+ * missing `$payload`, is a structured `EnvelopeError`.
361
+ */
362
+ declare const decodeEnvelopeAst: (ast: JsonAst) => Result<Envelope, EnvelopeError>;
363
+ /** Parse + decode an envelope from wire bytes. */
364
+ declare const decodeEnvelope: (json: string) => Result<Envelope, EnvelopeError>;
365
+ /**
366
+ * A kind the consumer does not understand, captured on the decode boundary.
367
+ * **Transport-only**: reachable here and nowhere on the authoring/encode path —
368
+ * no host can construct one to emit. `payload` is the *verbatim parsed AST*, so
369
+ * re-rendering it reproduces the producer's bytes (must-ignore-but-preserve);
370
+ * `requiredProfile` is the profile the artifact declared it needs (when a
371
+ * well-formed one was present), so the consumer can name what it is missing in a
372
+ * degraded placeholder. Mirrors F# `Versioning.UnknownKind`.
373
+ */
374
+ interface UnknownKind {
375
+ readonly kind: string;
376
+ readonly payload: JsonAst;
377
+ readonly requiredProfile?: Profile;
378
+ }
379
+ /** The result of a tolerant decode: a fully-understood `T`, or a preserved `Unknown`. */
380
+ type Decoded<T> = {
381
+ readonly known: true;
382
+ readonly value: T;
383
+ } | {
384
+ readonly known: false;
385
+ readonly unknown: UnknownKind;
386
+ };
387
+ /**
388
+ * Tolerantly decode one artifact AST. `tagOf` reads its discriminator; `isKnown`
389
+ * reports whether this consumer understands that tag; `decodeKnown` decodes a
390
+ * known one. An *unrecognised* tag is NOT an error — it becomes a transport-only
391
+ * `Unknown` carrying the verbatim parsed `payload` and any declared
392
+ * `requiredProfile`. A genuinely malformed object (no discriminator at all)
393
+ * still fails via `tagOf`. Mirrors F# `Versioning.decodeTolerant`.
394
+ */
395
+ declare const decodeTolerant: <T>(tagOf: (ast: JsonAst) => Result<string, EnvelopeError>, isKnown: (tag: string) => boolean, decodeKnown: (ast: JsonAst) => Result<T, EnvelopeError>, ast: JsonAst) => Result<Decoded<T>, EnvelopeError>;
396
+ /**
397
+ * Re-encode a tolerant decode back to canonical wire bytes. The `Unknown` branch
398
+ * re-renders its preserved `payload` **verbatim** (must-ignore-but-preserve);
399
+ * the `Known` branch re-encodes through the real node encoder. Composed with the
400
+ * canonical renderer's deterministic key order, an unknown artifact round-trips
401
+ * byte-for-byte. Mirrors F# `Versioning.reencode` (specialised to `Node`).
402
+ */
403
+ declare const reencodeNode: (d: Decoded<Node<unknown>>) => string;
404
+ /**
405
+ * Tolerantly decode a bare (un-enveloped) `Node` wire form. A known kind decodes
406
+ * normally; an unknown top-level kind becomes a transport-only `Unknown` whose
407
+ * bytes round-trip verbatim through `reencodeNode` — a minor-ahead artifact no
408
+ * longer throws `WRONG_NODE_KIND` / `UNKNOWN_DU_CASE`. A genuinely malformed
409
+ * object (no `kind.$type`) still fails.
410
+ */
411
+ declare const decodeNodeTolerant: (json: string) => Result<Decoded<Node<unknown>>, EnvelopeError>;
412
+ /**
413
+ * The consumer entry point for reading a possibly-versioned `Node` artifact
414
+ * safely. Decodes the `$profile` / `$payload` envelope, negotiates the authored
415
+ * profile against `consumer`, and:
416
+ * - **Foreign** → refuses with `FOREIGN_PROFILE` (never silently mis-decodes);
417
+ * - **Current / Behind** → tolerantly decodes the payload (unknown kinds
418
+ * preserved) and returns the whole envelope re-encoded to canonical bytes.
419
+ *
420
+ * The re-encode is byte-identical to a canonical input (must-ignore-but-preserve
421
+ * keeps an unknown-kind payload verbatim), which is exactly what the
422
+ * `envelope-round-trip` corpus family asserts and the `envelope-reject` family
423
+ * asserts the `FOREIGN_PROFILE` refusal for.
424
+ */
425
+ declare const negotiateEnvelope: (json: string, consumer?: Profile) => Result<string, EnvelopeError>;
426
+
427
+ /** The envelope format-version tag key (`$`-prefixed, reserved per §2.1). */
428
+ declare const ELICITATION_KEY = "$elicitation";
429
+ /** The envelope format version this codec produces and accepts. */
430
+ declare const ELICITATION_VERSION = "1";
431
+ /**
432
+ * The elicitation error surface: the six §6 codes plus the §18.5 codes, kept
433
+ * OUT of the core `DecodeErrorCode` set (same posture as §15's
434
+ * `FOREIGN_PROFILE`).
435
+ */
436
+ type ElicitationErrorCode = DecodeErrorCode | 'UNSUPPORTED_VERSION' | 'UNDECLARED_FIELD' | 'CONTRACT_EMPTY' | 'CONTRACT_DUPLICATE_FIELD' | 'CONTRACT_UNKNOWN_NODE' | 'ANSWER_MISSING_FIELD' | 'ANSWER_UNDECLARED_FIELD' | 'ANSWER_TYPE_MISMATCH' | 'ANSWER_OUT_OF_SPACE' | 'DEFAULT_NONCONFORMANT';
437
+ interface ElicitationError {
438
+ readonly code: ElicitationErrorCode;
439
+ readonly path: string;
440
+ readonly message: string;
441
+ }
442
+ /**
443
+ * A value space — the platform's established `$type`-tagged space vocabulary
444
+ * (the same wire shape the capability codec uses). `min`/`max` inclusive;
445
+ * `anyString` is the only unbounded space.
446
+ */
447
+ type AnswerSpace = {
448
+ readonly kind: 'intRange';
449
+ readonly min: number;
450
+ readonly max: number;
451
+ } | {
452
+ readonly kind: 'floatRange';
453
+ readonly min: number;
454
+ readonly max: number;
455
+ } | {
456
+ readonly kind: 'stringLen';
457
+ readonly min: number;
458
+ readonly max: number;
459
+ } | {
460
+ readonly kind: 'enum';
461
+ readonly values: readonly string[];
462
+ } | {
463
+ readonly kind: 'anyString';
464
+ };
465
+ /** One declared answer field (§18.1). */
466
+ interface AnswerField {
467
+ /** The key this field's value takes in the answer object. */
468
+ readonly name: string;
469
+ /** The id of the NODE whose committed state carries the value. */
470
+ readonly nodeId: string;
471
+ /** The state key the node's binding writes the value under. */
472
+ readonly stateKey: string;
473
+ readonly space: AnswerSpace;
474
+ readonly required: boolean;
475
+ }
476
+ /** The declared answer contract — non-empty on the wire (`CONTRACT_EMPTY`). */
477
+ interface AnswerContract {
478
+ readonly fields: readonly AnswerField[];
479
+ }
480
+ /** A typed answer value — the JSON scalar shapes a space ranges over. */
481
+ type AnswerValue = string | number;
482
+ /** An answer object: declared field names → scalar values. */
483
+ type Answer = ReadonlyMap<string, AnswerValue>;
484
+ /** The elicitation envelope (§18.1). */
485
+ interface ElicitationEnvelope {
486
+ readonly id: string;
487
+ readonly tree: Node<unknown>;
488
+ readonly contract: AnswerContract;
489
+ /** Milliseconds, ≥ 1. DATA only — the presenting host's clock acts on it. */
490
+ readonly timeoutMs?: number;
491
+ /** A proposed answer; must itself conform to the contract. */
492
+ readonly defaultAnswer?: Answer;
493
+ }
494
+ /** The closed outcome set (§18.3). */
495
+ type ElicitationOutcome = {
496
+ readonly kind: 'Answered';
497
+ readonly answer: Answer;
498
+ } | {
499
+ readonly kind: 'Declined';
500
+ } | {
501
+ readonly kind: 'TimedOut';
502
+ } | {
503
+ readonly kind: 'Superseded';
504
+ readonly by?: string;
505
+ };
506
+ /** An outcome on the wire, correlated to its elicitation by id. */
507
+ interface ElicitationOutcomeEnvelope {
508
+ readonly elicitationId: string;
509
+ readonly outcome: ElicitationOutcome;
510
+ }
511
+ /**
512
+ * Validate a typed answer against `contract`, reporting at `basePath`.
513
+ * Deterministic fail-fast order: (1) undeclared keys, in Ordinal key order;
514
+ * (2) per contract field, in declaration order — missing-required, then
515
+ * JSON-type-vs-space, then in-space.
516
+ */
517
+ declare const validateAnswerAt: (basePath: string, contract: AnswerContract, answer: Answer) => Result<undefined, ElicitationError>;
518
+ /**
519
+ * Validate an answer at the standard `$.answer` root — the gate a resolution
520
+ * host runs before an `Answered` outcome reaches the asking agent.
521
+ */
522
+ declare const validateAnswer: (contract: AnswerContract, answer: Answer) => Result<undefined, ElicitationError>;
523
+ /**
524
+ * Encode an elicitation envelope to canonical wire bytes. Fails only when the
525
+ * tree is not wire-representable.
526
+ */
527
+ declare const encodeElicitation: (env: ElicitationEnvelope) => Result<string, ElicitationError>;
528
+ /**
529
+ * Decode + validate an elicitation envelope (fail-fast, §18.4 order): stray
530
+ * keys → version tag → id → tree (standard node decode, errors re-rooted
531
+ * under `$.tree`) → contract (structure, duplicates, node membership) →
532
+ * timeoutMs → default conformance.
533
+ */
534
+ declare const decodeElicitation: (json: string) => Result<ElicitationEnvelope, ElicitationError>;
535
+ /** Encode an outcome to canonical wire bytes. Total — no embedded tree. */
536
+ declare const encodeElicitationOutcome: (env: ElicitationOutcomeEnvelope) => string;
537
+ /**
538
+ * Decode an outcome envelope. Contract conformance of an `Answered` answer is
539
+ * NOT checked here (the outcome does not carry the contract) — a resolution
540
+ * host pairs the outcome with its pending elicitation and runs
541
+ * `validateAnswer` before the answer reaches the asking agent (§18.4).
542
+ */
543
+ declare const decodeElicitationOutcome: (json: string) => Result<ElicitationOutcomeEnvelope, ElicitationError>;
544
+ /**
545
+ * Validate a `{"answer": {…}, "contract": {…}}` conformance document — the
546
+ * operation the `elicitation-answer-accept` / `elicitation-answer-reject`
547
+ * corpus families drive. The document carries no tree, so the
548
+ * `CONTRACT_UNKNOWN_NODE` probe does not apply here.
549
+ */
550
+ declare const validateAnswerDocument: (json: string) => Result<undefined, ElicitationError>;
551
+
552
+ export { type Answer, type AnswerContract, type AnswerField, type AnswerSpace, type AnswerValue, type ApplyError, type ApplyErrorCode, type ApplyResult, type Compatibility, type DagOpRecord, type DagResultEnvelope, type DecodeError, type DecodeErrorCode, type Decoded, ELICITATION_KEY, ELICITATION_VERSION, type ElicitationEnvelope, type ElicitationError, type ElicitationErrorCode, type ElicitationOutcome, type ElicitationOutcomeEnvelope, type Envelope, type EnvelopeError, type EnvelopeErrorCode, type EvalEnv, type JsonAst, type MergeConflict, type MergeResult, type OpApplyTelemetryRecord, PAYLOAD_KEY, PROFILE_KEY, type ParseError, type Profile, REQUIRED_PROFILE_KEY, type SourceResolver, type TreeOp, type UnknownKind, apply, cellString, coerce, coreV1, decodeDagRecord, decodeElicitation, decodeElicitationOutcome, decodeEnvelope, decodeEnvelopeAst, decodeNode, decodeNodeTolerant, decodeOp, decodeTolerant, encodeCell, encodeColExpr, encodeDagRecord, encodeDataSource, encodeElicitation, encodeElicitationOutcome, encodeEnvelope, encodeNode, encodeOp, encodePipeline, evalErrorString, evalPipeline, evalPipelineInEnv, evalPipelineWith, evalPipelineWithInEnv, evalSource, field as jsonField, merge3Way, negotiate, negotiateEnvelope, noResolve, parse, pipelineParams, reencodeNode, renderAstCanonical, renderProfile, stepParams, tryParseProfile, validateAnswer, validateAnswerAt, validateAnswerDocument };