@hviana/sema 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/example/demo.js +25 -25
  2. package/dist/example/train_base.d.ts +21 -19
  3. package/dist/example/train_base.js +1952 -1684
  4. package/dist/src/alphabet.d.ts +3 -3
  5. package/dist/src/alphabet.js +27 -24
  6. package/dist/src/alu/src/alu.d.ts +194 -169
  7. package/dist/src/alu/src/alu.js +414 -374
  8. package/dist/src/alu/src/expr.d.ts +43 -36
  9. package/dist/src/alu/src/expr.js +278 -252
  10. package/dist/src/alu/src/index.d.ts +98 -9
  11. package/dist/src/alu/src/index.js +63 -9
  12. package/dist/src/alu/src/kernel-arith.d.ts +5 -1
  13. package/dist/src/alu/src/kernel-arith.js +285 -195
  14. package/dist/src/alu/src/kernel-bits.d.ts +5 -1
  15. package/dist/src/alu/src/kernel-bits.js +115 -81
  16. package/dist/src/alu/src/kernel-logic.js +63 -33
  17. package/dist/src/alu/src/kernel-nd.js +195 -155
  18. package/dist/src/alu/src/kernel-numeric.d.ts +80 -16
  19. package/dist/src/alu/src/kernel-numeric.js +331 -259
  20. package/dist/src/alu/src/operation.d.ts +132 -112
  21. package/dist/src/alu/src/operation.js +163 -149
  22. package/dist/src/alu/src/parser.d.ts +175 -168
  23. package/dist/src/alu/src/parser.js +421 -382
  24. package/dist/src/alu/src/resonance.d.ts +25 -16
  25. package/dist/src/alu/src/resonance.js +56 -49
  26. package/dist/src/alu/src/text.d.ts +7 -3
  27. package/dist/src/alu/src/text.js +37 -31
  28. package/dist/src/alu/src/value.d.ts +14 -14
  29. package/dist/src/alu/src/value.js +160 -150
  30. package/dist/src/alu/test/alu.test.js +656 -543
  31. package/dist/src/bytes.d.ts +5 -1
  32. package/dist/src/bytes.js +40 -33
  33. package/dist/src/config.d.ts +102 -102
  34. package/dist/src/config.js +83 -83
  35. package/dist/src/derive/src/deduction.d.ts +62 -60
  36. package/dist/src/derive/src/deduction.js +109 -103
  37. package/dist/src/derive/src/index.d.ts +7 -1
  38. package/dist/src/derive/src/priority-queue.d.ts +8 -8
  39. package/dist/src/derive/src/priority-queue.js +61 -57
  40. package/dist/src/derive/src/rewrite.d.ts +18 -15
  41. package/dist/src/derive/src/rewrite.js +67 -59
  42. package/dist/src/derive/src/trie.d.ts +56 -56
  43. package/dist/src/derive/src/trie.js +179 -175
  44. package/dist/src/derive/test/derive.test.js +100 -96
  45. package/dist/src/extension.d.ts +16 -13
  46. package/dist/src/geometry.d.ts +42 -17
  47. package/dist/src/geometry.js +266 -235
  48. package/dist/src/index.d.ts +17 -2
  49. package/dist/src/index.js +7 -1
  50. package/dist/src/ingest-cache.d.ts +30 -25
  51. package/dist/src/ingest-cache.js +127 -108
  52. package/dist/src/mind/articulation.d.ts +5 -1
  53. package/dist/src/mind/articulation.js +112 -77
  54. package/dist/src/mind/attention.d.ts +108 -40
  55. package/dist/src/mind/attention.js +871 -749
  56. package/dist/src/mind/canonical.d.ts +19 -4
  57. package/dist/src/mind/canonical.js +31 -27
  58. package/dist/src/mind/graph-search.d.ts +225 -201
  59. package/dist/src/mind/graph-search.js +821 -742
  60. package/dist/src/mind/index.d.ts +10 -2
  61. package/dist/src/mind/junction.d.ts +58 -31
  62. package/dist/src/mind/junction.js +237 -172
  63. package/dist/src/mind/learning.d.ts +52 -16
  64. package/dist/src/mind/learning.js +165 -143
  65. package/dist/src/mind/match.d.ts +69 -20
  66. package/dist/src/mind/match.js +318 -259
  67. package/dist/src/mind/mechanisms/alu.js +16 -16
  68. package/dist/src/mind/mechanisms/cast.d.ts +13 -9
  69. package/dist/src/mind/mechanisms/cast.js +456 -363
  70. package/dist/src/mind/mechanisms/confluence.d.ts +12 -8
  71. package/dist/src/mind/mechanisms/confluence.js +183 -152
  72. package/dist/src/mind/mechanisms/cover.d.ts +8 -2
  73. package/dist/src/mind/mechanisms/cover.js +210 -148
  74. package/dist/src/mind/mechanisms/extraction.d.ts +34 -8
  75. package/dist/src/mind/mechanisms/extraction.js +288 -234
  76. package/dist/src/mind/mechanisms/recall.d.ts +10 -6
  77. package/dist/src/mind/mechanisms/recall.js +185 -126
  78. package/dist/src/mind/mind.d.ts +154 -129
  79. package/dist/src/mind/mind.js +288 -258
  80. package/dist/src/mind/pipeline-mechanism.d.ts +124 -112
  81. package/dist/src/mind/pipeline-mechanism.js +172 -161
  82. package/dist/src/mind/pipeline.d.ts +14 -4
  83. package/dist/src/mind/pipeline.js +189 -125
  84. package/dist/src/mind/primitives.d.ts +32 -8
  85. package/dist/src/mind/primitives.js +117 -99
  86. package/dist/src/mind/rationale.d.ts +98 -86
  87. package/dist/src/mind/rationale.js +121 -113
  88. package/dist/src/mind/reasoning.d.ts +13 -2
  89. package/dist/src/mind/reasoning.js +166 -129
  90. package/dist/src/mind/recognition.d.ts +4 -1
  91. package/dist/src/mind/recognition.js +208 -183
  92. package/dist/src/mind/resonance.d.ts +22 -5
  93. package/dist/src/mind/resonance.js +0 -0
  94. package/dist/src/mind/trace.d.ts +25 -6
  95. package/dist/src/mind/trace.js +58 -50
  96. package/dist/src/mind/traverse.d.ts +58 -16
  97. package/dist/src/mind/traverse.js +357 -304
  98. package/dist/src/mind/types.d.ts +127 -120
  99. package/dist/src/mind/types.js +69 -60
  100. package/dist/src/rabitq-hnsw/src/database.d.ts +179 -177
  101. package/dist/src/rabitq-hnsw/src/database.js +359 -342
  102. package/dist/src/rabitq-hnsw/src/heap.d.ts +12 -12
  103. package/dist/src/rabitq-hnsw/src/heap.js +80 -75
  104. package/dist/src/rabitq-hnsw/src/hnsw.d.ts +103 -103
  105. package/dist/src/rabitq-hnsw/src/hnsw.js +454 -428
  106. package/dist/src/rabitq-hnsw/src/index.d.ts +6 -1
  107. package/dist/src/rabitq-hnsw/src/prng.d.ts +10 -10
  108. package/dist/src/rabitq-hnsw/src/prng.js +27 -25
  109. package/dist/src/rabitq-hnsw/src/rabitq.d.ts +69 -69
  110. package/dist/src/rabitq-hnsw/src/rabitq.js +262 -246
  111. package/dist/src/rabitq-hnsw/src/store.d.ts +132 -132
  112. package/dist/src/rabitq-hnsw/src/store.js +843 -752
  113. package/dist/src/rabitq-hnsw/test/hnsw.test.js +1054 -805
  114. package/dist/src/sema.d.ts +13 -9
  115. package/dist/src/sema.js +40 -26
  116. package/dist/src/store-sqlite.d.ts +166 -144
  117. package/dist/src/store-sqlite.js +660 -578
  118. package/dist/src/store.d.ts +630 -593
  119. package/dist/src/store.js +1581 -1440
  120. package/dist/src/vec.d.ts +9 -5
  121. package/dist/src/vec.js +73 -61
  122. package/package.json +2 -2
@@ -31,130 +31,174 @@ import { rItem, rNode, traceFail } from "../trace.js";
31
31
  * the answers' merits, decided the grounding.) Null when no skill
32
32
  * applies. */
33
33
  export async function extractBySkill(ctx, query, pre) {
34
- const t = ctx.trace?.enter("extractBySkill", [
35
- rItem(query, "query"),
36
- ]);
37
- const fail = traceFail(t);
38
- // Use climbAttentionAll to get the FULL ranked list, not just the
39
- // roots that cleared commitVotes' significance floor. The floor
40
- // gates further points of attention for fusion, but extraction only
41
- // needs ONE anchor that IS a span-shaped skill exemplar — and on
42
- // some seeds the top-voted anchor is not one (e.g. a concept-merge
43
- // nickname outvotes the painting exemplars on shared substrings,
44
- // while the exemplars' votes fall below the floor). Iterating the
45
- // ranked list instead of just the roots lets extraction reach the
46
- // first painting-exemplar anchor regardless of its floor status.
47
- //
48
- const { ranked } = await pre.attention();
49
- if (ranked.length === 0) {
50
- return fail("no consensus anchor — no skill to apply");
34
+ const t = ctx.trace?.enter("extractBySkill", [
35
+ rItem(query, "query"),
36
+ ]);
37
+ const fail = traceFail(t);
38
+ // Use climbAttentionAll to get the FULL ranked list, not just the
39
+ // roots that cleared commitVotes' significance floor. The floor
40
+ // gates further points of attention for fusion, but extraction only
41
+ // needs ONE anchor that IS a span-shaped skill exemplar — and on
42
+ // some seeds the top-voted anchor is not one (e.g. a concept-merge
43
+ // nickname outvotes the painting exemplars on shared substrings,
44
+ // while the exemplars' votes fall below the floor). Iterating the
45
+ // ranked list instead of just the roots lets extraction reach the
46
+ // first painting-exemplar anchor regardless of its floor status.
47
+ //
48
+ const { ranked } = await pre.attention();
49
+ if (ranked.length === 0) {
50
+ return fail("no consensus anchor — no skill to apply");
51
+ }
52
+ let exemplar = null;
53
+ let chosenAnchor = null;
54
+ let skipped = 0;
55
+ for (const cand of ranked) {
56
+ const ex = await pre.spanShapedOf(cand.anchor);
57
+ if (ex) {
58
+ exemplar = ex;
59
+ chosenAnchor = cand.anchor;
60
+ break;
51
61
  }
52
- let exemplar = null;
53
- let chosenAnchor = null;
54
- let skipped = 0;
55
- for (const cand of ranked) {
56
- const ex = await pre.spanShapedOf(cand.anchor);
57
- if (ex) {
58
- exemplar = ex;
59
- chosenAnchor = cand.anchor;
60
- break;
61
- }
62
- skipped++;
63
- }
64
- if (exemplar === null) {
65
- ctx.trace?.step("trySkillAnchors", [], [], `none of ${ranked.length} ranked anchor(s) is a span-shaped skill exemplar`);
66
- return fail("no consensus root is a span-shaped skill exemplar");
67
- }
68
- if (skipped > 0) {
69
- ctx.trace?.step("trySkillAnchors", [
70
- rItem(query.subarray(0, 0), `skipped ${skipped}`),
71
- rNode(ctx, chosenAnchor, "chosen"),
72
- ], [], `skipped ${skipped} anchor(s) that are not skill exemplars`);
73
- }
74
- const { contextBytes, answerBytes } = exemplar;
75
- const ansCtxRuns = answerRunsInContext(ctx, contextBytes, answerBytes);
76
- if (ansCtxRuns === null || ansCtxRuns.length === 0) {
77
- return fail("answer is not a subsequence of the context");
78
- }
79
- if (ansCtxRuns.length > 1) {
80
- ctx.trace?.step("decomposeAnswer", [rItem(answerBytes, "multi-piece-answer")], ansCtxRuns.map((r) => rItem(contextBytes.subarray(r.start, r.end), "piece", undefined, [
81
- r.start,
82
- r.end,
83
- ])), `answer splits into ${ansCtxRuns.length} piece(s) within the exemplar context`);
62
+ skipped++;
63
+ }
64
+ if (exemplar === null) {
65
+ ctx.trace?.step(
66
+ "trySkillAnchors",
67
+ [],
68
+ [],
69
+ `none of ${ranked.length} ranked anchor(s) is a span-shaped skill exemplar`,
70
+ );
71
+ return fail("no consensus root is a span-shaped skill exemplar");
72
+ }
73
+ if (skipped > 0) {
74
+ ctx.trace?.step(
75
+ "trySkillAnchors",
76
+ [
77
+ rItem(query.subarray(0, 0), `skipped ${skipped}`),
78
+ rNode(ctx, chosenAnchor, "chosen"),
79
+ ],
80
+ [],
81
+ `skipped ${skipped} anchor(s) that are not skill exemplars`,
82
+ );
83
+ }
84
+ const { contextBytes, answerBytes } = exemplar;
85
+ const ansCtxRuns = answerRunsInContext(ctx, contextBytes, answerBytes);
86
+ if (ansCtxRuns === null || ansCtxRuns.length === 0) {
87
+ return fail("answer is not a subsequence of the context");
88
+ }
89
+ if (ansCtxRuns.length > 1) {
90
+ ctx.trace?.step(
91
+ "decomposeAnswer",
92
+ [rItem(answerBytes, "multi-piece-answer")],
93
+ ansCtxRuns.map((r) =>
94
+ rItem(contextBytes.subarray(r.start, r.end), "piece", undefined, [
95
+ r.start,
96
+ r.end,
97
+ ])
98
+ ),
99
+ `answer splits into ${ansCtxRuns.length} piece(s) within the exemplar context`,
100
+ );
101
+ }
102
+ const pieces = [];
103
+ const accounted = [];
104
+ for (let ri = 0; ri < ansCtxRuns.length; ri++) {
105
+ const run = ansCtxRuns[ri];
106
+ const isLast = ri === ansCtxRuns.length - 1;
107
+ const framePreLen = Math.min(run.start, ctx.space.maxGroup);
108
+ const framePre = run.start > 0
109
+ ? contextBytes.subarray(run.start - framePreLen, run.start)
110
+ : null;
111
+ const frames = [];
112
+ let start = 0;
113
+ if (framePre) {
114
+ const prePos = locate(ctx, query, framePre, 0, pre.rec.sites);
115
+ if (prePos < 0) {
116
+ continue;
117
+ }
118
+ start = prePos + framePre.length;
119
+ frames.push([prePos, start]); // the located frame IS matched evidence
84
120
  }
85
- const pieces = [];
86
- const accounted = [];
87
- for (let ri = 0; ri < ansCtxRuns.length; ri++) {
88
- const run = ansCtxRuns[ri];
89
- const isLast = ri === ansCtxRuns.length - 1;
90
- const framePreLen = Math.min(run.start, ctx.space.maxGroup);
91
- const framePre = run.start > 0
92
- ? contextBytes.subarray(run.start - framePreLen, run.start)
93
- : null;
94
- const frames = [];
95
- let start = 0;
96
- if (framePre) {
97
- const prePos = locate(ctx, query, framePre, 0, pre.rec.sites);
98
- if (prePos < 0)
99
- continue;
100
- start = prePos + framePre.length;
101
- frames.push([prePos, start]); // the located frame IS matched evidence
121
+ let end;
122
+ if (isLast) {
123
+ if (run.end < contextBytes.length) {
124
+ const framePostLen = Math.min(
125
+ contextBytes.length - run.end,
126
+ ctx.space.maxGroup,
127
+ );
128
+ const framePost = contextBytes.subarray(
129
+ run.end,
130
+ run.end + framePostLen,
131
+ );
132
+ const postPos = locate(
133
+ ctx,
134
+ query.subarray(start),
135
+ framePost,
136
+ 0,
137
+ pre.rec.sites,
138
+ );
139
+ if (postPos < 0) {
140
+ continue;
102
141
  }
103
- let end;
104
- if (isLast) {
105
- if (run.end < contextBytes.length) {
106
- const framePostLen = Math.min(contextBytes.length - run.end, ctx.space.maxGroup);
107
- const framePost = contextBytes.subarray(run.end, run.end + framePostLen);
108
- const postPos = locate(ctx, query.subarray(start), framePost, 0, pre.rec.sites);
109
- if (postPos < 0)
110
- continue;
111
- end = start + postPos;
112
- frames.push([end, end + framePost.length]); // matched post-frame
113
- }
114
- else {
115
- end = query.length;
116
- }
117
- }
118
- else {
119
- const nextRun = ansCtxRuns[ri + 1];
120
- const nextPreLen = Math.min(nextRun.start, ctx.space.maxGroup);
121
- const nextPre = contextBytes.subarray(nextRun.start - nextPreLen, nextRun.start);
122
- const nextPos = locate(ctx, query.subarray(start), nextPre, 0, pre.rec.sites);
123
- if (nextPos < 0) {
124
- end = start + run.ansLen;
125
- }
126
- else {
127
- end = start + nextPos;
128
- frames.push([end, end + nextPre.length]); // matched next-frame
129
- }
130
- }
131
- if (start >= end)
132
- continue;
133
- pieces.push(query.subarray(start, end));
134
- accounted.push(...frames);
135
- // Bounded on both sides ⇒ the read span itself is explained (see doc).
136
- // frames carries the pre-border (when the answer is not at the context's
137
- // start) and the located right border (post-frame or next piece's
138
- // pre-frame); only when BOTH borders were located is the read bounded.
139
- const preBounded = run.start === 0 || frames.some(([, e]) => e === start);
140
- const postBounded = frames.some(([b]) => b === end);
141
- if (preBounded && postBounded)
142
- accounted.push([start, end]);
142
+ end = start + postPos;
143
+ frames.push([end, end + framePost.length]); // matched post-frame
144
+ } else {
145
+ end = query.length;
146
+ }
147
+ } else {
148
+ const nextRun = ansCtxRuns[ri + 1];
149
+ const nextPreLen = Math.min(nextRun.start, ctx.space.maxGroup);
150
+ const nextPre = contextBytes.subarray(
151
+ nextRun.start - nextPreLen,
152
+ nextRun.start,
153
+ );
154
+ const nextPos = locate(
155
+ ctx,
156
+ query.subarray(start),
157
+ nextPre,
158
+ 0,
159
+ pre.rec.sites,
160
+ );
161
+ if (nextPos < 0) {
162
+ end = start + run.ansLen;
163
+ } else {
164
+ end = start + nextPos;
165
+ frames.push([end, end + nextPre.length]); // matched next-frame
166
+ }
143
167
  }
144
- if (pieces.length === 0) {
145
- return fail("no answer piece's frame located in the query");
168
+ if (start >= end) {
169
+ continue;
146
170
  }
147
- const out = pieces.length === 1 ? pieces[0] : concatBytes(pieces);
148
- t?.done([rItem(out, "extracted")], pieces.length === 1
149
- ? `apply a learnt extraction skill read the analogous span of the query` +
150
- ` framed like "${decodeText(answerBytes)}" sits in its exemplar`
151
- : `apply a learnt MULTI-PIECE skill read ${pieces.length} analogous` +
152
- ` pieces of the query and synthesize them like "${decodeText(answerBytes)}"`);
153
- return {
154
- bytes: out,
155
- accounted,
156
- unexplained: unexplainedLabel(query, accounted),
157
- };
171
+ pieces.push(query.subarray(start, end));
172
+ accounted.push(...frames);
173
+ // Bounded on both sides the read span itself is explained (see doc).
174
+ // frames carries the pre-border (when the answer is not at the context's
175
+ // start) and the located right border (post-frame or next piece's
176
+ // pre-frame); only when BOTH borders were located is the read bounded.
177
+ const preBounded = run.start === 0 || frames.some(([, e]) => e === start);
178
+ const postBounded = frames.some(([b]) => b === end);
179
+ if (preBounded && postBounded) {
180
+ accounted.push([start, end]);
181
+ }
182
+ }
183
+ if (pieces.length === 0) {
184
+ return fail("no answer piece's frame located in the query");
185
+ }
186
+ const out = pieces.length === 1 ? pieces[0] : concatBytes(pieces);
187
+ t?.done(
188
+ [rItem(out, "extracted")],
189
+ pieces.length === 1
190
+ ? `apply a learnt extraction skill — read the analogous span of the query` +
191
+ ` framed like "${decodeText(answerBytes)}" sits in its exemplar`
192
+ : `apply a learnt MULTI-PIECE skill — read ${pieces.length} analogous` +
193
+ ` pieces of the query and synthesize them like "${
194
+ decodeText(answerBytes)
195
+ }"`,
196
+ );
197
+ return {
198
+ bytes: out,
199
+ accounted,
200
+ unexplained: unexplainedLabel(query, accounted),
201
+ };
158
202
  }
159
203
  // ── The two span-shape readings: OPEN acceptance vs. STRONG decomposition ──
160
204
  //
@@ -181,52 +225,55 @@ export async function extractBySkill(ctx, query, pre) {
181
225
  * when no greedy longest-run decomposition exists. Adjacent runs that
182
226
  * connect contiguously are merged. */
183
227
  export function answerRunsInContext(_ctx, context, answer) {
184
- const pos = indexOf(context, answer, 0);
185
- if (pos >= 0) {
186
- return [{ start: pos, end: pos + answer.length, ansLen: answer.length }];
187
- }
188
- const runs = [];
189
- let ai = 0;
190
- let ci = 0;
191
- while (ai < answer.length) {
192
- // Longest match of the remaining answer at any position of the remaining
193
- // context: one direct extend per context position — O(|ctx|·match) per
194
- // run, replacing the previous longest-first indexOf countdown whose
195
- // repeated scans were cubic on long sparse-subsequence answers.
196
- let bestLen = 0;
197
- let bestPos = -1;
198
- for (let p = ci; p < context.length; p++) {
199
- let l = 0;
200
- const maxL = Math.min(context.length - p, answer.length - ai);
201
- if (maxL <= bestLen)
202
- break; // no later position can beat the best
203
- while (l < maxL && context[p + l] === answer[ai + l])
204
- l++;
205
- if (l > bestLen) {
206
- bestLen = l;
207
- bestPos = p;
208
- if (ai + l === answer.length)
209
- break; // the whole remainder matched
210
- }
228
+ const pos = indexOf(context, answer, 0);
229
+ if (pos >= 0) {
230
+ return [{ start: pos, end: pos + answer.length, ansLen: answer.length }];
231
+ }
232
+ const runs = [];
233
+ let ai = 0;
234
+ let ci = 0;
235
+ while (ai < answer.length) {
236
+ // Longest match of the remaining answer at any position of the remaining
237
+ // context: one direct extend per context position — O(|ctx|·match) per
238
+ // run, replacing the previous longest-first indexOf countdown whose
239
+ // repeated scans were cubic on long sparse-subsequence answers.
240
+ let bestLen = 0;
241
+ let bestPos = -1;
242
+ for (let p = ci; p < context.length; p++) {
243
+ let l = 0;
244
+ const maxL = Math.min(context.length - p, answer.length - ai);
245
+ if (maxL <= bestLen) {
246
+ break; // no later position can beat the best
247
+ }
248
+ while (l < maxL && context[p + l] === answer[ai + l]) {
249
+ l++;
250
+ }
251
+ if (l > bestLen) {
252
+ bestLen = l;
253
+ bestPos = p;
254
+ if (ai + l === answer.length) {
255
+ break; // the whole remainder matched
211
256
  }
212
- if (bestLen === 0)
213
- return null;
214
- runs.push({ start: bestPos, end: bestPos + bestLen, ansLen: bestLen });
215
- ai += bestLen;
216
- ci = bestPos + bestLen;
257
+ }
217
258
  }
218
- const merged = [];
219
- for (const r of runs) {
220
- const last = merged[merged.length - 1];
221
- if (last && r.start === last.end) {
222
- last.end = r.end;
223
- last.ansLen += r.ansLen;
224
- }
225
- else {
226
- merged.push({ ...r });
227
- }
259
+ if (bestLen === 0) {
260
+ return null;
261
+ }
262
+ runs.push({ start: bestPos, end: bestPos + bestLen, ansLen: bestLen });
263
+ ai += bestLen;
264
+ ci = bestPos + bestLen;
265
+ }
266
+ const merged = [];
267
+ for (const r of runs) {
268
+ const last = merged[merged.length - 1];
269
+ if (last && r.start === last.end) {
270
+ last.end = r.end;
271
+ last.ansLen += r.ansLen;
272
+ } else {
273
+ merged.push({ ...r });
228
274
  }
229
- return merged.length > 0 ? merged : null;
275
+ }
276
+ return merged.length > 0 ? merged : null;
230
277
  }
231
278
  /** Check whether an anchor is a span-shaped skill exemplar: it represents a
232
279
  * fact whose context and answer together form a span-in-context pattern.
@@ -234,41 +281,42 @@ export function answerRunsInContext(_ctx, context, answer) {
234
281
  * itself is the context. Otherwise the anchor's prevOf parents provide
235
282
  * candidate contexts, and the longest one whose span is span-shaped wins. */
236
283
  export async function skillExemplar(ctx, anchor, guide) {
237
- if (ctx.store.hasNext(anchor)) {
238
- const contextBytes = read(ctx, anchor);
239
- const answerBytes = await follow(ctx, anchor, guide);
240
- if (answerBytes !== null && isSpanShaped(ctx, contextBytes, answerBytes)) {
241
- return { contextBytes, answerBytes };
242
- }
243
- return null;
284
+ if (ctx.store.hasNext(anchor)) {
285
+ const contextBytes = read(ctx, anchor);
286
+ const answerBytes = await follow(ctx, anchor, guide);
287
+ if (answerBytes !== null && isSpanShaped(ctx, contextBytes, answerBytes)) {
288
+ return { contextBytes, answerBytes };
244
289
  }
245
- const answerBytes = read(ctx, anchor);
246
- // Candidate contexts, capped at the hub bound (a common answer's reverse
247
- // fan-in is corpus-sized).
248
- const capped = ctx.store.prevFirst(anchor, hubBound(ctx));
249
- const spanShaped = [];
250
- for (const p of capped) {
251
- const ctxB = read(ctx, p);
252
- if (ctxB.length > 0 && isSpanShaped(ctx, ctxB, answerBytes)) {
253
- spanShaped.push({ id: p, bytes: ctxB });
254
- }
290
+ return null;
291
+ }
292
+ const answerBytes = read(ctx, anchor);
293
+ // Candidate contexts, capped at the hub bound (a common answer's reverse
294
+ // fan-in is corpus-sized).
295
+ const capped = ctx.store.prevFirst(anchor, hubBound(ctx));
296
+ const spanShaped = [];
297
+ for (const p of capped) {
298
+ const ctxB = read(ctx, p);
299
+ if (ctxB.length > 0 && isSpanShaped(ctx, ctxB, answerBytes)) {
300
+ spanShaped.push({ id: p, bytes: ctxB });
255
301
  }
256
- if (spanShaped.length === 0)
257
- return null;
258
- // Among span-shaped contexts, the longest wins (the smallest spanning frame
259
- // heuristic's dual: more frame to locate in the query); the query gist,
260
- // when given, breaks LENGTH TIES via chooseAmong the same reverse-regime
261
- // disambiguator every context pick uses, whose gist cache spares the
262
- // re-fold this block once paid per tied candidate. Same strict first-seen
263
- // tie-break as the hand loop it replaces.
264
- const maxLen = Math.max(...spanShaped.map((s) => s.bytes.length));
265
- const longest = spanShaped.filter((s) => s.bytes.length === maxLen);
266
- let contextBytes = longest[0].bytes;
267
- if (guide && longest.length > 1) {
268
- const pick = chooseAmong(ctx, longest.map((s) => s.id), guide).id;
269
- contextBytes = longest.find((s) => s.id === pick).bytes;
270
- }
271
- return { contextBytes, answerBytes };
302
+ }
303
+ if (spanShaped.length === 0) {
304
+ return null;
305
+ }
306
+ // Among span-shaped contexts, the longest wins (the smallest spanning frame
307
+ // heuristic's dual: more frame to locate in the query); the query gist,
308
+ // when given, breaks LENGTH TIES via chooseAmong the same reverse-regime
309
+ // disambiguator every context pick uses, whose gist cache spares the
310
+ // re-fold this block once paid per tied candidate. Same strict first-seen
311
+ // tie-break as the hand loop it replaces.
312
+ const maxLen = Math.max(...spanShaped.map((s) => s.bytes.length));
313
+ const longest = spanShaped.filter((s) => s.bytes.length === maxLen);
314
+ let contextBytes = longest[0].bytes;
315
+ if (guide && longest.length > 1) {
316
+ const pick = chooseAmong(ctx, longest.map((s) => s.id), guide).id;
317
+ contextBytes = longest.find((s) => s.id === pick).bytes;
318
+ }
319
+ return { contextBytes, answerBytes };
272
320
  }
273
321
  /** Whether the answer is a SPARSE subsequence of the context (bytes in
274
322
  * order, arbitrary gaps) — the OPEN span-shape reading (see the section
@@ -285,12 +333,13 @@ export async function skillExemplar(ctx, anchor, guide) {
285
333
  * (a full river fold) per CANDIDATE in skillExemplar's √N-capped loop —
286
334
  * pure cost, no discrimination. */
287
335
  export function isSpanShaped(_ctx, context, answer) {
288
- let ai = 0;
289
- for (let ci = 0; ci < context.length && ai < answer.length; ci++) {
290
- if (context[ci] === answer[ai])
291
- ai++;
336
+ let ai = 0;
337
+ for (let ci = 0; ci < context.length && ai < answer.length; ci++) {
338
+ if (context[ci] === answer[ai]) {
339
+ ai++;
292
340
  }
293
- return ai === answer.length;
341
+ }
342
+ return ai === answer.length;
294
343
  }
295
344
  /** STRICT containment: the answer's resolved node appears in the context's
296
345
  * folded tree, or the answer occurs as one CONTIGUOUS byte run of the
@@ -301,42 +350,47 @@ export function isSpanShaped(_ctx, context, answer) {
301
350
  * c…o…l…d in order), and gating fusion on it silently starved multi-topic
302
351
  * queries of their further points of attention. */
303
352
  export function containsSpan(ctx, context, answer) {
304
- const ansId = resolve(ctx, answer);
305
- if (ansId !== null) {
306
- let found = false;
307
- foldTree(ctx, perceive(ctx, context), 0, (_n, _s, _e, node) => {
308
- if (node === ansId)
309
- found = true;
310
- });
311
- if (found)
312
- return true;
353
+ const ansId = resolve(ctx, answer);
354
+ if (ansId !== null) {
355
+ let found = false;
356
+ foldTree(ctx, perceive(ctx, context), 0, (_n, _s, _e, node) => {
357
+ if (node === ansId) {
358
+ found = true;
359
+ }
360
+ });
361
+ if (found) {
362
+ return true;
313
363
  }
314
- return indexOf(context, answer, 0) >= 0;
364
+ }
365
+ return indexOf(context, answer, 0) >= 0;
315
366
  }
316
367
  // ── Pipeline mechanism ──────────────────────────────────────────────────────
317
368
  export const extractionMechanism = {
318
- name: "extraction",
319
- provenance: "extract",
320
- async floor(_ctx, _query, pre, worthRunning) {
321
- // Extraction's floor is always exactly CONCEPT+STEP when it exists —
322
- // same investment discipline as CAST's (see cast.ts): when the bound
323
- // already cannot beat the incumbent, return it UNINVESTED (never
324
- // first-touch the climb just to be pruned).
325
- if (!worthRunning(CONCEPT + STEP))
326
- return CONCEPT + STEP;
327
- if ((await pre.attention()).ranked.length === 0)
328
- return null;
329
- return CONCEPT + STEP;
330
- },
331
- async run(ctx, query, pre) {
332
- const ex = await extractBySkill(ctx, query, pre);
333
- if (!ex)
334
- return [];
335
- return [{
336
- bytes: ex.bytes,
337
- accounted: ex.accounted,
338
- moves: CONCEPT + STEP * ex.accounted.length,
339
- unexplained: ex.unexplained,
340
- }];
341
- },
369
+ name: "extraction",
370
+ provenance: "extract",
371
+ async floor(_ctx, _query, pre, worthRunning) {
372
+ // Extraction's floor is always exactly CONCEPT+STEP when it exists —
373
+ // same investment discipline as CAST's (see cast.ts): when the bound
374
+ // already cannot beat the incumbent, return it UNINVESTED (never
375
+ // first-touch the climb just to be pruned).
376
+ if (!worthRunning(CONCEPT + STEP)) {
377
+ return CONCEPT + STEP;
378
+ }
379
+ if ((await pre.attention()).ranked.length === 0) {
380
+ return null;
381
+ }
382
+ return CONCEPT + STEP;
383
+ },
384
+ async run(ctx, query, pre) {
385
+ const ex = await extractBySkill(ctx, query, pre);
386
+ if (!ex) {
387
+ return [];
388
+ }
389
+ return [{
390
+ bytes: ex.bytes,
391
+ accounted: ex.accounted,
392
+ moves: CONCEPT + STEP * ex.accounted.length,
393
+ unexplained: ex.unexplained,
394
+ }];
395
+ },
342
396
  };
@@ -2,12 +2,16 @@ import type { MindContext } from "../types.js";
2
2
  import type { PipelineMechanism, Precomputed } from "../pipeline-mechanism.js";
3
3
  /** A recall result. */
4
4
  export interface RecallResult {
5
- bytes: Uint8Array;
6
- echoed: boolean;
7
- accounted: Array<[number, number]>;
8
- moves: number;
9
- unexplained: string;
5
+ bytes: Uint8Array;
6
+ echoed: boolean;
7
+ accounted: Array<[number, number]>;
8
+ moves: number;
9
+ unexplained: string;
10
10
  }
11
11
  /** Recall the answer by resonating the whole query against the content index. */
12
- export declare function recallByResonance(ctx: MindContext, query: Uint8Array, pre: Precomputed): Promise<RecallResult | null>;
12
+ export declare function recallByResonance(
13
+ ctx: MindContext,
14
+ query: Uint8Array,
15
+ pre: Precomputed,
16
+ ): Promise<RecallResult | null>;
13
17
  export declare const recallMechanism: PipelineMechanism;