@orkestrel/interpret 0.0.2 → 0.0.3

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,2133 +0,0 @@
1
- import { Definition } from '@orkestrel/reason';
2
- import { EmitterErrorHandler } from '@orkestrel/emitter';
3
- import { EmitterHooks } from '@orkestrel/emitter';
4
- import { EmitterInterface } from '@orkestrel/emitter';
5
- import { FieldPath } from '@orkestrel/contract';
6
- import { ReasonResult } from '@orkestrel/reason';
7
- import { Subject } from '@orkestrel/reason';
8
- import { SymbolicExpression } from '@orkestrel/reason';
9
-
10
- /** An unresolved field surfaced as a human-readable question, never bare prose. */
11
- export declare interface Ambiguity {
12
- readonly field: FieldPath;
13
- readonly question: string;
14
- readonly candidates: readonly string[];
15
- readonly required: boolean;
16
- }
17
-
18
- /**
19
- * Replace every whole-word occurrence of a map's keys with their values.
20
- *
21
- * @remarks
22
- * Word-boundary safe (`in` never matches inside `information`) and
23
- * case-insensitive; each key is regex-escaped via the core-root
24
- * `escapeRegExp` before compiling, so a caller-supplied phrase containing
25
- * regex metacharacters is matched literally. Multiple keys apply in
26
- * `Object.entries` order — the `Normalizer` stage sequences its three maps
27
- * (contractions, abbreviations, corrections) with three separate calls.
28
- *
29
- * @param text - The text to substitute within
30
- * @param map - The `{ from: to }` substitution map
31
- * @returns `text` with every whole-word match replaced
32
- *
33
- * @example
34
- * ```ts
35
- * import { applyReplacements } from '@src/core'
36
- *
37
- * applyReplacements("can't stop", { "can't": 'cannot' }) // 'cannot stop'
38
- * applyReplacements('information', { in: 'IN' }) // 'information' — word-boundary safe
39
- * ```
40
- */
41
- export declare function applyReplacements(text: string, map: Readonly<Record<string, string>>): string;
42
-
43
- /**
44
- * Assign already-extracted numbers to a matched template's entity mappings.
45
- *
46
- * @remarks
47
- * Strategy, in order: (1) a SINGLE mapping collects every number (an array
48
- * when more than one, a scalar otherwise) at `CONFIDENCE_COLLECT`. Otherwise,
49
- * per mapping, the rightmost token in the text that equals the entity name
50
- * (`CONFIDENCE_EXACT`), an alias exactly (`CONFIDENCE_ALIAS`), or an alias
51
- * fuzzily (via `matchAlias`, confidence = its returned score) becomes a
52
- * keyword anchor; anchors sort left-to-right and each claims its nearest
53
- * unused number by text position. (3) Any mapping still unfilled claims the
54
- * next unused number positionally, at `CONFIDENCE_POSITIONAL`. Every entity
55
- * carries provenance `category: 'extracted'` with `detail` naming the
56
- * strategy that filled it (`'collect' | 'keyword' | 'alias' | 'positional'`).
57
- * Runs ONLY after a template has matched (an orchestrator-owned step, never
58
- * inside `Extractor`, which stays template-agnostic).
59
- *
60
- * @param numbers - The numbers already extracted from `text` via `extractNumbers`
61
- * @param mappings - The matched template's entity mappings
62
- * @param text - The same text `numbers` was extracted from (for keyword proximity)
63
- * @param similarity - The fuzzy alias-match score threshold (0..1)
64
- * @returns The assigned entities, one per filled mapping
65
- *
66
- * @example
67
- * ```ts
68
- * import { assignEntities } from '@src/core'
69
- *
70
- * const mappings = [
71
- * { entity: 'age', aliases: ['years old'], field: 'age' },
72
- * { entity: 'score', aliases: ['credit score'], field: 'score' },
73
- * ]
74
- * assignEntities([25, 720], mappings, '25 year old with score 720', 0.8)
75
- * // [{ name: 'age', value: 25, ... }, { name: 'score', value: 720, ... }]
76
- * ```
77
- */
78
- export declare function assignEntities(numbers: readonly number[], mappings: readonly EntityMapping[], text: string, similarity: number): readonly Entity[];
79
-
80
- /**
81
- * Render a value into a canonical, key-order-stable string — the pre-image
82
- * of `digestValue`.
83
- *
84
- * @remarks
85
- * Ported from the app's `raters` digest machinery (`app/core/raters/helpers.ts`)
86
- * — record keys sort before serialization so a re-ordered object canonicalizes
87
- * identically; arrays keep position order (position is meaningful).
88
- * Cycle-safe and total (AGENTS §14): `visited` tracks the object ancestors
89
- * along the CURRENT recursion path (not a global "seen" set, so the same
90
- * object reachable twice via non-cyclic sibling branches still canonicalizes
91
- * normally); revisiting an ancestor renders that node as the literal string
92
- * `'[cycle]'` instead of recursing — deterministic, never throws, never
93
- * overflows the call stack.
94
- *
95
- * @param value - The value to canonicalize
96
- * @param visited - The object ancestors along the current recursion path (internal; omit at the call site)
97
- * @returns The canonical string form
98
- *
99
- * @example
100
- * ```ts
101
- * import { canonicalize } from '@src/core'
102
- *
103
- * canonicalize({ b: 1, a: 2 }) === canonicalize({ a: 2, b: 1 }) // true
104
- * ```
105
- */
106
- export declare function canonicalize(value: unknown, visited?: ReadonlySet<object>): string;
107
-
108
- /**
109
- * The `Clarifier` stage: resolves same-domain carry-over, template defaults,
110
- * and declaratively computed fields against an already-assigned entity set,
111
- * surfacing an {@link Ambiguity} for every required mapping that stays
112
- * unresolved.
113
- *
114
- * @remarks
115
- * Resolution order: fresh (already-assigned) entities always win; carry-over
116
- * fills a mapping only from the SAME domain's most recent prior turn (a
117
- * domain change drops carry-over entirely) and never overwrites a fresh
118
- * value; template defaults fill any field still unresolved after carry-over;
119
- * computed fields resolve in dependency (topological) order via
120
- * `resolveExpression` — an unresolved input, a non-finite result, or a
121
- * dependency cycle leaves the field a gap, never landing an entity. `floor`
122
- * (from {@link ClarifierOptions}, never hardcoded — AGENTS-flagged scsr
123
- * defect 4) gates whether a resolved entity's confidence counts as
124
- * "resolved enough" when raising ambiguities — a field with a value below
125
- * `floor` still raises its ambiguity.
126
- *
127
- * @example
128
- * ```ts
129
- * import { Clarifier } from '@src/core'
130
- *
131
- * const clarifier = new Clarifier({ floor: 0.3 })
132
- * clarifier.clarify(
133
- * [],
134
- * {
135
- * id: 't1',
136
- * name: 'Arithmetic',
137
- * domain: 'arithmetic',
138
- * intents: ['calculate'],
139
- * mappings: [{ entity: 'value', aliases: [], field: 'value', required: true }],
140
- * defaults: [],
141
- * computations: [],
142
- * definition: { reasoning: 'symbolic', id: 't1', name: 'Arithmetic', equations: [], variables: {} },
143
- * },
144
- * undefined,
145
- * { action: 'calculate', domain: 'arithmetic', confidence: 1 },
146
- * ) // { entities: [], ambiguities: [{ field: 'value', ... }], complete: false }
147
- * ```
148
- */
149
- export declare class Clarifier implements ClarifierInterface {
150
- #private;
151
- constructor(options?: ClarifierOptions);
152
- clarify(entities: readonly Entity[], template: Template, context: InterpretContextInterface | undefined, intent: Intent): ClarifyResult;
153
- }
154
-
155
- /**
156
- * The `Clarifier` stage contract: resolve carry-over, defaults, and computed
157
- * fields against a set of already-assigned entities, surfacing ambiguities
158
- * for anything required that stays unresolved.
159
- */
160
- export declare interface ClarifierInterface {
161
- clarify(entities: readonly Entity[], template: Template, context: InterpretContextInterface | undefined, intent: Intent): ClarifyResult;
162
- }
163
-
164
- /**
165
- * Options for `createClarifier` / the `Clarifier` constructor.
166
- *
167
- * @remarks
168
- * `floor` is the confidence axis honored when raising ambiguities — never
169
- * hardcoded (scsr hardcoded its confidence constant instead of honoring the
170
- * configured value).
171
- */
172
- export declare interface ClarifierOptions {
173
- readonly floor?: number;
174
- }
175
-
176
- /** The `Clarifier` stage's output: resolved entities plus any remaining ambiguities. */
177
- export declare interface ClarifyResult {
178
- readonly entities: readonly Entity[];
179
- readonly ambiguities: readonly Ambiguity[];
180
- readonly complete: boolean;
181
- }
182
-
183
- /**
184
- * Classify the action + domain intent of a text against caller-supplied
185
- * vocabularies.
186
- *
187
- * @remarks
188
- * `actions` maps a token to an action name — the first matching token in
189
- * `text` (left to right) wins, at `CONFIDENCE_EXACT`. `domains` maps a domain
190
- * name to its keyword list — the domain with the most matching tokens wins
191
- * (ties keep the earliest-declared domain), also at `CONFIDENCE_EXACT`.
192
- * Combined confidence (PINNED): both fire → their average; exactly one fires
193
- * → its value times `0.5`; neither → `0`. There is no built-in worldview and
194
- * no auto-classification from a registered template's own `domain` name — a
195
- * caller MUST list a template's domain among `domains` for it to classify.
196
- * No `floor` parameter: the confidence floor gate lives at the orchestrator's
197
- * `matchTemplate` step, never inside classification itself.
198
- *
199
- * @param text - The (normalized) text to classify
200
- * @param actions - The caller's token → action-name vocabulary
201
- * @param domains - The caller's domain-name → keyword-list vocabulary
202
- * @returns The classified intent
203
- *
204
- * @example
205
- * ```ts
206
- * import { classifyIntent } from '@src/core'
207
- *
208
- * classifyIntent('calculate my rate', { calculate: 'compute' }, { rating: ['rate'] })
209
- * // { action: 'compute', domain: 'rating', confidence: 1 }
210
- * classifyIntent('hello', {}, {}) // { action: '', domain: '', confidence: 0 }
211
- * ```
212
- */
213
- export declare function classifyIntent(text: string, actions: Readonly<Record<string, string>>, domains: Readonly<Record<string, readonly string[]>>): Intent;
214
-
215
- /**
216
- * Collapse every run of whitespace to a single space and trim the ends.
217
- *
218
- * @param text - The text to collapse
219
- * @returns The collapsed text
220
- *
221
- * @example
222
- * ```ts
223
- * import { collapseWhitespace } from '@src/core'
224
- *
225
- * collapseWhitespace(' a b\t c ') // 'a b c'
226
- * ```
227
- */
228
- export declare function collapseWhitespace(text: string): string;
229
-
230
- /**
231
- * A declaratively computed field: evaluate `expression` against the entities
232
- * already resolved for this interpretation, and land the result on `field`.
233
- *
234
- * @remarks
235
- * Renamed from scsr's `InferenceRule` — the reasons engine already owns
236
- * `Inference` for fact derivation, a different concept. `expression` is a
237
- * reasons {@link SymbolicExpression} tree (pure JSON `Variable` / `Constant`
238
- * / `Operation`), evaluated by the pure `resolveExpression` helper rather
239
- * than a closure, so a `Template` stays JSON-serializable end to end.
240
- * Dependencies are derived from the tree (`variablesOf`) — scsr's explicit
241
- * `from: string[]` list is gone.
242
- */
243
- export declare interface ComputedField {
244
- readonly field: FieldPath;
245
- readonly expression: SymbolicExpression;
246
- }
247
-
248
- /** Confidence assigned to an exact alias-phrase entity match. */
249
- export declare const CONFIDENCE_ALIAS = 0.9;
250
-
251
- /** Confidence assigned to a same-domain carried-over field. */
252
- export declare const CONFIDENCE_CARRIED = 0.7;
253
-
254
- /** Confidence assigned when a single entity mapping collects every extracted number. */
255
- export declare const CONFIDENCE_COLLECT = 0.9;
256
-
257
- /** Confidence assigned to a successfully resolved computed field. */
258
- export declare const CONFIDENCE_COMPUTED = 0.9;
259
-
260
- /** Confidence assigned to a template default fill. */
261
- export declare const CONFIDENCE_DEFAULT = 1;
262
-
263
- /** Confidence assigned to an exact keyword-proximity entity match. */
264
- export declare const CONFIDENCE_EXACT = 1;
265
-
266
- /** Confidence assigned to a positional (order-based) entity match fallback. */
267
- export declare const CONFIDENCE_POSITIONAL = 0.7;
268
-
269
- /**
270
- * Create a clarifier — carry-over, defaults, and computed-field resolution
271
- * against an assigned entity set.
272
- *
273
- * @param options - Optional confidence `floor` for raised ambiguities
274
- * @returns A stateless {@link ClarifierInterface}
275
- *
276
- * @example
277
- * ```ts
278
- * import { createClarifier } from '@src/core'
279
- *
280
- * const clarifier = createClarifier({ floor: 0.5 })
281
- * ```
282
- */
283
- export declare function createClarifier(options?: ClarifierOptions): ClarifierInterface;
284
-
285
- /**
286
- * Create a definition registry.
287
- *
288
- * @param options - Optional initial seed collection
289
- * @returns A working {@link DefinitionManagerInterface}
290
- *
291
- * @example
292
- * ```ts
293
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
294
- * import { createDefinitionManager } from '@src/core'
295
- *
296
- * const definitions = createDefinitionManager({
297
- * definitions: [quantitativeDefinition('d1', 'D1', [factorGroup('total', 'sum', [fieldFactor('value', 'value')])])],
298
- * })
299
- * definitions.size // 1
300
- * ```
301
- */
302
- export declare function createDefinitionManager(options?: DefinitionManagerOptions): DefinitionManagerInterface;
303
-
304
- /**
305
- * Create a template-agnostic intent classifier and number extractor.
306
- *
307
- * @param options - Optional caller `actions` / `domains` vocabularies
308
- * @returns A stateless {@link ExtractorInterface}
309
- *
310
- * @example
311
- * ```ts
312
- * import { createExtractor } from '@src/core'
313
- *
314
- * const extractor = createExtractor({
315
- * actions: { calculate: 'calculate' },
316
- * domains: { arithmetic: ['arithmetic'] },
317
- * })
318
- * extractor.extract('calculate arithmetic 42').numbers // [42]
319
- * ```
320
- */
321
- export declare function createExtractor(options?: ExtractorOptions): ExtractorInterface;
322
-
323
- /**
324
- * Create a prompt formatter.
325
- *
326
- * @param options - Optional caller intent-verb phrasing map
327
- * @returns A stateless {@link FormatterInterface}
328
- *
329
- * @example
330
- * ```ts
331
- * import { createFormatter } from '@src/core'
332
- *
333
- * const formatter = createFormatter({ verbs: { calculate: 'Calculate' } })
334
- * ```
335
- */
336
- export declare function createFormatter(options?: FormatterOptions): FormatterInterface;
337
-
338
- /**
339
- * Create a subject/definition generator.
340
- *
341
- * @param options - Currently an empty extension seam
342
- * @returns A stateless {@link GeneratorInterface}
343
- *
344
- * @example
345
- * ```ts
346
- * import { createGenerator } from '@src/core'
347
- *
348
- * const generator = createGenerator()
349
- * ```
350
- */
351
- export declare function createGenerator(_options?: GeneratorOptions): GeneratorInterface;
352
-
353
- /**
354
- * Create an interpretation orchestrator.
355
- *
356
- * @remarks
357
- * `interpret()` is genuinely synchronous and runs the fixed five-stage
358
- * pipeline `[normalize, extract, clarify, format, generate]`. Every stage
359
- * slot (`normalizer` / `extractor` / `clarifier` / `formatter` / `generator`)
360
- * is bring-your-own — a supplied implementation is used as-is, else the
361
- * built-in stage is constructed from the matching per-stage options.
362
- *
363
- * @param options - Optional templates, context, stage implementations, the
364
- * `similarity` / `floor` axes, and emitter hooks
365
- * @returns A working {@link InterpretInterface}
366
- *
367
- * @example
368
- * ```ts
369
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
370
- * import { createInterpret } from '@src/core'
371
- *
372
- * const interpret = createInterpret({
373
- * extractor: { extract: () => ({ intent: { action: 'calculate', domain: 'arithmetic', confidence: 1 }, numbers: [42], complete: true }) },
374
- * templates: [
375
- * {
376
- * id: 't1',
377
- * name: 'Arithmetic',
378
- * domain: 'arithmetic',
379
- * intents: ['calculate'],
380
- * mappings: [{ entity: 'value', aliases: [], field: 'value' }],
381
- * defaults: [],
382
- * computations: [],
383
- * definition: quantitativeDefinition('t1', 'Arithmetic', [
384
- * factorGroup('total', 'sum', [fieldFactor('value', 'value')]),
385
- * ]),
386
- * },
387
- * ],
388
- * })
389
- * interpret.interpret('calculate arithmetic 42').subject // { value: 42 }
390
- * ```
391
- */
392
- export declare function createInterpret(options?: InterpretOptions): InterpretInterface;
393
-
394
- /**
395
- * Create a cross-turn interpretation context.
396
- *
397
- * @param options - Optional `session` label and `history` ring-buffer cap
398
- * @returns A working {@link InterpretContextInterface}
399
- *
400
- * @example
401
- * ```ts
402
- * import { createInterpretContext } from '@src/core'
403
- *
404
- * const context = createInterpretContext({ session: 'turn-1', history: 4 })
405
- * context.previous() // []
406
- * ```
407
- */
408
- export declare function createInterpretContext(options?: InterpretContextOptions): InterpretContextInterface;
409
-
410
- /**
411
- * Create a lexicon-driven reverse-direction rendering engine.
412
- *
413
- * @remarks
414
- * Stateless — `phrase` / `label` / `line` / `value` are total lookups into a
415
- * caller `Lexicon` merged over `DEFAULT_LEXICON`, and `describe` / `narrate`
416
- * compose them over a reasons `Definition` / `ReasonResult`.
417
- *
418
- * @param options - Optional `lexicon` and `formatters` map
419
- * @returns A stateless {@link NarratorInterface}
420
- *
421
- * @example
422
- * ```ts
423
- * import { createNarrator } from '@src/core'
424
- *
425
- * const narrator = createNarrator({ lexicon: { templates: { 'subject.empty': 'nothing here' } } })
426
- * narrator.line('subject.empty', {}) // 'nothing here'
427
- * ```
428
- */
429
- export declare function createNarrator(options?: NarratorOptions): NarratorInterface;
430
-
431
- /**
432
- * Create a text normalizer.
433
- *
434
- * @param options - Optional contraction / abbreviation / correction maps,
435
- * merged over the neutral built-in defaults
436
- * @returns A stateless {@link NormalizerInterface}
437
- *
438
- * @example
439
- * ```ts
440
- * import { createNormalizer } from '@src/core'
441
- *
442
- * createNormalizer().normalize("it's cold") // { text: 'it is cold', changes: [{ from: "it's", to: 'it is' }] }
443
- * ```
444
- */
445
- export declare function createNormalizer(options?: NormalizerOptions): NormalizerInterface;
446
-
447
- /**
448
- * Create a subject registry.
449
- *
450
- * @remarks
451
- * Mints its own record ids on `add` when none is supplied — a `Subject`
452
- * carries no `id` field of its own.
453
- *
454
- * @param options - Optional initial seed collection
455
- * @returns A working {@link SubjectManagerInterface}
456
- *
457
- * @example
458
- * ```ts
459
- * import { createSubjectManager } from '@src/core'
460
- *
461
- * const subjects = createSubjectManager({ subjects: [{ value: 1 }] })
462
- * subjects.size // 1
463
- * ```
464
- */
465
- export declare function createSubjectManager(options?: SubjectManagerOptions): SubjectManagerInterface;
466
-
467
- /**
468
- * Build and validate one interpretation template from plain data.
469
- *
470
- * @remarks
471
- * The factory/coercer pair for template intake (AGENTS §4.6.1): this throws
472
- * on malformed data, while `parseTemplate` returns `undefined`. Data failing
473
- * {@link isTemplate} throws `InterpretError('INVALID_TEMPLATE', …)`.
474
- *
475
- * @param data - The candidate template data
476
- * @returns The same data, now known to satisfy {@link Template}
477
- * @throws {@link InterpretError} `INVALID_TEMPLATE` when `data` fails validation
478
- *
479
- * @example
480
- * ```ts
481
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
482
- * import { createTemplate } from '@src/core'
483
- *
484
- * const template = createTemplate({
485
- * id: 't1', name: 'Arithmetic', domain: 'arithmetic', intents: ['calculate'],
486
- * mappings: [{ entity: 'value', aliases: [], field: 'value' }], defaults: [], computations: [],
487
- * definition: quantitativeDefinition('t1', 'Arithmetic', [factorGroup('total', 'sum', [fieldFactor('value', 'value')])]),
488
- * })
489
- * template.id // 't1'
490
- * ```
491
- */
492
- export declare function createTemplate(data: Template): Template;
493
-
494
- /**
495
- * Create a template registry.
496
- *
497
- * @param options - Optional initial seed collection
498
- * @returns A working {@link TemplateManagerInterface}
499
- *
500
- * @example
501
- * ```ts
502
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
503
- * import { createTemplate, createTemplateManager } from '@src/core'
504
- *
505
- * const template = createTemplate({
506
- * id: 't1', name: 'Arithmetic', domain: 'arithmetic', intents: ['calculate'],
507
- * mappings: [{ entity: 'value', aliases: [], field: 'value' }], defaults: [], computations: [],
508
- * definition: quantitativeDefinition('t1', 'Arithmetic', [factorGroup('total', 'sum', [fieldFactor('value', 'value')])]),
509
- * })
510
- * const templates = createTemplateManager({ templates: [template] })
511
- * templates.size // 1
512
- * ```
513
- */
514
- export declare function createTemplateManager(options?: TemplateManagerOptions): TemplateManagerInterface;
515
-
516
- /** Neutral built-in abbreviation expansions for `Normalizer` — empty by default. */
517
- export declare const DEFAULT_ABBREVIATIONS: Readonly<Record<string, string>>;
518
-
519
- /** Neutral built-in action-verb vocabulary for `Extractor#extract`'s intent classification — empty by default. */
520
- export declare const DEFAULT_ACTIONS: Readonly<Record<string, string>>;
521
-
522
- /**
523
- * Neutral built-in contraction expansions for `Normalizer` — small on
524
- * purpose; callers merge their own map over this one.
525
- */
526
- export declare const DEFAULT_CONTRACTIONS: Readonly<Record<string, string>>;
527
-
528
- /** Neutral built-in misspelling corrections for `Normalizer` — empty by default. */
529
- export declare const DEFAULT_CORRECTIONS: Readonly<Record<string, string>>;
530
-
531
- /** Neutral built-in domain-keyword vocabulary for `Extractor#extract`'s intent classification — empty by default. */
532
- export declare const DEFAULT_DOMAINS: Readonly<Record<string, readonly string[]>>;
533
-
534
- /**
535
- * Default `floor` for `createInterpret` / `matchTemplate` — the minimum
536
- * intent confidence a template match (or the classified intent itself) must
537
- * clear.
538
- */
539
- export declare const DEFAULT_INTERPRET_FLOOR = 0.3;
540
-
541
- /** Default `history` cap for an `InterpretContext`'s `previous()` ring buffer. */
542
- export declare const DEFAULT_INTERPRET_HISTORY = 16;
543
-
544
- /**
545
- * Default `similarity` for `createInterpret` / `matchAlias` — the fuzzy
546
- * alias-match score threshold (0..1).
547
- *
548
- * @remarks
549
- * Domain-qualified (not a bare `DEFAULT_SIMILARITY`) so the name stays free
550
- * of collision on the shared `@src/core` barrel, mirroring the
551
- * `DEFAULT_REASON_BAIL` precedent.
552
- */
553
- export declare const DEFAULT_INTERPRET_SIMILARITY = 0.8;
554
-
555
- /**
556
- * The neutral default `Lexicon` a `Narrator` merges caller data over.
557
- *
558
- * @remarks
559
- * `phrases` and `labels` are empty — there is no built-in vocabulary or
560
- * label overrides (AGENTS §21 mechanism-never-policy). `templates` carries
561
- * the structural, display-neutral strings the reverse helpers formerly
562
- * hardcoded, keyed by
563
- * `{table}.{reasoning}` for the four reasons kinds, `result.quantitative.failed`
564
- * for the quantitative-result failure suffix, and `subject.fields` /
565
- * `subject.empty` for `describeSubject`. Every string is a plain
566
- * `interpolateMessage` template — `{{name}}`-style placeholders resolved
567
- * against the caller-supplied `values` record.
568
- */
569
- export declare const DEFAULT_LEXICON: Lexicon;
570
-
571
- /** Neutral built-in intent-verb phrasing for `Formatter#format` — empty by default. */
572
- export declare const DEFAULT_VERBS: Readonly<Record<string, string>>;
573
-
574
- /**
575
- * The definition registry — a self-owning, versioned and content-hashed
576
- * record-holder for the reasons {@link Definition}s an interpretation produces.
577
- *
578
- * @remarks
579
- * Mirrors {@link TemplateManager}: `add` defaults each record id to the
580
- * definition's own `id`, derives `hash` from the definition CONTENT
581
- * (id-independent), and bumps `version` ONLY when that hash changes at a reused
582
- * id — an identical re-add keeps its version. The batch `remove(ids)` form is
583
- * all-or-nothing; `destroy()` is idempotent and every method afterwards throws
584
- * `InterpretError('DESTROYED', …)`.
585
- *
586
- * @example
587
- * ```ts
588
- * import { symbolicDefinition } from '@orkestrel/reason'
589
- * import { DefinitionManager } from '@src/core'
590
- *
591
- * const manager = new DefinitionManager()
592
- * const record = manager.add(symbolicDefinition('rate', 'Rate', []))
593
- * record.id // 'rate'
594
- * manager.add(symbolicDefinition('rate', 'Rate', [])).version // 1 — identical re-add, no bump
595
- * ```
596
- */
597
- export declare class DefinitionManager implements DefinitionManagerInterface {
598
- #private;
599
- constructor(options?: DefinitionManagerOptions);
600
- get emitter(): EmitterInterface<DefinitionManagerEventMap>;
601
- get size(): number;
602
- has(id: string): boolean;
603
- definition(id: string): DefinitionRecord | undefined;
604
- definitions(): readonly DefinitionRecord[];
605
- add(definition: Definition, options?: ManagerAddOptions): DefinitionRecord;
606
- remove(ids: readonly string[]): boolean;
607
- remove(id: string): boolean;
608
- remove(): void;
609
- destroy(): void;
610
- }
611
-
612
- /** The push observation surface of a {@link DefinitionManagerInterface} (AGENTS §13). */
613
- export declare type DefinitionManagerEventMap = {
614
- /** A definition record was added — carries its record id. */
615
- readonly add: readonly [id: string];
616
- /** A definition record was removed — carries its record id. */
617
- readonly remove: readonly [id: string];
618
- /** The manager was destroyed. */
619
- readonly destroy: readonly [];
620
- };
621
-
622
- /** The definition registry — a self-owning, versioned/hashed record-holder. */
623
- export declare interface DefinitionManagerInterface {
624
- readonly emitter: EmitterInterface<DefinitionManagerEventMap>;
625
- readonly size: number;
626
- has(id: string): boolean;
627
- definition(id: string): DefinitionRecord | undefined;
628
- definitions(): readonly DefinitionRecord[];
629
- add(definition: Definition, options?: ManagerAddOptions): DefinitionRecord;
630
- remove(ids: readonly string[]): boolean;
631
- remove(id: string): boolean;
632
- remove(): void;
633
- destroy(): void;
634
- }
635
-
636
- /** Options for `createDefinitionManager` / the `DefinitionManager` constructor — the initial seed collection. */
637
- export declare interface DefinitionManagerOptions {
638
- readonly definitions?: readonly Definition[];
639
- readonly on?: EmitterHooks<DefinitionManagerEventMap>;
640
- readonly error?: EmitterErrorHandler;
641
- }
642
-
643
- /** A versioned, content-hashed {@link Definition} as held by a {@link DefinitionManagerInterface}. */
644
- export declare interface DefinitionRecord {
645
- readonly id: string;
646
- readonly definition: Definition;
647
- readonly version: number;
648
- readonly hash: string;
649
- }
650
-
651
- /**
652
- * Copy-on-write write a value at a (possibly nested) field path on a subject.
653
- *
654
- * @remarks
655
- * Never mutates `subject` — every level a `field` array descends through is
656
- * freshly copied, so the input and every intermediate record stay untouched
657
- * (AGENTS §11). Prototype-pollution-safe: a `field` containing `__proto__`,
658
- * `prototype`, or `constructor` at ANY segment (checked against
659
- * `UNSAFE_FIELD_SEGMENTS`) is refused as a no-op, returning `subject`
660
- * unchanged. A non-record value already sitting at an intermediate segment is
661
- * replaced by a fresh record rather than descended into.
662
- *
663
- * @param subject - The subject to derive from
664
- * @param field - The (possibly nested) field path to write
665
- * @param value - The value to write
666
- * @returns A fresh subject with `value` written at `field`, or `subject`
667
- * unchanged when `field` carries an unsafe segment
668
- *
669
- * @example
670
- * ```ts
671
- * import { setField } from '@src/core'
672
- *
673
- * setField({ age: 25 }, 'age', 30) // { age: 30 }
674
- * setField({}, ['address', 'city'], 'Reno') // { address: { city: 'Reno' } }
675
- * setField({}, ['__proto__', 'polluted'], true) // {} — refused, unchanged
676
- * ```
677
- */
678
- /**
679
- * Derive the sibling field path for a computed aggregate of `field` — the
680
- * suffix is appended to `field`'s OWN last segment, so the aggregate nests
681
- * beside the source field rather than flattening past it.
682
- *
683
- * @remarks
684
- * For an array {@link FieldPath} (e.g. `['address', 'amounts']`) with suffix
685
- * `'Sum'` the result is `['address', 'amountsSum']` — nested beside
686
- * `address.amounts`. For a plain string field the result stays a flat string
687
- * (`'amounts'` → `'amountsSum'`), matching the existing single-key behavior.
688
- *
689
- * @param field - The source field path
690
- * @param suffix - The aggregate suffix (`'Sum'`, `'Count'`, `'Average'`, `'Minimum'`, `'Maximum'`)
691
- * @returns The sibling field path for the aggregate
692
- *
693
- * @example
694
- * ```ts
695
- * import { deriveAggregateField } from '@src/core'
696
- *
697
- * deriveAggregateField(['address', 'amounts'], 'Sum') // ['address', 'amountsSum']
698
- * deriveAggregateField('amounts', 'Sum') // 'amountsSum'
699
- * ```
700
- */
701
- export declare function deriveAggregateField(field: FieldPath, suffix: string): FieldPath;
702
-
703
- /**
704
- * Render a one-line, display-neutral description of a reasons `Subject`,
705
- * through an injected `Narrator`.
706
- *
707
- * @remarks
708
- * Complements — never duplicates — the raters `describe*` family (which
709
- * describes RATERS artifacts); this describes REASONS artifacts. Every field
710
- * renders via `narrator.label` + `narrator.value` (looked up under the
711
- * `'units'` phrase table, falling back to `'plain'`) — the wording is fully
712
- * lexicon-driven (AGENTS §21 mechanism-never-policy); `Definition` /
713
- * `ReasonResult` narration lives on `Narrator#describe` / `Narrator#narrate`
714
- * directly.
715
- *
716
- * @param subject - The subject to describe
717
- * @param narrator - The lexicon-driven renderer to render field labels/values/lines through
718
- * @returns A one-line description, the lexicon's `'subject.empty'` line when empty
719
- *
720
- * @example
721
- * ```ts
722
- * import { createNarrator, describeSubject } from '@src/core'
723
- *
724
- * describeSubject({ age: 25, income: 50000 }, createNarrator()) // 'with age: 25, income: 50000'
725
- * ```
726
- */
727
- export declare function describeSubject(subject: Subject, narrator: NarratorInterface): string;
728
-
729
- /**
730
- * Compute a canonical structural digest of a pure-JSON value — a key-order-
731
- * stable FNV-1a hash rendered as an 8-hex-digit string.
732
- *
733
- * @remarks
734
- * Pure ECMAScript, no host crypto (AGENTS §17.7) — the same algorithm as the
735
- * app's `digest`, ported into core so `Interpretation.digest` and the
736
- * versioned managers can hash without leaving strict core.
737
- *
738
- * @param value - The value to digest
739
- * @returns The 8-character hex digest
740
- *
741
- * @example
742
- * ```ts
743
- * import { digestValue } from '@src/core'
744
- *
745
- * digestValue({ a: 1 }) === digestValue({ a: 1 }) // true — deterministic
746
- * ```
747
- */
748
- export declare function digestValue(value: unknown): string;
749
-
750
- /** One value assigned to a template's entity mapping, with its provenance and confidence. */
751
- export declare interface Entity {
752
- readonly name: string;
753
- readonly value: unknown;
754
- readonly provenance: Provenance;
755
- readonly confidence: number;
756
- }
757
-
758
- /**
759
- * One entity-extraction rule inside a {@link Template}: which literal alias
760
- * phrases identify a value, and which subject field it lands on.
761
- *
762
- * @remarks
763
- * `aliases` are literal phrases (no `RegExp` — templates stay JSON), matched
764
- * exact-then-fuzzy against tokens surrounding an extracted number. `required`
765
- * marks the field as needing an {@link Ambiguity} when it stays unresolved.
766
- */
767
- export declare interface EntityMapping {
768
- readonly entity: string;
769
- readonly aliases: readonly string[];
770
- readonly field: FieldPath;
771
- readonly required?: boolean;
772
- }
773
-
774
- /**
775
- * Escape every regex metacharacter in `text` so it matches literally when
776
- * compiled into a `RegExp`.
777
- *
778
- * @param text - The literal text to escape
779
- * @returns `text` with every regex metacharacter backslash-escaped
780
- *
781
- * @example
782
- * ```ts
783
- * import { escapeRegExp } from '@src/core'
784
- *
785
- * escapeRegExp('a.b*c') // 'a\\.b\\*c'
786
- * new RegExp(escapeRegExp('a.b*c')).test('a.b*c') // true
787
- * ```
788
- */
789
- export declare function escapeRegExp(text: string): string;
790
-
791
- /**
792
- * Mine every numeric literal from text — optional leading `$`, thousands
793
- * commas, an optional decimal fraction, an optional trailing `%`.
794
- *
795
- * @remarks
796
- * Numbers-only: this is the module's entire extraction contract (AGENTS
797
- * §21 mechanism-never-policy) — no date, text-entity, or negation parsing.
798
- *
799
- * @param text - The text to scan
800
- * @returns Every extracted number, in left-to-right order
801
- *
802
- * @example
803
- * ```ts
804
- * import { extractNumbers } from '@src/core'
805
- *
806
- * extractNumbers('income was $50,000, age 25') // [50000, 25]
807
- * ```
808
- */
809
- export declare function extractNumbers(text: string): readonly number[];
810
-
811
- /**
812
- * The `Extractor` stage: template-agnostic intent classification plus raw
813
- * numeric-entity mining.
814
- *
815
- * @remarks
816
- * Deliberately never named `Parser` — the `contracts` module already owns
817
- * `Parser<T>`, so a class of that name would collide in type space (AGENTS
818
- * §21, design ledger 3). `extract` never sees a `Template`: numbers →
819
- * entity ASSIGNMENT is a separate orchestrator-owned step that runs only
820
- * after a template has matched (`assignEntities` in `helpers.ts`), not
821
- * inside this stage (the defect-3 fix — scsr's parser mined template-shaped
822
- * entities directly and only worked via an `instanceof` hack).
823
- *
824
- * @example
825
- * ```ts
826
- * import { Extractor } from '@src/core'
827
- *
828
- * const extractor = new Extractor({
829
- * actions: { calculate: 'compute' },
830
- * domains: { rating: ['rate'] },
831
- * })
832
- * extractor.extract('calculate my rate at 85')
833
- * // { intent: { action: 'compute', domain: 'rating', confidence: 1 }, numbers: [85], complete: true }
834
- * ```
835
- */
836
- export declare class Extractor implements ExtractorInterface {
837
- #private;
838
- constructor(options?: ExtractorOptions);
839
- extract(text: string): ExtractResult;
840
- }
841
-
842
- /** The `Extractor` stage contract: template-agnostic intent classification + raw number mining. */
843
- export declare interface ExtractorInterface {
844
- extract(text: string): ExtractResult;
845
- }
846
-
847
- /**
848
- * Options for `createExtractor` / the `Extractor` constructor.
849
- *
850
- * @remarks
851
- * `actions` / `domains` are the caller's intent vocabulary — there is no
852
- * built-in worldview (AGENTS-flagged scsr defect). Neither `floor` nor
853
- * `similarity` lives here: the confidence floor gate is the orchestrator's
854
- * `matchTemplate` step, never the classifier itself.
855
- */
856
- export declare interface ExtractorOptions {
857
- readonly actions?: Readonly<Record<string, string>>;
858
- readonly domains?: Readonly<Record<string, readonly string[]>>;
859
- }
860
-
861
- /**
862
- * The `Extractor` stage's output: intent classification plus raw numbers.
863
- *
864
- * @remarks
865
- * Template-agnostic by design — extraction never sees a `Template`, only the
866
- * text. `numbers`, not template-named entities; entity ASSIGNMENT is a
867
- * separate orchestrator step run only after a template has matched (see
868
- * `assignEntities` in `helpers.ts`).
869
- */
870
- export declare interface ExtractResult {
871
- readonly intent: Intent;
872
- readonly numbers: readonly number[];
873
- readonly complete: boolean;
874
- }
875
-
876
- /** A fallback value a {@link Template} fills onto a field left unresolved by extraction. */
877
- export declare interface FieldDefault {
878
- readonly field: FieldPath;
879
- readonly value: unknown;
880
- }
881
-
882
- /**
883
- * One audited field of the built subject — its resolved value, provenance,
884
- * and confidence.
885
- *
886
- * @remarks
887
- * Emitted for EVERY field that lands in the generated subject, including
888
- * defaults and computed fields (scsr silently omitted those from its audit
889
- * trail; this closes that gap).
890
- */
891
- export declare interface FieldMapping {
892
- readonly field: FieldPath;
893
- readonly entity?: string;
894
- readonly value: unknown;
895
- readonly provenance: Provenance;
896
- readonly confidence: number;
897
- }
898
-
899
- /** The `Formatter` stage's output: the refined natural-language prompt. */
900
- export declare interface FormatResult {
901
- readonly prompt: string;
902
- }
903
-
904
- /**
905
- * The `Formatter` stage: renders the refined natural-language prompt for a
906
- * matched template.
907
- *
908
- * @remarks
909
- * Shape: `{verb} {template.name}` + ` with {label}: {value}, …` for every
910
- * non-default entity (via the core-root `formatField`) + `
911
- * (defaults: {label}: {value}, …)` for default-provenance entities + `
912
- * needed: {question} …` for every ambiguity. `verbs` maps `intent.action` to
913
- * its display verb; an action absent from the map falls back to the action
914
- * string itself. Every parameter is read — no ignored `context` argument
915
- * (AGENTS-flagged scsr defect 5).
916
- *
917
- * @example
918
- * ```ts
919
- * import { Formatter } from '@src/core'
920
- *
921
- * const formatter = new Formatter({ verbs: { calculate: 'Calculate' } })
922
- * formatter.format(
923
- * { action: 'calculate', domain: 'arithmetic', confidence: 1 },
924
- * {
925
- * id: 't1',
926
- * name: 'Arithmetic',
927
- * domain: 'arithmetic',
928
- * intents: ['calculate'],
929
- * mappings: [],
930
- * defaults: [],
931
- * computations: [],
932
- * definition: { reasoning: 'symbolic', id: 't1', name: 'Arithmetic', equations: [], variables: {} },
933
- * },
934
- * [],
935
- * [],
936
- * ) // { prompt: 'Calculate Arithmetic' }
937
- * ```
938
- */
939
- export declare class Formatter implements FormatterInterface {
940
- #private;
941
- constructor(options?: FormatterOptions);
942
- format(intent: Intent, template: Template, entities: readonly Entity[], ambiguities: readonly Ambiguity[]): FormatResult;
943
- }
944
-
945
- /** The `Formatter` stage contract: render the refined natural-language prompt for a matched template. */
946
- export declare interface FormatterInterface {
947
- format(intent: Intent, template: Template, entities: readonly Entity[], ambiguities: readonly Ambiguity[]): FormatResult;
948
- }
949
-
950
- /** Options for `createFormatter` / the `Formatter` constructor — caller-supplied intent-verb phrasing. */
951
- export declare interface FormatterOptions {
952
- readonly verbs?: Readonly<Record<string, string>>;
953
- }
954
-
955
- /** The `Generator` stage's output: the built subject/definition pair plus its full field audit. */
956
- export declare interface GenerateResult {
957
- readonly subject: Subject;
958
- readonly definition: Definition;
959
- readonly mappings: readonly FieldMapping[];
960
- readonly confidence: number;
961
- }
962
-
963
- /**
964
- * The `Generator` stage: builds the final `Subject` from a fully resolved
965
- * entity set, plus its complete field audit.
966
- *
967
- * @remarks
968
- * `entity → field` via `template.mappings` (an `EntityMapping.entity` name
969
- * lookup); an entity whose name matches no mapping lands on the field named
970
- * by its OWN `name` — the shape `Clarifier` uses for its synthesized
971
- * default/computed entities, so one lookup rule serves both extraction-
972
- * mapped and template-data-derived fields. A single-element array value
973
- * unwraps to its scalar; a multi-element, ALL-numeric array value stays an
974
- * array AND additionally emits five aggregate fields (`Sum` / `Count` /
975
- * `Average` / `Minimum` / `Maximum`, provenance `computed`), each derived as
976
- * a SIBLING path beside the source field via `deriveAggregateField` — for an
977
- * array `FieldPath` (e.g. `['address', 'amounts']`) the aggregates nest
978
- * (`['address', 'amountsSum']`, ...); for a plain string field they stay
979
- * flat (`'amountsSum'`), matching the source field's own shape.
980
- * `confidence` is the mean of the input entities' own confidences (`0` for
981
- * an empty entity set). A `FieldMapping` is emitted for EVERY field that
982
- * lands on the subject, including defaults, computed fields, and aggregates
983
- * — scsr silently omitted defaults/computed from its audit trail; this
984
- * closes that gap. `GeneratorOptions` is a reserved extension seam — the
985
- * stage has no knobs yet, so construction takes no arguments until one
986
- * exists.
987
- *
988
- * @example
989
- * ```ts
990
- * import { Generator } from '@src/core'
991
- *
992
- * const generator = new Generator()
993
- * generator.generate(
994
- * [
995
- * {
996
- * name: 'value',
997
- * value: 42,
998
- * provenance: { category: 'extracted', detail: 'collect' },
999
- * confidence: 0.9,
1000
- * },
1001
- * ],
1002
- * {
1003
- * id: 't1',
1004
- * name: 'Arithmetic',
1005
- * domain: 'arithmetic',
1006
- * intents: ['calculate'],
1007
- * mappings: [{ entity: 'value', aliases: [], field: 'value' }],
1008
- * defaults: [],
1009
- * computations: [],
1010
- * definition: { reasoning: 'symbolic', id: 't1', name: 'Arithmetic', equations: [], variables: {} },
1011
- * },
1012
- * ) // { subject: { value: 42 }, mappings: [...], confidence: 0.9, ... }
1013
- * ```
1014
- */
1015
- declare class Generator_2 implements GeneratorInterface {
1016
- generate(entities: readonly Entity[], template: Template): GenerateResult;
1017
- }
1018
- export { Generator_2 as Generator }
1019
-
1020
- /** The `Generator` stage contract: build the final subject/definition pair plus its field audit. */
1021
- export declare interface GeneratorInterface {
1022
- generate(entities: readonly Entity[], template: Template): GenerateResult;
1023
- }
1024
-
1025
- /**
1026
- * Options for `createGenerator` / the `Generator` constructor.
1027
- *
1028
- * @remarks
1029
- * Currently an empty extension seam — the `Generator` stage takes no
1030
- * configuration today, but keeps its own options type so a future knob
1031
- * never has to change the `GeneratorInterface#generate` call signature.
1032
- */
1033
- export declare interface GeneratorOptions {
1034
- }
1035
-
1036
- /**
1037
- * The classified action + domain for one interpretation, with a combined
1038
- * confidence.
1039
- *
1040
- * @remarks
1041
- * Produced by `classifyIntent` against caller-supplied `actions` / `domains`
1042
- * vocabularies only — there is no built-in en-US worldview and no
1043
- * auto-classification from a registered template's own `domain` name.
1044
- */
1045
- export declare interface Intent {
1046
- readonly action: string;
1047
- readonly domain: string;
1048
- readonly confidence: number;
1049
- }
1050
-
1051
- /**
1052
- * Interpolate `{{dotted.path}}` tokens in a message template against a record.
1053
- *
1054
- * @remarks
1055
- * Each token is split on `.` into a {@link FieldPath} array and resolved with
1056
- * the contracts `resolveField` (a plain string field is ONE key, never
1057
- * dot-split — the split here is the token-to-path bridge). A finite number
1058
- * renders with `en-US` thousand grouping (`5010` → `5,010`); any other
1059
- * resolved value String-coerces. An UNRESOLVED path (the resolved value is
1060
- * `undefined`) renders as the empty string — the deterministic "nothing to
1061
- * show" rule.
1062
- *
1063
- * @param template - The message template carrying `{{dotted.path}}` tokens
1064
- * @param record - The record tokens resolve against
1065
- * @returns The template with every token replaced
1066
- *
1067
- * @example
1068
- * ```ts
1069
- * import { interpolateMessage } from '@src/core'
1070
- *
1071
- * interpolateMessage('Limit is {{limit}}', { limit: 5010 }) // 'Limit is 5,010'
1072
- * interpolateMessage('Missing {{gone}}', {}) // 'Missing '
1073
- * ```
1074
- */
1075
- export declare function interpolateMessage(template: string, record: Readonly<Record<string, unknown>>): string;
1076
-
1077
- /**
1078
- * The interpretation orchestrator — the sole public entry point of the
1079
- * `interprets` module, mirroring the reasons `Reason` orchestrator shape.
1080
- *
1081
- * @remarks
1082
- * `interpret()` is genuinely SYNCHRONOUS (scsr's was fake-async with zero
1083
- * `await`s) and runs a fixed five-stage pipeline —
1084
- * `[normalize, extract, clarify, format, generate]` — each producing one
1085
- * {@link StageRecord}. Between `extract` and `clarify` the orchestrator matches
1086
- * the classified {@link Intent} against its registered {@link Template}s and,
1087
- * on a match, assigns the extracted numbers to that template's mappings
1088
- * (`assignEntities`) — a template-owned step, not a sixth stage. No match, or a
1089
- * matched template whose intent confidence falls below the configured `floor`,
1090
- * yields an explicit, auditable INCOMPLETE result (a `field: 'intent'`
1091
- * ambiguity, absent subject/definition) rather than scsr's arbitrary
1092
- * `templates[0]` fallback. A stage THROW is caught, marked on its record AND on
1093
- * `failures`, emitted as `error`, and still yields a visible incomplete result
1094
- * — never a silent fallback. Every result carries a `digest` over its original
1095
- * text plus the matched template id/version and the built subject/definition,
1096
- * so re-running the same text against the same template version reproduces the
1097
- * same digest (the replay contract). `describe` / `narrate` are the reverse
1098
- * direction (structure → prose). `destroy()` is idempotent, tears down the
1099
- * registry and context, then destroys the emitter LAST (AGENTS §13); every
1100
- * method afterwards except the {@link emitter} getter throws
1101
- * `InterpretError('DESTROYED', …)`.
1102
- *
1103
- * @example
1104
- * ```ts
1105
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
1106
- * import { Interpret, Extractor } from '@src/core'
1107
- *
1108
- * const interpret = new Interpret({
1109
- * extractor: new Extractor({ actions: { calculate: 'calculate' }, domains: { arithmetic: ['arithmetic'] } }),
1110
- * templates: [
1111
- * {
1112
- * id: 't1',
1113
- * name: 'Arithmetic',
1114
- * domain: 'arithmetic',
1115
- * intents: ['calculate'],
1116
- * mappings: [{ entity: 'value', aliases: [], field: 'value' }],
1117
- * defaults: [],
1118
- * computations: [],
1119
- * definition: quantitativeDefinition('t1', 'Arithmetic', [
1120
- * factorGroup('total', 'sum', [fieldFactor('value', 'value')]),
1121
- * ]),
1122
- * },
1123
- * ],
1124
- * })
1125
- * interpret.interpret('calculate arithmetic 42').subject // { value: 42 }
1126
- * ```
1127
- */
1128
- export declare class Interpret implements InterpretInterface {
1129
- #private;
1130
- constructor(options?: InterpretOptions);
1131
- get emitter(): EmitterInterface<InterpretEventMap>;
1132
- interpret(text: string): Interpretation;
1133
- register(template: Template): void;
1134
- unregister(id: string): boolean;
1135
- template(id: string): Template | undefined;
1136
- templates(): readonly Template[];
1137
- describe(definition: Definition): string;
1138
- narrate(result: ReasonResult): string;
1139
- destroy(): void;
1140
- }
1141
-
1142
- /** Default `id` for an `Interpret` orchestrator. */
1143
- export declare const INTERPRET_ID = "interpret";
1144
-
1145
- /**
1146
- * The full, replayable outcome of one `interpret()` call.
1147
- *
1148
- * @remarks
1149
- * `subject` / `definition` are absent on an incomplete `NO_TEMPLATE` /
1150
- * `LOW_CONFIDENCE` result — there is never a fabricated fallback template
1151
- * (scsr's `templates[0]` double-fallback defect). `stages` always holds
1152
- * exactly five records, `[normalize, extract, clarify, format, generate]`,
1153
- * in order. `digest` is `digestValue` over `{text, templateId,
1154
- * templateVersion, subject, definition}` — re-running the same original text
1155
- * against the same template version reproduces the same digest (the replay
1156
- * contract).
1157
- */
1158
- export declare interface Interpretation {
1159
- readonly text: string;
1160
- readonly normalized: string;
1161
- readonly intent: Intent;
1162
- readonly entities: readonly Entity[];
1163
- readonly subject?: Subject;
1164
- readonly definition?: Definition;
1165
- readonly mappings: readonly FieldMapping[];
1166
- readonly ambiguities: readonly Ambiguity[];
1167
- readonly prompt: string;
1168
- readonly stages: readonly StageRecord[];
1169
- readonly failures: readonly StageFailure[];
1170
- readonly complete: boolean;
1171
- readonly confidence: number;
1172
- readonly digest: string;
1173
- }
1174
-
1175
- /**
1176
- * Cross-turn interpretation context — a capped, replayable history of
1177
- * completed {@link Interpretation}s plus the subject and definition registries
1178
- * carry-over reads from.
1179
- *
1180
- * @remarks
1181
- * `previous()` is a capped ring buffer (newest-last, oldest dropped once the
1182
- * `history` cap is reached — `DEFAULT_INTERPRET_HISTORY` by default, ≥ 3
1183
- * preserves the carry-over pin) rather than scsr's unbounded `previous` array.
1184
- * `entities()` flattens every entity across the buffered history, most recent
1185
- * last — the read a `Clarifier`'s same-domain carry-over consults. `add` pushes
1186
- * one result and trims to the cap; `clear` resets the history and both
1187
- * registries WITHOUT tearing the context down; `destroy()` is idempotent and
1188
- * every method afterwards throws `InterpretError('DESTROYED', …)`.
1189
- *
1190
- * @example
1191
- * ```ts
1192
- * import { InterpretContext } from '@src/core'
1193
- *
1194
- * const context = new InterpretContext({ session: 's1', history: 8 })
1195
- * context.session // 's1'
1196
- * context.previous() // []
1197
- * ```
1198
- */
1199
- export declare class InterpretContext implements InterpretContextInterface {
1200
- #private;
1201
- constructor(options?: InterpretContextOptions);
1202
- get emitter(): EmitterInterface<InterpretContextEventMap>;
1203
- get session(): string | undefined;
1204
- get subjects(): SubjectManagerInterface;
1205
- get definitions(): DefinitionManagerInterface;
1206
- previous(): readonly Interpretation[];
1207
- entities(): readonly Entity[];
1208
- add(result: Interpretation): void;
1209
- clear(): void;
1210
- destroy(): void;
1211
- }
1212
-
1213
- /**
1214
- * The push observation surface of an {@link InterpretContextInterface} (AGENTS
1215
- * §13).
1216
- *
1217
- * @remarks
1218
- * An {@link Interpretation} carries no `id` of its own — `add` carries the
1219
- * entry's `digest` instead, the closest content-derived identity it has.
1220
- */
1221
- export declare type InterpretContextEventMap = {
1222
- /** A completed interpretation was added to the history — carries its digest. */
1223
- readonly add: readonly [digest: string];
1224
- /** The history and both registries were cleared. */
1225
- readonly clear: readonly [];
1226
- /** The context was destroyed. */
1227
- readonly destroy: readonly [];
1228
- };
1229
-
1230
- /**
1231
- * Cross-turn interpretation context: a capped, replayable history plus the
1232
- * subject/definition registries carry-over reads from.
1233
- *
1234
- * @remarks
1235
- * `previous()` returns the ring buffer newest-last, capped at the
1236
- * configured `history` (default `DEFAULT_INTERPRET_HISTORY`). `entities()`
1237
- * flattens every entity recorded across the buffered history, most recent
1238
- * last — the read carry-over consults. `add` pushes one completed
1239
- * {@link Interpretation}, dropping the oldest entry once the cap is reached.
1240
- */
1241
- export declare interface InterpretContextInterface {
1242
- readonly emitter: EmitterInterface<InterpretContextEventMap>;
1243
- readonly session?: string;
1244
- readonly subjects: SubjectManagerInterface;
1245
- readonly definitions: DefinitionManagerInterface;
1246
- previous(): readonly Interpretation[];
1247
- entities(): readonly Entity[];
1248
- add(result: Interpretation): void;
1249
- clear(): void;
1250
- destroy(): void;
1251
- }
1252
-
1253
- /** Options for `createInterpretContext` / the `InterpretContext` constructor. */
1254
- export declare interface InterpretContextOptions {
1255
- readonly session?: string;
1256
- readonly history?: number;
1257
- readonly on?: EmitterHooks<InterpretContextEventMap>;
1258
- readonly error?: EmitterErrorHandler;
1259
- }
1260
-
1261
- /**
1262
- * An error thrown by the interprets layer.
1263
- *
1264
- * @remarks
1265
- * Thrown for: an injected stage implementation throwing during its phase
1266
- * (`NORMALIZE_FAILED` / `EXTRACT_FAILED` / `CLARIFY_FAILED` /
1267
- * `FORMAT_FAILED` / `GENERATE_FAILED`), `createTemplate` handed data that
1268
- * fails `isTemplate` (`INVALID_TEMPLATE`), and any use of a destroyed
1269
- * `Interpret` / manager / context (`DESTROYED`). `NO_TEMPLATE` and
1270
- * `LOW_CONFIDENCE` never throw — they surface as a visible incomplete
1271
- * {@link Interpretation} instead (never an arbitrary fallback template).
1272
- * `context`, when present, carries the offending stage / template id.
1273
- */
1274
- export declare class InterpretError extends Error {
1275
- readonly code: InterpretErrorCode;
1276
- readonly context?: Readonly<Record<string, unknown>>;
1277
- constructor(code: InterpretErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
1278
- }
1279
-
1280
- /**
1281
- * Coded misuse / failure conditions thrown as an {@link InterpretError} or
1282
- * carried on a {@link StageFailure}.
1283
- *
1284
- * @remarks
1285
- * `NORMALIZE_FAILED` / `EXTRACT_FAILED` / `CLARIFY_FAILED` / `FORMAT_FAILED`
1286
- * / `GENERATE_FAILED` — an injected stage implementation threw during that
1287
- * phase. `NO_TEMPLATE` — no registered {@link Template} scored at or above
1288
- * the confidence floor (or the registry is empty). `LOW_CONFIDENCE` — a
1289
- * template matched but the classified {@link Intent}'s confidence fell below
1290
- * the floor. `INVALID_TEMPLATE` — `createTemplate` was handed data that
1291
- * fails `isTemplate`. `DESTROYED` — any use of a destroyed entity.
1292
- */
1293
- export declare type InterpretErrorCode = 'NORMALIZE_FAILED' | 'EXTRACT_FAILED' | 'CLARIFY_FAILED' | 'FORMAT_FAILED' | 'GENERATE_FAILED' | 'NO_TEMPLATE' | 'LOW_CONFIDENCE' | 'INVALID_TEMPLATE' | 'DESTROYED';
1294
-
1295
- /**
1296
- * The push observation surface of an {@link InterpretInterface} (AGENTS §13).
1297
- *
1298
- * @remarks
1299
- * `interpret` fires once per completed `interpret()` call (complete OR
1300
- * incomplete — visibility is the point, unlike scsr's silent fallbacks).
1301
- * `register` fires when a template is registered, carrying its id. `error`
1302
- * fires with the raw thrown value when an injected stage implementation
1303
- * throws. `destroy` fires once on teardown. Listener isolation is the
1304
- * emitter's own (AGENTS §13) — never routed onto this map.
1305
- */
1306
- export declare type InterpretEventMap = {
1307
- /** An `interpret()` call completed — carries the full result. */
1308
- readonly interpret: readonly [result: Interpretation];
1309
- /** A template was registered — carries its id. */
1310
- readonly register: readonly [templateId: string];
1311
- /** An injected stage implementation threw — carries the raw thrown value. */
1312
- readonly error: readonly [error: unknown];
1313
- /** The orchestrator was destroyed. */
1314
- readonly destroy: readonly [];
1315
- };
1316
-
1317
- /**
1318
- * The interpretation orchestrator — the sole public entry point, mirroring
1319
- * `reasons`' `Reason` orchestrator shape.
1320
- *
1321
- * @remarks
1322
- * `interpret` is genuinely SYNCHRONOUS (scsr's `interpret()` was fake-async
1323
- * with zero `await`s). `register` / `unregister` / `template` / `templates`
1324
- * delegate to an internal {@link TemplateManagerInterface} but expose plain
1325
- * {@link Template} data, not the richer versioned record. `describe` /
1326
- * `narrate` are the reverse direction — structure-to-prose, complementing
1327
- * (never duplicating) raters' `describe*` family. After `destroy()` every
1328
- * method except the `emitter` getter and `destroy` itself throws
1329
- * `InterpretError('DESTROYED', …)`; `destroy()` is idempotent and tears the
1330
- * emitter down LAST.
1331
- */
1332
- export declare interface InterpretInterface {
1333
- readonly emitter: EmitterInterface<InterpretEventMap>;
1334
- interpret(text: string): Interpretation;
1335
- register(template: Template): void;
1336
- unregister(id: string): boolean;
1337
- template(id: string): Template | undefined;
1338
- templates(): readonly Template[];
1339
- describe(definition: Definition): string;
1340
- narrate(result: ReasonResult): string;
1341
- destroy(): void;
1342
- }
1343
-
1344
- /**
1345
- * Options for `createInterpret` / the `Interpret` constructor.
1346
- *
1347
- * @remarks
1348
- * `templates` seeds the registry. `context` supplies a shared
1349
- * {@link InterpretContextInterface} (a fresh one is constructed when
1350
- * omitted). Each stage slot is BRING-YOUR-OWN — a supplied implementation is
1351
- * used as-is, else the built-in stage is constructed from the matching
1352
- * per-stage options. `similarity` (fuzzy alias-match threshold, default
1353
- * `DEFAULT_INTERPRET_SIMILARITY`) and `floor` (intent confidence floor,
1354
- * default `DEFAULT_INTERPRET_FLOOR`) are two distinct, clearly named axes —
1355
- * both honored wherever they apply, never a single overloaded `threshold`
1356
- * (scsr's defect). `history` caps the context's `previous()` ring buffer.
1357
- * `on` — initial event listeners (AGENTS §8). `error` — the emitter's
1358
- * listener-error handler (AGENTS §13).
1359
- */
1360
- export declare interface InterpretOptions {
1361
- readonly templates?: readonly Template[];
1362
- readonly context?: InterpretContextInterface;
1363
- readonly normalizer?: NormalizerInterface;
1364
- readonly extractor?: ExtractorInterface;
1365
- readonly clarifier?: ClarifierInterface;
1366
- readonly formatter?: FormatterInterface;
1367
- readonly generator?: GeneratorInterface;
1368
- readonly similarity?: number;
1369
- readonly floor?: number;
1370
- readonly history?: number;
1371
- readonly lexicon?: Lexicon;
1372
- readonly formatters?: Readonly<Record<string, NarratorFormatter>>;
1373
- readonly on?: EmitterHooks<InterpretEventMap>;
1374
- readonly error?: EmitterErrorHandler;
1375
- }
1376
-
1377
- /**
1378
- * The five fixed pipeline phases an {@link InterpretInterface#interpret} run
1379
- * produces one {@link StageRecord} for, in order.
1380
- *
1381
- * @remarks
1382
- * Deliberately NOT named `Stage` — raters already owns that identifier for
1383
- * its worksheet derivation axis (`'factor' | 'group' | 'total'`); the two are
1384
- * unrelated concepts on the shared `@src/core` barrel.
1385
- */
1386
- export declare type InterpretStage = 'normalize' | 'extract' | 'clarify' | 'format' | 'generate';
1387
-
1388
- /**
1389
- * Determine whether a value is a {@link ComputedField} — a declaratively
1390
- * computed field carrying a reasons {@link SymbolicExpression} tree.
1391
- *
1392
- * @param value - The value to test
1393
- * @returns `true` when `value` is a well-formed computed field
1394
- *
1395
- * @example
1396
- * ```ts
1397
- * import { constant, operation, variable } from '@orkestrel/reason'
1398
- * import { isComputedField } from '@src/core'
1399
- *
1400
- * isComputedField({
1401
- * field: 'monthly',
1402
- * expression: operation('divide', variable('deductible'), constant(12)),
1403
- * }) // true
1404
- * isComputedField({ field: 'monthly', expression: { form: 'variable' } }) // false — name missing
1405
- * ```
1406
- */
1407
- export declare function isComputedField(value: unknown): value is ComputedField;
1408
-
1409
- /**
1410
- * Determine whether a value is an {@link EntityMapping} — a literal
1411
- * alias-phrase extraction rule pointing at a subject field.
1412
- *
1413
- * @param value - The value to test
1414
- * @returns `true` when `value` is a well-formed entity mapping
1415
- *
1416
- * @example
1417
- * ```ts
1418
- * import { isEntityMapping } from '@src/core'
1419
- *
1420
- * isEntityMapping({ entity: 'age', aliases: ['years old'], field: 'age' }) // true
1421
- * isEntityMapping({ entity: 'age', aliases: [/\d+/], field: 'age' }) // false — RegExp alias
1422
- * ```
1423
- */
1424
- export declare function isEntityMapping(value: unknown): value is EntityMapping;
1425
-
1426
- /**
1427
- * Determine whether a value is a {@link FieldDefault} — a fallback value a
1428
- * {@link Template} fills onto an unresolved field.
1429
- *
1430
- * @remarks
1431
- * `value` is unconstrained (any value, including `null` or `undefined`) as
1432
- * long as the key is present — the trivially-true guard `notOf(unionOf())`
1433
- * mirrors the reasons `Check.value` precedent.
1434
- *
1435
- * @param value - The value to test
1436
- * @returns `true` when `value` is a well-formed field default
1437
- *
1438
- * @example
1439
- * ```ts
1440
- * import { isFieldDefault } from '@src/core'
1441
- *
1442
- * isFieldDefault({ field: 'term', value: 12 }) // true
1443
- * isFieldDefault({ field: 'term' }) // false — value missing
1444
- * ```
1445
- */
1446
- export declare function isFieldDefault(value: unknown): value is FieldDefault;
1447
-
1448
- /**
1449
- * Narrow an unknown caught value to an {@link InterpretError}.
1450
- *
1451
- * @param value - The value to test (typically a `catch` binding)
1452
- * @returns `true` when `value` is an {@link InterpretError}
1453
- *
1454
- * @example
1455
- * ```ts
1456
- * import { isInterpretError } from '@src/core'
1457
- *
1458
- * try {
1459
- * interpret.template('missing')
1460
- * } catch (error) {
1461
- * if (isInterpretError(error) && error.code === 'DESTROYED') return
1462
- * }
1463
- * ```
1464
- */
1465
- export declare function isInterpretError(value: unknown): value is InterpretError;
1466
-
1467
- /**
1468
- * Determine whether a value is a {@link Template} — a named, versionable
1469
- * interpretation template.
1470
- *
1471
- * @remarks
1472
- * `definition` is validated with reasons' `isDefinition` — a `Template`'s
1473
- * definition is already expressed in terrain reasons vocabulary, so no
1474
- * parallel interprets-owned definition guard exists.
1475
- *
1476
- * @param value - The value to test
1477
- * @returns `true` when `value` is a well-formed template
1478
- *
1479
- * @example
1480
- * ```ts
1481
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
1482
- * import { isTemplate } from '@src/core'
1483
- *
1484
- * isTemplate({
1485
- * id: 't1',
1486
- * name: 'Arithmetic',
1487
- * domain: 'arithmetic',
1488
- * intents: ['calculate'],
1489
- * mappings: [],
1490
- * defaults: [],
1491
- * computations: [],
1492
- * definition: quantitativeDefinition('t1', 'Arithmetic', [
1493
- * factorGroup('total', 'sum', [fieldFactor('value', 'value')]),
1494
- * ]),
1495
- * }) // true
1496
- * isTemplate({ id: 't1' }) // false — most fields missing
1497
- * ```
1498
- */
1499
- export declare function isTemplate(value: unknown): value is Template;
1500
-
1501
- /**
1502
- * Caller-injected wording data for the reverse direction — mechanism, never
1503
- * policy (AGENTS §21). Every phrase, label, and template string a `Narrator`
1504
- * renders is DATA supplied here, never a core literal.
1505
- *
1506
- * @remarks
1507
- * `phrases` is a two-level lookup (`table` → `key` → phrase) for domain
1508
- * vocabulary swaps (e.g. `comparison.equals` → `'is'`). `labels` maps a
1509
- * dotted `FieldPath` string to its display label, falling back to
1510
- * `formatField` when absent. `templates` maps a template id (e.g.
1511
- * `'definition.quantitative'`, `'result.symbolic'`, `'subject.fields'`) to an
1512
- * `interpolateMessage` template string — see `DEFAULT_LEXICON` for the
1513
- * pinned neutral key set.
1514
- */
1515
- export declare interface Lexicon {
1516
- readonly phrases?: Readonly<Record<string, Readonly<Record<string, string>>>>;
1517
- readonly labels?: Readonly<Record<string, string>>;
1518
- readonly templates?: Readonly<Record<string, string>>;
1519
- }
1520
-
1521
- /**
1522
- * Per-call options shared by every manager's `add` method.
1523
- *
1524
- * @remarks
1525
- * `id` overrides the minted record id. `TemplateManagerInterface#add` /
1526
- * `DefinitionManagerInterface#add` default to the added value's own `id`
1527
- * field when omitted; `SubjectManagerInterface#add` mints a fresh id when
1528
- * omitted, since a `Subject` carries no `id` field of its own.
1529
- */
1530
- export declare interface ManagerAddOptions {
1531
- readonly id?: string;
1532
- }
1533
-
1534
- /**
1535
- * The best `scoreSimilarity` a token achieves against a list of aliases,
1536
- * gated by a threshold.
1537
- *
1538
- * @param token - The token to score
1539
- * @param aliases - The alias phrases to score against
1540
- * @param threshold - The minimum score to report (an explicit no-match below it)
1541
- * @returns The best score when it meets `threshold`, else `0`
1542
- *
1543
- * @example
1544
- * ```ts
1545
- * import { matchAlias } from '@src/core'
1546
- *
1547
- * matchAlias('valu', ['value', 'amount'], 0.6) // ~0.86 — fuzzy hit on 'value'
1548
- * matchAlias('xyz', ['value', 'amount'], 0.6) // 0 — no alias clears the threshold
1549
- * ```
1550
- */
1551
- export declare function matchAlias(token: string, aliases: readonly string[], threshold: number): number;
1552
-
1553
- /**
1554
- * Find the best-scoring registered template for a classified intent, gated
1555
- * by a confidence floor.
1556
- *
1557
- * @remarks
1558
- * Explicit no-match (AGENTS-flagged scsr defect 2 — never an arbitrary
1559
- * `templates[0]` fallback): an empty registry, or a best score strictly below
1560
- * `floor`, both return `undefined`.
1561
- *
1562
- * @param intent - The classified intent
1563
- * @param templates - The registered templates to score
1564
- * @param floor - The minimum score a match must clear
1565
- * @returns The best-scoring template, or `undefined` on no qualifying match
1566
- *
1567
- * @example
1568
- * ```ts
1569
- * import { matchTemplate } from '@src/core'
1570
- *
1571
- * matchTemplate({ action: '', domain: '', confidence: 0 }, [], 0.3) // undefined — empty registry
1572
- * ```
1573
- */
1574
- export declare function matchTemplate(intent: Intent, templates: readonly Template[], floor: number): Template | undefined;
1575
-
1576
- /**
1577
- * A stateless, TOTAL, lexicon-driven rendering engine for the reverse
1578
- * direction — the reverse-direction mirror of the forward `Formatter`'s
1579
- * `verbs` seam (AGENTS §21 mechanism-never-policy).
1580
- *
1581
- * @remarks
1582
- * Every wording decision is DATA — a caller-supplied `Lexicon` merged, per
1583
- * sub-record (`phrases` / `labels` / `templates`), OVER `DEFAULT_LEXICON`.
1584
- * Stateless and holds no resources: no emitter, no `destroy()` (deliberate —
1585
- * there is nothing to release). Every method is total (never throws); a
1586
- * lexicon or formatter miss degrades to its documented fallback rather than
1587
- * a thrown error, and every lookup guards with `Object.hasOwn` so an
1588
- * adversarial key (`toString`, `constructor`, `__proto__`) misses cleanly
1589
- * instead of reading an inherited prototype member.
1590
- *
1591
- * @example
1592
- * ```ts
1593
- * import { Narrator } from '@src/core'
1594
- *
1595
- * const narrator = new Narrator({
1596
- * lexicon: { phrases: { comparison: { equals: 'is' } } },
1597
- * formatters: { money: (value) => `$${String(value)}` },
1598
- * })
1599
- * narrator.phrase('comparison', 'equals', 'equals') // 'is'
1600
- * narrator.phrase('comparison', 'missing', 'equals') // 'equals' — fallback
1601
- * narrator.value('money', 5) // '$5'
1602
- * ```
1603
- */
1604
- export declare class Narrator implements NarratorInterface {
1605
- #private;
1606
- constructor(options?: NarratorOptions);
1607
- phrase(table: string, key: string, fallback?: string): string;
1608
- label(field: FieldPath): string;
1609
- line(id: string, values: Readonly<Record<string, unknown>>): string;
1610
- value(unit: string, raw: unknown): string;
1611
- describe(definition: Definition): string;
1612
- narrate(result: ReasonResult): string;
1613
- }
1614
-
1615
- /**
1616
- * A pure formatting function for one lexicon `value()` unit.
1617
- *
1618
- * @remarks
1619
- * A `Narrator` calls this from `value()` inside a `try`/`catch` (AGENTS §21 —
1620
- * a wording engine must never crash a render) — a throwing formatter is
1621
- * caught and the raw value falls back to `String(raw)`.
1622
- */
1623
- export declare type NarratorFormatter = (value: unknown) => string;
1624
-
1625
- /**
1626
- * The `Narrator` contract — a stateless, TOTAL, lexicon-driven rendering
1627
- * engine for the reverse direction.
1628
- *
1629
- * @remarks
1630
- * Every method is total — never throws. A lookup miss degrades to a
1631
- * `fallback` (when supplied), the lookup key itself, or a computed fallback
1632
- * (`formatField` for `label`, `String(raw)` for `value`) — never a thrown
1633
- * error, even for adversarial prototype-chain keys (`toString`,
1634
- * `constructor`, `__proto__`), guarded with `Object.hasOwn` at every lookup.
1635
- * `phrase` looks up a two-level `table`/`key` pair in the lexicon's
1636
- * `phrases`. `label` renders a field's display label from `labels`, falling
1637
- * back to `formatField`. `line` interpolates a named `templates` entry
1638
- * against `values`, falling back to an empty string when the id is absent.
1639
- * `value` runs a named formatter over a raw value, catching a throw and
1640
- * falling back to `String(raw)`. `describe` / `narrate` compose these
1641
- * primitives over a reasons `Definition` / `ReasonResult`.
1642
- */
1643
- export declare interface NarratorInterface {
1644
- phrase(table: string, key: string, fallback?: string): string;
1645
- label(field: FieldPath): string;
1646
- line(id: string, values: Readonly<Record<string, unknown>>): string;
1647
- value(unit: string, raw: unknown): string;
1648
- describe(definition: Definition): string;
1649
- narrate(result: ReasonResult): string;
1650
- }
1651
-
1652
- /** Options for `createNarrator` / the `Narrator` constructor. */
1653
- export declare interface NarratorOptions {
1654
- readonly lexicon?: Lexicon;
1655
- readonly formatters?: Readonly<Record<string, NarratorFormatter>>;
1656
- }
1657
-
1658
- /**
1659
- * The `Normalizer` stage: applies contraction, abbreviation, and correction
1660
- * substitutions in order, then collapses whitespace.
1661
- *
1662
- * @remarks
1663
- * Each caller map is merged OVER the neutral built-in default for its slot —
1664
- * `text` is never mutated (AGENTS §11). Every substitution actually applied
1665
- * (one entry per matching map KEY, not per occurrence) is recorded on the
1666
- * result's `changes`, in `contractions → abbreviations → corrections` order,
1667
- * so the audit trail explains every character difference between `text` and
1668
- * `NormalizeResult.text` (the final whitespace collapse carries no entry of
1669
- * its own — it is structural cleanup, not a substitution).
1670
- *
1671
- * @example
1672
- * ```ts
1673
- * import { Normalizer } from '@src/core'
1674
- *
1675
- * const normalizer = new Normalizer({ contractions: { "can't": 'cannot' } })
1676
- * normalizer.normalize("can't stop")
1677
- * // { text: 'cannot stop', changes: [{ from: "can't", to: 'cannot' }] }
1678
- * ```
1679
- */
1680
- export declare class Normalizer implements NormalizerInterface {
1681
- #private;
1682
- constructor(options?: NormalizerOptions);
1683
- normalize(text: string): NormalizeResult;
1684
- }
1685
-
1686
- /** The `Normalizer` stage's output: the cleaned text plus every substitution applied. */
1687
- export declare interface NormalizeResult {
1688
- readonly text: string;
1689
- readonly changes: readonly TextChange[];
1690
- }
1691
-
1692
- /** The `Normalizer` stage contract: raw text in, cleaned text + applied changes out. */
1693
- export declare interface NormalizerInterface {
1694
- normalize(text: string): NormalizeResult;
1695
- }
1696
-
1697
- /**
1698
- * Options for `createNormalizer` / the `Normalizer` constructor.
1699
- *
1700
- * @remarks
1701
- * Each map is merged OVER the neutral built-in defaults, applied in order
1702
- * contractions → abbreviations → corrections, before whitespace collapse.
1703
- */
1704
- export declare interface NormalizerOptions {
1705
- readonly contractions?: Readonly<Record<string, string>>;
1706
- readonly abbreviations?: Readonly<Record<string, string>>;
1707
- readonly corrections?: Readonly<Record<string, string>>;
1708
- }
1709
-
1710
- /**
1711
- * The numeric-entity extraction pattern shared by `extractNumbers` and
1712
- * `assignEntities` — an optional leading `$`, thousands-comma-grouped digits,
1713
- * an optional decimal fraction, and an optional trailing `%`.
1714
- *
1715
- * @remarks
1716
- * Carries the global flag, so every call site builds a fresh `RegExp` from
1717
- * `.source` / `.flags` (mirrors the core-root `PLACEHOLDER_PATTERN` pattern)
1718
- * rather than sharing this instance's mutable `lastIndex` across scans.
1719
- */
1720
- export declare const NUMBER_PATTERN: RegExp;
1721
-
1722
- /**
1723
- * Parse a JSON string into a `Template`, or `undefined` on invalid JSON or a
1724
- * shape that fails `isTemplate`.
1725
- *
1726
- * @remarks
1727
- * The module's sole JSON boundary (design §4) — an `Interpretation` and the
1728
- * versioned records are produced internally, never deserialized from
1729
- * untrusted JSON; replay re-runs `interpret`, it does not deserialize a
1730
- * stored result.
1731
- *
1732
- * @param value - The JSON text to parse
1733
- * @returns The parsed template, or `undefined`
1734
- *
1735
- * @example
1736
- * ```ts
1737
- * import { parseTemplate } from '@src/core'
1738
- *
1739
- * parseTemplate('not json') // undefined
1740
- * ```
1741
- */
1742
- export declare function parseTemplate(value: string): Template | undefined;
1743
-
1744
- /** How one value landed — its origin category plus an optional strategy detail. */
1745
- export declare interface Provenance {
1746
- readonly category: ProvenanceCategory;
1747
- readonly detail?: string;
1748
- }
1749
-
1750
- /**
1751
- * How one {@link FieldMapping} / {@link Entity} value was obtained.
1752
- *
1753
- * @remarks
1754
- * `extracted` — mined from the raw text via keyword / alias / positional
1755
- * matching. `carried` — reused from a same-domain prior turn in an
1756
- * {@link InterpretContextInterface}. `default` — filled from a
1757
- * {@link Template}'s {@link FieldDefault}. `computed` — derived by evaluating
1758
- * a {@link ComputedField}'s expression. `subject` — already present on an
1759
- * injected base subject, untouched by this interpretation.
1760
- */
1761
- export declare type ProvenanceCategory = 'extracted' | 'carried' | 'default' | 'computed' | 'subject';
1762
-
1763
- /**
1764
- * Evaluate a symbolic expression tree against resolved bindings.
1765
- *
1766
- * @remarks
1767
- * THE critical leaf (design-pinned, engine-parity semantics): an absent
1768
- * `right` operand on a binary operation defaults to `0` — matching
1769
- * `SymbolicReasoner`'s internal `#evaluate` — and is always passed as an
1770
- * EXPLICIT numeric operand, so the same tree evaluates identically here and
1771
- * inside the engine. Each arithmetic step delegates to the reasons
1772
- * `applyOperation` pure function, mapping the node's `.operator` field onto
1773
- * its `operator` parameter. An unresolved input variable, or a non-finite
1774
- * result (`NaN` from a divide-by-zero, or an overflowing `±Infinity`),
1775
- * becomes a gap — `undefined`, never landing on a subject.
1776
- *
1777
- * @param expression - The expression tree to evaluate
1778
- * @param bindings - The resolved variable bindings
1779
- * @returns The evaluated number, or `undefined` on an unresolved input or a
1780
- * non-finite result
1781
- *
1782
- * @example
1783
- * ```ts
1784
- * import { constant, operation, variable } from '@orkestrel/reason'
1785
- * import { resolveExpression } from '@src/core'
1786
- *
1787
- * resolveExpression(operation('divide', variable('deductible'), constant(12)), { deductible: 6000 }) // 500
1788
- * resolveExpression(operation('divide', constant(1), constant(0)), {}) // undefined — NaN gap
1789
- * resolveExpression(variable('missing'), {}) // undefined — unresolved input
1790
- * ```
1791
- */
1792
- export declare function resolveExpression(expression: SymbolicExpression, bindings: Readonly<Record<string, number>>): number | undefined;
1793
-
1794
- /**
1795
- * Bigram (Dice coefficient) string similarity, case-insensitive.
1796
- *
1797
- * @param a - The first string
1798
- * @param b - The second string
1799
- * @returns A score in `[0, 1]` — `1` for an exact (case-insensitive) match,
1800
- * `0` when either string is shorter than 2 characters and they are not equal
1801
- *
1802
- * @example
1803
- * ```ts
1804
- * import { scoreSimilarity } from '@src/core'
1805
- *
1806
- * scoreSimilarity('rate', 'rate') // 1
1807
- * scoreSimilarity('rate', 'value') // 0 — no shared bigrams
1808
- * ```
1809
- */
1810
- export declare function scoreSimilarity(a: string, b: string): number;
1811
-
1812
- /**
1813
- * Score how well a classified intent matches one template's domain + action.
1814
- *
1815
- * @param intent - The classified intent
1816
- * @param template - The candidate template
1817
- * @returns A score in `[0, 1]` — the mean of the domain match (`1`/`0`) and
1818
- * the action match (`1`/`0`, `template.intents` containing `intent.action`)
1819
- *
1820
- * @example
1821
- * ```ts
1822
- * import { scoreTemplate } from '@src/core'
1823
- *
1824
- * scoreTemplate(
1825
- * { action: 'compute', domain: 'rating', confidence: 1 },
1826
- * { id: 't1', name: 'T', domain: 'rating', intents: ['compute'], mappings: [], defaults: [], computations: [], definition: { reasoning: 'symbolic', id: 't1', name: 'T', equations: [], variables: {} } },
1827
- * ) // 1
1828
- * ```
1829
- */
1830
- export declare function scoreTemplate(intent: Intent, template: Template): number;
1831
-
1832
- export declare function setField(subject: Subject, field: FieldPath, value: unknown): Subject;
1833
-
1834
- /** A visible marker for a stage that threw, carrying its coded reason. */
1835
- export declare interface StageFailure {
1836
- readonly stage: InterpretStage;
1837
- readonly code: InterpretErrorCode;
1838
- readonly message: string;
1839
- }
1840
-
1841
- /**
1842
- * A structured input/output snapshot of one pipeline phase.
1843
- *
1844
- * @remarks
1845
- * `input` / `output` are live structured values, never a stringified JSON
1846
- * blob. No `duration` field — strict core forbids wall-clock timing
1847
- * (AGENTS §17.7); the audit story here is structural, not temporal.
1848
- */
1849
- export declare interface StageRecord {
1850
- readonly stage: InterpretStage;
1851
- readonly input: unknown;
1852
- readonly output: unknown;
1853
- readonly failed: boolean;
1854
- readonly error?: string;
1855
- }
1856
-
1857
- /**
1858
- * The subject registry — a self-owning, versioned and content-hashed
1859
- * record-holder that mints its OWN record identity for every {@link Subject}
1860
- * (a `Subject` carries no `id` field of its own).
1861
- *
1862
- * @remarks
1863
- * The defect-7 fix: scsr keyed stored subjects by their definition's id, so
1864
- * successive same-domain turns silently overwrote one shared subject. Here each
1865
- * `add` mints a fresh `subject-{n}` id (deterministic per instance, no host
1866
- * randomness — AGENTS §17.7) unless the caller overrides it via
1867
- * `ManagerAddOptions.id`. `hash` is content-derived (id-independent) and
1868
- * `version` bumps ONLY when the hash changes at a reused id. The batch
1869
- * `remove(ids)` form is all-or-nothing; `destroy()` is idempotent and every
1870
- * method afterwards throws `InterpretError('DESTROYED', …)`.
1871
- *
1872
- * @example
1873
- * ```ts
1874
- * import { SubjectManager } from '@src/core'
1875
- *
1876
- * const manager = new SubjectManager()
1877
- * const first = manager.add({ age: 25 })
1878
- * const second = manager.add({ age: 30 })
1879
- * first.id !== second.id // true — each subject gets its own identity
1880
- * ```
1881
- */
1882
- export declare class SubjectManager implements SubjectManagerInterface {
1883
- #private;
1884
- constructor(options?: SubjectManagerOptions);
1885
- get emitter(): EmitterInterface<SubjectManagerEventMap>;
1886
- get size(): number;
1887
- has(id: string): boolean;
1888
- subject(id: string): SubjectRecord | undefined;
1889
- subjects(): readonly SubjectRecord[];
1890
- add(subject: Subject, options?: ManagerAddOptions): SubjectRecord;
1891
- remove(ids: readonly string[]): boolean;
1892
- remove(id: string): boolean;
1893
- remove(): void;
1894
- destroy(): void;
1895
- }
1896
-
1897
- /** The push observation surface of a {@link SubjectManagerInterface} (AGENTS §13). */
1898
- export declare type SubjectManagerEventMap = {
1899
- /** A subject record was added — carries its (own-minted) record id. */
1900
- readonly add: readonly [id: string];
1901
- /** A subject record was removed — carries its record id. */
1902
- readonly remove: readonly [id: string];
1903
- /** The manager was destroyed. */
1904
- readonly destroy: readonly [];
1905
- };
1906
-
1907
- /**
1908
- * The subject registry — a self-owning, versioned/hashed record-holder
1909
- * that mints its own record ids (a `Subject` carries none).
1910
- */
1911
- export declare interface SubjectManagerInterface {
1912
- readonly emitter: EmitterInterface<SubjectManagerEventMap>;
1913
- readonly size: number;
1914
- has(id: string): boolean;
1915
- subject(id: string): SubjectRecord | undefined;
1916
- subjects(): readonly SubjectRecord[];
1917
- add(subject: Subject, options?: ManagerAddOptions): SubjectRecord;
1918
- remove(ids: readonly string[]): boolean;
1919
- remove(id: string): boolean;
1920
- remove(): void;
1921
- destroy(): void;
1922
- }
1923
-
1924
- /** Options for `createSubjectManager` / the `SubjectManager` constructor — the initial seed collection. */
1925
- export declare interface SubjectManagerOptions {
1926
- readonly subjects?: readonly Subject[];
1927
- readonly on?: EmitterHooks<SubjectManagerEventMap>;
1928
- readonly error?: EmitterErrorHandler;
1929
- }
1930
-
1931
- /**
1932
- * A versioned, content-hashed {@link Subject} as held by a
1933
- * {@link SubjectManagerInterface}.
1934
- *
1935
- * @remarks
1936
- * `id` is the manager's OWN minted identity — never `definition.id` — so
1937
- * successive turns never silently overwrite one shared subject (scsr's
1938
- * defect).
1939
- */
1940
- export declare interface SubjectRecord {
1941
- readonly id: string;
1942
- readonly subject: Subject;
1943
- readonly version: number;
1944
- readonly hash: string;
1945
- }
1946
-
1947
- /**
1948
- * A named, versionable interpretation template: which intents it answers,
1949
- * how to mine entities for it, its fallback data, its computed fields, and
1950
- * the reasons `Definition` it ultimately produces a `Subject` for.
1951
- *
1952
- * @remarks
1953
- * `definition` is inline and already expressed in terrain reasons vocabulary
1954
- * (`reasoning` / `Check` / `terms` / `form` / `origin`) — there is no
1955
- * scsr-era `template.id === definition.id` invariant; a `Template` and its
1956
- * `Definition` are simply the same authored record. `intents` lists the
1957
- * `Intent.action` values this template answers.
1958
- */
1959
- export declare interface Template {
1960
- readonly id: string;
1961
- readonly name: string;
1962
- readonly domain: string;
1963
- readonly intents: readonly string[];
1964
- readonly mappings: readonly EntityMapping[];
1965
- readonly defaults: readonly FieldDefault[];
1966
- readonly computations: readonly ComputedField[];
1967
- readonly definition: Definition;
1968
- }
1969
-
1970
- /**
1971
- * The template registry — a self-owning, versioned and content-hashed
1972
- * record-holder for the {@link Template}s an `Interpret` orchestrator matches
1973
- * against.
1974
- *
1975
- * @remarks
1976
- * `size` (never `count` — the sole tally in scope) plus the AGENTS §9.1
1977
- * singular/plural accessors (`template` / `templates`) and the §9.2 batch
1978
- * `remove` overloads. `add` derives each record's `hash` from the template's
1979
- * CONTENT (id-independent — the same template data hashes identically under
1980
- * any record id) and bumps `version` ONLY when that hash changes: an identical
1981
- * re-add keeps its version (unlike scsr, which bumped on every add). The
1982
- * batch `remove(ids)` form is ALL-OR-NOTHING — any id absent from the registry
1983
- * leaves the collection untouched and returns `false`. `destroy()` is
1984
- * idempotent; every method afterwards throws `InterpretError('DESTROYED', …)`.
1985
- *
1986
- * @example
1987
- * ```ts
1988
- * import { factorGroup, fieldFactor, quantitativeDefinition } from '@orkestrel/reason'
1989
- * import { TemplateManager } from '@src/core'
1990
- *
1991
- * const manager = new TemplateManager()
1992
- * const record = manager.add({
1993
- * id: 't1',
1994
- * name: 'Arithmetic',
1995
- * domain: 'arithmetic',
1996
- * intents: ['calculate'],
1997
- * mappings: [],
1998
- * defaults: [],
1999
- * computations: [],
2000
- * definition: quantitativeDefinition('t1', 'Arithmetic', [
2001
- * factorGroup('total', 'sum', [fieldFactor('value', 'value')]),
2002
- * ]),
2003
- * })
2004
- * record.version // 1
2005
- * manager.size // 1
2006
- * ```
2007
- */
2008
- export declare class TemplateManager implements TemplateManagerInterface {
2009
- #private;
2010
- constructor(options?: TemplateManagerOptions);
2011
- get emitter(): EmitterInterface<TemplateManagerEventMap>;
2012
- get size(): number;
2013
- has(id: string): boolean;
2014
- template(id: string): TemplateRecord | undefined;
2015
- templates(): readonly TemplateRecord[];
2016
- add(template: Template, options?: ManagerAddOptions): TemplateRecord;
2017
- remove(ids: readonly string[]): boolean;
2018
- remove(id: string): boolean;
2019
- remove(): void;
2020
- destroy(): void;
2021
- }
2022
-
2023
- /**
2024
- * The push observation surface of a {@link TemplateManagerInterface} (AGENTS
2025
- * §13) — an id-keyed registry, so `add` / `remove` are the events (never
2026
- * ordered-list `append`/`prepend`).
2027
- */
2028
- export declare type TemplateManagerEventMap = {
2029
- /** A template record was added — carries its record id. */
2030
- readonly add: readonly [id: string];
2031
- /** A template record was removed — carries its record id. */
2032
- readonly remove: readonly [id: string];
2033
- /** The manager was destroyed. */
2034
- readonly destroy: readonly [];
2035
- };
2036
-
2037
- /**
2038
- * The template registry — a self-owning, versioned/hashed record-holder
2039
- * (AGENTS §9.1 singular/plural accessors, §9.2 batch overloads).
2040
- *
2041
- * @remarks
2042
- * `size` (never `count` — this is the sole tally in scope) mirrors the
2043
- * raters `ProgramManagerInterface` registry precedent. `remove`'s batch form
2044
- * is all-or-nothing: any missing id in the list leaves the collection
2045
- * untouched and returns `false`.
2046
- */
2047
- export declare interface TemplateManagerInterface {
2048
- readonly emitter: EmitterInterface<TemplateManagerEventMap>;
2049
- readonly size: number;
2050
- has(id: string): boolean;
2051
- template(id: string): TemplateRecord | undefined;
2052
- templates(): readonly TemplateRecord[];
2053
- add(template: Template, options?: ManagerAddOptions): TemplateRecord;
2054
- remove(ids: readonly string[]): boolean;
2055
- remove(id: string): boolean;
2056
- remove(): void;
2057
- destroy(): void;
2058
- }
2059
-
2060
- /** Options for `createTemplateManager` / the `TemplateManager` constructor — the initial seed collection. */
2061
- export declare interface TemplateManagerOptions {
2062
- readonly templates?: readonly Template[];
2063
- readonly on?: EmitterHooks<TemplateManagerEventMap>;
2064
- readonly error?: EmitterErrorHandler;
2065
- }
2066
-
2067
- /**
2068
- * A versioned, content-hashed {@link Template} as held by a
2069
- * {@link TemplateManagerInterface}.
2070
- *
2071
- * @remarks
2072
- * `version` bumps only when `hash` (derived from `template`'s content, not
2073
- * `id`) actually changes — an identical re-add keeps the same version,
2074
- * unlike scsr's version-bumps-on-every-add defect.
2075
- */
2076
- export declare interface TemplateRecord {
2077
- readonly id: string;
2078
- readonly template: Template;
2079
- readonly version: number;
2080
- readonly hash: string;
2081
- }
2082
-
2083
- /** One normalization substitution applied to the raw text. */
2084
- export declare interface TextChange {
2085
- readonly from: string;
2086
- readonly to: string;
2087
- }
2088
-
2089
- /**
2090
- * Split text into lowercase tokens, stripping punctuation outside a small
2091
- * numeric/currency-safe allowlist.
2092
- *
2093
- * @remarks
2094
- * Shared by `classifyIntent` and `assignEntities` — pure ECMAScript
2095
- * (no locale-aware `Intl` segmentation), so multi-byte / astral text tokenizes
2096
- * on ASCII word boundaries only.
2097
- *
2098
- * @param text - The text to tokenize
2099
- * @returns The lowercase tokens, punctuation-stripped, empty tokens dropped
2100
- *
2101
- * @example
2102
- * ```ts
2103
- * import { tokenize } from '@src/core'
2104
- *
2105
- * tokenize('The rate is 85%.') // ['the', 'rate', 'is', '85%']
2106
- * ```
2107
- */
2108
- export declare function tokenize(text: string): readonly string[];
2109
-
2110
- /**
2111
- * Prototype-pollution-unsafe field-path segments — `setField` refuses to
2112
- * write ANY path containing one, returning its input unchanged.
2113
- */
2114
- export declare const UNSAFE_FIELD_SEGMENTS: readonly string[];
2115
-
2116
- /**
2117
- * Collect every variable name referenced by a symbolic expression tree, in
2118
- * first-occurrence order.
2119
- *
2120
- * @param expression - The expression tree to scan
2121
- * @returns The referenced variable names, deduplicated
2122
- *
2123
- * @example
2124
- * ```ts
2125
- * import { constant, operation, variable } from '@orkestrel/reason'
2126
- * import { variablesOf } from '@src/core'
2127
- *
2128
- * variablesOf(operation('divide', variable('deductible'), constant(12))) // ['deductible']
2129
- * ```
2130
- */
2131
- export declare function variablesOf(expression: SymbolicExpression): readonly string[];
2132
-
2133
- export { }