@hviana/sema 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/src/mind/attention.d.ts +14 -2
  2. package/dist/src/mind/attention.js +54 -8
  3. package/dist/src/mind/bridge.d.ts +30 -0
  4. package/dist/src/mind/bridge.js +569 -0
  5. package/dist/src/mind/match.d.ts +15 -2
  6. package/dist/src/mind/match.js +3 -8
  7. package/dist/src/mind/mechanisms/cast.d.ts +54 -0
  8. package/dist/src/mind/mechanisms/cast.js +268 -37
  9. package/dist/src/mind/mechanisms/cover.js +16 -31
  10. package/dist/src/mind/mechanisms/recall.js +66 -0
  11. package/dist/src/mind/reasoning.d.ts +11 -0
  12. package/dist/src/mind/reasoning.js +58 -2
  13. package/dist/src/mind/recognition.js +127 -7
  14. package/dist/src/mind/traverse.js +16 -15
  15. package/dist/src/mind/types.d.ts +39 -2
  16. package/dist/src/mind/types.js +38 -7
  17. package/dist/src/store.d.ts +12 -3
  18. package/dist/src/store.js +9 -3
  19. package/package.json +1 -1
  20. package/src/mind/attention.ts +65 -7
  21. package/src/mind/bridge.ts +596 -0
  22. package/src/mind/match.ts +19 -5
  23. package/src/mind/mechanisms/cast.ts +290 -38
  24. package/src/mind/mechanisms/cover.ts +23 -36
  25. package/src/mind/mechanisms/recall.ts +79 -0
  26. package/src/mind/reasoning.ts +71 -2
  27. package/src/mind/recognition.ts +132 -7
  28. package/src/mind/traverse.ts +16 -17
  29. package/src/mind/types.ts +70 -6
  30. package/src/store.ts +19 -5
  31. package/test/36-already-answered-fusion.test.mjs +128 -0
  32. package/test/37-cluster-dispersion-fusion.test.mjs +190 -0
  33. package/test/38-reason-restate-guard.test.mjs +94 -0
  34. package/test/39-cast-restate-guard.test.mjs +102 -0
  35. package/test/40-choosenext-scale-guard.test.mjs +75 -0
  36. package/test/41-seatofnode-direction.test.mjs +85 -0
  37. package/test/42-recognise-trace-idempotence.test.mjs +106 -0
  38. package/test/43-cast-analog-seat.test.mjs +244 -0
  39. package/test/44-recognise-edge-whitespace.test.mjs +63 -0
  40. package/test/45-liftanswer-restated-trim.test.mjs +60 -0
  41. package/test/46-recognise-multibyte-edge.test.mjs +85 -0
  42. package/test/47-cast-comparison-coverage.test.mjs +134 -0
  43. package/test/48-recognise-turn-connective.test.mjs +125 -0
  44. package/test/49-natural-units-synonym-bridge.test.mjs +175 -0
  45. package/test/50-cast-analog-consensus-floor.test.mjs +179 -0
@@ -5,8 +5,17 @@ import type { AncestorReach, Attention, AttentionRead, DFMode, MindContext, Regi
5
5
  * attention — those that cleared commitVotes' significance floor. */
6
6
  export declare function climbAttention(ctx: MindContext, query: Uint8Array, k: number, mode?: DFMode): Promise<Attention[]>;
7
7
  /** Full read-out of one consensus climb: both the roots (dominant points of
8
- * attention) and the entire ranked list. Cached via ctx.climbMemo when
9
- * ctx.trace is null. */
8
+ * attention) and the entire ranked list. Cached via ctx.climbMemo, ALWAYS —
9
+ * see {@link recognise} for why this memo (and recognise()'s own) must
10
+ * never be skipped while tracing: computeAttention's collectRegions walks
11
+ * the query's perceived tree via the same foldTree whose subtree-resolution
12
+ * fast path makes a second call on identical bytes non-idempotent once
13
+ * ctx._resolvedSubtrees is warm (which a multi-turn conversation's shared
14
+ * prefix subtrees guarantee by the second turn). A cache hit still emits
15
+ * a trace step — abbreviated, since the full per-sub-region voting detail
16
+ * {@link traceAttention} builds isn't preserved by the cached read-out —
17
+ * so a traced response is never silently blacked out for a repeated
18
+ * query. */
10
19
  export declare function climbAttentionAll(ctx: MindContext, query: Uint8Array, k: number, mode?: DFMode): Promise<AttentionRead>;
11
20
  export declare function computeAttention(ctx: MindContext, query: Uint8Array, k: number, mode: DFMode): Promise<AttentionRead>;
12
21
  export declare function collectRegions(ctx: MindContext, query: Uint8Array): Region[];
@@ -42,6 +51,8 @@ export declare function poolVotes(ctx: MindContext, regionVotes: readonly Region
42
51
  /** Per-anchor SCALE-INVARIANT support: Σ RegionVote.absorbed over the
43
52
  * distinct contributing regions — see Attention.breadth. */
44
53
  regionSupport: Map<number, number>;
54
+ /** Per-anchor contributing region spans — see Attention.clusters. */
55
+ regionSpans: Map<number, Array<[number, number]>>;
45
56
  steps: DerivationStep[];
46
57
  };
47
58
  export declare function commitVotes(ctx: MindContext, pooled: {
@@ -53,6 +64,7 @@ export declare function commitVotes(ctx: MindContext, pooled: {
53
64
  w: number;
54
65
  }>;
55
66
  regionSupport: Map<number, number>;
67
+ regionSpans: Map<number, Array<[number, number]>>;
56
68
  steps: DerivationStep[];
57
69
  }, sat: SaturationInfo, regions: readonly Region[], regionVoter: ReadonlyArray<{
58
70
  id: number;
@@ -15,7 +15,7 @@ import { leafIdRun } from "./canonical.js";
15
15
  import { corpusN, edgeAncestors } from "./traverse.js";
16
16
  import { cachedRead, junctionContainersFrom, junctionSeeds, junctionSynonyms, walkCache, } from "./junction.js";
17
17
  import { indexOf } from "../bytes.js";
18
- import { rNode, traceDerivation } from "./trace.js";
18
+ import { rItem, rNode, traceDerivation } from "./trace.js";
19
19
  // ── Public entry points ───────────────────────────────────────────────────
20
20
  /** Climb the query's perceived byte regions up the structural DAG via
21
21
  * resonance, pool the evidence, and return only the ROOT points of
@@ -24,12 +24,21 @@ export async function climbAttention(ctx, query, k, mode = "inverse") {
24
24
  return (await climbAttentionAll(ctx, query, k, mode)).roots;
25
25
  }
26
26
  /** Full read-out of one consensus climb: both the roots (dominant points of
27
- * attention) and the entire ranked list. Cached via ctx.climbMemo when
28
- * ctx.trace is null. */
27
+ * attention) and the entire ranked list. Cached via ctx.climbMemo, ALWAYS —
28
+ * see {@link recognise} for why this memo (and recognise()'s own) must
29
+ * never be skipped while tracing: computeAttention's collectRegions walks
30
+ * the query's perceived tree via the same foldTree whose subtree-resolution
31
+ * fast path makes a second call on identical bytes non-idempotent once
32
+ * ctx._resolvedSubtrees is warm (which a multi-turn conversation's shared
33
+ * prefix subtrees guarantee by the second turn). A cache hit still emits
34
+ * a trace step — abbreviated, since the full per-sub-region voting detail
35
+ * {@link traceAttention} builds isn't preserved by the cached read-out —
36
+ * so a traced response is never silently blacked out for a repeated
37
+ * query. */
29
38
  export async function climbAttentionAll(ctx, query, k, mode = "inverse") {
30
39
  // Content-keyed memo — works for both single-turn respond() and multi-turn
31
- // respondTurn(). Skipped while tracing.
32
- if (ctx.climbMemo && !ctx.trace) {
40
+ // respondTurn().
41
+ if (ctx.climbMemo) {
33
42
  const contentKey = latin1Key(query);
34
43
  const modeKey = `${k}:${mode}`;
35
44
  let byRead = ctx.climbMemo.get(contentKey);
@@ -37,8 +46,11 @@ export async function climbAttentionAll(ctx, query, k, mode = "inverse") {
37
46
  ctx.climbMemo.set(contentKey, byRead = new Map());
38
47
  }
39
48
  const hit = byRead.get(modeKey);
40
- if (hit !== undefined)
49
+ if (hit !== undefined) {
50
+ ctx.trace?.step("climbConsensus", [rItem(query, "query")], hit.roots.map((r) => rNode(ctx, r.anchor, "anchor", r.vote)), `(cached) consensus already computed for this query — ` +
51
+ `${hit.roots.length} point(s) of attention`);
41
52
  return hit;
53
+ }
42
54
  const read = await computeAttention(ctx, query, k, mode);
43
55
  byRead.set(modeKey, read);
44
56
  return read;
@@ -401,6 +413,7 @@ export function poolVotes(ctx, regionVotes, sat, N) {
401
413
  const votesIdf = new Map();
402
414
  const support = new Map();
403
415
  const regionSupport = new Map();
416
+ const regionSpans = new Map();
404
417
  const steps = [];
405
418
  let order = 0;
406
419
  for (const pc of pool.values()) {
@@ -409,6 +422,7 @@ export function poolVotes(ctx, regionVotes, sat, N) {
409
422
  const premises = [];
410
423
  const seenRi = new Set();
411
424
  let breadthSum = 0;
425
+ const spans = [];
412
426
  for (const c of pc.contributions) {
413
427
  const p0 = c.premises[0].item;
414
428
  if (p0.kind !== "region" || seenRi.has(p0.ri))
@@ -417,8 +431,10 @@ export function poolVotes(ctx, regionVotes, sat, N) {
417
431
  const rv = regionVotes[p0.ri];
418
432
  breadthSum += rv.absorbed ?? 1;
419
433
  premises.push({ kind: "form", span: [rv.start, rv.end] });
434
+ spans.push([rv.start, rv.end]);
420
435
  }
421
436
  regionSupport.set(pc.item.id, breadthSum);
437
+ regionSpans.set(pc.item.id, spans);
422
438
  steps.push({
423
439
  order: order++,
424
440
  move: "pool-vote",
@@ -448,10 +464,39 @@ export function poolVotes(ctx, regionVotes, sat, N) {
448
464
  }
449
465
  }
450
466
  }
451
- return { votes, votesIdf, support, regionSupport, steps };
467
+ return { votes, votesIdf, support, regionSupport, regionSpans, steps };
468
+ }
469
+ /** The number of DISTINCT clusters a root's contributing regions form —
470
+ * see Attention.clusters. Two regions belong to the same cluster iff the
471
+ * gap between them is strictly less than one river-fold quantum W: at
472
+ * that distance there is no room for a genuinely separate, independently
473
+ * perceivable unit of content between them (the same "smallest meaningful
474
+ * distinction" quantum {@link reachThreshold}'s own doc invokes). A gap
475
+ * of a full quantum or more means real, separate structure could sit
476
+ * between the two spans, so they count as independent corroboration.
477
+ * Strict `<` (not `<=`): verified against gap 3.1's own "gender equality"
478
+ * root, whose two genuine clusters sit EXACTLY W bytes apart — `<= W`
479
+ * would wrongly merge them into one and break that pinned requirement. */
480
+ function countClusters(spans, W) {
481
+ if (spans.length === 0)
482
+ return 0;
483
+ const sorted = [...spans].sort((a, b) => a[0] - b[0]);
484
+ let clusters = 1;
485
+ let curEnd = sorted[0][1];
486
+ for (let i = 1; i < sorted.length; i++) {
487
+ const [s, e] = sorted[i];
488
+ if (s - curEnd < W) {
489
+ curEnd = Math.max(curEnd, e);
490
+ }
491
+ else {
492
+ clusters++;
493
+ curEnd = e;
494
+ }
495
+ }
496
+ return clusters;
452
497
  }
453
498
  export function commitVotes(ctx, pooled, sat, regions, regionVoter, N) {
454
- const { votes, votesIdf, support, regionSupport, steps } = pooled;
499
+ const { votes, votesIdf, support, regionSupport, regionSpans, steps } = pooled;
455
500
  if (votes.size === 0) {
456
501
  traceAttention(ctx, regions, regionVoter, [], steps);
457
502
  return { roots: [], ranked: [] };
@@ -470,6 +515,7 @@ export function commitVotes(ctx, pooled, sat, regions, regionVoter, N) {
470
515
  start: s.start,
471
516
  end: s.end,
472
517
  breadth: (regionSupport.get(anchor) ?? 0) / totalRegions,
518
+ clusters: countClusters(regionSpans.get(anchor) ?? [], ctx.space.maxGroup),
473
519
  };
474
520
  })
475
521
  .sort((a, b) => b.vote - a.vote);
@@ -0,0 +1,30 @@
1
+ import type { MindContext } from "./types.js";
2
+ /** One accepted substitution: query span [qs,qe) stands in for the
3
+ * candidate context's span — recorded for the rationale trace. */
4
+ interface Substitution {
5
+ qs: number;
6
+ qe: number;
7
+ cs: number;
8
+ ce: number;
9
+ }
10
+ /** A bridged grounding proposal: the trained context to ground, the query
11
+ * spans its alignment accounts for, and the substitutions that closed it. */
12
+ export interface BridgeHit {
13
+ id: number;
14
+ accounted: Array<[number, number]>;
15
+ subs: Substitution[];
16
+ }
17
+ /** True when some query byte-range left UNACCOUNTED by `spans` contains a
18
+ * STORED window — content the store has seen that the proposed reading
19
+ * simply ignores. The IGNORED-KNOWN principle: a span may be dismissed
20
+ * only when the store itself has never seen it; known content the
21
+ * alignment failed to account for is grounds for refusal, while genuinely
22
+ * novel spans (an untrained word, stray punctuation) remain tolerable.
23
+ * Shared by the substitution bridge's own acceptance and CAST's
24
+ * frame-tier comparison gate (cast.ts). Pure attestation — no
25
+ * similarity, no constants. */
26
+ export declare function dismissedKnownContent(ctx: MindContext, query: Uint8Array, spans: ReadonlyArray<readonly [number, number]>): boolean;
27
+ /** Recall's corroborated-substitution bridge — see the module comment.
28
+ * Returns the best bridged grounding proposal, or null. */
29
+ export declare function substitutionBridge(ctx: MindContext, query: Uint8Array, proposed?: ReadonlyArray<number>): Promise<BridgeHit | null>;
30
+ export {};