@polycode-projects/the-mechanical-code-talker 3.1.0 → 3.1.3
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 +2 -0
- package/corpus/seon/relations.jsonl +2 -0
- package/package.json +3 -1
- package/src/adapters/repository-interface.mjs +3 -0
- package/src/domain/ask-vocab.mjs +23 -0
- package/src/domain/ask.mjs +63 -13
- package/src/domain/code-explorer-hints.mjs +4 -0
- package/src/domain/codegraph.mjs +25 -17
- package/src/domain/codeplan/graph-delta.mjs +1 -0
- package/src/domain/concept.mjs +16 -0
- package/src/domain/interpret/normalize.mjs +16 -1
- package/src/domain/module-paths.mjs +6 -4
- package/src/domain/paraphrase-ing8.mjs +195 -0
- package/src/domain/real-word-collisions.json +1 -1
- package/src/domain/router/resolver.mjs +2 -0
- package/src/services/chat-session.mjs +5 -0
- package/src/services/chat.mjs +207 -32
- package/src/services/finish.mjs +6 -1
- package/src/surfaces/web/memory-ask-browser.bundle.js +113 -113
- package/src/tools/handlers/kit.mjs +5 -3
- package/src/tools/handlers/tmct-context.mjs +3 -2
- package/src/tools/handlers/tmct-export.mjs +9 -3
- package/src/tools/handlers/tmct-members.mjs +1 -1
- package/src/tools/handlers/tmct-search.mjs +1 -0
- package/src/tools/schema-docs.mjs +10 -0
- package/src/tools/server.mjs +9 -4
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// paraphrase-ing8.mjs — the deterministic ING-8 equivalence checker: the harder
|
|
2
|
+
// whole-document paraphrase shapes ING-7's own checker (verifySubClassParaphrase,
|
|
3
|
+
// ./paraphrase.mjs) doesn't cover — non-isa relations (has/creates/capableOf),
|
|
4
|
+
// multi-sentence documents, and synonym substitution over that closed relation
|
|
5
|
+
// vocabulary.
|
|
6
|
+
//
|
|
7
|
+
// Strategy, mirroring verifySubClassParaphrase's own pattern: wherever the
|
|
8
|
+
// predicate space allows exact re-derivation (a single isa fact on both sides),
|
|
9
|
+
// reuse ING-7's own closure re-derivation directly. Everywhere else — every
|
|
10
|
+
// non-isa relation, and every multi-sentence document — there is no entailment
|
|
11
|
+
// closure to re-derive over, so the fallback is normalization + a closed
|
|
12
|
+
// synonym-template table per relation, recognized and compared as an exact set.
|
|
13
|
+
// A document holding a sentence outside the closed template set is reported
|
|
14
|
+
// unverified, never guessed — the same "verified, never instead of the
|
|
15
|
+
// original" discipline paraphrase.mjs itself states for the isa case.
|
|
16
|
+
|
|
17
|
+
import { recoverSubClassTriple, verifySubClassParaphrase } from "./paraphrase.mjs";
|
|
18
|
+
import { normFactTerm, fnv1aHex } from "./hash.mjs";
|
|
19
|
+
|
|
20
|
+
const articleFor = (word) => (/^[aeiou]/i.test(String(word || "")) ? "an" : "a");
|
|
21
|
+
|
|
22
|
+
// Every template reads "SUBJECT verb OBJECT" left to right, same discipline as
|
|
23
|
+
// paraphrase.mjs's SUBCLASS_TEMPLATES — no passive/reordered form, since that's
|
|
24
|
+
// the shape most likely to invert subject/object under a naive recognizer.
|
|
25
|
+
const HAS_TEMPLATES = [
|
|
26
|
+
(s, o) => `${s} has ${articleFor(o)} ${o}`,
|
|
27
|
+
(s, o) => `${s} possesses ${articleFor(o)} ${o}`,
|
|
28
|
+
(s, o) => `${s} owns ${articleFor(o)} ${o}`,
|
|
29
|
+
(s, o) => `${s} carries ${articleFor(o)} ${o}`,
|
|
30
|
+
];
|
|
31
|
+
const HAS_RECOGNIZERS = [
|
|
32
|
+
/^(.+?)\s+has\s+an?\s+(.+)$/i,
|
|
33
|
+
/^(.+?)\s+possesses\s+an?\s+(.+)$/i,
|
|
34
|
+
/^(.+?)\s+owns\s+an?\s+(.+)$/i,
|
|
35
|
+
/^(.+?)\s+carries\s+an?\s+(.+)$/i,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const CREATES_TEMPLATES = [
|
|
39
|
+
(s, o) => `${s} creates ${o}`,
|
|
40
|
+
(s, o) => `${s} produces ${o}`,
|
|
41
|
+
(s, o) => `${s} generates ${o}`,
|
|
42
|
+
(s, o) => `${s} causes ${o}`,
|
|
43
|
+
];
|
|
44
|
+
const CREATES_RECOGNIZERS = [
|
|
45
|
+
/^(.+?)\s+creates\s+(.+)$/i,
|
|
46
|
+
/^(.+?)\s+produces\s+(.+)$/i,
|
|
47
|
+
/^(.+?)\s+generates\s+(.+)$/i,
|
|
48
|
+
/^(.+?)\s+causes\s+(.+)$/i,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
const CAPABLEOF_TEMPLATES = [
|
|
52
|
+
(s, o) => `${s} can ${o}`,
|
|
53
|
+
(s, o) => `${s} is able to ${o}`,
|
|
54
|
+
(s, o) => `${s} knows how to ${o}`,
|
|
55
|
+
];
|
|
56
|
+
const CAPABLEOF_RECOGNIZERS = [
|
|
57
|
+
/^(.+?)\s+can\s+(.+)$/i,
|
|
58
|
+
/^(.+?)\s+is\s+able\s+to\s+(.+)$/i,
|
|
59
|
+
/^(.+?)\s+knows\s+how\s+to\s+(.+)$/i,
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
// The closed non-isa relation vocabulary this checker recognizes. Each family
|
|
63
|
+
// pairs its templates and recognizers by index, same convention as isa.
|
|
64
|
+
const RELATION_FAMILIES = {
|
|
65
|
+
has: { templates: HAS_TEMPLATES, recognizers: HAS_RECOGNIZERS },
|
|
66
|
+
creates: { templates: CREATES_TEMPLATES, recognizers: CREATES_RECOGNIZERS },
|
|
67
|
+
capableOf: { templates: CAPABLEOF_TEMPLATES, recognizers: CAPABLEOF_RECOGNIZERS },
|
|
68
|
+
};
|
|
69
|
+
export const RELATION_FAMILY_IDS = Object.keys(RELATION_FAMILIES);
|
|
70
|
+
|
|
71
|
+
/** Deterministic template pick — same (subject, object) always picks the same
|
|
72
|
+
* template, spread across the table by a pure hash (paraphrase.mjs's own
|
|
73
|
+
* pickTemplateIndex pattern). `variantIndex`, when given, overrides the hash
|
|
74
|
+
* pick (a corpus generator wanting two DIFFERENT closed phrasings for the same
|
|
75
|
+
* fact passes distinct indices explicitly rather than relying on the hash). */
|
|
76
|
+
function pickTemplateIndex(subject, object, templateCount, variantIndex) {
|
|
77
|
+
if (variantIndex !== null && variantIndex !== undefined) {
|
|
78
|
+
return ((variantIndex % templateCount) + templateCount) % templateCount;
|
|
79
|
+
}
|
|
80
|
+
const h = fnv1aHex(`${subject}\0${object}`);
|
|
81
|
+
return parseInt(h.slice(0, 8), 16) % templateCount;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Generate a closed-template phrasing of `subject <family> object` — never
|
|
85
|
+
* null, always one of RELATION_FAMILIES[family]'s templates. Rule/template-
|
|
86
|
+
* based only, no LLM, matching paraphrase.mjs's paraphraseSubClass. */
|
|
87
|
+
export function paraphraseRelation(family, subject, object, variantIndex = null) {
|
|
88
|
+
const def = RELATION_FAMILIES[family];
|
|
89
|
+
if (!def) throw new Error(`paraphrase-ing8: unknown relation family "${family}"`);
|
|
90
|
+
const idx = pickTemplateIndex(subject, object, def.templates.length, variantIndex);
|
|
91
|
+
return def.templates[idx](subject, object);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Recover {subject, object} from text matching one of `family`'s closed
|
|
95
|
+
* recognizers — null if it matches none of them. A plain closed-set regex
|
|
96
|
+
* match, never a fuzzy/NLP parse, the exact inverse of paraphraseRelation. */
|
|
97
|
+
export function recoverRelationTriple(family, text) {
|
|
98
|
+
const def = RELATION_FAMILIES[family];
|
|
99
|
+
if (!def) return null;
|
|
100
|
+
const s = String(text || "").trim();
|
|
101
|
+
for (const re of def.recognizers) {
|
|
102
|
+
const m = s.match(re);
|
|
103
|
+
if (m) return { subject: m[1].trim(), object: m[2].trim() };
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Verify one relation-family paraphrase (mirrors verifySubClassParaphrase's
|
|
109
|
+
* single-fact contract, but for a non-isa relation): the paraphrase text must
|
|
110
|
+
* recover to the SAME (subject, object) under `family`'s closed recognizers.
|
|
111
|
+
* No closure to re-derive over for a non-isa relation, so this is a direct
|
|
112
|
+
* normalized compare, never a fuzzy one. */
|
|
113
|
+
export function verifyRelationParaphrase(family, subject, object, paraphraseText) {
|
|
114
|
+
const recovered = recoverRelationTriple(family, paraphraseText);
|
|
115
|
+
if (!recovered) return { verified: false };
|
|
116
|
+
const verified = normFactTerm(recovered.subject) === normFactTerm(subject)
|
|
117
|
+
&& normFactTerm(recovered.object) === normFactTerm(object);
|
|
118
|
+
return { verified };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---- whole-document recognition: isa + every relation family above ----
|
|
122
|
+
|
|
123
|
+
/** Recover a single (family, subject, object) triple from one sentence: isa
|
|
124
|
+
* first (paraphrase.mjs's closed subclass templates), then every relation
|
|
125
|
+
* family in RELATION_FAMILY_IDS order — null if the sentence matches none of
|
|
126
|
+
* the closed templates. Family keywords ("is a kind of" vs "has" vs "creates"
|
|
127
|
+
* vs "can" …) don't overlap, so recognition order never resolves a genuine
|
|
128
|
+
* ambiguity, only which closed family a sentence belongs to. */
|
|
129
|
+
export function recoverAnyTriple(sentence) {
|
|
130
|
+
const isaHit = recoverSubClassTriple(sentence);
|
|
131
|
+
if (isaHit) return { family: "isa", subject: isaHit.subject, object: isaHit.object };
|
|
132
|
+
for (const family of RELATION_FAMILY_IDS) {
|
|
133
|
+
const hit = recoverRelationTriple(family, sentence);
|
|
134
|
+
if (hit) return { family, subject: hit.subject, object: hit.object };
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function splitSentences(text) {
|
|
140
|
+
return String(text || "")
|
|
141
|
+
.split(/(?<=[.!?])\s+/)
|
|
142
|
+
.map((s) => s.trim())
|
|
143
|
+
.filter(Boolean);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const tripleKey = (t) => `${t.family}\0${normFactTerm(t.subject)}\0${normFactTerm(t.object)}`;
|
|
147
|
+
|
|
148
|
+
/** Recover EVERY sentence of a document as a closed-template triple — null (the
|
|
149
|
+
* whole document unrecognized) the moment any sentence fails to match, never a
|
|
150
|
+
* partial list standing in for the whole. */
|
|
151
|
+
export function recoverDocumentTriples(text) {
|
|
152
|
+
const sentences = splitSentences(text);
|
|
153
|
+
if (!sentences.length) return null;
|
|
154
|
+
const triples = [];
|
|
155
|
+
for (const sentence of sentences) {
|
|
156
|
+
const t = recoverAnyTriple(sentence.replace(/\.+$/, ""));
|
|
157
|
+
if (!t) return null;
|
|
158
|
+
triples.push(t);
|
|
159
|
+
}
|
|
160
|
+
return triples;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Verify a whole-document ING-8 paraphrase pair: does `restatementText` say
|
|
164
|
+
* exactly what `inputText` says, no more and no less? A single isa fact on
|
|
165
|
+
* both sides re-derives the subclass closure directly through
|
|
166
|
+
* verifySubClassParaphrase (ING-7's own exact-re-derivation strategy).
|
|
167
|
+
* Everything else — a non-isa relation, or more than one sentence on either
|
|
168
|
+
* side — recovers every sentence of both texts against the closed template
|
|
169
|
+
* set above and requires the exact same (family, subject, object) SET, order
|
|
170
|
+
* irrelevant. Never verified when either side holds a sentence outside the
|
|
171
|
+
* closed templates, or when the two recovered sets differ in size or content
|
|
172
|
+
* (a dropped fact and an invented fact both fail this the same way — the
|
|
173
|
+
* checker declines rather than guesses which). */
|
|
174
|
+
export function verifyIng8Paraphrase(inputText, restatementText) {
|
|
175
|
+
const inputTriples = recoverDocumentTriples(inputText);
|
|
176
|
+
const restatementTriples = recoverDocumentTriples(restatementText);
|
|
177
|
+
if (!inputTriples || !restatementTriples) {
|
|
178
|
+
return { verified: false, reason: "unrecognized", inputTriples, restatementTriples };
|
|
179
|
+
}
|
|
180
|
+
const bothSingleIsa = inputTriples.length === 1 && restatementTriples.length === 1
|
|
181
|
+
&& inputTriples[0].family === "isa" && restatementTriples[0].family === "isa";
|
|
182
|
+
if (bothSingleIsa) {
|
|
183
|
+
const { subject, object } = inputTriples[0];
|
|
184
|
+
const restatementSentence = splitSentences(restatementText)[0].replace(/\.+$/, "");
|
|
185
|
+
const { verified } = verifySubClassParaphrase(subject, object, restatementSentence);
|
|
186
|
+
return { verified, method: "isa-closure", inputTriples, restatementTriples };
|
|
187
|
+
}
|
|
188
|
+
if (inputTriples.length !== restatementTriples.length) {
|
|
189
|
+
return { verified: false, reason: "fact count mismatch", inputTriples, restatementTriples };
|
|
190
|
+
}
|
|
191
|
+
const inputKeys = inputTriples.map(tripleKey).sort();
|
|
192
|
+
const restatementKeys = restatementTriples.map(tripleKey).sort();
|
|
193
|
+
const verified = inputKeys.every((k, i) => k === restatementKeys[i]);
|
|
194
|
+
return { verified, method: "closed-template-set", inputTriples, restatementTriples };
|
|
195
|
+
}
|