@hviana/sema 0.4.2 → 0.4.4

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.
Files changed (135) hide show
  1. package/dist/example/demo.d.ts +1 -0
  2. package/dist/example/demo.js +39 -0
  3. package/dist/example/train_base.d.ts +87 -0
  4. package/dist/example/train_base.js +2252 -0
  5. package/dist/src/alphabet.d.ts +7 -0
  6. package/dist/src/alphabet.js +33 -0
  7. package/dist/src/alu/src/alu.d.ts +185 -0
  8. package/dist/src/alu/src/alu.js +440 -0
  9. package/dist/src/alu/src/expr.d.ts +61 -0
  10. package/dist/src/alu/src/expr.js +318 -0
  11. package/dist/src/alu/src/index.d.ts +11 -0
  12. package/dist/src/alu/src/index.js +19 -0
  13. package/dist/src/alu/src/kernel-arith.d.ts +16 -0
  14. package/dist/src/alu/src/kernel-arith.js +264 -0
  15. package/dist/src/alu/src/kernel-bits.d.ts +19 -0
  16. package/dist/src/alu/src/kernel-bits.js +152 -0
  17. package/dist/src/alu/src/kernel-logic.d.ts +4 -0
  18. package/dist/src/alu/src/kernel-logic.js +60 -0
  19. package/dist/src/alu/src/kernel-nd.d.ts +3 -0
  20. package/dist/src/alu/src/kernel-nd.js +208 -0
  21. package/dist/src/alu/src/kernel-numeric.d.ts +54 -0
  22. package/dist/src/alu/src/kernel-numeric.js +366 -0
  23. package/dist/src/alu/src/operation.d.ts +168 -0
  24. package/dist/src/alu/src/operation.js +189 -0
  25. package/dist/src/alu/src/parser.d.ts +221 -0
  26. package/dist/src/alu/src/parser.js +577 -0
  27. package/dist/src/alu/src/resonance.d.ts +55 -0
  28. package/dist/src/alu/src/resonance.js +126 -0
  29. package/dist/src/alu/src/text.d.ts +31 -0
  30. package/dist/src/alu/src/text.js +73 -0
  31. package/dist/src/alu/src/value.d.ts +109 -0
  32. package/dist/src/alu/src/value.js +300 -0
  33. package/dist/src/alu/test/alu.test.d.ts +1 -0
  34. package/dist/src/alu/test/alu.test.js +764 -0
  35. package/dist/src/bytes.d.ts +14 -0
  36. package/dist/src/bytes.js +59 -0
  37. package/dist/src/canon.d.ts +26 -0
  38. package/dist/src/canon.js +57 -0
  39. package/dist/src/config.d.ts +111 -0
  40. package/dist/src/config.js +91 -0
  41. package/dist/src/derive/src/deduction.d.ts +136 -0
  42. package/dist/src/derive/src/deduction.js +159 -0
  43. package/dist/src/derive/src/index.d.ts +8 -0
  44. package/dist/src/derive/src/index.js +11 -0
  45. package/dist/src/derive/src/priority-queue.d.ts +20 -0
  46. package/dist/src/derive/src/priority-queue.js +73 -0
  47. package/dist/src/derive/src/rewrite.d.ts +56 -0
  48. package/dist/src/derive/src/rewrite.js +100 -0
  49. package/dist/src/derive/src/trie.d.ts +90 -0
  50. package/dist/src/derive/src/trie.js +217 -0
  51. package/dist/src/derive/test/derive.test.d.ts +1 -0
  52. package/dist/src/derive/test/derive.test.js +122 -0
  53. package/dist/src/extension.d.ts +37 -0
  54. package/dist/src/extension.js +7 -0
  55. package/dist/src/geometry.d.ts +175 -0
  56. package/dist/src/geometry.js +823 -0
  57. package/dist/src/index.d.ts +17 -0
  58. package/dist/src/index.js +19 -0
  59. package/dist/src/ingest-cache.d.ts +41 -0
  60. package/dist/src/ingest-cache.js +165 -0
  61. package/dist/src/meter.d.ts +176 -0
  62. package/dist/src/meter.js +274 -0
  63. package/dist/src/mind/articulation.d.ts +6 -0
  64. package/dist/src/mind/articulation.js +99 -0
  65. package/dist/src/mind/attention.d.ts +414 -0
  66. package/dist/src/mind/attention.js +2082 -0
  67. package/dist/src/mind/bridge.d.ts +39 -0
  68. package/dist/src/mind/bridge.js +972 -0
  69. package/dist/src/mind/canonical.d.ts +34 -0
  70. package/dist/src/mind/canonical.js +93 -0
  71. package/dist/src/mind/graph-search.d.ts +294 -0
  72. package/dist/src/mind/graph-search.js +996 -0
  73. package/dist/src/mind/index.d.ts +9 -0
  74. package/dist/src/mind/index.js +5 -0
  75. package/dist/src/mind/junction.d.ts +137 -0
  76. package/dist/src/mind/junction.js +342 -0
  77. package/dist/src/mind/learning.d.ts +75 -0
  78. package/dist/src/mind/learning.js +270 -0
  79. package/dist/src/mind/match.d.ts +181 -0
  80. package/dist/src/mind/match.js +655 -0
  81. package/dist/src/mind/mechanisms/alu.d.ts +4 -0
  82. package/dist/src/mind/mechanisms/alu.js +36 -0
  83. package/dist/src/mind/mechanisms/cast.d.ts +89 -0
  84. package/dist/src/mind/mechanisms/cast.js +784 -0
  85. package/dist/src/mind/mechanisms/confluence.d.ts +24 -0
  86. package/dist/src/mind/mechanisms/confluence.js +255 -0
  87. package/dist/src/mind/mechanisms/cover.d.ts +6 -0
  88. package/dist/src/mind/mechanisms/cover.js +227 -0
  89. package/dist/src/mind/mechanisms/extraction.d.ts +33 -0
  90. package/dist/src/mind/mechanisms/extraction.js +300 -0
  91. package/dist/src/mind/mechanisms/recall.d.ts +16 -0
  92. package/dist/src/mind/mechanisms/recall.js +364 -0
  93. package/dist/src/mind/mind.d.ts +337 -0
  94. package/dist/src/mind/mind.js +626 -0
  95. package/dist/src/mind/pipeline-mechanism.d.ts +172 -0
  96. package/dist/src/mind/pipeline-mechanism.js +465 -0
  97. package/dist/src/mind/pipeline.d.ts +49 -0
  98. package/dist/src/mind/pipeline.js +275 -0
  99. package/dist/src/mind/primitives.d.ts +66 -0
  100. package/dist/src/mind/primitives.js +306 -0
  101. package/dist/src/mind/rationale.d.ts +139 -0
  102. package/dist/src/mind/rationale.js +163 -0
  103. package/dist/src/mind/reasoning.d.ts +40 -0
  104. package/dist/src/mind/reasoning.js +280 -0
  105. package/dist/src/mind/recognition.d.ts +20 -0
  106. package/dist/src/mind/recognition.js +504 -0
  107. package/dist/src/mind/resonance.d.ts +23 -0
  108. package/dist/src/mind/resonance.js +0 -0
  109. package/dist/src/mind/trace.d.ts +15 -0
  110. package/dist/src/mind/trace.js +73 -0
  111. package/dist/src/mind/traverse.d.ts +126 -0
  112. package/dist/src/mind/traverse.js +650 -0
  113. package/dist/src/mind/types.d.ts +333 -0
  114. package/dist/src/mind/types.js +130 -0
  115. package/dist/src/rabitq-ivf/src/database.d.ts +113 -0
  116. package/dist/src/rabitq-ivf/src/database.js +201 -0
  117. package/dist/src/rabitq-ivf/src/index.d.ts +7 -0
  118. package/dist/src/rabitq-ivf/src/index.js +4 -0
  119. package/dist/src/rabitq-ivf/src/ivf.d.ts +200 -0
  120. package/dist/src/rabitq-ivf/src/ivf.js +1165 -0
  121. package/dist/src/rabitq-ivf/src/prng.d.ts +19 -0
  122. package/dist/src/rabitq-ivf/src/prng.js +36 -0
  123. package/dist/src/rabitq-ivf/src/rabitq.d.ts +95 -0
  124. package/dist/src/rabitq-ivf/src/rabitq.js +283 -0
  125. package/dist/src/sema.d.ts +31 -0
  126. package/dist/src/sema.js +63 -0
  127. package/dist/src/store-sqlite.d.ts +184 -0
  128. package/dist/src/store-sqlite.js +942 -0
  129. package/dist/src/store.d.ts +678 -0
  130. package/dist/src/store.js +1703 -0
  131. package/dist/src/vec.d.ts +31 -0
  132. package/dist/src/vec.js +109 -0
  133. package/package.json +1 -1
  134. package/src/mind/bridge.ts +55 -18
  135. package/src/mind/mind.ts +11 -2
@@ -0,0 +1,280 @@
1
+ // reasoning.ts — multi-hop reasoning + multi-topic fusion (Section 4 of the mind).
2
+ //
3
+ // reason — extend an answer forward across facts (multi-hop)
4
+ // fuseAttention — fuse independent points of attention (multi-topic)
5
+ import { rItem, rNode } from "./trace.js";
6
+ import { bytesEqual, indexOf } from "../bytes.js";
7
+ import { resolve } from "./primitives.js";
8
+ import { hubBound } from "./traverse.js";
9
+ import { follow, haloSiblings, project } from "./match.js";
10
+ import { joinWithBridge, pivotInto } from "./resonance.js";
11
+ /** Whether `bytes` is a proper byte-subspan of `query` — already present in
12
+ * the question, so voicing it back only restates part of what was asked,
13
+ * never answers it. The exact guard recallByResonance already applies to
14
+ * its OWN grounding candidates (tier 1's `restates`, tier 2's subspan
15
+ * check, tier 0b's argument-binding subspan check) — every mechanism that
16
+ * walks a LEARNT CONTINUATION EDGE past an already-vetted grounding
17
+ * (reason()'s own hops below, and CAST's `projectCounterfactual` seat
18
+ * substitution — see cast.ts) needs the same guard applied to what the
19
+ * walk turns up, since `follow()`/`chooseNext`/`pivotInto` know nothing of
20
+ * the query at all — only of what structurally continues what. */
21
+ export function restatesQuery(query, bytes) {
22
+ return bytes.length < query.length && indexOf(query, bytes, 0) >= 0;
23
+ }
24
+ /** Extend a grounded answer forward across facts (multi-hop reasoning).
25
+ * Pivots on the longest unconsumed learnt context each answer contains,
26
+ * then follows the pivot's continuation to the next fact. Repeats up
27
+ * to `cfg.recallQueryK` hops. `preConsumed` carries node ids already
28
+ * spoken for by the grounding stage (cover/extract/CAST). `pre` is the
29
+ * response's shared pre-computation — the post-grounding stages read the
30
+ * same container the mechanisms did. */
31
+ export async function reason(ctx, query, answer, preConsumed, pre) {
32
+ // Echo guard: a query that is ITSELF a learnt continuation (some context's
33
+ // answer) is being asked back at the system — hopping forward from it would
34
+ // chain through the very fact that produced it and echo the conversation
35
+ // back. The grounded answer alone is the honest read-out. Deliberately a
36
+ // broad structural gate; pinned by test/31-audit.
37
+ const qId = pre.queryResolved;
38
+ if (qId !== null && ctx.store.prevCount(qId) > 0)
39
+ return answer;
40
+ const consumed = new Set();
41
+ // Consume a node and its neighbours for pivot-cycle prevention — CAPPED at
42
+ // the hub bound, via the store's LIMITed edge reads: a common continuation's
43
+ // reverse fan-in (and a hub context's forward fan-out) is corpus-sized, and
44
+ // no per-hop operation may grow with the corpus. The cap follows the one
45
+ // convention every fan-out decision uses (first √N in the relation's own
46
+ // read order); a pivot suppressed only by a beyond-cap neighbour may now
47
+ // fire — the same visibility trade chooseNext documents.
48
+ const bound = hubBound(ctx);
49
+ const consumeNode = (id) => {
50
+ if (id === null)
51
+ return;
52
+ consumed.add(id);
53
+ for (const p of ctx.store.prevFirst(id, bound))
54
+ consumed.add(p);
55
+ };
56
+ const consumeAll = (id) => {
57
+ if (id === null)
58
+ return;
59
+ consumeNode(id);
60
+ for (const n of ctx.store.nextFirst(id, bound))
61
+ consumed.add(n);
62
+ };
63
+ // Pre-consume whatever the grounding stage already spoke for. The halo
64
+ // sweep is one ANN query per node — cap it at haloQueryK sweeps (cover
65
+ // grounding can pre-consume one node per recognised site, O(query length));
66
+ // nodes past the cap are still consumed directly, they just skip the
67
+ // synonym expansion.
68
+ const preconsume = async () => {
69
+ let haloSweeps = 0;
70
+ for (const id of preConsumed) {
71
+ consumeNode(id);
72
+ if (haloSweeps >= ctx.cfg.haloQueryK)
73
+ continue;
74
+ const h = ctx.store.halo(id);
75
+ if (!h)
76
+ continue;
77
+ haloSweeps++;
78
+ for (const sib of await haloSiblings(ctx, id, h))
79
+ consumeNode(sib.id);
80
+ }
81
+ };
82
+ if (ctx.meter) {
83
+ await ctx.meter.time("reason.preconsumeHalos", preconsume);
84
+ }
85
+ else {
86
+ await preconsume();
87
+ }
88
+ let cur = answer;
89
+ const qv = pre.guide; // the response-wide guide IS the query's gist
90
+ let t;
91
+ const startedFrom = answer;
92
+ for (let hop = 0; hop < ctx.cfg.recallQueryK; hop++) {
93
+ const curId = resolve(ctx, cur);
94
+ consumeNode(curId);
95
+ // Forward-absorb: follow only UNCONSUMED continuations. The gate below
96
+ // checks an unconsumed edge EXISTS, but follow()'s chooseNext knows
97
+ // nothing of `consumed` and may still walk to a consumed fixpoint —
98
+ // absorbing it would repeat content the grounding stage already spoke
99
+ // for, so a consumed fixpoint falls through to the pivot step instead.
100
+ if (curId !== null &&
101
+ ctx.store.nextFirst(curId, bound).some((n) => !consumed.has(n))) {
102
+ const fwd = await follow(ctx, curId, qv);
103
+ const fwdId = fwd !== null ? resolve(ctx, fwd) : null;
104
+ if (fwd !== null && !bytesEqual(fwd, cur) &&
105
+ (fwdId === null || !consumed.has(fwdId)) &&
106
+ !restatesQuery(query, fwd)) {
107
+ consumeAll(curId);
108
+ t ??= ctx.trace?.enter("reason", [
109
+ rItem(startedFrom, "grounded"),
110
+ ]);
111
+ ctx.trace?.step("absorbForward", [rItem(cur, "answer", curId)], [rItem(fwd, "answer", resolve(ctx, fwd) ?? undefined)], "the answer is itself a learnt fact — follow its continuation to the fixpoint");
112
+ cur = fwd;
113
+ continue;
114
+ }
115
+ }
116
+ // Pivot: find the longest unconsumed learnt context the answer contains.
117
+ consumeAll(curId);
118
+ const pivot = await pivotInto(ctx, cur, consumed);
119
+ if (pivot === null)
120
+ break;
121
+ const fc = await follow(ctx, pivot, qv);
122
+ consumeAll(pivot);
123
+ if (fc === null || bytesEqual(fc, cur) || restatesQuery(query, fc))
124
+ break;
125
+ t ??= ctx.trace?.enter("reason", [rItem(startedFrom, "grounded")]);
126
+ ctx.trace?.step("pivotStep", [rItem(cur, "answer"), rNode(ctx, pivot, "pivot")], [rItem(fc, "answer", resolve(ctx, fc) ?? undefined)], "pivot on the shared span this answer contains, then step forward across that fact");
127
+ cur = fc;
128
+ }
129
+ t?.done([rItem(cur, "answer", resolve(ctx, cur) ?? undefined)], "the multi-hop chain's fixpoint");
130
+ return cur;
131
+ }
132
+ /** Fuse independent points of attention into one answer (multi-topic).
133
+ * When the consensus climb finds more than one dominant point, each
134
+ * independent point grounds its own answer; they are bridged together
135
+ * by any learnt connector the graph holds between them. */
136
+ export async function fuseAttention(ctx, query, primary, pre,
137
+ /** True when `primary` never touched the consensus climb at all — e.g. a
138
+ * pure ALU computation, which has no anchor of its own. commitVotes
139
+ * ALWAYS admits the dominant root regardless of its vote (attention.ts:
140
+ * "roots.length === 0 || …") on the assumption a lone root already IS
141
+ * primary's own source; that assumption is exactly backwards when
142
+ * primary is unclimbed. Absent or false preserves the original
143
+ * behaviour exactly. */
144
+ unclimbed = false,
145
+ /** The query spans `primary`'s own grounding stands on — used ONLY to place
146
+ * primary in the fused reading order (see below). Resolved by the caller,
147
+ * which is the layer that knows how a given grounding records its evidence;
148
+ * fuseAttention just reads a position from it. Empty or absent preserves
149
+ * the original behaviour exactly. */
150
+ primarySpans = []) {
151
+ // When the answer is structurally drawn from the query itself
152
+ // (extraction), it already spans all the query's pieces — fusion
153
+ // would only add noise from unrelated stored contexts. The gate is
154
+ // STRICT containment (resolved node in the query's tree, or a contiguous
155
+ // byte run): the old sparse-subsequence test was trivially satisfied by
156
+ // short answers over long queries, silently starving multi-topic queries
157
+ // of fusion.
158
+ if (containsSpan(ctx, query, primary))
159
+ return primary;
160
+ // The committed points of attention ARE the shared climb's roots (same
161
+ // query, same k, same DF mode) — read them from Precomputed instead of
162
+ // re-climbing, so even a traced response pays for the climb once.
163
+ const forest = (await pre.attention()).roots;
164
+ // A LONE root is ordinarily primary's own source — nothing to fuse. But
165
+ // when primary is unclimbed, the lone root was never checked against
166
+ // anything: it is admitted by commitVotes unconditionally, so it may be
167
+ // genuine consensus (Attention.breadth dominates — most of the query's
168
+ // OWN regions corroborate it) or a coincidental echo (breadth does not
169
+ // dominate — see test/35-attention-confidence). breadth is the SCALE-
170
+ // INVARIANT read of exactly this question: the raw IDF vote cannot serve
171
+ // here, since it is an absolute ln(N)-scaled quantity (a genuine root on
172
+ // a large store can score BELOW its own floor while a coincidental echo
173
+ // on a small one scores comfortably above its own, smaller, floor).
174
+ const lonePromotes = unclimbed && forest.length === 1 &&
175
+ forest[0].breadth > 0.5;
176
+ if (forest.length === 0 || (forest.length <= 1 && !lonePromotes)) {
177
+ return primary;
178
+ }
179
+ // WHERE THE QUERY ASKED FOR IT. The sort below orders the fused pieces by
180
+ // query position, which is the whole point of the `start` field: a
181
+ // multi-topic answer should read in the order the question posed its
182
+ // topics. Every ROOT carries its own start. `primary` did not — it was
183
+ // given forest[0].start, the FIRST attention root's position, which is
184
+ // primary's own source only when primary happens to come from that root.
185
+ // When it does not, primary is sorted to a position it never occupied.
186
+ //
187
+ // Observed live: "What is the capital of France? And what is 2 + 2?"
188
+ // answered "4The capital city of France is Paris." — the ALU result, whose
189
+ // evidence is the "2 + 2" span near the END of the query, inherited the
190
+ // France root's start of 0 and sorted ahead of the France answer. Both
191
+ // pieces were right; only the order was.
192
+ //
193
+ // primary's own position is the earliest query byte its grounding stands
194
+ // on. `accounted` is the cost-ladder read of that and is authoritative
195
+ // when non-empty; when it is empty the grounding is a pure COMPUTATION,
196
+ // whose evidence is its computed span — the same cost-ladder-vs-coverage
197
+ // distinction think() already draws for the fusion remainder ("`accounted`
198
+ // alone undercounts this ... cover prices its computed spans at near-zero
199
+ // and deliberately leaves them out"), read here for position instead of
200
+ // for coverage. With neither, nothing is known and the old behaviour
201
+ // (forest[0].start) stands.
202
+ const primaryStart = primarySpans.length > 0
203
+ ? primarySpans.reduce((m, [s]) => Math.min(m, s), Infinity)
204
+ : forest[0].start;
205
+ const pieces = [
206
+ { start: primaryStart, bytes: primary },
207
+ ];
208
+ const qv = pre.guide; // once, not per root
209
+ const rest = lonePromotes ? forest : forest.slice(1);
210
+ const t = ctx.trace?.enter("fuseAttention", [
211
+ rItem(primary, "primary"),
212
+ ...rest.map((r) => rNode(ctx, r.anchor, "point", r.vote)),
213
+ ]);
214
+ for (const root of rest) {
215
+ // DISPERSION: this root's contributing regions are confined to a
216
+ // single cluster (see Attention.clusters) — one local neighbourhood of
217
+ // the query, not several separate places. Raw region count already
218
+ // failed to discriminate a coincidental match from a genuine further
219
+ // topic (test/24 gap 3.1 vs test/35's echo); dispersion is a different
220
+ // question — not how MUCH evidence, but how many separate PLACES in the
221
+ // query corroborate it — and a coincidental match is structurally
222
+ // confined to one cluster no matter how strongly it resonates.
223
+ //
224
+ // EXCEPTION: crossRegionVotes' own joint conclusions (a query naming
225
+ // two attributes that were only ever learnt TOGETHER — test/34's own
226
+ // binding corpus) are inherently ONE fused context and are pooled from
227
+ // a single synthetic region, so they always read as one cluster even
228
+ // though they already, by construction, account for both original
229
+ // mentions. `breadth` (dominates — the same > half-the-query bar used
230
+ // everywhere else) still correctly recognises these: a genuine joint
231
+ // binding explains the MAJORITY of the query's regions on its own,
232
+ // which a coincidental echo never does (verified: test/35's echo tops
233
+ // out at 0.40). So a root is trusted when EITHER measure alone
234
+ // indicates real signal — excluded only when BOTH are weak. Cheap and
235
+ // synchronous — checked before the async already-answered walk below.
236
+ if (root.clusters < 2 && root.breadth <= 0.5) {
237
+ ctx.trace?.step("singleCluster", [rNode(ctx, root.anchor, "point", root.vote)], [], "this point's evidence is confined to one local neighbourhood of the query — not trusted as an independent topic");
238
+ continue;
239
+ }
240
+ // ALREADY ANSWERED: this root's own learnt continuation — the same
241
+ // content-addressed walk reason()'s echo guard already trusts
242
+ // (`ctx.store.prevCount(qId) > 0`), here applied per-candidate instead
243
+ // of to the whole query — is VERBATIM present later in the query. A
244
+ // query that embeds both an exchange's ask and its own already-given
245
+ // reply (a conversation's turn plus its own prior answer, concatenated
246
+ // raw by addTurn — or any caller pasting a transcript into one
247
+ // respond() call; the check is Mind-bookkeeping-free, so it treats both
248
+ // identically) has already spoken this root's answer — fusing it in
249
+ // again would only restate it. Deliberately NOT a magnitude measure:
250
+ // it fires on exact content-addressed recurrence, not on how strongly
251
+ // the root resonates.
252
+ const cont = await follow(ctx, root.anchor, qv);
253
+ if (cont !== null && cont.length > 0 && indexOf(query, cont, root.end) >= 0) {
254
+ ctx.trace?.step("alreadyAnswered", [rNode(ctx, root.anchor, "point", root.vote)], [rItem(cont, "continuation")], "this point's own learnt continuation already appears later in the query — already answered, not fused");
255
+ continue;
256
+ }
257
+ const g = await project(ctx, root.anchor, qv);
258
+ if (g === null || g.length === 0)
259
+ continue;
260
+ if (pieces.some((p) => indexOf(p.bytes, g, 0) >= 0))
261
+ continue;
262
+ pieces.push({ start: root.start, bytes: g });
263
+ }
264
+ if (pieces.length === 1) {
265
+ t?.done([rItem(primary, "answer")], "no further independent point grounded");
266
+ return primary;
267
+ }
268
+ pieces.sort((a, b) => a.start - b.start);
269
+ let out = pieces[0].bytes;
270
+ for (let i = 1; i < pieces.length; i++) {
271
+ // An approximate-resonance miss (or a genuinely unlearnt junction) joins
272
+ // the pieces bare — joinWithBridge surfaces it as a bridgeMiss step.
273
+ out = await joinWithBridge(ctx, out, pieces[i].bytes);
274
+ }
275
+ t?.done([rItem(out, "answer", resolve(ctx, out) ?? undefined)], `fused ${pieces.length} independent points of attention into one answer`);
276
+ return out;
277
+ }
278
+ // (resonance.js is already a static dependency above — `bridge` — so the old
279
+ // dynamic import of pivotInto guarded against a cycle that does not exist.)
280
+ import { containsSpan } from "./match.js";
@@ -0,0 +1,20 @@
1
+ import type { MindContext, Recognition, Segment } from "./types.js";
2
+ /** Decompose a byte stream into every stored form that leads somewhere
3
+ * (has a continuation edge or a halo). Two complementary readings:
4
+ *
5
+ * • structural — walk the query's own perceived tree, naming each subtree
6
+ * by findLeaf at the leaves and findBranch above. Catches every form
7
+ * aligned to the query's segmentation.
8
+ *
9
+ * • canonical — re-derive the store's segmentation directly: at each byte,
10
+ * the longest known leaf, chained into flat branches. Names forms the
11
+ * query's own cut cannot, and records sub-leaf boundaries as `splits`.
12
+ *
13
+ * Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus. */
14
+ export declare function recognise(ctx: MindContext, bytes: Uint8Array): Recognition;
15
+ /** Segment bytes using the geometry's own groupings — leaf-parent
16
+ * nodes from the perceived tree, with consecutive bare leaves merged
17
+ * into one segment. Each segment's gist is perceived from its bytes
18
+ * IN ISOLATION, so the same content has the same gist regardless of
19
+ * where it appears. */
20
+ export declare function segment(ctx: MindContext, bytes: Uint8Array): Segment[];