@polycode-projects/the-mechanical-code-talker 1.5.5 → 1.8.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 +123 -14
- package/ROADMAP.md +233 -1392
- package/bin/tmct.mjs +479 -98
- package/corpus/README.md +3 -0
- package/corpus/generated/README.md +43 -0
- package/corpus/generated/ace-surface-variants.jsonl +17 -0
- package/corpus/generated/manifest.json +9 -0
- package/corpus/tier2/generate.mjs +14668 -0
- package/corpus/tier2/human-examples-large.jsonl +1928 -0
- package/corpus/tier2/human-examples-medium.jsonl +356 -0
- package/corpus/tier2/human-examples.jsonl +120 -0
- package/corpus/tier2/human-large.jsonl +12001 -0
- package/corpus/tier2/human-medium.jsonl +944 -0
- package/corpus/tier2/human.jsonl +664 -0
- package/corpus/tier2/manifest.json +42 -0
- package/package.json +14 -8
- package/src/answer-variants.json +47 -0
- package/src/answer-variants.mjs +67 -0
- package/src/ask-browser-entry.mjs +34 -0
- package/src/ask-browser.bundle.js +5095 -0
- package/src/ask-vocab.mjs +93 -8
- package/src/ask.mjs +451 -49
- package/src/chat.mjs +1273 -137
- package/src/cli-args.mjs +164 -0
- package/src/codegraph.mjs +170 -32
- package/src/extensions.mjs +100 -19
- package/src/grammar/ace.mjs +85 -3
- package/src/grammar/lexicon-core.json +9531 -63
- package/src/grammar/lexicon.mjs +58 -8
- package/src/graph-merge.mjs +114 -0
- package/src/index.mjs +14 -0
- package/src/init.mjs +40 -14
- package/src/interpret/normalize.mjs +75 -1
- package/src/interpret/strategies/grammar.mjs +10 -0
- package/src/interpret/strategies/keywords.mjs +20 -0
- package/src/interpret/strategies/noise-strip.mjs +73 -4
- package/src/memory/core.mjs +466 -8
- package/src/router/goal-reasoner.mjs +41 -7
- package/src/router/guardrail.mjs +37 -7
- package/src/router/resolver.mjs +50 -4
- package/src/sessions.mjs +5 -1
- package/src/source.mjs +54 -1
- package/src/syllogise.mjs +398 -27
- package/src/toml-config.mjs +13 -4
- package/src/viz.mjs +541 -0
package/src/grammar/ace.mjs
CHANGED
|
@@ -39,6 +39,11 @@ import {
|
|
|
39
39
|
predicateOf, numberOf, classify,
|
|
40
40
|
} from "./lexicon.mjs";
|
|
41
41
|
|
|
42
|
+
// "a"/"an" are the only ACE determiners that are grammatically SINGULAR-ONLY —
|
|
43
|
+
// "the" and a bare/no determiner are number-neutral (see resolveNP's
|
|
44
|
+
// singularOnly below, and lexicon.mjs's lookupNoun doc for what this prunes).
|
|
45
|
+
const SINGULAR_ONLY_DET = new Set(["a", "an"]);
|
|
46
|
+
|
|
42
47
|
export const PATTERN_SUB_CLASS_OF = "subClassOf";
|
|
43
48
|
export const PATTERN_TYPE_ASSERTION = "typeAssertion";
|
|
44
49
|
export const PATTERN_RELATION = "relation";
|
|
@@ -89,22 +94,29 @@ const stripDet = (tokens) =>
|
|
|
89
94
|
* Returns { term, individual, extras, unknown } — `term` null on a miss with
|
|
90
95
|
* the undeclared tokens in `unknown` (empty `unknown` = structurally
|
|
91
96
|
* unparseable phrase → the caller returns a hard null). `extras` carries the
|
|
92
|
-
* pattern-8 adjective triples (subclass axioms / hasValue restriction).
|
|
97
|
+
* pattern-8 adjective triples (subclass axioms / hasValue restriction).
|
|
98
|
+
*
|
|
99
|
+
* `singularOnly` is grammatical-agreement pruning (see lexicon.mjs's
|
|
100
|
+
* lookupNoun doc): true only when the ORIGINAL (pre-strip) phrase opened
|
|
101
|
+
* with "a"/"an" — the one signal that a singular-plural-fold collision
|
|
102
|
+
* (die/dice, person/people, tooth/teeth) can be resolved by, rather than
|
|
103
|
+
* silently committing to whichever the lexicon happens to fold to first. */
|
|
93
104
|
function resolveNP(lexicon, tokensIn) {
|
|
94
105
|
const ns = lexicon.ns;
|
|
106
|
+
const singularOnly = tokensIn.length > 1 && SINGULAR_ONLY_DET.has(tokensIn[0].toLowerCase());
|
|
95
107
|
const tokens = stripDet(tokensIn);
|
|
96
108
|
if (tokens.length === 1) {
|
|
97
109
|
const t = tokens[0];
|
|
98
110
|
const proper = lookupProperName(lexicon, t);
|
|
99
111
|
if (proper) return { term: `${ns}${proper}`, individual: true, extras: [], unknown: [] };
|
|
100
112
|
if (CODE_REF.test(t)) return { term: `${ns}${t}`, individual: true, extras: [], unknown: [] };
|
|
101
|
-
const noun = lookupNoun(lexicon, t);
|
|
113
|
+
const noun = lookupNoun(lexicon, t, { singularOnly });
|
|
102
114
|
if (noun) return { term: `${ns}${noun.lemma}`, individual: false, noun, extras: [], unknown: [] };
|
|
103
115
|
return { term: null, individual: false, extras: [], unknown: [t] };
|
|
104
116
|
}
|
|
105
117
|
if (tokens.length === 2) {
|
|
106
118
|
const adj = lookupAdjective(lexicon, tokens[0]);
|
|
107
|
-
const noun = lookupNoun(lexicon, tokens[1]);
|
|
119
|
+
const noun = lookupNoun(lexicon, tokens[1], { singularOnly });
|
|
108
120
|
if (adj && noun) {
|
|
109
121
|
const term = `${ns}${adj.lemma}-${noun.lemma}`;
|
|
110
122
|
const extras = [
|
|
@@ -178,6 +190,76 @@ function parseRelation(lexicon, toks, lower) {
|
|
|
178
190
|
return null;
|
|
179
191
|
}
|
|
180
192
|
|
|
193
|
+
// ---- ambiguity: breadth-first candidate parses, dead ends pruned, survivors
|
|
194
|
+
// surfaced rather than guessed (the operator's own framing — see
|
|
195
|
+
// PLAN_DID_YOU_SEE_HER_DUCK.md's Origin section). parseRelation just above is
|
|
196
|
+
// UNCHANGED — it is still the greedy, first-verb-position-wins fast path
|
|
197
|
+
// every existing caller keeps using, so every single-reading sentence (the
|
|
198
|
+
// overwhelming majority) is completely unaffected. parseRelationHits and
|
|
199
|
+
// parseAceAmbiguous below are a separate, ADDITIVE scan that a caller opts
|
|
200
|
+
// into only when it wants to know whether more than one reading survives. ----
|
|
201
|
+
|
|
202
|
+
/** Pattern 3 — EVERY verb-position split, not just the first: for each token
|
|
203
|
+
* index that lookupVerb recognizes, resolve both sides and keep it ONLY if
|
|
204
|
+
* it is a complete, valid parse (a genuine hit — a missOrNull/null split is
|
|
205
|
+
* a dead end, pruned here rather than surfaced as "ambiguity"). Duplicate
|
|
206
|
+
* logic with parseRelation is deliberate: parseRelation must stay byte-for-
|
|
207
|
+
* byte unchanged for every existing caller, so this is a standalone reader,
|
|
208
|
+
* not a refactor of shared internals. */
|
|
209
|
+
function parseRelationHits(lexicon, toks, lower) {
|
|
210
|
+
const hits = [];
|
|
211
|
+
for (let i = 1; i < toks.length - 1; i += 1) {
|
|
212
|
+
const verb = lookupVerb(lexicon, lower[i]);
|
|
213
|
+
if (!verb) continue;
|
|
214
|
+
let objStart = i + 1;
|
|
215
|
+
if (verb.prep) {
|
|
216
|
+
if (lower[objStart] !== verb.prep) continue;
|
|
217
|
+
objStart += 1;
|
|
218
|
+
if (objStart >= toks.length) continue;
|
|
219
|
+
}
|
|
220
|
+
const np1 = resolveNP(lexicon, toks.slice(0, i));
|
|
221
|
+
const np2 = resolveNP(lexicon, toks.slice(objStart));
|
|
222
|
+
if (np1.term == null || np2.term == null) continue; // dead end
|
|
223
|
+
hits.push({
|
|
224
|
+
i,
|
|
225
|
+
verbLemma: verb.lemma,
|
|
226
|
+
subject: np1.term,
|
|
227
|
+
object: np2.term,
|
|
228
|
+
result: hit(PATTERN_RELATION, [np1, np2], [
|
|
229
|
+
{ subject: np1.term, predicate: predicateOf(verb, lexicon.ns), object: np2.term, kind: "owl:ObjectProperty" },
|
|
230
|
+
]),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return hits;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Public ambiguity surface: parse `sentence` and, ONLY when more than one
|
|
237
|
+
* independent, COMPLETE relation-pattern reading survives (parseRelationHits
|
|
238
|
+
* above), return them all, each labeled by the token it read as the verb.
|
|
239
|
+
* Returns null for the overwhelming majority of sentences: anything not
|
|
240
|
+
* relation-shaped (mirrors parseAce's own dispatch gate exactly, so this
|
|
241
|
+
* only ever fires on a sentence parseAce would ALSO route to parseRelation),
|
|
242
|
+
* and any relation-shaped sentence with 0 or 1 surviving readings — the
|
|
243
|
+
* ordinary parseAce path is authoritative and untouched either way. */
|
|
244
|
+
export function parseAceAmbiguous(sentence, lexicon = loadLexicon()) {
|
|
245
|
+
const toks = tokenize(sentence);
|
|
246
|
+
if (toks.length < 4) return null; // 3 tokens: exactly one verb position is even possible
|
|
247
|
+
const lower = toks.map((t) => t.toLowerCase());
|
|
248
|
+
if (lower[0] === "every" || lower[0] === "no") return null;
|
|
249
|
+
if (/'s$/.test(lower[0]) && lower[0].length > 2) return null;
|
|
250
|
+
if (lower[0] === "the" && lower.includes("of") && lower.includes("is")) return null;
|
|
251
|
+
if (lower.indexOf("is") > 0) return null;
|
|
252
|
+
const hits = parseRelationHits(lexicon, toks, lower);
|
|
253
|
+
if (hits.length < 2) return null;
|
|
254
|
+
return {
|
|
255
|
+
pattern: PATTERN_RELATION,
|
|
256
|
+
sentence,
|
|
257
|
+
readings: hits.map(({ i, verbLemma, subject, object, result }) => ({
|
|
258
|
+
i, verbLemma, subject, object, ...result,
|
|
259
|
+
})),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
181
263
|
/** Pattern 8 (copula arm) — "X is ADJ": data adjective → datatype-property
|
|
182
264
|
* assertion; subclass adjective → rdf:type (individual) / rdfs:subClassOf. */
|
|
183
265
|
function adjectiveCopula(lexicon, pattern, np1, adj) {
|