@polycode-projects/the-mechanical-code-talker 1.9.2 → 1.10.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/README.md +441 -202
- package/bin/tmct.mjs +126 -1
- package/package.json +4 -2
- package/src/answer-variants.mjs +8 -36
- package/src/ask-browser-entry.mjs +5 -23
- package/src/ask-browser.bundle.js +1 -2
- package/src/ask-nlp.mjs +9 -23
- package/src/ask-vocab.mjs +139 -589
- package/src/ask.mjs +627 -1729
- package/src/chat.mjs +1684 -2872
- package/src/cli-args.mjs +14 -28
- package/src/codegraph.mjs +236 -644
- package/src/completions/complete.mjs +18 -62
- package/src/completions/graph-adapter.mjs +14 -60
- package/src/completions/group.mjs +12 -68
- package/src/completions/infer.mjs +38 -126
- package/src/completions/prune.mjs +17 -70
- package/src/completions/rank.mjs +16 -69
- package/src/completions/search.mjs +8 -31
- package/src/concept.mjs +32 -88
- package/src/conformance.mjs +11 -15
- package/src/corpus/conceptnet.mjs +31 -89
- package/src/corpus/templates.mjs +19 -45
- package/src/corpus/unknown-ingest.mjs +31 -92
- package/src/embed.mjs +10 -22
- package/src/extensions.mjs +50 -154
- package/src/finish.mjs +35 -91
- package/src/grammar/ace.mjs +16 -40
- package/src/grammar/assert.mjs +1 -1
- package/src/grammar/lexicon-core.json +1 -1
- package/src/grammar/lexicon.mjs +9 -27
- package/src/graph-merge.mjs +2 -3
- package/src/hash.mjs +6 -14
- package/src/index.mjs +6 -10
- package/src/init.mjs +38 -125
- package/src/interpret/fuzzy.mjs +10 -29
- package/src/interpret/merge.mjs +9 -27
- package/src/interpret/normalize.mjs +137 -585
- package/src/interpret/pipeline.mjs +23 -71
- package/src/interpret/strategies/ace.mjs +7 -31
- package/src/interpret/strategies/constructions.mjs +14 -41
- package/src/interpret/strategies/grammar.mjs +21 -60
- package/src/interpret/strategies/keywords.mjs +42 -131
- package/src/interpret/strategies/noise-strip.mjs +18 -89
- package/src/memory/bias.mjs +11 -54
- package/src/memory/blocks.mjs +18 -69
- package/src/memory/core.mjs +171 -591
- package/src/memory/fold.mjs +0 -0
- package/src/memory/inspect.mjs +7 -25
- package/src/memory/shacl.mjs +10 -39
- package/src/memory/trust.mjs +26 -127
- package/src/memory-ask-browser-entry.mjs +7 -30
- package/src/memory-ask-browser.bundle.js +1 -1
- package/src/paraphrase.mjs +20 -53
- package/src/planning.mjs +15 -157
- package/src/prose-nlp.mjs +4 -17
- package/src/prose.mjs +19 -67
- package/src/providers/bootstrap.mjs +1 -2
- package/src/providers/fixture.mjs +1 -2
- package/src/providers/graph-service.mjs +28 -59
- package/src/repository-interface.mjs +6 -8
- package/src/router/drive.mjs +183 -0
- package/src/router/goal-reasoner.mjs +66 -231
- package/src/router/guardrail.mjs +20 -58
- package/src/router/planner.mjs +15 -46
- package/src/router/registry.mjs +13 -43
- package/src/router/resolver.mjs +46 -131
- package/src/router/results.mjs +231 -0
- package/src/schema-docs.mjs +10 -27
- package/src/server-http.mjs +10 -19
- package/src/server.mjs +22 -28
- package/src/sessions.mjs +15 -30
- package/src/source-slice.mjs +5 -7
- package/src/source.mjs +10 -20
- package/src/syllogise.mjs +187 -575
- package/src/telemetry.mjs +3 -3
- package/src/toml-config.mjs +4 -4
- package/src/tui/app.mjs +9 -19
- package/src/viz.mjs +66 -123
- package/src/wink-model.mjs +10 -24
package/src/memory/fold.mjs
CHANGED
|
Binary file
|
package/src/memory/inspect.mjs
CHANGED
|
@@ -1,19 +1,7 @@
|
|
|
1
|
-
// memory/inspect.mjs — seeing into the memory as TEXT
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// - the memory graph grouped by OWL superclass (Fact / Utterance / Session,
|
|
6
|
-
// plus any other class present), counts with BALANCED samples scaled
|
|
7
|
-
// log-wise to class size (a 10,000-fact class shows ~8 exemplars, a
|
|
8
|
-
// 3-session class shows all 3);
|
|
9
|
-
// - top facts ranked by PROVENANCE BREADTH (a fact the corpus AND the chat
|
|
10
|
-
// both asserted outranks a single-writer fact), provenance verbatim;
|
|
11
|
-
// - recent Q→A utterance pairs (read off the mgx:inReplyTo edges);
|
|
12
|
-
// - the block-index summary (blocks, indexed tokens, top PageRank blocks).
|
|
13
|
-
//
|
|
14
|
-
// Pure renderers over loaded payloads + one thin I/O wrapper (inspectMemory).
|
|
15
|
-
// Everything degrades honestly: an empty memory renders as the empty story,
|
|
16
|
-
// never an error.
|
|
1
|
+
// memory/inspect.mjs — seeing into the memory as TEXT: one renderer serves
|
|
2
|
+
// both the `/memory` chat command and the `tmct memory` CLI, terse or
|
|
3
|
+
// verbose. Pure renderers + one thin I/O wrapper (inspectMemory); an empty
|
|
4
|
+
// memory renders as the empty story, never an error.
|
|
17
5
|
|
|
18
6
|
import { loadMemory, UTTERANCE_CLASS, IN_REPLY_TO_PROP, readFactRows, findContradictions } from "./core.mjs";
|
|
19
7
|
import { loadBlockIndex } from "./blocks.mjs";
|
|
@@ -67,10 +55,7 @@ export function renderMemory({ memory, blocks }, { verbose = false } = {}) {
|
|
|
67
55
|
for (const ind of balancedSample(of, k)) lines.push(` ${truncate(ind.label, textCap)}`);
|
|
68
56
|
}
|
|
69
57
|
|
|
70
|
-
// ---- top facts by
|
|
71
|
-
// Trust folds source-type prior + corroboration + recency, so a corroborated
|
|
72
|
-
// operator-stated fact outranks a lone web scrape by construction; provenance
|
|
73
|
-
// rides along (verbatim in verbose) for the audit trail.
|
|
58
|
+
// ---- top facts by computed trust (source prior + corroboration + recency) ----
|
|
74
59
|
const ranked = readFactRows(memory)
|
|
75
60
|
.filter((r) => r.sourceIds.length || r.provenance)
|
|
76
61
|
.sort((a, b) => b.trust - a.trust
|
|
@@ -128,14 +113,11 @@ export function renderMemory({ memory, blocks }, { verbose = false } = {}) {
|
|
|
128
113
|
lines.push("", "blocks — none folded yet (a session folds when it ends).");
|
|
129
114
|
}
|
|
130
115
|
|
|
131
|
-
// ---- explore hooks:
|
|
132
|
-
// stored, so /memory is a springboard for drilling in, not just a dump ----
|
|
116
|
+
// ---- explore hooks: runnable example queries built from what's stored ----
|
|
133
117
|
if (individuals.length) {
|
|
134
118
|
const clean = (t) => typeof t === "string" && /^[a-z][a-z0-9]+(?: [a-z0-9]{2,}){0,2}$/.test(t) && t.length <= 22 && !/^\d+$/.test(t);
|
|
135
119
|
const facts = readFactRows(memory);
|
|
136
|
-
// Rank
|
|
137
|
-
// them) so the hooks land on rich, recognisable categories (function, class, …),
|
|
138
|
-
// not a lone ConceptNet oddity.
|
|
120
|
+
// Rank "what is a X" candidates by category size (how many facts point at them).
|
|
139
121
|
const freq = new Map();
|
|
140
122
|
for (const f of facts) if (clean(f.object)) freq.set(f.object, (freq.get(f.object) || 0) + 1);
|
|
141
123
|
const terms = [...freq.entries()]
|
package/src/memory/shacl.mjs
CHANGED
|
@@ -1,37 +1,9 @@
|
|
|
1
|
-
// memory/shacl.mjs —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// app/ontology/shapes.ttl. This file is a small, HAND-ROLLED validator that
|
|
8
|
-
// implements exactly what that spec describes, in plain JS, against
|
|
9
|
-
// memory/core.mjs's own {id, label, class, attributes} individual shape.
|
|
10
|
-
//
|
|
11
|
-
// Deliberately NOT wired to a real SHACL/RDF-JS engine. `shacl-engine` +
|
|
12
|
-
// `rdf-ext` were tried first (the plan's named tooling, matching
|
|
13
|
-
// marginalia's own choice) and rejected on measurement: shacl-engine
|
|
14
|
-
// transitively pulls in `@comunica/query-sparql-rdfjs-lite` — a full
|
|
15
|
-
// federated SPARQL query engine — across 560+ packages (~7700 added
|
|
16
|
-
// package-lock.json lines), wildly disproportionate to tmct's "pure-JS,
|
|
17
|
-
// minimal-deps" floor (5 runtime deps before this) and to what three closed,
|
|
18
|
-
// bounded shapes actually need. `src/conformance.mjs` already proves this
|
|
19
|
-
// project is comfortable with imperative shape assertions instead of a
|
|
20
|
-
// general engine (a Repository-Interface contract-test suite, not a memory
|
|
21
|
-
// gate — a DIFFERENT thing from this file, see its own header); this module
|
|
22
|
-
// is the same discipline applied to memory-write validation. Keep
|
|
23
|
-
// ontology/memory-shapes.ttl and this file in sync BY HAND when either shape
|
|
24
|
-
// changes — the .ttl is documentation here, not machine-read.
|
|
25
|
-
//
|
|
26
|
-
// Every shape below is PERMISSIVE beyond memory/core.mjs's own existing
|
|
27
|
-
// structural floor (appendFact/appendRule already throw before ever reaching
|
|
28
|
-
// mutateMemory if subject/predicate/object or name/kind/slots are missing —
|
|
29
|
-
// this gate mirrors, not tightens, that floor) and treats every OPTIONAL
|
|
30
|
-
// attribute (provenance chief among them — appendFact's own signature
|
|
31
|
-
// defaults `provenance` to `""`, and real call sites/tests legitimately omit
|
|
32
|
-
// it, e.g. re-writing createdAt without re-asserting provenance) as OPTIONAL
|
|
33
|
-
// here too: a violation only fires on genuine structural malformation, never
|
|
34
|
-
// on a legitimately sparse-but-valid write or upsert of existing data.
|
|
1
|
+
// memory/shacl.mjs — SHACL-style ingest gate for tmct's own memory graph.
|
|
2
|
+
// Hand-rolled validator mirroring ontology/memory-shapes.ttl (kept in sync by
|
|
3
|
+
// hand) against memory/core.mjs's {id, label, class, attributes} shape — not
|
|
4
|
+
// wired to a real SHACL/RDF-JS engine (disproportionate deps for three closed
|
|
5
|
+
// shapes). Permissive beyond core.mjs's own structural floor: a violation
|
|
6
|
+
// only fires on genuine malformation, never a sparse-but-valid write.
|
|
35
7
|
|
|
36
8
|
const MEMORY_CLASSES = new Set(["Utterance", "Fact", "Session", "Source", "Rule"]);
|
|
37
9
|
const RULE_KINDS = new Set(["compose2", "filter", "recursive"]);
|
|
@@ -62,8 +34,8 @@ function checkIndividual(ind, violations) {
|
|
|
62
34
|
|
|
63
35
|
/** FactShape (mgx:FactShape): the reified subject/predicate/object, each
|
|
64
36
|
* present and non-empty; mgx:factProvenance, WHEN PRESENT, must be
|
|
65
|
-
* non-empty (optional at this gate —
|
|
66
|
-
*
|
|
37
|
+
* non-empty (optional at this gate — appendFact's own API allows an
|
|
38
|
+
* empty/omitted provenance). */
|
|
67
39
|
function checkFact(ind, violations) {
|
|
68
40
|
for (const prop of ["rdf:subject", "rdf:predicate", "rdf:object"]) {
|
|
69
41
|
if (!nonEmpty(attrValue(ind, prop))) violations.push(`a Fact needs a non-empty ${prop}`);
|
|
@@ -101,9 +73,8 @@ export function validateIndividual(ind) {
|
|
|
101
73
|
|
|
102
74
|
/** The ingest gate: throw a clear, aggregated error if `ind` violates the
|
|
103
75
|
* shape contract, so a malformed Fact/Rule never reaches mutateMemory's
|
|
104
|
-
* write. Synchronous
|
|
105
|
-
*
|
|
106
|
-
* caller's promise correctly; a non-throwing sync return awaits to itself). */
|
|
76
|
+
* write. Synchronous, but safe to `await` — a sync throw inside an async
|
|
77
|
+
* caller still rejects its promise correctly. */
|
|
107
78
|
export function assertIndividualValid(ind) {
|
|
108
79
|
const r = validateIndividual(ind);
|
|
109
80
|
if (!r.ok) {
|
package/src/memory/trust.mjs
CHANGED
|
@@ -1,64 +1,15 @@
|
|
|
1
|
-
// memory/trust.mjs — deterministic, explainable, auditable trust over a
|
|
2
|
-
// Sources (
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// - a Source-TYPE PRIOR (operator > teach > provider > corpus > corpusWeak >
|
|
8
|
-
// extracted > web > entailed);
|
|
9
|
-
// - CORROBORATION over the fact's distinct Sources by noisy-OR
|
|
10
|
-
// (1 − Π(1 − wᵢ), capped at 1) — two independent web sources (0.4) reach
|
|
11
|
-
// 0.64, a lone operator fact is already 1.0;
|
|
12
|
-
// - a bounded RECENCY nudge in [0.9, 1.0] from createdAt, half-life decayed —
|
|
13
|
-
// the codegraph "capped nudge" philosophy, so recency breaks ties and
|
|
14
|
-
// freshens but never flips a source-type ordering by itself.
|
|
15
|
-
//
|
|
16
|
-
// A fourth, per-Source bounded nudge folds into the type-prior term above (not a
|
|
17
|
-
// separate multiplicative stage): each Source may carry mgx:sourceReliability in
|
|
18
|
-
// [0.5, 1.5] (neutral 1.0 when absent, true of every Source until a session's
|
|
19
|
-
// actor-level trust — sessionReliabilityFrom, core.mjs's recomputeSourceReliability —
|
|
20
|
-
// starts writing it), so a session with a track record of corroborated facts
|
|
21
|
-
// nudges its own Source's contribution up, one contradicted repeatedly nudges it
|
|
22
|
-
// down — additive and safe: absent, every existing score is byte-identical.
|
|
23
|
-
//
|
|
24
|
-
// For ENTAILED facts (tier-5): trust = min(premise trusts) × rule-confidence — a
|
|
25
|
-
// conclusion is only as trustworthy as its weakest premise. Premises may be
|
|
26
|
-
// absent for now, so this is a documented HOOK: pass opts.premiseTrusts (and
|
|
27
|
-
// opts.ruleConfidence) and it engages; otherwise an entailed fact scores off its
|
|
28
|
-
// bare 0.3 prior like any other Source.
|
|
29
|
-
//
|
|
30
|
-
// This module is PURE and import-free of core.mjs (no cycle): it reads Source
|
|
31
|
-
// individuals by their attribute props and returns { score, inputs }. core.mjs
|
|
32
|
-
// materialises the score onto the Fact (mgx:trustScore) plus the inputs it was
|
|
33
|
-
// computed from (mgx:trustInputs), so every score is reproducible and auditable.
|
|
1
|
+
// memory/trust.mjs — deterministic, explainable, auditable trust over a
|
|
2
|
+
// Fact's Sources. Trust = f(source-type prior, noisy-OR corroboration,
|
|
3
|
+
// recency decay), nudged by each Source's mgx:sourceReliability (neutral 1.0
|
|
4
|
+
// when absent). Entailed facts: min(premise trusts) × rule-confidence when
|
|
5
|
+
// premises are supplied, else the bare entailed prior. Pure, import-free of
|
|
6
|
+
// core.mjs; core.mjs materialises the result onto the Fact.
|
|
34
7
|
|
|
35
8
|
export const TRUST_SCORE_PROP = "mgx:trustScore";
|
|
36
9
|
export const TRUST_INPUTS_PROP = "mgx:trustInputs";
|
|
37
10
|
|
|
38
|
-
/** Source-type priors — the
|
|
39
|
-
*
|
|
40
|
-
* entailment. `teach` is the chat teach lane's natural-frame writes
|
|
41
|
-
* ("remember that …", "<Name> owns <X>") — still operator speech, but
|
|
42
|
-
* through a looser recognizer than the ACE-parsed operator assert, so it
|
|
43
|
-
* sits just below the operator prior. The entailed value is a FLOOR before
|
|
44
|
-
* premise adjustment (see the entailed hook below).
|
|
45
|
-
*
|
|
46
|
-
* `corpusWeak` is for corpus-sourced facts whose underlying relation is real
|
|
47
|
-
* but low-precision (ConceptNet's /r/RelatedTo — ambiguous, undirected
|
|
48
|
-
* association, unlike the specific typed relations the plain `corpus` prior
|
|
49
|
-
* covers) — still a curated, committed dataset (above `web`), just not
|
|
50
|
-
* asserting the same strength of claim (below `corpus`).
|
|
51
|
-
*
|
|
52
|
-
* `extracted` is scripts/extract-facts-from-text.mjs's batch reader: it runs
|
|
53
|
-
* the SAME deterministic teach/assert recognizer as `teach`/`operator`, but
|
|
54
|
-
* unattended over an arbitrary document nobody in-session vetted sentence by
|
|
55
|
-
* sentence — the recognizer is exact/closed-set (no guessing), but the
|
|
56
|
-
* SOURCE DOCUMENT is unreviewed, so it sits just above `web` (also an
|
|
57
|
-
* unreviewed external source) and below `corpusWeak`/`corpus` (curated,
|
|
58
|
-
* committed datasets) and `teach` (a human typing into the live chat).
|
|
59
|
-
*
|
|
60
|
-
* Both are computed from the Source's type exactly like every other tier —
|
|
61
|
-
* never hand-set on a Fact directly. */
|
|
11
|
+
/** Source-type priors — computed from the Source's type only, never hand-set
|
|
12
|
+
* on a Fact directly. */
|
|
62
13
|
export const SOURCE_PRIOR = Object.freeze({
|
|
63
14
|
operator: 1.0,
|
|
64
15
|
teach: 0.95,
|
|
@@ -73,14 +24,9 @@ export const SOURCE_PRIOR = Object.freeze({
|
|
|
73
24
|
export const RECENCY_HALF_LIFE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
74
25
|
export const RECENCY_FLOOR = 0.9; // recency multiplier stays within [0.9, 1.0]
|
|
75
26
|
|
|
76
|
-
// Actor-level (session-scoped) trust — a bounded
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
// lives in [SOURCE_RELIABILITY_MIN, SOURCE_RELIABILITY_MAX], NEUTRAL (1.0,
|
|
80
|
-
// exactly — no rounding drift) when absent, which is true of every Source
|
|
81
|
-
// until core.mjs's recomputeSourceReliability starts writing it. Neutral-when-
|
|
82
|
-
// absent makes this safely additive: every existing score is byte-identical
|
|
83
|
-
// until something actually writes the attribute.
|
|
27
|
+
// Actor-level (session-scoped) trust — a bounded nudge on a Source's type
|
|
28
|
+
// prior. Neutral (1.0) until core.mjs's recomputeSourceReliability writes it,
|
|
29
|
+
// so this stays additive-safe.
|
|
84
30
|
export const SOURCE_RELIABILITY_MIN = 0.5;
|
|
85
31
|
export const SOURCE_RELIABILITY_MAX = 1.5;
|
|
86
32
|
export const SOURCE_RELIABILITY_NEUTRAL = 1.0;
|
|
@@ -88,10 +34,8 @@ export const SOURCE_RELIABILITY_NEUTRAL = 1.0;
|
|
|
88
34
|
const round = (n, p = 6) => Number(n.toFixed(p));
|
|
89
35
|
const sourceTypeOf = (s) => (s?.attributes || []).find((a) => a.prop === "mgx:sourceType")?.value || "";
|
|
90
36
|
|
|
91
|
-
/** A Source's reliability multiplier:
|
|
92
|
-
*
|
|
93
|
-
* out-of-range stored value is defended against, never trusted blindly), or
|
|
94
|
-
* the neutral 1.0 when the attribute is absent/unparseable. */
|
|
37
|
+
/** A Source's reliability multiplier: raw mgx:sourceReliability, clamped to
|
|
38
|
+
* [SOURCE_RELIABILITY_MIN, MAX], or neutral 1.0 when absent/unparseable. */
|
|
95
39
|
function sourceReliabilityOf(s) {
|
|
96
40
|
const raw = (s?.attributes || []).find((a) => a.prop === "mgx:sourceReliability")?.value;
|
|
97
41
|
if (raw === undefined) return SOURCE_RELIABILITY_NEUTRAL;
|
|
@@ -100,12 +44,8 @@ function sourceReliabilityOf(s) {
|
|
|
100
44
|
return Math.max(SOURCE_RELIABILITY_MIN, Math.min(SOURCE_RELIABILITY_MAX, n));
|
|
101
45
|
}
|
|
102
46
|
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
105
|
-
* A half-life decay: freshly written ≈ 1.0, ancient → RECENCY_FLOOR. An unknown
|
|
106
|
-
* or unparseable timestamp yields 1.0 (no penalty) — recency only ever nudges
|
|
107
|
-
* down from a full score, it never invents one.
|
|
108
|
-
*/
|
|
47
|
+
/** Bounded recency multiplier in [RECENCY_FLOOR, 1], half-life decayed from
|
|
48
|
+
* createdAt. An unparseable timestamp yields 1.0 (no penalty). */
|
|
109
49
|
export function recencyNudge(createdAt, now = Date.now(), halfLifeMs = RECENCY_HALF_LIFE_MS) {
|
|
110
50
|
const t = Date.parse(createdAt);
|
|
111
51
|
if (!Number.isFinite(t)) return 1;
|
|
@@ -115,31 +55,18 @@ export function recencyNudge(createdAt, now = Date.now(), halfLifeMs = RECENCY_H
|
|
|
115
55
|
|
|
116
56
|
/**
|
|
117
57
|
* Pure trust for one Fact. `fact` supplies `{ sourceIds: [...], createdAt }`;
|
|
118
|
-
*
|
|
119
|
-
* map — exactly what a memory payload's Source individuals key into). Distinct
|
|
58
|
+
* Source individuals resolve from `sourcesById` ({ id: Source }). Distinct
|
|
120
59
|
* sources are corroborated by noisy-OR over their type priors and nudged by
|
|
121
60
|
* recency. Deterministic given the same inputs and `opts.now`.
|
|
122
61
|
*
|
|
123
|
-
* opts:
|
|
124
|
-
*
|
|
125
|
-
* - halfLifeMs recency half-life override
|
|
126
|
-
* - premiseTrusts entailed hook: [trusts] of the conclusion's premise Facts
|
|
127
|
-
* - ruleConfidence entailed hook: the rule's confidence in [0,1] (default 1)
|
|
128
|
-
*
|
|
129
|
-
* Returns { score, inputs } — `inputs` (the source-type multiset, corroboration
|
|
130
|
-
* count, createdAt and the recency multiplier) is stored alongside the score so
|
|
131
|
-
* "why does this rank high?" is answerable from the record.
|
|
62
|
+
* opts: now, halfLifeMs, premiseTrusts/ruleConfidence (entailed hook).
|
|
63
|
+
* Returns { score, inputs } — inputs recorded for audit.
|
|
132
64
|
*/
|
|
133
65
|
export function computeTrust(fact, sourcesById = {}, opts = {}) {
|
|
134
66
|
const now = typeof opts.now === "number" ? opts.now : Date.now();
|
|
135
67
|
const ids = Array.isArray(fact?.sourceIds) ? fact.sourceIds : [];
|
|
136
68
|
|
|
137
|
-
// distinct sources
|
|
138
|
-
// mgx:sourceReliability (neutral 1.0 when absent — see sourceReliabilityOf).
|
|
139
|
-
// `types` stays the plain type multiset (the audit-trail shape callers/tests
|
|
140
|
-
// already read off `inputs.sourceTypes` is unchanged); `priors` is the
|
|
141
|
-
// per-source EFFECTIVE prior (type prior × reliability, clamped to [0,1])
|
|
142
|
-
// the noisy-OR below actually corroborates over.
|
|
69
|
+
// distinct sources -> type priors × mgx:sourceReliability, clamped to [0,1]
|
|
143
70
|
const seen = new Set();
|
|
144
71
|
const types = [];
|
|
145
72
|
const priors = [];
|
|
@@ -179,43 +106,15 @@ export function computeTrust(fact, sourcesById = {}, opts = {}) {
|
|
|
179
106
|
return { score, inputs };
|
|
180
107
|
}
|
|
181
108
|
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
// this, a SINGLE data point saturates the score immediately (asserted=1,
|
|
185
|
-
// contradicted=0 → the bare max 1.5) — measured in practice, this broke the
|
|
186
|
-
// standing invariant that a lone, uncontradicted teach-sourced fact must
|
|
187
|
-
// still score below the operator prior (0.95 × 1.5 clamps past 1.0). This
|
|
188
|
-
// pseudo-count keeps a thin track record close to NEUTRAL and only lets a
|
|
189
|
-
// session earn a confident nudge once it has a real history — the classic
|
|
190
|
-
// Bayesian-smoothing shape (Laplace/"add-k" pseudo-count) for small-sample
|
|
191
|
-
// rates.
|
|
109
|
+
// Laplace/"add-k" pseudo-count: without it a single data point would saturate
|
|
110
|
+
// mgx:sourceReliability to the bare max/min immediately.
|
|
192
111
|
export const RELIABILITY_CONFIDENCE_PSEUDOCOUNT = 19;
|
|
193
112
|
|
|
194
|
-
/**
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
* mgx:sourceReliability lives in (B1 above), so the result of this function is
|
|
200
|
-
* exactly what a caller materialises onto a session's Source individual.
|
|
201
|
-
*
|
|
202
|
-
* Monotonic in the right direction: more uncontradicted assertions → closer to
|
|
203
|
-
* the max (1.5); more contradicted ones → closer to the min (0.5) — but
|
|
204
|
-
* CONFIDENCE-SCALED by sample size, so a session with only one or two
|
|
205
|
-
* assertions stays close to neutral (1.0) either way, and only a real track
|
|
206
|
-
* record (many assertions) earns a confident swing toward an extreme. Zero
|
|
207
|
-
* assertions is exactly neutral (1.0) — no track record, no opinion, matching
|
|
208
|
-
* the neutral default a Source without this attribute at all already gets
|
|
209
|
-
* (sourceReliabilityOf above). The shape:
|
|
210
|
-
* net = clamp[-1,1]((asserted − 2×contradicted) / max(1, asserted))
|
|
211
|
-
* confidence = asserted / (asserted + RELIABILITY_CONFIDENCE_PSEUDOCOUNT)
|
|
212
|
-
* ratio = (net × confidence + 1) / 2 → [0, 1], 0.5 at confidence 0
|
|
213
|
-
* reliability = MIN + (MAX − MIN) × ratio
|
|
214
|
-
* Each contradicted fact costs DOUBLE an asserted fact's worth of `net` (the
|
|
215
|
-
* `2×` term), so a session that is right twice and wrong once nets a positive
|
|
216
|
-
* but reduced score rather than a wash — corroboration should count for less
|
|
217
|
-
* than the reputational cost of a contradiction. Deterministic, no I/O.
|
|
218
|
-
*/
|
|
113
|
+
/** Pure actor-level reliability from a session's asserted-vs-contradicted
|
|
114
|
+
* track record (findContradictions, core.mjs), confidence-scaled by sample
|
|
115
|
+
* size so a thin record stays near neutral (1.0). Bounded to
|
|
116
|
+
* [SOURCE_RELIABILITY_MIN, MAX]. A contradicted fact costs double an
|
|
117
|
+
* asserted one's weight. */
|
|
219
118
|
export function sessionReliabilityFrom({ factsAsserted = 0, factsContradicted = 0 } = {}) {
|
|
220
119
|
const asserted = Math.max(0, Number(factsAsserted) || 0);
|
|
221
120
|
const contradicted = Math.max(0, Number(factsContradicted) || 0);
|
|
@@ -1,36 +1,13 @@
|
|
|
1
1
|
// memory-ask-browser-entry.mjs — the esbuild entry for `tmct viz`'s embedded
|
|
2
|
-
// "Ask the graph" panel's
|
|
2
|
+
// "Ask the graph" panel's memory-graph engine.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
// (
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// anticipates: re-exports just `factAnswer` (src/chat.mjs) — tmct's REAL
|
|
10
|
-
// memory-graph answer engine, the same one `npm run chat` uses — plus
|
|
11
|
-
// `createInMemoryStore` (src/memory/core.mjs), which is how the panel hands
|
|
12
|
-
// `factAnswer` the page's already-embedded PAYLOAD with ZERO fs I/O: a
|
|
13
|
-
// Backend-B handle's `loadMemory` branch returns `handle.payload` directly, no
|
|
14
|
-
// bundle-time module shimming needed (see factAnswer's own doc comment,
|
|
15
|
-
// src/chat.mjs, for the full reasoning — a simpler, more robust mechanism than
|
|
16
|
-
// intercepting loadMemory at bundle time, since it reuses machinery the
|
|
17
|
-
// codebase already ships and tests, rather than a new esbuild-only code path
|
|
18
|
-
// that could drift from the real one).
|
|
19
|
-
//
|
|
20
|
-
// `factAnswer` is called with `envelope: null, miss: true` — the exact,
|
|
21
|
-
// already-documented "no envelope available" bootstrap path (chat.mjs's own
|
|
22
|
-
// comments: "the FIRST turn of a graph-less session... leaves `envelope` null
|
|
23
|
-
// for the rest of THIS turn's processing" — a real, tested code path, not a
|
|
24
|
-
// hack) — which arms factAnswer's own bare-question regex fallbacks
|
|
25
|
-
// (BARE_WHATIS_RE and friends) to parse the query directly, with no
|
|
26
|
-
// dependency on the much larger structural-graph parse pipeline
|
|
27
|
-
// (dispatchTool/loadGraph, server.mjs) that pipeline needs a real --repo code
|
|
28
|
-
// index for and this panel has no use for.
|
|
4
|
+
// Re-exports `factAnswer` (src/chat.mjs) and `createInMemoryStore`
|
|
5
|
+
// (src/memory/core.mjs), which lets the panel hand `factAnswer` the page's
|
|
6
|
+
// already-embedded payload with zero fs I/O. Called with `envelope: null,
|
|
7
|
+
// miss: true` to arm factAnswer's bare-question regex fallbacks directly,
|
|
8
|
+
// bypassing the structural-graph parse pipeline this panel has no use for.
|
|
29
9
|
import { factAnswer } from "./chat.mjs";
|
|
30
10
|
import { createInMemoryStore, normFactTerm } from "./memory/core.mjs";
|
|
31
11
|
|
|
32
|
-
// normFactTerm is re-exported too
|
|
33
|
-
// memory-engine's answer" heuristic (guessTermIdFromQuery) needs the SAME
|
|
34
|
-
// term normalization the CLI's own `--term` seed flag and factAnswer's own
|
|
35
|
-
// subject/object matching use, never a second hand-rolled copy.
|
|
12
|
+
// normFactTerm is re-exported too, for viz.mjs's client-side term normalization.
|
|
36
13
|
globalThis.tmctMemoryAsk = { factAnswer, createInMemoryStore, normFactTerm };
|
|
@@ -2630,7 +2630,7 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
|
|
|
2630
2630
|
// (neither recognizes the bare "is in" idiom once "else" sits in front of it).
|
|
2631
2631
|
// The only rescue was the relaxation cascade's drop-unmatched layer — but that
|
|
2632
2632
|
// layer refuses to accept a relaxed reading that still renders an honest EMPTY
|
|
2633
|
-
// (
|
|
2633
|
+
// (: relaxation must turn a miss into a real answer, never into
|
|
2634
2634
|
// another kind of miss), so a genuinely empty class ("what else is in
|
|
2635
2635
|
// Task.complete" — a method, no members) bottomed out at the bare grammar
|
|
2636
2636
|
// wall instead of the specific "no contains edges" receipt. Routing this
|
package/src/paraphrase.mjs
CHANGED
|
@@ -1,48 +1,23 @@
|
|
|
1
|
-
// paraphrase.mjs —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// running tmct's OWN deterministic inference machinery (`src/syllogise.mjs`)
|
|
7
|
-
// against both the original and the paraphrase.
|
|
1
|
+
// paraphrase.mjs — a surface-realization variant of an isa-family
|
|
2
|
+
// (`rdfs:subClassOf`) teach confirmation, shown alongside the original, never
|
|
3
|
+
// replacing it, and only when verified against tmct's own inference engine
|
|
4
|
+
// (src/syllogise.mjs). Scoped to isa-family facts only — the one predicate
|
|
5
|
+
// family deriveSubClassClosure reasons over.
|
|
8
6
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// `PLAN_BREADTH_FIRST_NLU.md` §8 canonical example) already anchors on;
|
|
16
|
-
// widening to the other predicate families is a natural, separately-scoped
|
|
17
|
-
// follow-on once this shape is proven live.
|
|
18
|
-
//
|
|
19
|
-
// The generator and the recognizer are a MATCHED PAIR by construction — every
|
|
20
|
-
// template `paraphraseSubClass` can produce has a corresponding branch in
|
|
21
|
-
// `recoverSubClassTriple` that parses it back to exactly the same
|
|
22
|
-
// {subject, object} pair. This is a CLOSED set (never open-ended NLP), so
|
|
23
|
-
// recognition is exact, not fuzzy. "Verified" means: re-derive the
|
|
24
|
-
// `rdfs:subClassOf` transitive closure (the real conclusions this fact would
|
|
25
|
-
// license, via `deriveSubClassClosure`) once seeded with the ORIGINAL triple
|
|
26
|
-
// and once seeded with the triple RECOVERED FROM the paraphrase, over the
|
|
27
|
-
// SAME existing taught edges — the two closures must be identical (both
|
|
28
|
-
// derived edge SETS byte-for-byte equal). A generator bug that silently
|
|
29
|
-
// swapped subject/object (a real risk for a passive-voice template) would be
|
|
30
|
-
// caught here even in a graph with no other taught facts at all, because a
|
|
31
|
-
// swapped pair changes the closure's own subject/object roles the moment any
|
|
32
|
-
// OTHER edge touches either term — exactly the "must entail the same
|
|
33
|
-
// conclusions, neither may contradict the other" check the Ambition asks for,
|
|
34
|
-
// not a shallower string-equality stand-in for it.
|
|
7
|
+
// Verification: re-derive the rdfs:subClassOf closure once from the original
|
|
8
|
+
// triple and once from the triple recovered by parsing the paraphrase back —
|
|
9
|
+
// over the same existing taught edges, the two closures must be identical.
|
|
10
|
+
// This catches a generator bug that silently swapped subject/object, since a
|
|
11
|
+
// swap changes the closure's roles as soon as any other edge touches either
|
|
12
|
+
// term.
|
|
35
13
|
|
|
36
14
|
import { deriveSubClassClosure } from "./syllogise.mjs";
|
|
37
15
|
import { normFactTerm } from "./memory/core.mjs";
|
|
38
16
|
import { fnv1aHex } from "./hash.mjs";
|
|
39
17
|
|
|
40
18
|
// Every template reads "SUBJECT ⊑ OBJECT" left to right — no passive/reordered
|
|
41
|
-
// form
|
|
42
|
-
//
|
|
43
|
-
// left-to-right order fixed across every template keeps the recognizer trivial
|
|
44
|
-
// AND correct by construction, rather than needing the closure check to catch
|
|
45
|
-
// a bug the generator could have avoided entirely.
|
|
19
|
+
// form, since that's the shape most likely to invert subject/object under a
|
|
20
|
+
// naive regex recognizer.
|
|
46
21
|
const articleFor = (word) => (/^[aeiou]/i.test(String(word || "")) ? "an" : "a");
|
|
47
22
|
const SUBCLASS_TEMPLATES = [
|
|
48
23
|
(s, o) => `${s} is a kind of ${o}`,
|
|
@@ -51,10 +26,8 @@ const SUBCLASS_TEMPLATES = [
|
|
|
51
26
|
(s, o) => `${s} counts as ${articleFor(o)} ${o}`,
|
|
52
27
|
];
|
|
53
28
|
|
|
54
|
-
// One regex per template above, in the SAME order
|
|
55
|
-
//
|
|
56
|
-
// side can never silently desync from the other without a test catching it
|
|
57
|
-
// (see paraphrase.test.mjs's own round-trip check over every template).
|
|
29
|
+
// One regex per template above, in the SAME order (paired by index, not
|
|
30
|
+
// derived from the generator).
|
|
58
31
|
const SUBCLASS_RECOGNIZERS = [
|
|
59
32
|
/^(.+?)\s+is\s+a\s+kind\s+of\s+(.+)$/i,
|
|
60
33
|
/^(.+?)\s+is\s+a\s+type\s+of\s+(.+)$/i,
|
|
@@ -92,17 +65,11 @@ export function recoverSubClassTriple(text) {
|
|
|
92
65
|
return null;
|
|
93
66
|
}
|
|
94
67
|
|
|
95
|
-
/**
|
|
96
|
-
* from the paraphrase's
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* straight off taught Fact rows elsewhere in this codebase). Returns
|
|
101
|
-
* `{verified, closure}` — `closure` is the original triple's own derived
|
|
102
|
-
* conclusions (handed back so a caller can show/log them), `verified` is
|
|
103
|
-
* false when the paraphrase's recovered triple doesn't reparse, doesn't match
|
|
104
|
-
* the original's normalized (subject, object), or derives a different
|
|
105
|
-
* closure — any one of those means the paraphrase must NOT be shown. */
|
|
68
|
+
/** Verify: re-derive the rdfs:subClassOf closure from the original triple and
|
|
69
|
+
* from the paraphrase's recovered triple, over the same existing taught
|
|
70
|
+
* edges. Returns `{verified, closure}` — `verified` is false if the
|
|
71
|
+
* paraphrase doesn't reparse, doesn't match the original (subject, object),
|
|
72
|
+
* or derives a different closure. */
|
|
106
73
|
export function verifySubClassParaphrase(subject, object, paraphraseText, existingEdges = []) {
|
|
107
74
|
const origSubj = normFactTerm(subject);
|
|
108
75
|
const origObj = normFactTerm(object);
|