@polycode-projects/the-mechanical-code-talker 1.10.3 → 1.10.6
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/package.json +1 -1
- package/src/chat.mjs +64 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
|
package/src/chat.mjs
CHANGED
|
@@ -4121,15 +4121,16 @@ const KNOW_ABOUT_RE = /^(?:what\s+do\s+you\s+know\s+about|what(?:'s|s|\s+is)\s+i
|
|
|
4121
4121
|
/** How many facts a single answer lists before the remainder is paged with "more". */
|
|
4122
4122
|
const FACT_ANSWER_CAP = 32;
|
|
4123
4123
|
|
|
4124
|
-
/**
|
|
4124
|
+
/** Five sibling readers closing the gap left
|
|
4125
4125
|
* by ISA_ASK_RE's own family: forward yes/no and reverse-by-object shapes for
|
|
4126
4126
|
* `mgx:capableOf`, `mgx:hasA`, and the ISA-family predicates. None of these
|
|
4127
|
-
*
|
|
4127
|
+
* five leads ("can"/"could", "does/do … have", "what can … do", "what has", "what inherit(s)")
|
|
4128
4128
|
* overlaps KNOW_ABOUT_RE's fixed leads above, or RELATION_FACT_YESNO_RE/
|
|
4129
4129
|
* RELATION_WHO_ASK_RE's required leading "is/are/was/were" (those two live in
|
|
4130
4130
|
* the separate factReadBack, only ever reached via `factAnswer(...) ??
|
|
4131
4131
|
* factReadBack(...)` — never both). */
|
|
4132
4132
|
const CAN_ASK_RE = /^(?:can|could)\s+(?:an?\s+)?([\w'-]+(?:\s+[\w'-]+)*?)\s+([a-z]+)[?.!\s]*$/i;
|
|
4133
|
+
const DOES_HAVE_ASK_RE = /^(?:does|do)\s+(?:an?\s+|the\s+)?(.+?)\s+have\s+(?:an?\s+|the\s+)?(.+?)[?.!\s]*$/i;
|
|
4133
4134
|
const WHAT_CAN_DO_RE = /^what\s+can\s+(?:an?\s+)?(.+?)\s+do[?.!\s]*$/i;
|
|
4134
4135
|
const WHAT_HAS_RE = /^what\s+has\s+(?:an?\s+)?(.+?)[?.!\s]*$/i;
|
|
4135
4136
|
// "what is used for riding" / "what can be used for riding" / "what is for
|
|
@@ -4349,6 +4350,22 @@ export async function factAnswer(memoryDir, query, envelope, miss, biasByBundle
|
|
|
4349
4350
|
return null;
|
|
4350
4351
|
}
|
|
4351
4352
|
|
|
4353
|
+
// (b2b) "does a dog have a tail" — yes iff a remembered mgx:hasA fact says
|
|
4354
|
+
// so: the forward yes/no mirror of WHAT_HAS_RE below, with the same
|
|
4355
|
+
// single-hit lookup and "never a guessed no" discipline as CAN_ASK_RE
|
|
4356
|
+
// above. Only diverts on a REAL hit, so a code-shaped "does app.mjs have
|
|
4357
|
+
// tests" (no hasA fact) keeps whatever miss text already stands.
|
|
4358
|
+
const doesHave = q.match(DOES_HAVE_ASK_RE);
|
|
4359
|
+
if (doesHave) {
|
|
4360
|
+
const subj = factTermVariants(normFactTerm, doesHave[1]);
|
|
4361
|
+
const obj = factTermVariants(normFactTerm, doesHave[2]);
|
|
4362
|
+
const hit = (await memoryFacts(memoryDir)).find(
|
|
4363
|
+
(f) => f.predicate === "mgx:hasA" && subj.has(f.subject) && obj.has(f.object),
|
|
4364
|
+
);
|
|
4365
|
+
if (hit) return { text: `yes — ${renderFactLine(hit)}`, replace: true };
|
|
4366
|
+
return null;
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4352
4369
|
// (b3) "what can a dog do" — every remembered mgx:capableOf fact for the
|
|
4353
4370
|
// subject, open-list. Reuses the meta-lane's subject-hits/rank/render/
|
|
4354
4371
|
// paginate recipe (lane (a) above) verbatim, with the predicate hardcoded.
|
|
@@ -5486,7 +5503,42 @@ async function factReadBack(memoryDir, query, envelope, miss, graph = null, focu
|
|
|
5486
5503
|
}
|
|
5487
5504
|
}
|
|
5488
5505
|
}
|
|
5489
|
-
|
|
5506
|
+
// Every yes-chase and the disjoint "no" above missed. The old
|
|
5507
|
+
// unconditional decline here fell through to the structural "couldn't
|
|
5508
|
+
// parse this as a graph question" wall — actively misleading for a KNOWN
|
|
5509
|
+
// subject, twice over: the question DID parse, and the wall's hint
|
|
5510
|
+
// suggests the exact shape the user just typed. When the subject has
|
|
5511
|
+
// remembered isa-family facts, answer with an honest, specific miss that
|
|
5512
|
+
// cites what IS remembered instead. An unknown subject still declines to
|
|
5513
|
+
// the standing wall/teach-hint path — and this never guesses a "no".
|
|
5514
|
+
// The SUBJECT'S OWN variants only — subjCandidates was augmented above
|
|
5515
|
+
// with the graph entity's class noun (the CLASS↔INSTANCE bridge), and
|
|
5516
|
+
// filtering on it here would cite facts about that noun ("class ⊑
|
|
5517
|
+
// component") as if they were facts about the asked subject ("Widget").
|
|
5518
|
+
const directSubjVariants = factTermVariants(normFactTerm, isaAsk[1]);
|
|
5519
|
+
const knownSubjectIsa = isa.filter((f) => directSubjVariants.has(f.subject)).sort(byTrust);
|
|
5520
|
+
const subjectWord = isaAsk[1].trim();
|
|
5521
|
+
const kindWord = stripTrailingDiscourseTag(isaAsk[2]).trim();
|
|
5522
|
+
if (knownSubjectIsa.length) {
|
|
5523
|
+
const shown = knownSubjectIsa.slice(0, 3).map(renderFactLine).join("; ");
|
|
5524
|
+
return {
|
|
5525
|
+
text: `I can't confirm that — nothing I remember says ${subjectWord} is a ${kindWord}. I do know: ${shown}. If it's true, teach me: "${subjectWord} is a kind of ${kindWord}".`,
|
|
5526
|
+
replace: true,
|
|
5527
|
+
miss: true, // still a MISS in the turn record — honest wording, not an answer
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5530
|
+
// Subject with NO isa facts: only divert when it's mentioned NOWHERE at
|
|
5531
|
+
// all (no fact row on either side, no code entity by id OR class noun) —
|
|
5532
|
+
// a subject known via OTHER predicates ("ahab is male") or the code graph
|
|
5533
|
+
// keeps the old decline, so nothing downstream is ever shadowed.
|
|
5534
|
+
if (!ent && !noun && !rows.some((f) => subjCandidates.has(f.subject) || subjCandidates.has(f.object))) {
|
|
5535
|
+
return {
|
|
5536
|
+
text: `I can't confirm that — I don't know "${subjectWord}" at all yet. If it's true, teach me: "${subjectWord} is a kind of ${kindWord}".`,
|
|
5537
|
+
replace: true,
|
|
5538
|
+
miss: true,
|
|
5539
|
+
};
|
|
5540
|
+
}
|
|
5541
|
+
return null; // the honest miss stands (never a guessed "no")
|
|
5490
5542
|
}
|
|
5491
5543
|
|
|
5492
5544
|
// (a1c-i) CARDINALITY MONOTONICITY — "does every X have at least N Y" over
|
|
@@ -7088,6 +7140,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
7088
7140
|
if (memoryDir) {
|
|
7089
7141
|
bareMetaHit = (await factAnswer(memoryDir, gateQuery, envelope, miss, biasByBundle, cache))
|
|
7090
7142
|
?? (await factReadBack(memoryDir, gateQuery, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
|
|
7143
|
+
if (bareMetaHit?.miss) bareMetaHit = null; // an honest-miss return never diverts the gate
|
|
7091
7144
|
// A bare "what is X" with NO taught fact but a KNOWN curated corpus term
|
|
7092
7145
|
// ("what is cache", no article) needs the same "only diverts on a REAL
|
|
7093
7146
|
// hit" treatment — curatedDefinitionAnswer otherwise only ever runs once
|
|
@@ -7158,8 +7211,14 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
7158
7211
|
?? (await factReadBack(memoryDir, query, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
|
|
7159
7212
|
if (fact) {
|
|
7160
7213
|
answer = fact.replace ? fact.text : `${answer}\n${fact.text}`;
|
|
7161
|
-
|
|
7162
|
-
|
|
7214
|
+
// A fact-lane return flagged `miss` is an HONEST MISS in better words
|
|
7215
|
+
// (the isa ladder's "I can't confirm that" closers) — the turn record
|
|
7216
|
+
// keeps miss=true and via stays untouched, so miss-rate metrics and
|
|
7217
|
+
// recall's own miss-gated lanes see it exactly like the wall it replaced.
|
|
7218
|
+
if (!fact.miss) {
|
|
7219
|
+
via = "fact";
|
|
7220
|
+
recordMiss = false;
|
|
7221
|
+
}
|
|
7163
7222
|
if (fact.pending) factPending = fact.pending; // a truncated fact list → paginable remainder
|
|
7164
7223
|
if (typeof fact.trust === "number") entailedTrust = fact.trust; // live-chase trust (see the `entailedTrust` declaration above)
|
|
7165
7224
|
note(trace, `lane: (3) memory facts — factAnswer/factReadBack matched (memoryDir=${memoryDir})`);
|