@hviana/sema 0.6.0 → 0.7.2
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.
- package/.github/workflows/release.yml +80 -0
- package/AGENTS.md +53 -9
- package/HOW_IT_WORKS.md +17 -16
- package/dist/src/meter.d.ts +14 -4
- package/dist/src/meter.js +27 -3
- package/dist/src/mind/attention.js +22 -20
- package/dist/src/mind/graph-search.d.ts +43 -9
- package/dist/src/mind/graph-search.js +82 -15
- package/dist/src/mind/junction.d.ts +13 -0
- package/dist/src/mind/junction.js +26 -1
- package/dist/src/mind/mechanisms/cover.js +23 -2
- package/dist/src/mind/mechanisms/prefix-completion.d.ts +2 -1
- package/dist/src/mind/mechanisms/prefix-completion.js +40 -20
- package/dist/src/mind/mechanisms/recall.js +8 -4
- package/dist/src/mind/pipeline-mechanism.d.ts +0 -24
- package/dist/src/mind/pipeline-mechanism.js +13 -36
- package/dist/src/mind/pipeline.d.ts +24 -0
- package/dist/src/mind/pipeline.js +71 -5
- package/dist/src/mind/recognition.d.ts +15 -1
- package/dist/src/mind/recognition.js +15 -1
- package/dist/src/mind/resonance.js +54 -12
- package/dist/src/store.js +22 -1
- package/jsr.json +1 -1
- package/package.json +7 -2
- package/src/meter.ts +27 -4
- package/src/mind/attention.ts +22 -19
- package/src/mind/graph-search.ts +93 -16
- package/src/mind/junction.ts +25 -1
- package/src/mind/mechanisms/cover.ts +23 -4
- package/src/mind/mechanisms/prefix-completion.ts +40 -20
- package/src/mind/mechanisms/recall.ts +8 -4
- package/src/mind/pipeline-mechanism.ts +13 -42
- package/src/mind/pipeline.ts +106 -5
- package/src/mind/recognition.ts +19 -2
- package/src/mind/resonance.ts +84 -49
- package/src/store.ts +21 -1
- package/test/89-completion-recursion.test.mjs +230 -0
- package/test/90-connector-read-cap.test.mjs +130 -0
- package/test/91-branch-bytes-cache.test.mjs +152 -0
- package/test/93-regime-prediction.test.mjs +148 -0
- package/test/94-cross-region-budget.test.mjs +67 -0
- package/test/95-wide-resonance-removed.test.mjs +109 -0
|
@@ -96,6 +96,19 @@ export declare function cachedRead(ctx: MindContext, cache: WalkCache | null, id
|
|
|
96
96
|
* edgeAncestors' question and wrong for this one: a junction container
|
|
97
97
|
* is legitimately reached across many containing structures. Half the
|
|
98
98
|
* successful junctions would be lost.
|
|
99
|
+
*
|
|
100
|
+
* REFUTED EARLY-STOP (side-cone exhaustion, §2.17's "real saturation"):
|
|
101
|
+
* stopping the walk the moment ONE side's upward cone is emptied is wrong,
|
|
102
|
+
* in both a hub-guarded form and a hub-flagged form. The junction test is
|
|
103
|
+
* a BYTE containment over the UNION of the two cones, and a junction can be
|
|
104
|
+
* structurally reachable from only ONE side — the side whose seed is a
|
|
105
|
+
* FOLD sub-node of the container (test/16: "cold or hot" is reached from
|
|
106
|
+
* the window "cold", but the 3-byte answer "hot" is not a 4-byte window of
|
|
107
|
+
* it, so "hot"'s cone is empty while the junction still lies ahead in
|
|
108
|
+
* "cold"'s cone; test/34's n-ary binding fails the hub-guarded form the
|
|
109
|
+
* same way). "One cone exhausted" therefore never proves "no junction
|
|
110
|
+
* left", and the walk must keep the √N·W budget as its NET after the
|
|
111
|
+
* per-node saturations below.
|
|
99
112
|
* • per-node hub guards — parent fan-outs beyond √N are hubs (not
|
|
100
113
|
* expanded); each node contributes at most one √N page of containers;
|
|
101
114
|
* √N collected candidates decide. */
|
|
@@ -134,6 +134,19 @@ function cachedContainers(ctx, cache, id, limit) {
|
|
|
134
134
|
* edgeAncestors' question and wrong for this one: a junction container
|
|
135
135
|
* is legitimately reached across many containing structures. Half the
|
|
136
136
|
* successful junctions would be lost.
|
|
137
|
+
*
|
|
138
|
+
* REFUTED EARLY-STOP (side-cone exhaustion, §2.17's "real saturation"):
|
|
139
|
+
* stopping the walk the moment ONE side's upward cone is emptied is wrong,
|
|
140
|
+
* in both a hub-guarded form and a hub-flagged form. The junction test is
|
|
141
|
+
* a BYTE containment over the UNION of the two cones, and a junction can be
|
|
142
|
+
* structurally reachable from only ONE side — the side whose seed is a
|
|
143
|
+
* FOLD sub-node of the container (test/16: "cold or hot" is reached from
|
|
144
|
+
* the window "cold", but the 3-byte answer "hot" is not a 4-byte window of
|
|
145
|
+
* it, so "hot"'s cone is empty while the junction still lies ahead in
|
|
146
|
+
* "cold"'s cone; test/34's n-ary binding fails the hub-guarded form the
|
|
147
|
+
* same way). "One cone exhausted" therefore never proves "no junction
|
|
148
|
+
* left", and the walk must keep the √N·W budget as its NET after the
|
|
149
|
+
* per-node saturations below.
|
|
137
150
|
* • per-node hub guards — parent fan-outs beyond √N are hubs (not
|
|
138
151
|
* expanded); each node contributes at most one √N page of containers;
|
|
139
152
|
* √N collected candidates decide. */
|
|
@@ -172,7 +185,19 @@ unordered = false) {
|
|
|
172
185
|
id,
|
|
173
186
|
d: 0,
|
|
174
187
|
}));
|
|
175
|
-
while (stack.length > 0 && out.length < bound
|
|
188
|
+
while (stack.length > 0 && out.length < bound) {
|
|
189
|
+
// BUDGET EXHAUSTION IS AN ABSTENTION, AND IT MUST BE VISIBLE (§2.13). The
|
|
190
|
+
// walk stops with work still on the stack, the caller reads "no container"
|
|
191
|
+
// and falls through to a lower ladder rung — indistinguishable, from the
|
|
192
|
+
// outside, from a walk that looked everywhere and found nothing. With a
|
|
193
|
+
// SHARED budget (cross-region's one k·W allowance per tier) an EARLIER
|
|
194
|
+
// pair can drain it, so a later pair's exact tier may never run at all;
|
|
195
|
+
// this counter is the only thing that says so.
|
|
196
|
+
if (b.n-- <= 0) {
|
|
197
|
+
if (ctx.meter)
|
|
198
|
+
ctx.meter.junctionBudgetExhausted++;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
176
201
|
const { id: x, d } = stack.pop();
|
|
177
202
|
if (ctx.meter)
|
|
178
203
|
ctx.meter.junctionPops++;
|
|
@@ -42,7 +42,9 @@ export async function resolveConnectors(ctx, sites, query) {
|
|
|
42
42
|
// transcript evidence: cover still needs the site for structural context,
|
|
43
43
|
// but liftAnswer will trim that continuation as already answered. Building
|
|
44
44
|
// pairwise/n-ary bridges for it can only create connectors that are later
|
|
45
|
-
// discarded
|
|
45
|
+
// discarded — a semantically neutral gate (it removes work whose product
|
|
46
|
+
// liftAnswer throws away), and a cumulative (multi-turn) query is exactly
|
|
47
|
+
// where such already-answered continuations recur.
|
|
46
48
|
let answered = 0;
|
|
47
49
|
const ordered = [...sites]
|
|
48
50
|
.sort((a, b) => a.start - b.start)
|
|
@@ -56,7 +58,26 @@ export async function resolveConnectors(ctx, sites, query) {
|
|
|
56
58
|
if (query === undefined || ctx.answeredSpans.length === 0)
|
|
57
59
|
return true;
|
|
58
60
|
const continuations = ctx.store.nextFirst(s.payload, hubBound(ctx));
|
|
59
|
-
return !continuations.some((answer) =>
|
|
61
|
+
return !continuations.some((answer) => {
|
|
62
|
+
// PREFIX-CAPPED (AGENTS §2.8): a candidate longer than the query cannot
|
|
63
|
+
// occur INSIDE it, so read one byte past the query's length — enough to
|
|
64
|
+
// detect the overflow — and reject without reconstructing the rest.
|
|
65
|
+
// The `+ 1` is what makes the test exact rather than a truncation: a
|
|
66
|
+
// result of exactly `query.length + 1` bytes is known to be too long,
|
|
67
|
+
// and anything shorter is the candidate's COMPLETE content, so the
|
|
68
|
+
// substring test below is the same test as before. (The same overflow
|
|
69
|
+
// probe bridge.ts:256 already uses.)
|
|
70
|
+
//
|
|
71
|
+
// This loop runs up to hubBound(ctx) = √N reads PER SITE, and only on a
|
|
72
|
+
// multi-turn response — `answeredSpans` is empty for a plain respond(),
|
|
73
|
+
// so the probe does not execute there. The cap cannot reduce the read
|
|
74
|
+
// COUNT — only a semantic change to the "already answered" test could —
|
|
75
|
+
// but it bounds each read by the query instead of by the corpus, which
|
|
76
|
+
// is what §2.8 asks for and what rescues a SHORT query: at 3 bytes this
|
|
77
|
+
// reads 4 bytes per candidate instead of the ~231 it averaged before.
|
|
78
|
+
const bytes = read(ctx, answer, query.length + 1);
|
|
79
|
+
return bytes.length <= query.length && indexOf(query, bytes, 0) >= 0;
|
|
80
|
+
});
|
|
60
81
|
});
|
|
61
82
|
const bridgePair = async (l, r) => {
|
|
62
83
|
if (l === r || links.has(l + "," + r))
|
|
@@ -16,7 +16,8 @@ export interface PrefixCompletion {
|
|
|
16
16
|
* it, when the continuation is sub-quantum, when a candidate's continuation
|
|
17
17
|
* cannot be read through, or when the candidates disagree.
|
|
18
18
|
*
|
|
19
|
-
* `ranked` must be a list the caller has ALREADY fetched
|
|
19
|
+
* `ranked` must be a list the caller has ALREADY fetched (the write side's
|
|
20
|
+
* window index, or the response's memoised top-k); this mechanism never
|
|
20
21
|
* resonates on its own (see the header's cost note). */
|
|
21
22
|
export declare function prefixCompletion(ctx: MindContext, query: Uint8Array, ranked: ReadonlyArray<number>): PrefixCompletion | null;
|
|
22
23
|
export declare const prefixMechanism: PipelineMechanism;
|
|
@@ -45,15 +45,23 @@
|
|
|
45
45
|
// from `resonate(k)` at k = 24, 256 AND 2048 — while forms scoring LOWER
|
|
46
46
|
// (Germany 0.5670, Yemen 0.5591) are returned. `k` only reorders WITHIN
|
|
47
47
|
// the IVF clusters already probed, exactly as Store.resonate's doc warns,
|
|
48
|
-
// so no k recovers it.
|
|
48
|
+
// so no k recovers it.
|
|
49
49
|
//
|
|
50
|
-
// So this is a RETRIEVABILITY gap, not a semantic one, and
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
50
|
+
// So this is a RETRIEVABILITY gap, not a semantic one, and the ANN is the wrong
|
|
51
|
+
// instrument for it: a proper prefix's gist cannot rank its own continuation.
|
|
52
|
+
// The repair is CONTENT-ADDRESSED (§2.3) — `formsOpenedBy` (traverse.ts) reads
|
|
53
|
+
// the leaf-id WINDOW index the write side already maintains and answers "which
|
|
54
|
+
// trained forms does this byte run open?" in a bounded √N walk. That is this
|
|
55
|
+
// mechanism's first supply. The response's memoised top-k `resonance()` is the
|
|
56
|
+
// second, for prefixes long enough that the gist still ranks the form; it is
|
|
57
|
+
// read, never re-issued.
|
|
58
|
+
//
|
|
59
|
+
// AN EXHAUSTIVE ANN LIST IS NOT A SUPPLY HERE, AND WAS REMOVED. This tier once
|
|
60
|
+
// read `Precomputed.wideResonance()` — a full-index `resonate(guide, √N,
|
|
61
|
+
// exhaustive)` — on the argument that the target "ranks 8 with `exhaustive`".
|
|
62
|
+
// It bought an O(k) need at O(index) cost (measured: 244K annVectorReads per
|
|
63
|
+
// refusing query, ~1.5 s) for candidates the window index proposes directly.
|
|
64
|
+
// See pipeline-mechanism.ts's REMOVED note; test/95 pins its absence.
|
|
57
65
|
//
|
|
58
66
|
// THREE GUARDS, each falsified into existence by measurement — do not drop any:
|
|
59
67
|
//
|
|
@@ -97,7 +105,8 @@ import { STEP } from "../graph-search.js";
|
|
|
97
105
|
* it, when the continuation is sub-quantum, when a candidate's continuation
|
|
98
106
|
* cannot be read through, or when the candidates disagree.
|
|
99
107
|
*
|
|
100
|
-
* `ranked` must be a list the caller has ALREADY fetched
|
|
108
|
+
* `ranked` must be a list the caller has ALREADY fetched (the write side's
|
|
109
|
+
* window index, or the response's memoised top-k); this mechanism never
|
|
101
110
|
* resonates on its own (see the header's cost note). */
|
|
102
111
|
export function prefixCompletion(ctx, query, ranked) {
|
|
103
112
|
const W = ctx.space.maxGroup;
|
|
@@ -204,9 +213,9 @@ export const prefixMechanism = {
|
|
|
204
213
|
provenance: "prefix",
|
|
205
214
|
async floor(ctx, query, _pre, worthRunning) {
|
|
206
215
|
// One projection: the form is voiced whole, nothing is substituted.
|
|
207
|
-
// INVESTMENT DISCIPLINE — the supplies below are
|
|
208
|
-
//
|
|
209
|
-
// bound can still beat the incumbent.
|
|
216
|
+
// INVESTMENT DISCIPLINE — the supplies below are a bounded √N window walk
|
|
217
|
+
// and the response's memoised top-k resonance read, so neither is touched
|
|
218
|
+
// until the bound can still beat the incumbent.
|
|
210
219
|
if (!worthRunning(STEP))
|
|
211
220
|
return STEP;
|
|
212
221
|
// A query with no room for a perceivable continuation inside the phrase
|
|
@@ -218,14 +227,25 @@ export const prefixMechanism = {
|
|
|
218
227
|
return STEP;
|
|
219
228
|
},
|
|
220
229
|
async run(ctx, query, pre) {
|
|
221
|
-
//
|
|
222
|
-
// the
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
// guards
|
|
227
|
-
|
|
228
|
-
|
|
230
|
+
// ONE SUPPLY PASS, not a two-tier `??`. The window index (exact,
|
|
231
|
+
// content-addressed) and the response's memoised top-k (approximate) are
|
|
232
|
+
// concatenated and the three guards decide ONCE over the union. A
|
|
233
|
+
// first-then-fallback chain would let the APPROXIMATE tier override the
|
|
234
|
+
// EXACT one (§2.3): when formsOpenedBy finds two continuations, guard 3
|
|
235
|
+
// returns null and the fallback re-runs the guards on resonance's top-k
|
|
236
|
+
// alone — which, seeing only one of the two forms, would voice it. That is
|
|
237
|
+
// precisely the disagreement-suppression guard 3 exists to prevent, and it
|
|
238
|
+
// is the exact tier's ambiguity being washed away by the approximate tier.
|
|
239
|
+
// Evaluating the union means a disagreement the window index saw can never
|
|
240
|
+
// be hidden by what the ANN happens to rank. The ANN read is the
|
|
241
|
+
// response's ONE memoised top-k (§2.11), already paid by recall's refusal
|
|
242
|
+
// path on the queries where this mechanism fires, so reading it here is not
|
|
243
|
+
// a second index scan.
|
|
244
|
+
const ids = [
|
|
245
|
+
...formsOpenedBy(ctx, query),
|
|
246
|
+
...(await pre.resonance()).map((h) => h.id),
|
|
247
|
+
];
|
|
248
|
+
const completed = prefixCompletion(ctx, query, ids);
|
|
229
249
|
if (completed === null)
|
|
230
250
|
return [];
|
|
231
251
|
return [{
|
|
@@ -306,10 +306,14 @@ export async function recallByResonance(ctx, query, pre) {
|
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
// 3b. Corroborated-substitution bridge — refusal-path only (bridge.ts).
|
|
309
|
-
// The
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
|
|
309
|
+
// The bridge's proposal source is the response's ONE top-k read — the same
|
|
310
|
+
// list recall already ranked above — never an exhaustive √N scan. The
|
|
311
|
+
// bridge's own candidate cap is 2·recallQueryK, so top-k proposals are
|
|
312
|
+
// exactly the budget it can consume, and every proposal is byte-verified
|
|
313
|
+
// downstream (§2.3). Reuse the memoised `resonance()`; scanning every IVF
|
|
314
|
+
// cluster here once made every honest refusal cost hundreds of ms regardless
|
|
315
|
+
// of k.
|
|
316
|
+
const wideIds = async () => (await pre.resonance()).map((h) => h.id);
|
|
313
317
|
// Every gist-based tier has failed; before refusing, align the query
|
|
314
318
|
// byte-for-byte against the trained contexts its own stored windows
|
|
315
319
|
// anchor, accepting mismatches only as corpus-attested, concept-bar
|
|
@@ -63,30 +63,6 @@ export declare class Precomputed {
|
|
|
63
63
|
* duplication a profile shows as doubled `annVectorReads` with nothing to
|
|
64
64
|
* account for it. Cached BY PROMISE, so a second caller awaits the first. */
|
|
65
65
|
resonance(): Promise<ReadonlyArray<Hit>>;
|
|
66
|
-
private _wide?;
|
|
67
|
-
/** The response's WIDE candidate list — the top-k when the query's gist has
|
|
68
|
-
* no concept-level match anywhere, and an exhaustive √N read when it does.
|
|
69
|
-
*
|
|
70
|
-
* Every mechanism that has to look PAST the top-k reads this one list: the
|
|
71
|
-
* substitution bridge, prefix completion and the frame filler all did, and
|
|
72
|
-
* it was memoised inside recall for exactly that reason (measured: 490 ms
|
|
73
|
-
* median re-issued against 13 ms non-exhaustive, 36x). A memo inside one
|
|
74
|
-
* mechanism only serves that mechanism's own tiers, so it lives here now —
|
|
75
|
-
* the same move `resonance` made for the top-k.
|
|
76
|
-
*
|
|
77
|
-
* THE CONDITION IS THE TOP HIT'S SCORE, NOT THE CORPUS SIZE. When nothing
|
|
78
|
-
* ranks at concept level, an exhaustive ANN only scores more vectors below
|
|
79
|
-
* the bar (profiled at 38K–40K annVectorReads per refusing query on a 325K-
|
|
80
|
-
* context store); the structural channels — junction walks, anchor climbs,
|
|
81
|
-
* the write side's window index — are the correct proposal source there,
|
|
82
|
-
* because the ANN cannot propose what the gist cannot rank. This was once
|
|
83
|
-
* spelled `corpusN(ctx) <= (k · W)³`, which asks a different question and
|
|
84
|
-
* answers it wrongly at exactly the scale it was written from: at N =
|
|
85
|
-
* 325,608 with k = 24 and W = 4 the cube is 884,736, so that store took the
|
|
86
|
-
* exhaustive branch — the very branch measured above. Measured cost of the
|
|
87
|
-
* mismatch: substitutionBridge 8,544 ms of a 19,548 ms think (44%), against
|
|
88
|
-
* 1,248 ms and 14,218 ms without it, every answer byte-identical. */
|
|
89
|
-
wideResonance(): Promise<ReadonlyArray<number>>;
|
|
90
66
|
private _frames?;
|
|
91
67
|
/** THE FRAME INVENTORY — every ranked candidate that reads as an instance of
|
|
92
68
|
* the same frame as the query, each with the query spans it leaves VARIABLE
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
// 4. TRAVELING EVIDENCE — run() returns MechanismResult with accounted, moves,
|
|
14
14
|
// and unexplained. The pipeline computes the weight.
|
|
15
15
|
import { indexOf } from "../bytes.js";
|
|
16
|
-
import {
|
|
16
|
+
import { dominates } from "../geometry.js";
|
|
17
17
|
import { windowIds } from "./canonical.js";
|
|
18
18
|
import { read, resolve } from "./primitives.js";
|
|
19
19
|
import { alignGraded, frameSlots, skillExemplar, } from "./match.js";
|
|
20
20
|
import { climbAttentionAll } from "./attention.js";
|
|
21
|
-
import {
|
|
21
|
+
import { sharedReachMemo } from "./traverse.js";
|
|
22
22
|
// ── Precomputed ──────────────────────────────────────────────────────────────
|
|
23
23
|
//
|
|
24
24
|
// Precomputed is a LAZY container for structural analyses of the query — the
|
|
@@ -128,40 +128,17 @@ export class Precomputed {
|
|
|
128
128
|
resonance() {
|
|
129
129
|
return this._resonance ??= this.shared("resonance", () => this.ctx.store.resonate(this.guide, this.k));
|
|
130
130
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
* THE CONDITION IS THE TOP HIT'S SCORE, NOT THE CORPUS SIZE. When nothing
|
|
143
|
-
* ranks at concept level, an exhaustive ANN only scores more vectors below
|
|
144
|
-
* the bar (profiled at 38K–40K annVectorReads per refusing query on a 325K-
|
|
145
|
-
* context store); the structural channels — junction walks, anchor climbs,
|
|
146
|
-
* the write side's window index — are the correct proposal source there,
|
|
147
|
-
* because the ANN cannot propose what the gist cannot rank. This was once
|
|
148
|
-
* spelled `corpusN(ctx) <= (k · W)³`, which asks a different question and
|
|
149
|
-
* answers it wrongly at exactly the scale it was written from: at N =
|
|
150
|
-
* 325,608 with k = 24 and W = 4 the cube is 884,736, so that store took the
|
|
151
|
-
* exhaustive branch — the very branch measured above. Measured cost of the
|
|
152
|
-
* mismatch: substitutionBridge 8,544 ms of a 19,548 ms think (44%), against
|
|
153
|
-
* 1,248 ms and 14,218 ms without it, every answer byte-identical. */
|
|
154
|
-
wideResonance() {
|
|
155
|
-
return this._wide ??= this.shared("wideResonance", async () => {
|
|
156
|
-
const hits = await this.resonance();
|
|
157
|
-
if (hits.length > 0 &&
|
|
158
|
-
hits[0].score >= conceptThreshold(this.ctx.store.D)) {
|
|
159
|
-
const exhaustive = await this.ctx.store.resonate(this.guide, hubBound(this.ctx), true);
|
|
160
|
-
return exhaustive.map((h) => h.id);
|
|
161
|
-
}
|
|
162
|
-
return hits.map((h) => h.id);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
131
|
+
// REMOVED — the WIDE exhaustive-√N resonance list (`wideResonance`). It ran
|
|
132
|
+
// `resonate(guide, √N, exhaustive=true)` whenever the top hit cleared
|
|
133
|
+
// conceptThreshold, so consumers could look "past the top-k". Every consumer
|
|
134
|
+
// only ever needed ≤ 2·recallQueryK proposals (the substitution bridge's own
|
|
135
|
+
// candidate cap) or a content-addressed answer (prefix completion's
|
|
136
|
+
// formsOpenedBy), and every proposal is byte-verified downstream (§2.3), so
|
|
137
|
+
// the exhaustive scan bought recall at O(index) cost for an O(k) need —
|
|
138
|
+
// measured: 244K annVectorReads per refusing query, ~1.5 s, every answer
|
|
139
|
+
// byte-identical to a top-k read. The two consumers now read `resonance()`
|
|
140
|
+
// (the one top-k read) and the write side's window index respectively — see
|
|
141
|
+
// recall.ts and prefix-completion.ts.
|
|
165
142
|
_frames;
|
|
166
143
|
/** THE FRAME INVENTORY — every ranked candidate that reads as an instance of
|
|
167
144
|
* the same frame as the query, each with the query spans it leaves VARIABLE
|
|
@@ -37,6 +37,30 @@ export interface NarrowDecisionData {
|
|
|
37
37
|
version: 1;
|
|
38
38
|
margin: number;
|
|
39
39
|
}
|
|
40
|
+
/** Structured payload of the "regimePrediction" rationale step — the R8
|
|
41
|
+
* observation exposed as data. After the first mechanism (cover, which §2.6
|
|
42
|
+
* runs first) grounds or abstains, the market's whole outcome is already
|
|
43
|
+
* determined by the one cost ladder: the consensus climb runs exactly when
|
|
44
|
+
* `worthRunning(2 * STEP)` is true — CAST (floor 2·STEP) is the cheapest
|
|
45
|
+
* mechanism that first-touches it, and confluence (3·STEP) / extraction
|
|
46
|
+
* (CONCEPT+STEP) are only reached after CAST is. An incumbent at or below
|
|
47
|
+
* that floor prunes CAST and, with it, the climb (retrieval); anything above
|
|
48
|
+
* — or no incumbent — runs the full market and the climb (composition).
|
|
49
|
+
* Purely observational; never read by inference. */
|
|
50
|
+
export interface RegimePredictionData {
|
|
51
|
+
version: 1;
|
|
52
|
+
/** retrieval | composition — the two regimes R1 measured as a ~100× cost
|
|
53
|
+
* step. */
|
|
54
|
+
regime: "retrieval" | "composition";
|
|
55
|
+
/** The incumbent's grade once the first mechanism's turn is over (it ran, or
|
|
56
|
+
* it was skipped), or null when nothing has grounded — `best === null`,
|
|
57
|
+
* which is composition with no incumbent. */
|
|
58
|
+
incumbentGrade: number | null;
|
|
59
|
+
/** The cheapest composition floor in grade units (`grade(2 * STEP)` = 2,
|
|
60
|
+
* CAST's floor) — the bar the incumbent must sit at or below for the
|
|
61
|
+
* consensus climb to be skipped. */
|
|
62
|
+
climbFloorGrade: number;
|
|
63
|
+
}
|
|
40
64
|
/** Think: a single lightest-derivation exploration of the Sema graph.
|
|
41
65
|
*
|
|
42
66
|
* Every answer travels the same path:
|
|
@@ -92,9 +92,21 @@ export async function think(ctx, query, mechs) {
|
|
|
92
92
|
};
|
|
93
93
|
// ── Pre-computation ──────────────────────────────────────────────────
|
|
94
94
|
const mechanisms = mechs ?? defaultMechanisms;
|
|
95
|
-
const
|
|
95
|
+
const meter = ctx.meter;
|
|
96
|
+
// recognition is a shared analysis (§2.14 contract 5): it does the query's
|
|
97
|
+
// own store work (perceive → foldTree → resolve), which used to land in
|
|
98
|
+
// `think` and in nothing narrower — the meter's one accounting surface must
|
|
99
|
+
// charge it to itself, exactly as attention/weave/resonance are charged.
|
|
100
|
+
// SYNCHRONOUS phase: recognition is on the sync side of §2.10's seam, so it
|
|
101
|
+
// is timed with `timeSync` — wrapping it in a promise would make a profiled
|
|
102
|
+
// response await where an unprofiled one does not.
|
|
103
|
+
const rec = meter
|
|
104
|
+
? meter.timeSync("recognise", () => recognise(ctx, query))
|
|
105
|
+
: recognise(ctx, query);
|
|
96
106
|
// Phase 1: collect computed spans from mechanisms that implement parse()
|
|
97
|
-
const computed =
|
|
107
|
+
const computed = meter
|
|
108
|
+
? await meter.time("collectComputed", () => collectComputed(ctx, mechanisms, query))
|
|
109
|
+
: await collectComputed(ctx, mechanisms, query);
|
|
98
110
|
if (computed.length > 0) {
|
|
99
111
|
ctx.trace?.step("computeExtensions", [rItem(query, "query")], computed.map((u) => rItem(query.subarray(u.i, u.j), "operand", undefined, [u.i, u.j])), `extensions recognised and evaluated ${computed.length} computation(s)`);
|
|
100
112
|
for (const u of computed) {
|
|
@@ -107,6 +119,9 @@ export async function think(ctx, query, mechs) {
|
|
|
107
119
|
// method on Precomputed, first-touched by whichever mechanism's floor
|
|
108
120
|
// survives its cheap gates and the worthRunning check. A query no
|
|
109
121
|
// mechanism climbs for (e.g. one an extension decided) never climbs.
|
|
122
|
+
// NOT phased: the constructor itself is trivial (it only derives `k`), so a
|
|
123
|
+
// phase here would add a zero-work entry to every profiled report — the meter
|
|
124
|
+
// attributes WORK (§2.14); the trace already represents structure.
|
|
110
125
|
const pre = new Precomputed(ctx, query, rec, computed, ctx._edgeGuide);
|
|
111
126
|
const grade = (w) => Math.floor(w / STEP);
|
|
112
127
|
const unaccounted = (spans) => unexplainedSpans(query.length, spans)
|
|
@@ -151,12 +166,59 @@ export async function think(ctx, query, mechs) {
|
|
|
151
166
|
best = c;
|
|
152
167
|
};
|
|
153
168
|
const worthRunning = (floor) => best === null || grade(floor) < grade(best.weight);
|
|
169
|
+
// REGIME PREDICTION (R8) — observational only. Once the FIRST mechanism has
|
|
170
|
+
// had its turn (cover, which §2.6 places first and floors at 0), the market's
|
|
171
|
+
// outcome is already determined by the one cost ladder: the consensus climb
|
|
172
|
+
// runs exactly when `worthRunning(2 * STEP)` is true — CAST (floor 2·STEP) is
|
|
173
|
+
// the cheapest mechanism that first-touches it, so an incumbent at or below
|
|
174
|
+
// grade 2 prunes CAST and, with it, confluence (3·STEP) and extraction
|
|
175
|
+
// (CONCEPT+STEP) (retrieval); anything above — or no incumbent — runs the
|
|
176
|
+
// full market and the climb (composition). The predicate is `worthRunning`,
|
|
177
|
+
// the same function the loop itself uses — nothing is computed here that the
|
|
178
|
+
// engine had not already computed, and nothing is read back by inference.
|
|
179
|
+
//
|
|
180
|
+
// EMITTED BEFORE THE SECOND MECHANISM'S FLOOR, never after some mechanism's
|
|
181
|
+
// run: a "prediction" published after the fact could assert "the climb will
|
|
182
|
+
// not run" about a climb that already ran — which is what happens whenever
|
|
183
|
+
// the first mechanism is SKIPPED (null floor or pruned) and the block sits at
|
|
184
|
+
// the end of the first mechanism that actually ran. Emitting on entry to
|
|
185
|
+
// iteration 1 makes the claim true by construction, whatever the first
|
|
186
|
+
// mechanism did, and keeps the payload identical on the ordinary path (the
|
|
187
|
+
// incumbent cannot change between the two positions).
|
|
188
|
+
let regimeReported = false;
|
|
189
|
+
const reportRegime = () => {
|
|
190
|
+
if (regimeReported)
|
|
191
|
+
return;
|
|
192
|
+
regimeReported = true;
|
|
193
|
+
const climbFloorGrade = grade(2 * STEP);
|
|
194
|
+
// TS narrows `best` to null in the outer flow (it cannot see the closure
|
|
195
|
+
// assignments in `consider`) — cast back, the same read-back as `decided`
|
|
196
|
+
// below.
|
|
197
|
+
const incumbent = best;
|
|
198
|
+
const incumbentGrade = incumbent === null ? null : grade(incumbent.weight);
|
|
199
|
+
const regime = worthRunning(2 * STEP)
|
|
200
|
+
? "composition"
|
|
201
|
+
: "retrieval";
|
|
202
|
+
ctx.trace?.step("regimePrediction", [rItem(query, "query")], [], regime === "retrieval"
|
|
203
|
+
? `retrieval regime — incumbent grade ${incumbentGrade} ≤ climb floor ${climbFloorGrade}, ` +
|
|
204
|
+
`so no mechanism floored above that grade runs; the consensus climb will not run`
|
|
205
|
+
: `composition regime — ${incumbentGrade === null
|
|
206
|
+
? "no incumbent (nothing grounded)"
|
|
207
|
+
: `incumbent grade ${incumbentGrade}`} above climb floor ${climbFloorGrade}, so the full market and climb run`, undefined, {
|
|
208
|
+
version: 1,
|
|
209
|
+
regime,
|
|
210
|
+
incumbentGrade,
|
|
211
|
+
climbFloorGrade,
|
|
212
|
+
});
|
|
213
|
+
};
|
|
154
214
|
// Phase 3: grounding loop
|
|
155
215
|
// Per-mechanism accounting (src/meter.ts). The market's whole premise is
|
|
156
216
|
// that mechanisms compete on one cost scale — so the profiling read-out is
|
|
157
217
|
// also per-mechanism, uniformly: the loop never asks which one it holds.
|
|
158
|
-
|
|
159
|
-
|
|
218
|
+
for (let mi = 0; mi < mechanisms.length; mi++) {
|
|
219
|
+
const mech = mechanisms[mi];
|
|
220
|
+
if (mi > 0)
|
|
221
|
+
reportRegime();
|
|
160
222
|
const floor = meter
|
|
161
223
|
? await meter.time(`${mech.name}.floor`, () => mech.floor(ctx, query, pre, worthRunning))
|
|
162
224
|
: await mech.floor(ctx, query, pre, worthRunning);
|
|
@@ -193,6 +255,10 @@ export async function think(ctx, query, mechs) {
|
|
|
193
255
|
});
|
|
194
256
|
}
|
|
195
257
|
}
|
|
258
|
+
// A market of ONE mechanism never reaches iteration 1; the step is still
|
|
259
|
+
// emitted exactly once per think(), so a consumer never has to ask whether
|
|
260
|
+
// the list was long enough for the prediction to exist.
|
|
261
|
+
reportRegime();
|
|
196
262
|
// (TS cannot see the closure assignments into `best` and narrows it to its
|
|
197
263
|
// initial null, so the read-back needs the assertion.)
|
|
198
264
|
const decided = best;
|
|
@@ -337,7 +403,7 @@ export async function think(ctx, query, mechs) {
|
|
|
337
403
|
? reasoned
|
|
338
404
|
: meter
|
|
339
405
|
? await meter.time("fuse", () => fuseAttention(ctx, query, reasoned, pre, unclimbed, primarySpans))
|
|
340
|
-
: await fuseAttention(ctx, query, reasoned, pre, unclimbed,
|
|
406
|
+
: await fuseAttention(ctx, query, reasoned, pre, unclimbed, primarySpans);
|
|
341
407
|
done(fused, "grounded, reasoned forward, fused across points of attention");
|
|
342
408
|
return { bytes: fused, provenance };
|
|
343
409
|
}
|
|
@@ -10,7 +10,21 @@ import type { MindContext, Recognition, Segment } from "./types.js";
|
|
|
10
10
|
* the longest known leaf, chained into flat branches. Names forms the
|
|
11
11
|
* query's own cut cannot, and records sub-leaf boundaries as `splits`.
|
|
12
12
|
*
|
|
13
|
-
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus.
|
|
13
|
+
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus.
|
|
14
|
+
*
|
|
15
|
+
* ONE READING PER BYTE STREAM, deliberately: there is no "cheap mode" that
|
|
16
|
+
* skips the edge-trim fallbacks. A `trimmed` variant was tried and REFUTED
|
|
17
|
+
* twice over. Its premise — "the trims only recover misaligned FRAGMENTS, so
|
|
18
|
+
* a consumer whose gate rejects fragments loses nothing" — is false: the
|
|
19
|
+
* left/right trim loops below exist precisely to find WHOLE trained forms
|
|
20
|
+
* embedded at an offset the query's own fold did not cut, and such a form has
|
|
21
|
+
* no structural parents or containers, so it passes the pivot's fragment gate
|
|
22
|
+
* and is exactly the candidate a multi-hop chain steps through. Skipping them
|
|
23
|
+
* narrows the pivot's evidence silently. And a per-caller variant has to key
|
|
24
|
+
* the memo by the variant, which breaks the "computed at most once" property
|
|
25
|
+
* (§2.11): the pipeline recognises a grounded answer untrimmed for
|
|
26
|
+
* `preConsumed`, and the pivot then recognises the same bytes again — the
|
|
27
|
+
* saving inverts into a doubling on the path it was measured for. */
|
|
14
28
|
export declare function recognise(ctx: MindContext, bytes: Uint8Array): Recognition;
|
|
15
29
|
/** Segment bytes using the geometry's own groupings — leaf-parent
|
|
16
30
|
* nodes from the perceived tree, with consecutive bare leaves merged
|
|
@@ -21,7 +21,21 @@ import { isChunk } from "../sema.js";
|
|
|
21
21
|
* the longest known leaf, chained into flat branches. Names forms the
|
|
22
22
|
* query's own cut cannot, and records sub-leaf boundaries as `splits`.
|
|
23
23
|
*
|
|
24
|
-
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus.
|
|
24
|
+
* Both O(n · maxGroup) bounded O(1) probes — never a scan of the corpus.
|
|
25
|
+
*
|
|
26
|
+
* ONE READING PER BYTE STREAM, deliberately: there is no "cheap mode" that
|
|
27
|
+
* skips the edge-trim fallbacks. A `trimmed` variant was tried and REFUTED
|
|
28
|
+
* twice over. Its premise — "the trims only recover misaligned FRAGMENTS, so
|
|
29
|
+
* a consumer whose gate rejects fragments loses nothing" — is false: the
|
|
30
|
+
* left/right trim loops below exist precisely to find WHOLE trained forms
|
|
31
|
+
* embedded at an offset the query's own fold did not cut, and such a form has
|
|
32
|
+
* no structural parents or containers, so it passes the pivot's fragment gate
|
|
33
|
+
* and is exactly the candidate a multi-hop chain steps through. Skipping them
|
|
34
|
+
* narrows the pivot's evidence silently. And a per-caller variant has to key
|
|
35
|
+
* the memo by the variant, which breaks the "computed at most once" property
|
|
36
|
+
* (§2.11): the pipeline recognises a grounded answer untrimmed for
|
|
37
|
+
* `preConsumed`, and the pivot then recognises the same bytes again — the
|
|
38
|
+
* saving inverts into a doubling on the path it was measured for. */
|
|
25
39
|
export function recognise(ctx, bytes) {
|
|
26
40
|
// Content-keyed memo — works for both single-turn respond() and multi-turn
|
|
27
41
|
// respondTurn() (where the map persists across calls). ALWAYS consulted,
|
|
@@ -9,7 +9,7 @@ import { mergeThreshold } from "../geometry.js";
|
|
|
9
9
|
import { concat2, concatBytes, indexOf } from "../bytes.js";
|
|
10
10
|
import { gistOf, read, resolve, walkTree } from "./primitives.js";
|
|
11
11
|
import { perceive } from "./primitives.js";
|
|
12
|
-
import {
|
|
12
|
+
import { argmaxCosine, candidateGist, hubBound } from "./traverse.js";
|
|
13
13
|
import { cachedRead, junctionContainers, junctionSynonyms, walkCache, } from "./junction.js";
|
|
14
14
|
import { recognise } from "./recognition.js";
|
|
15
15
|
// ── The bridge — the junction between two adjacent results ──────────────────
|
|
@@ -288,6 +288,12 @@ export async function pivotInto(ctx, answer, consumed, voiced = []) {
|
|
|
288
288
|
for (const c of n.kids)
|
|
289
289
|
queue.push(c); // breadth-first: larger regions first
|
|
290
290
|
}
|
|
291
|
+
// THE FULL recognition, memo-shared with every other reader of these bytes.
|
|
292
|
+
// A "skip the edge trims here" variant was refuted (see recognise's own
|
|
293
|
+
// note): those trims are what find a WHOLE trained form embedded at an
|
|
294
|
+
// offset the answer's fold did not cut, and such a form is parentless,
|
|
295
|
+
// container-free and edge-bearing — i.e. exactly what the filter below
|
|
296
|
+
// ADMITS as a pivot, not what it rejects.
|
|
291
297
|
const rec = recognise(ctx, answer);
|
|
292
298
|
for (const s of rec.sites) {
|
|
293
299
|
if (!consumed.has(s.payload) && ctx.store.hasNext(s.payload)) {
|
|
@@ -296,7 +302,33 @@ export async function pivotInto(ctx, answer, consumed, voiced = []) {
|
|
|
296
302
|
}
|
|
297
303
|
// Byte containment, longest wins — the answer literally contains the
|
|
298
304
|
// pivot's bytes, and the biggest well-evidenced span is the real pivot.
|
|
299
|
-
|
|
305
|
+
//
|
|
306
|
+
// REAL SATURATION, not a hard cap: the score IS the candidate's byte
|
|
307
|
+
// length, so the scan is DECIDED the moment the first candidate that passes
|
|
308
|
+
// every filter is found in DESCENDING length order — a shorter candidate can
|
|
309
|
+
// never outscore it. `contentLen` (the prefix-capped length read, §2.8) is
|
|
310
|
+
// the cheap ordering key, and the first-inserted tie-break is made explicit
|
|
311
|
+
// (`a.index - b.index`) so equal lengths keep `scored`'s insertion order —
|
|
312
|
+
// exactly the tie argmaxBy(strict) used to keep. The bytes of at most ONE
|
|
313
|
+
// winning candidate are read; every shorter candidate the probes proposed is
|
|
314
|
+
// skipped without reconstruction, where the old argmax read them all.
|
|
315
|
+
const ranked = [...scored.keys()]
|
|
316
|
+
.map((id, index) => ({
|
|
317
|
+
id,
|
|
318
|
+
index,
|
|
319
|
+
len: ctx.store.contentLen(id, answer.length + 1),
|
|
320
|
+
}))
|
|
321
|
+
.sort((a, b) => b.len - a.len || a.index - b.index);
|
|
322
|
+
let pivotId = null;
|
|
323
|
+
for (const c of ranked) {
|
|
324
|
+
const id = c.id;
|
|
325
|
+
// A ZERO-LENGTH candidate is not a pivot. `argmaxBy(…, 0, strict)` used to
|
|
326
|
+
// carry this floor in its threshold argument, and dropping it here would
|
|
327
|
+
// admit an empty node: `indexOf(answer, <empty>)` returns 0, so every
|
|
328
|
+
// filter below passes and the chain would hop through nothing (§2.13 —
|
|
329
|
+
// empty bytes are truthy).
|
|
330
|
+
if (c.len === 0)
|
|
331
|
+
continue;
|
|
300
332
|
// A PIVOT MUST BE A THING THE CORPUS DEPOSITED, NOT A PIECE OF ONE.
|
|
301
333
|
// "Longest wins" ranks candidates but never asks whether the winner is
|
|
302
334
|
// an entity at all, and by the time a chain reaches here `consumeAll`
|
|
@@ -330,18 +362,28 @@ export async function pivotInto(ctx, answer, consumed, voiced = []) {
|
|
|
330
362
|
// what `parents`/`containers` record. Reasoning steps THROUGH a fact;
|
|
331
363
|
// a span that was never a fact on its own is not one to step through.
|
|
332
364
|
// No constant enters — it is a structural predicate, not a threshold.
|
|
333
|
-
if (ctx.store.hasParents(id) || ctx.store.hasContainers(id))
|
|
334
|
-
|
|
335
|
-
|
|
365
|
+
if (ctx.store.hasParents(id) || ctx.store.hasContainers(id))
|
|
366
|
+
continue;
|
|
367
|
+
// A candidate whose bytes are LONGER than the answer cannot be a
|
|
368
|
+
// substring of it — `indexOf` would return −1 regardless. Prune by
|
|
369
|
+
// length BEFORE reconstructing the bytes: `read` is an UNCAPPED read
|
|
370
|
+
// (AGENTS §2.8), and a resonated context far longer than the answer is
|
|
371
|
+
// exactly the candidate that makes it cost a whole deposit's worth of
|
|
372
|
+
// reconstruction for a containment test that must fail. `contentLen`
|
|
373
|
+
// with the `answer.length + 1` cap is the prefix-capped length read the
|
|
374
|
+
// same contract prescribes; the prune is byte-identical to the old
|
|
375
|
+
// `indexOf` miss (it returns −1 for a needle longer than the haystack).
|
|
376
|
+
if (c.len > answer.length)
|
|
377
|
+
continue;
|
|
336
378
|
const bytes = read(ctx, id);
|
|
337
379
|
if (indexOf(answer, bytes, 0) < 0)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
return
|
|
380
|
+
continue;
|
|
381
|
+
if (voiced.some((v) => indexOf(v, bytes, 0) >= 0))
|
|
382
|
+
continue;
|
|
383
|
+
pivotId = id;
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
return pivotId;
|
|
345
387
|
}
|
|
346
388
|
/** Which of the given labelled forms a span MEANS — generic resonance over
|
|
347
389
|
* perceived gists. Each anchor form's gist is memoised; the span's gist
|