@hviana/sema 0.2.9 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +71 -5
- package/dist/src/derive/src/deduction.d.ts +12 -1
- package/dist/src/derive/src/deduction.js +5 -1
- package/dist/src/derive/src/index.d.ts +1 -0
- package/dist/src/geometry.d.ts +3 -30
- package/dist/src/geometry.js +330 -82
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/meter.d.ts +171 -0
- package/dist/src/meter.js +269 -0
- package/dist/src/mind/attention.d.ts +5 -4
- package/dist/src/mind/attention.js +254 -23
- package/dist/src/mind/bridge.d.ts +10 -1
- package/dist/src/mind/bridge.js +179 -10
- package/dist/src/mind/canonical.d.ts +6 -1
- package/dist/src/mind/canonical.js +6 -1
- package/dist/src/mind/graph-search.d.ts +9 -0
- package/dist/src/mind/graph-search.js +59 -19
- package/dist/src/mind/index.d.ts +2 -0
- package/dist/src/mind/junction.d.ts +10 -0
- package/dist/src/mind/junction.js +14 -0
- package/dist/src/mind/learning.d.ts +32 -4
- package/dist/src/mind/learning.js +26 -4
- package/dist/src/mind/match.d.ts +40 -0
- package/dist/src/mind/match.js +125 -1
- package/dist/src/mind/mechanisms/cast.js +63 -6
- package/dist/src/mind/mechanisms/extraction.d.ts +0 -34
- package/dist/src/mind/mechanisms/extraction.js +1 -88
- package/dist/src/mind/mechanisms/recall.d.ts +3 -0
- package/dist/src/mind/mechanisms/recall.js +77 -14
- package/dist/src/mind/mind.d.ts +59 -5
- package/dist/src/mind/mind.js +115 -93
- package/dist/src/mind/pipeline-mechanism.d.ts +33 -3
- package/dist/src/mind/pipeline-mechanism.js +179 -10
- package/dist/src/mind/pipeline.d.ts +29 -0
- package/dist/src/mind/pipeline.js +79 -21
- package/dist/src/mind/primitives.d.ts +11 -15
- package/dist/src/mind/primitives.js +47 -28
- package/dist/src/mind/reasoning.d.ts +7 -1
- package/dist/src/mind/reasoning.js +40 -8
- package/dist/src/mind/recognition.js +93 -20
- package/dist/src/mind/traverse.d.ts +11 -0
- package/dist/src/mind/traverse.js +88 -7
- package/dist/src/mind/types.d.ts +39 -5
- package/dist/src/store.d.ts +15 -0
- package/dist/src/store.js +91 -6
- package/package.json +1 -1
- package/src/derive/src/deduction.ts +15 -0
- package/src/derive/src/index.ts +1 -0
- package/src/geometry.ts +350 -122
- package/src/index.ts +1 -0
- package/src/meter.ts +333 -0
- package/src/mind/attention.ts +276 -31
- package/src/mind/bridge.ts +187 -10
- package/src/mind/canonical.ts +6 -1
- package/src/mind/graph-search.ts +60 -21
- package/src/mind/index.ts +6 -0
- package/src/mind/junction.ts +12 -0
- package/src/mind/learning.ts +46 -5
- package/src/mind/match.ts +146 -1
- package/src/mind/mechanisms/cast.ts +62 -6
- package/src/mind/mechanisms/extraction.ts +2 -103
- package/src/mind/mechanisms/recall.ts +84 -17
- package/src/mind/mind.ts +144 -99
- package/src/mind/pipeline-mechanism.ts +203 -13
- package/src/mind/pipeline.ts +144 -36
- package/src/mind/primitives.ts +49 -33
- package/src/mind/reasoning.ts +39 -7
- package/src/mind/recognition.ts +89 -19
- package/src/mind/traverse.ts +89 -8
- package/src/mind/types.ts +39 -5
- package/src/store.ts +75 -6
- package/test/14-scaling.test.mjs +17 -7
- package/test/31-audit.test.mjs +4 -1
- package/test/33-multi-candidate.test.mjs +13 -1
- package/test/36-already-answered-fusion.test.mjs +10 -3
- package/test/46-recognise-multibyte-edge.test.mjs +3 -3
- package/test/53-cross-region-probe-instrumentation.test.mjs +36 -6
- package/test/54-evidence-k-instrumentation.test.mjs +175 -0
- package/test/55-cost-meter.test.mjs +284 -0
- package/test/56-bridge-identity-admission.test.mjs +209 -0
- package/test/57-fusion-order.test.mjs +104 -0
- package/test/58-subquantum-sites.test.mjs +112 -0
- package/test/59-fold-invariance.test.mjs +226 -0
package/dist/src/mind/mind.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export interface Conversation {
|
|
|
44
44
|
import type { AttentionRead, MindContext, Recognition } from "./types.js";
|
|
45
45
|
export type { AnchorRejectionReason, ClimbConsensusData, ConsensusAnchorTrace, ConsensusReachTrace, ConsensusRegionTrace, CrossRegionTier, JunctionVoteTrace, RegionOutcome, } from "./attention.js";
|
|
46
46
|
export type { AncestorReach, SaturationReason, SaturationStop, } from "./types.js";
|
|
47
|
+
import { type CostReport, Meter } from "../meter.js";
|
|
47
48
|
export interface MindOptions {
|
|
48
49
|
seed?: number;
|
|
49
50
|
recallQueryK?: number;
|
|
@@ -58,6 +59,15 @@ export interface MindOptions {
|
|
|
58
59
|
mechanisms?: import("./pipeline-mechanism.js").PipelineMechanism[];
|
|
59
60
|
/** Factories that receive the {@link ExtensionHost} and return mechanisms. */
|
|
60
61
|
mechanismFactories?: ((host: import("../extension.js").ExtensionHost) => import("./pipeline-mechanism.js").PipelineMechanism)[];
|
|
62
|
+
/** Measure the computational usage of every inference call — see
|
|
63
|
+
* src/meter.ts. Off by default and free when off (one null check per
|
|
64
|
+
* store read); on, each `respond`/`respondTurn` leaves a {@link
|
|
65
|
+
* Mind.lastCost} report behind. Counters are deterministic, so two runs
|
|
66
|
+
* of the same query on the same store are diffable; the millisecond
|
|
67
|
+
* fields are not. Profiling NEVER changes an answer — but note that
|
|
68
|
+
* attaching a RATIONALE does (traced responses bypass the ctx memos,
|
|
69
|
+
* AGENTS §2.11), so profile without a trace. */
|
|
70
|
+
profile?: boolean;
|
|
61
71
|
/** Content canonicalizer applied to EVERY response (any modality) for
|
|
62
72
|
* equivalence-class resolution — see src/canon.ts. Text entry points
|
|
63
73
|
* ({@link Mind.respondText}, {@link Mind.respondTurnText}) inject the
|
|
@@ -86,6 +96,19 @@ export declare class Mind implements MindContext {
|
|
|
86
96
|
* `false` to disable canonical resolution, or null to let each entry
|
|
87
97
|
* point decide (text entry points inject {@link textCanon}). */
|
|
88
98
|
private _canonOpt;
|
|
99
|
+
/** The work accumulator for the inference call in flight — see
|
|
100
|
+
* {@link MindContext.meter}. Non-null only between beginResponse and
|
|
101
|
+
* endResponse, and only when the Mind was constructed with
|
|
102
|
+
* `{ profile: true }`. */
|
|
103
|
+
meter: Meter | null;
|
|
104
|
+
/** Whether {@link MindOptions.profile} was set. */
|
|
105
|
+
private _profile;
|
|
106
|
+
/** The computational-usage report of the LAST completed inference call, or
|
|
107
|
+
* null when profiling is off (or nothing has been asked yet). Overwritten
|
|
108
|
+
* by every `respond`/`respondTurn`; copy it if you are aggregating. See
|
|
109
|
+
* {@link import("../meter.js").CostReport} and `sumReports`/`formatReport`
|
|
110
|
+
* for battery-level aggregation. */
|
|
111
|
+
lastCost: CostReport | null;
|
|
89
112
|
/** Memo of the consensus climb — content-keyed. See {@link MindContext.climbMemo}. */
|
|
90
113
|
climbMemo: Map<string, Map<string, AttentionRead>> | null;
|
|
91
114
|
/** Memo of recognise() — content-keyed. See {@link MindContext.recogniseMemo}. */
|
|
@@ -146,21 +169,49 @@ export declare class Mind implements MindContext {
|
|
|
146
169
|
/** Perceive input into a content-defined tree. Deterministic — identical
|
|
147
170
|
* bytes always produce an identical tree. Public for ingest-cache. */
|
|
148
171
|
perceive(input: Input, leafAt?: (i: number) => number | null, lookup?: (ids: number[]) => number | null): Sema;
|
|
149
|
-
/** Open one response's transient state — the tracer
|
|
150
|
-
* memos
|
|
151
|
-
*
|
|
172
|
+
/** Open one response's transient state — the tracer, the per-response
|
|
173
|
+
* memos, the work meter. The ONE place this state is created, and it
|
|
174
|
+
* serves BOTH entry points: `respond` takes fresh per-response memos,
|
|
175
|
+
* `respondTurn` passes its conversation, whose memos persist across turns
|
|
176
|
+
* (content-keyed, so the previous turn's results are found by this turn's
|
|
177
|
+
* sub-span calls) and whose `resolvedSubtrees` makes foldTree O(suffix)
|
|
178
|
+
* instead of O(context). respondTurn used to inline its own copy of this
|
|
179
|
+
* and of {@link endResponse}; the two drifted (a memo added to one was
|
|
180
|
+
* silently absent from the other), so there is exactly one pair now. */
|
|
152
181
|
private beginResponse;
|
|
182
|
+
/** Open (or leave closed) the response's work accumulator. Separate from
|
|
183
|
+
* {@link beginResponse} because {@link respondTurn} keeps its own
|
|
184
|
+
* conversation-scoped lifecycle and must not create fresh per-response
|
|
185
|
+
* memos — but it DOES meter, through this same pair. */
|
|
186
|
+
private _beginMeter;
|
|
187
|
+
/** Close the accumulator and publish its report. Detaching from the store
|
|
188
|
+
* matters: a Mind that shares a store with another Mind must not keep
|
|
189
|
+
* charging that store's reads to a finished response. */
|
|
190
|
+
private _endMeter;
|
|
153
191
|
/** The canonicalizer a response should carry: the Mind-level option when
|
|
154
192
|
* set (or none when explicitly disabled), else the entry point's own
|
|
155
193
|
* default — text entry points pass {@link textCanon}, binary ones null. */
|
|
156
194
|
private _canonFor;
|
|
157
195
|
/** Close one response's transient state — every per-response field, incl.
|
|
158
|
-
* the edge guide/choices `think` sets mid-flight.
|
|
196
|
+
* the edge guide/choices `think` sets mid-flight, and the meter's report.
|
|
197
|
+
*
|
|
198
|
+
* A conversation's memo MAPS were mutated in place, so `data.*` still
|
|
199
|
+
* points at them and there is nothing to save back. Clearing the Mind's
|
|
200
|
+
* references is what matters: a concurrently-started `respond()` swaps its
|
|
201
|
+
* own fresh maps into these pointers, and copying back from them here
|
|
202
|
+
* would inject a foreign response's memos into the conversation. */
|
|
159
203
|
private endResponse;
|
|
160
204
|
/** Shared response core — the one path from bytes to voiced answer.
|
|
161
205
|
* `respond` calls this directly; `respondTurn` has its own path
|
|
162
206
|
* with conversation-persistent memos and incremental perception. */
|
|
163
207
|
private _respondImpl;
|
|
208
|
+
/** The ONE path from query bytes to a voiced answer: ground (think), then
|
|
209
|
+
* re-voice in the asker's words (articulate). Both entry points run
|
|
210
|
+
* exactly this — they differ only in the LIFECYCLE around it (fresh
|
|
211
|
+
* per-response memos vs. a conversation's persistent ones) and in what
|
|
212
|
+
* they do with the answer afterwards. It must be called between
|
|
213
|
+
* {@link beginResponse} and {@link endResponse}. */
|
|
214
|
+
private _groundAndVoice;
|
|
164
215
|
respond(input: Input, inspectRationale?: InspectRationale): Promise<Response>;
|
|
165
216
|
/** Text view of {@link respond}. NUL bytes (0x00) are stripped before
|
|
166
217
|
* decoding — they are structural padding in text answers. LOSSY for a
|
|
@@ -235,7 +286,10 @@ export declare class Mind implements MindContext {
|
|
|
235
286
|
* read-out direction of the same operation, without recall's grounding
|
|
236
287
|
* ladder. If either side's acceptance rule changes, revisit the other. */
|
|
237
288
|
express(idOrV: number | Vec): Promise<Uint8Array>;
|
|
238
|
-
|
|
289
|
+
/** See {@link import("./learning.js").ingest} — `onDeposit`, when given,
|
|
290
|
+
* reports each ingested item's deposited root node ids
|
|
291
|
+
* ({@link DepositReport}); purely observational. */
|
|
292
|
+
ingest(input: Input | (Input | [Input, Input])[], second?: Input, onDeposit?: (report: import("./learning.js").DepositReport) => void): Promise<(Sema & {
|
|
239
293
|
id: number;
|
|
240
294
|
}) | undefined>;
|
|
241
295
|
private extensionHost;
|
package/dist/src/mind/mind.js
CHANGED
|
@@ -29,6 +29,9 @@ import { aluToMechanism, defaultMechanisms, think } from "./pipeline.js";
|
|
|
29
29
|
import { articulate } from "./articulation.js";
|
|
30
30
|
import { ingest } from "./learning.js";
|
|
31
31
|
import { rItem } from "./trace.js";
|
|
32
|
+
// The work meter is exported from src/index.ts (via src/meter.ts) — the one
|
|
33
|
+
// definition; the Mind only consumes it.
|
|
34
|
+
import { Meter } from "../meter.js";
|
|
32
35
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
33
36
|
// THE MIND
|
|
34
37
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -53,6 +56,19 @@ export class Mind {
|
|
|
53
56
|
* `false` to disable canonical resolution, or null to let each entry
|
|
54
57
|
* point decide (text entry points inject {@link textCanon}). */
|
|
55
58
|
_canonOpt = null;
|
|
59
|
+
/** The work accumulator for the inference call in flight — see
|
|
60
|
+
* {@link MindContext.meter}. Non-null only between beginResponse and
|
|
61
|
+
* endResponse, and only when the Mind was constructed with
|
|
62
|
+
* `{ profile: true }`. */
|
|
63
|
+
meter = null;
|
|
64
|
+
/** Whether {@link MindOptions.profile} was set. */
|
|
65
|
+
_profile = false;
|
|
66
|
+
/** The computational-usage report of the LAST completed inference call, or
|
|
67
|
+
* null when profiling is off (or nothing has been asked yet). Overwritten
|
|
68
|
+
* by every `respond`/`respondTurn`; copy it if you are aggregating. See
|
|
69
|
+
* {@link import("../meter.js").CostReport} and `sumReports`/`formatReport`
|
|
70
|
+
* for battery-level aggregation. */
|
|
71
|
+
lastCost = null;
|
|
56
72
|
/** Memo of the consensus climb — content-keyed. See {@link MindContext.climbMemo}. */
|
|
57
73
|
climbMemo = null;
|
|
58
74
|
/** Memo of recognise() — content-keyed. See {@link MindContext.recogniseMemo}. */
|
|
@@ -120,8 +136,9 @@ export class Mind {
|
|
|
120
136
|
this.store = storeArg;
|
|
121
137
|
}
|
|
122
138
|
else {
|
|
123
|
-
const { store: optsStore, mechanisms: userMechs, mechanismFactories: userFacts, canon: optsCanon, ...rest } = (optsOrCfg ?? {});
|
|
139
|
+
const { store: optsStore, mechanisms: userMechs, mechanismFactories: userFacts, canon: optsCanon, profile: optsProfile, ...rest } = (optsOrCfg ?? {});
|
|
124
140
|
this._canonOpt = optsCanon ?? null;
|
|
141
|
+
this._profile = optsProfile === true;
|
|
125
142
|
this.cfg = resolveConfig(rest);
|
|
126
143
|
this.store = optsStore ?? new SQliteStore({
|
|
127
144
|
maxGroup: this.cfg.geometry.maxGroup,
|
|
@@ -180,16 +197,44 @@ export class Mind {
|
|
|
180
197
|
perceive(input, leafAt, lookup) {
|
|
181
198
|
return perceiveImpl(this, input, leafAt, lookup);
|
|
182
199
|
}
|
|
183
|
-
/** Open one response's transient state — the tracer
|
|
184
|
-
* memos
|
|
185
|
-
*
|
|
186
|
-
|
|
200
|
+
/** Open one response's transient state — the tracer, the per-response
|
|
201
|
+
* memos, the work meter. The ONE place this state is created, and it
|
|
202
|
+
* serves BOTH entry points: `respond` takes fresh per-response memos,
|
|
203
|
+
* `respondTurn` passes its conversation, whose memos persist across turns
|
|
204
|
+
* (content-keyed, so the previous turn's results are found by this turn's
|
|
205
|
+
* sub-span calls) and whose `resolvedSubtrees` makes foldTree O(suffix)
|
|
206
|
+
* instead of O(context). respondTurn used to inline its own copy of this
|
|
207
|
+
* and of {@link endResponse}; the two drifted (a memo added to one was
|
|
208
|
+
* silently absent from the other), so there is exactly one pair now. */
|
|
209
|
+
beginResponse(inspectRationale, canon, conv) {
|
|
187
210
|
this.trace = inspectRationale ? new Rationale(inspectRationale) : null;
|
|
188
|
-
this.climbMemo = new Map();
|
|
189
|
-
this.recogniseMemo = new Map();
|
|
190
|
-
this.perceiveMemo = new Map();
|
|
211
|
+
this.climbMemo = conv ? conv.climbMemo : new Map();
|
|
212
|
+
this.recogniseMemo = conv ? conv.recogniseMemo : new Map();
|
|
213
|
+
this.perceiveMemo = conv ? conv.perceiveMemo : new Map();
|
|
214
|
+
this._resolvedSubtrees = conv ? conv.resolvedSubtrees : null;
|
|
191
215
|
this.canon = canon ?? null;
|
|
192
216
|
this.canonMemo = canon ? new Map() : null;
|
|
217
|
+
this._beginMeter();
|
|
218
|
+
}
|
|
219
|
+
/** Open (or leave closed) the response's work accumulator. Separate from
|
|
220
|
+
* {@link beginResponse} because {@link respondTurn} keeps its own
|
|
221
|
+
* conversation-scoped lifecycle and must not create fresh per-response
|
|
222
|
+
* memos — but it DOES meter, through this same pair. */
|
|
223
|
+
_beginMeter() {
|
|
224
|
+
if (!this._profile)
|
|
225
|
+
return;
|
|
226
|
+
this.meter = new Meter();
|
|
227
|
+
this.store.meter = this.meter;
|
|
228
|
+
}
|
|
229
|
+
/** Close the accumulator and publish its report. Detaching from the store
|
|
230
|
+
* matters: a Mind that shares a store with another Mind must not keep
|
|
231
|
+
* charging that store's reads to a finished response. */
|
|
232
|
+
_endMeter(queryBytes) {
|
|
233
|
+
if (this.meter === null)
|
|
234
|
+
return;
|
|
235
|
+
this.lastCost = this.meter.report(queryBytes);
|
|
236
|
+
this.store.meter = null;
|
|
237
|
+
this.meter = null;
|
|
193
238
|
}
|
|
194
239
|
/** The canonicalizer a response should carry: the Mind-level option when
|
|
195
240
|
* set (or none when explicitly disabled), else the entry point's own
|
|
@@ -200,12 +245,20 @@ export class Mind {
|
|
|
200
245
|
return this._canonOpt ?? entryDefault;
|
|
201
246
|
}
|
|
202
247
|
/** Close one response's transient state — every per-response field, incl.
|
|
203
|
-
* the edge guide/choices `think` sets mid-flight.
|
|
204
|
-
|
|
248
|
+
* the edge guide/choices `think` sets mid-flight, and the meter's report.
|
|
249
|
+
*
|
|
250
|
+
* A conversation's memo MAPS were mutated in place, so `data.*` still
|
|
251
|
+
* points at them and there is nothing to save back. Clearing the Mind's
|
|
252
|
+
* references is what matters: a concurrently-started `respond()` swaps its
|
|
253
|
+
* own fresh maps into these pointers, and copying back from them here
|
|
254
|
+
* would inject a foreign response's memos into the conversation. */
|
|
255
|
+
endResponse(queryBytes) {
|
|
256
|
+
this._endMeter(queryBytes);
|
|
205
257
|
this.trace = null;
|
|
206
258
|
this.climbMemo = null;
|
|
207
259
|
this.recogniseMemo = null;
|
|
208
260
|
this.perceiveMemo = null;
|
|
261
|
+
this._resolvedSubtrees = null;
|
|
209
262
|
this.canon = null;
|
|
210
263
|
this.canonMemo = null;
|
|
211
264
|
this._edgeGuide = null;
|
|
@@ -217,26 +270,38 @@ export class Mind {
|
|
|
217
270
|
async _respondImpl(queryBytes, inspectRationale, traceLabel = "respond", canon = null) {
|
|
218
271
|
this.beginResponse(inspectRationale, canon);
|
|
219
272
|
try {
|
|
220
|
-
|
|
221
|
-
rItem(queryBytes, "query"),
|
|
222
|
-
]);
|
|
223
|
-
const thought = await think(this, queryBytes, this.mechanisms);
|
|
224
|
-
if (thought === null) {
|
|
225
|
-
top?.done([], "nothing to perceive or an empty store — no answer");
|
|
226
|
-
return { v: null, bytes: new Uint8Array(0) };
|
|
227
|
-
}
|
|
228
|
-
const voiced = await articulate(this, thought.bytes, queryBytes);
|
|
229
|
-
top?.done([rItem(voiced, "answer", resolveImpl(this, voiced) ?? undefined)], "the answer, re-voiced in the asker's words");
|
|
230
|
-
return {
|
|
231
|
-
v: gistOf(this, voiced),
|
|
232
|
-
bytes: voiced,
|
|
233
|
-
provenance: thought.provenance,
|
|
234
|
-
};
|
|
273
|
+
return await this._groundAndVoice(queryBytes, traceLabel);
|
|
235
274
|
}
|
|
236
275
|
finally {
|
|
237
|
-
this.endResponse();
|
|
276
|
+
this.endResponse(queryBytes.length);
|
|
238
277
|
}
|
|
239
278
|
}
|
|
279
|
+
/** The ONE path from query bytes to a voiced answer: ground (think), then
|
|
280
|
+
* re-voice in the asker's words (articulate). Both entry points run
|
|
281
|
+
* exactly this — they differ only in the LIFECYCLE around it (fresh
|
|
282
|
+
* per-response memos vs. a conversation's persistent ones) and in what
|
|
283
|
+
* they do with the answer afterwards. It must be called between
|
|
284
|
+
* {@link beginResponse} and {@link endResponse}. */
|
|
285
|
+
async _groundAndVoice(queryBytes, traceLabel) {
|
|
286
|
+
const top = this.trace?.enter(traceLabel, [rItem(queryBytes, "query")]);
|
|
287
|
+
const meter = this.meter;
|
|
288
|
+
const thought = meter
|
|
289
|
+
? await meter.time("think", () => think(this, queryBytes, this.mechanisms))
|
|
290
|
+
: await think(this, queryBytes, this.mechanisms);
|
|
291
|
+
if (thought === null) {
|
|
292
|
+
top?.done([], "nothing to perceive or an empty store — no answer");
|
|
293
|
+
return { v: null, bytes: new Uint8Array(0) };
|
|
294
|
+
}
|
|
295
|
+
const voiced = meter
|
|
296
|
+
? await meter.time("articulate", () => articulate(this, thought.bytes, queryBytes))
|
|
297
|
+
: await articulate(this, thought.bytes, queryBytes);
|
|
298
|
+
top?.done([rItem(voiced, "answer", resolveImpl(this, voiced) ?? undefined)], "the answer, re-voiced in the asker's words");
|
|
299
|
+
return {
|
|
300
|
+
v: gistOf(this, voiced),
|
|
301
|
+
bytes: voiced,
|
|
302
|
+
provenance: thought.provenance,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
240
305
|
async respond(input, inspectRationale) {
|
|
241
306
|
// A STRING input is text by nature: it carries the text equivalence even
|
|
242
307
|
// through the generic entry point. Raw bytes / grids carry only the
|
|
@@ -358,82 +423,36 @@ export class Mind {
|
|
|
358
423
|
throw new Error(`Conversation ${conv.id} not found`);
|
|
359
424
|
const turnBytes = inputBytes(this, turn);
|
|
360
425
|
// Incremental perception — O(turn) instead of O(context).
|
|
361
|
-
|
|
426
|
+
this._growContext(data, turnBytes);
|
|
362
427
|
const newContext = data.bytes;
|
|
363
|
-
//
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
// the current turn's sub-span calls.
|
|
368
|
-
// _resolvedSubtrees — Sema-node-keyed; when the pyramid reuses
|
|
369
|
-
// prefix subtrees (identical objects), foldTree returns their
|
|
370
|
-
// ids immediately — O(suffix) instead of O(context).
|
|
371
|
-
this.perceiveMemo = data.perceiveMemo;
|
|
372
|
-
this.recogniseMemo = data.recogniseMemo;
|
|
373
|
-
this.climbMemo = data.climbMemo;
|
|
374
|
-
this._resolvedSubtrees = data.resolvedSubtrees;
|
|
375
|
-
this.trace = inspectRationale ? new Rationale(inspectRationale) : null;
|
|
376
|
-
// A string turn is text by nature — carry the text equivalence, same as
|
|
428
|
+
// The conversation's persistent memos and subtree cache are swapped in
|
|
429
|
+
// by beginResponse (see there) — the SAME lifecycle respond() uses, so a
|
|
430
|
+
// memo added in one place can never be missing from the other. A string
|
|
431
|
+
// turn is text by nature and carries the text equivalence, same as
|
|
377
432
|
// respond() (see _canonFor).
|
|
378
|
-
|
|
379
|
-
|
|
433
|
+
//
|
|
434
|
+
// No recognise-memo pre-seeding here: that used to be necessary because
|
|
435
|
+
// the flat/positional fold lost visibility into an earlier turn's own
|
|
436
|
+
// structure once later bytes shifted its position (foldTree no longer
|
|
437
|
+
// visited the turn's root node). The STABLE-PREFIX fold (see {@link
|
|
438
|
+
// ConversationData}) makes every turn's subtree independent of what
|
|
439
|
+
// follows it by construction, so recognise() finds it correctly on its
|
|
440
|
+
// own, first-touch, exactly once per turn.
|
|
441
|
+
this.beginResponse(inspectRationale, this._canonFor(typeof turn === "string" ? textCanon : null), data);
|
|
380
442
|
try {
|
|
381
|
-
|
|
382
|
-
// because the flat/positional fold lost visibility into an earlier
|
|
383
|
-
// turn's own structure once later bytes shifted its position
|
|
384
|
-
// (foldTree no longer visited the turn's root node). The
|
|
385
|
-
// STABLE-PREFIX fold (see {@link ConversationData}) makes every
|
|
386
|
-
// turn's subtree independent of what follows it by construction, so
|
|
387
|
-
// recognise() finds it correctly on its own, first-touch, exactly
|
|
388
|
-
// once per turn — no workaround needed, and none pre-empting
|
|
389
|
-
// recognise()'s own memo with a partial result.
|
|
390
|
-
const top = this.trace?.enter("respondTurn", [
|
|
391
|
-
rItem(newContext, "query"),
|
|
392
|
-
]);
|
|
393
|
-
const thought = await think(this, newContext, this.mechanisms);
|
|
394
|
-
if (thought === null) {
|
|
395
|
-
top?.done([], "nothing to perceive or an empty store — no answer");
|
|
396
|
-
return {
|
|
397
|
-
response: { v: null, bytes: new Uint8Array(0) },
|
|
398
|
-
state: this.conversationState(conv),
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
const voiced = await articulate(this, thought.bytes, newContext);
|
|
402
|
-
top?.done([rItem(voiced, "answer", resolveImpl(this, voiced) ?? undefined)], "the answer, re-voiced in the asker's words");
|
|
443
|
+
const response = await this._groundAndVoice(newContext, "respondTurn");
|
|
403
444
|
// The REPLY joins the accumulated context the same way a turn does
|
|
404
445
|
// ({@link addTurn}): raw byte append plus a boundary offset — never a
|
|
405
446
|
// separator. A conversation's context is the full exchange, exactly
|
|
406
447
|
// the cumulative continuous shape multi-turn training deposits, so a
|
|
407
448
|
// later turn can refer to what was ANSWERED ("which of those two…"),
|
|
408
449
|
// not only to what was asked.
|
|
409
|
-
if (
|
|
410
|
-
this.addTurn(conv,
|
|
411
|
-
return {
|
|
412
|
-
response: {
|
|
413
|
-
v: gistOf(this, voiced),
|
|
414
|
-
bytes: voiced,
|
|
415
|
-
provenance: thought.provenance,
|
|
416
|
-
},
|
|
417
|
-
state: this.conversationState(conv),
|
|
418
|
-
};
|
|
450
|
+
if (response.bytes.length > 0)
|
|
451
|
+
this.addTurn(conv, response.bytes);
|
|
452
|
+
return { response, state: this.conversationState(conv) };
|
|
419
453
|
}
|
|
420
454
|
finally {
|
|
421
|
-
|
|
422
|
-
// `data.*` still points at them. Re-assigning from the Mind-level
|
|
423
|
-
// pointers here was a no-op in the single-flight case and, under a
|
|
424
|
-
// concurrently-started respond() (which swaps its own fresh maps into
|
|
425
|
-
// those pointers), would inject a FOREIGN response's memos into this
|
|
426
|
-
// conversation. Clear Mind references — non-conversation respond()
|
|
427
|
-
// calls get fresh per-response memos.
|
|
428
|
-
this.trace = null;
|
|
429
|
-
this.perceiveMemo = null;
|
|
430
|
-
this.recogniseMemo = null;
|
|
431
|
-
this.climbMemo = null;
|
|
432
|
-
this.canon = null;
|
|
433
|
-
this.canonMemo = null;
|
|
434
|
-
this._resolvedSubtrees = null;
|
|
435
|
-
this._edgeGuide = null;
|
|
436
|
-
this._edgeChoice.clear();
|
|
455
|
+
this.endResponse(newContext.length);
|
|
437
456
|
}
|
|
438
457
|
}
|
|
439
458
|
/** Text view of {@link respondTurn}. See {@link respondText} for the
|
|
@@ -464,8 +483,11 @@ export class Mind {
|
|
|
464
483
|
return new Uint8Array(0);
|
|
465
484
|
}
|
|
466
485
|
// ── Learning ─────────────────────────────────────────────────────────────
|
|
467
|
-
|
|
468
|
-
|
|
486
|
+
/** See {@link import("./learning.js").ingest} — `onDeposit`, when given,
|
|
487
|
+
* reports each ingested item's deposited root node ids
|
|
488
|
+
* ({@link DepositReport}); purely observational. */
|
|
489
|
+
async ingest(input, second, onDeposit) {
|
|
490
|
+
return ingest(this, input, second, onDeposit);
|
|
469
491
|
}
|
|
470
492
|
// ── Extension Surface ────────────────────────────────────────────────────
|
|
471
493
|
extensionHost() {
|
|
@@ -37,9 +37,21 @@ export declare class Precomputed {
|
|
|
37
37
|
* any future identity-based mechanism reads the same cache. */
|
|
38
38
|
windowsOf(anchor: number): Map<number, number>;
|
|
39
39
|
/** Shared memo for {@link reachOf} (structural-IDF reads): a window's
|
|
40
|
-
* ancestor reach is a pure function of the read-only store, so one
|
|
41
|
-
*
|
|
42
|
-
|
|
40
|
+
* ancestor reach is a pure function of the read-only store, so one memo
|
|
41
|
+
* serves every mechanism that prices commonality — AND the consensus
|
|
42
|
+
* climb, which is the largest consumer and used to build its own. The
|
|
43
|
+
* ONE definition of its lifetime lives in traverse.ts
|
|
44
|
+
* ({@link sharedReachMemo}): response-scoped for respond(),
|
|
45
|
+
* conversation-scoped across turns, always cold under a trace. */
|
|
46
|
+
private _reach?;
|
|
47
|
+
get reachMemo(): Map<number, AncestorReach>;
|
|
48
|
+
/** Charge a lazily-shared analysis to its OWN phase rather than to the
|
|
49
|
+
* mechanism that happened to first-touch it. Without this the profile
|
|
50
|
+
* reads as "cast.floor costs 2 s" when what actually cost 2 s is the
|
|
51
|
+
* consensus climb — which cast merely paid for on everyone's behalf, and
|
|
52
|
+
* which every later consumer then got free. Attribution must follow the
|
|
53
|
+
* work, not the caller. */
|
|
54
|
+
private shared;
|
|
43
55
|
private _attention?;
|
|
44
56
|
/** The full consensus climb (roots + ranked anchors) — the query-level
|
|
45
57
|
* evidence CAST, confluence, extraction, recall's scaffolding tier, and
|
|
@@ -109,6 +121,24 @@ export interface MechanismResult {
|
|
|
109
121
|
/** Override the mechanism's default provenance for this result.
|
|
110
122
|
* When absent, the pipeline uses `mech.provenance`. */
|
|
111
123
|
provenance?: string;
|
|
124
|
+
/** This grounding is a COMPLETE trained answer — post-grounding must not
|
|
125
|
+
* extend it. Declared by the mechanism about its own result, exactly like
|
|
126
|
+
* `accounted`/`used`/`unexplained`; the decider honours the property and
|
|
127
|
+
* never asks which mechanism set it, so the market stays uniform.
|
|
128
|
+
*
|
|
129
|
+
* Set it only when the answer is a stored form's OWN continuation reached
|
|
130
|
+
* through an identity claim about the query — i.e. the query IS some
|
|
131
|
+
* trained context, so its continuation is the whole read-out and a further
|
|
132
|
+
* multi-hop pivot would chain PAST the fact that produced the answer.
|
|
133
|
+
* That is the same reasoning `reason`'s echo guard already applies to a
|
|
134
|
+
* query that resolves exactly (see reasoning.ts); this carries the claim
|
|
135
|
+
* for the mechanisms that establish the identity by another route.
|
|
136
|
+
*
|
|
137
|
+
* Observed without it: the correct "What is the process of
|
|
138
|
+
* photosynthesis?" grounding was pivoted forward four times, out of the
|
|
139
|
+
* fact that answered it and into an unrelated "Hello! How can I assist you
|
|
140
|
+
* today?" conversational turn. */
|
|
141
|
+
complete?: boolean;
|
|
112
142
|
}
|
|
113
143
|
export interface PipelineMechanism {
|
|
114
144
|
/** Stable identifier for trace/debug. */
|