@polycode-projects/the-mechanical-code-talker 2.11.0 → 2.11.1
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/data/templates/responses.jsonl +1 -0
- package/package.json +1 -1
- package/src/domain/ask-vocab.mjs +17 -0
- package/src/domain/ask.mjs +51 -1
- package/src/domain/grammar/ace.mjs +43 -3
- package/src/domain/interpret/normalize.mjs +6 -2
- package/src/domain/interpret/strategies/grammar.mjs +47 -17
- package/src/domain/interpret/strategies/keywords.mjs +53 -4
- package/src/domain/router/registry.mjs +8 -1
- package/src/services/chat.mjs +189 -40
- package/src/surfaces/web/memory-ask-browser.bundle.js +120 -120
package/src/services/chat.mjs
CHANGED
|
@@ -1082,6 +1082,11 @@ const CAPABILITY_PHRASES = [
|
|
|
1082
1082
|
// — closed to the same self-referential noun set META_ORIENT_RE uses, so a
|
|
1083
1083
|
// real module/class named literally "repo"/"codebase" is never at risk.
|
|
1084
1084
|
/^what(?:'s|s|\s+is) in (?:this|the)\s+(?:app|codebase|repo|repository|project|code)\??$/i,
|
|
1085
|
+
// "can I ask you something random" — an ordinary conversational preamble
|
|
1086
|
+
// asking permission before a real question, not itself a question about
|
|
1087
|
+
// anything nameable. Answered the same as any other vague opener: sure,
|
|
1088
|
+
// here's what I can help with.
|
|
1089
|
+
/^can i ask (?:you\s+)?something(?:\s+random)?\??$/i,
|
|
1085
1090
|
];
|
|
1086
1091
|
/** IDENTITY questions — "who/what are you", by name, in plain or ESL-ish phrasing.
|
|
1087
1092
|
* Routed to a self-description (identity-self) that works regardless of graph
|
|
@@ -1132,6 +1137,25 @@ const AI_IDENTITY_PHRASES = [
|
|
|
1132
1137
|
// product path (no-LLM constitution, deterministic offline reasoning), so
|
|
1133
1138
|
// this is a real "no", not the generic capability listing.
|
|
1134
1139
|
/^can (?:you|u) (?:browse|access|use|go on|connect to) the internet\??$/i,
|
|
1140
|
+
// "can you look things up on the internet" / "browse the web to check
|
|
1141
|
+
// something" — the same offline-capability question as the entry just
|
|
1142
|
+
// above, worded around "web"/"look up" instead of "browse the internet".
|
|
1143
|
+
// With no closed match, this fell into a code-graph module-name search for
|
|
1144
|
+
// the literal words instead of the plain "no, I'm offline" answer.
|
|
1145
|
+
/^can (?:you|u) look (?:things?\s+)?up on the internet\??$/i,
|
|
1146
|
+
/^can (?:you|u) (?:browse|search|check) the web(?:\s+to\s+check\s+(?:something|this|that))?\??$/i,
|
|
1147
|
+
// "are you like chatgpt or gemini or something" — the comparison phrasing
|
|
1148
|
+
// of the "are you an AI" question, a named model or two followed by a
|
|
1149
|
+
// trailing "or something" hedge.
|
|
1150
|
+
/^(?:are you|r u)\s+like\s+(?:chatgpt|gpt|claude|gemini|llama)(?:\s+or\s+(?:chatgpt|gpt|claude|gemini|llama))*\s+or\s+something\??$/i,
|
|
1151
|
+
// "what model are you, gpt-4 or claude or something else" — the SAME
|
|
1152
|
+
// open-pick question "what model are you built on" already answers, just
|
|
1153
|
+
// without the "built/based/running on" bridge phrase.
|
|
1154
|
+
/^what model are you,?\s*(?:gpt-?\d(?:\.\d)?|chatgpt|claude|gemini|llama)(?:\s+or\s+(?:gpt-?\d(?:\.\d)?|chatgpt|claude|gemini|llama|something\s+else))*\??$/i,
|
|
1155
|
+
// "can you use an LLM to answer this" — asks the identical no-LLM question
|
|
1156
|
+
// as "do you use ai", just with "use an LLM to answer this" as the verb
|
|
1157
|
+
// phrase instead of a bare "use ai".
|
|
1158
|
+
/^can (?:you|u) use (?:an? )?(?:llm|ai|gpt|chatgpt) to answer (?:this|that|it)\??$/i,
|
|
1135
1159
|
];
|
|
1136
1160
|
|
|
1137
1161
|
/** META-COMMAND/SESSION questions — a RETURNING USER checking whether a
|
|
@@ -1219,10 +1243,25 @@ const FEELINGS_PHRASES = [
|
|
|
1219
1243
|
// "keeps growing as new natural phrasings surface" precedent.
|
|
1220
1244
|
/^how (?:are|r) (?:you|u) doing(?:\s+today)?\??$/i,
|
|
1221
1245
|
/^what(?:'s|s|\s+is) your (?:favou?rite\s+(?:colou?r|food|movie|book|band|song|number)|name)\??$/i,
|
|
1222
|
-
/^do you (?:get|ever get) bored\??$/i,
|
|
1246
|
+
/^do you (?:get|ever get) (?:bored|tired)\??$/i,
|
|
1223
1247
|
/^what do you do for fun\??$/i,
|
|
1224
1248
|
/^can you (?:tell|make)\s+(?:me\s+)?(?:a\s+)?jokes?\??$/i,
|
|
1225
1249
|
/^do you (?:know|know anything|know much)\s+about\s+(?:movies?|sports?|music|tv|television)(?:\s+or\s+(?:movies?|sports?|music|tv|television))?\??$/i,
|
|
1250
|
+
// "do you think dogs are smarter than cats" — a personal-opinion comparison,
|
|
1251
|
+
// same family as the direct personal questions above: closed to a small
|
|
1252
|
+
// comparative-adjective vocabulary, open on the two compared things (the
|
|
1253
|
+
// SAME discipline PHRASING_FRAMES' object captures use elsewhere).
|
|
1254
|
+
/^do you think .+?\s+(?:is|are)\s+(?:smarter|dumber|better|worse|nicer|cooler|cuter|friendlier|stronger|faster)\s+than\s+.+\??$/i,
|
|
1255
|
+
];
|
|
1256
|
+
/** "can you make up an answer if you don't actually know" — a direct probe of
|
|
1257
|
+
* the product's own headline promise (the honest miss: a query it can't
|
|
1258
|
+
* ground gets a refusal, never a guess). With no closed match this fell to
|
|
1259
|
+
* the plain grammar wall instead of confirming the very thing it asked
|
|
1260
|
+
* about. Same family/placement as FEELINGS_PHRASES just above. */
|
|
1261
|
+
const HONEST_MISS_PHRASES = [
|
|
1262
|
+
/^can (?:you|u) make up an answer if you (?:don'?t|do\s+not) (?:actually\s+|really\s+)?know\??$/i,
|
|
1263
|
+
/^(?:do|would) you (?:ever\s+)?(?:make (?:something|stuff) up|make up an answer)(?:\s+if you (?:don'?t|do\s+not) know)?\??$/i,
|
|
1264
|
+
/^what if you (?:don'?t|do\s+not) know (?:the\s+)?answer\??$/i,
|
|
1226
1265
|
];
|
|
1227
1266
|
/** "whats 2+2" — a bare arithmetic expression, not a code/vocabulary question
|
|
1228
1267
|
* at all. With no closed-set match of its own, this fell into the SAME
|
|
@@ -1235,6 +1274,15 @@ const FEELINGS_PHRASES = [
|
|
|
1235
1274
|
* structural answer, never this decline. "+"/"*"/"/" have no such
|
|
1236
1275
|
* collision in tmct's own vocabulary. */
|
|
1237
1276
|
const ARITHMETIC_RE = /\d+\s*[+*/]\s*\d+/;
|
|
1277
|
+
/** A bare SQL data-definition/manipulation statement typed at the prompt
|
|
1278
|
+
* ("DROP TABLE users;") — nonsense as a code-graph question, and with no
|
|
1279
|
+
* closed match of its own this fell to the ≤3-word catch-all's identity/
|
|
1280
|
+
* orientation blurb, the same wrong-flavor miss ARITHMETIC_RE exists to
|
|
1281
|
+
* avoid for arithmetic. Each alternative requires the FULL SQL clause shape
|
|
1282
|
+
* (not just the bare leading verb), so an ordinary English imperative
|
|
1283
|
+
* ("update the readme") is never caught here — "table"/"from"/"into"/"set"
|
|
1284
|
+
* are the words that make this unambiguously SQL rather than English. */
|
|
1285
|
+
const SQL_STATEMENT_RE = /^(?:(?:drop|truncate|alter)\s+table|delete\s+from|insert\s+into|update\s+[a-z0-9_.]+\s+set)\s+[a-z0-9_.]+\b/i;
|
|
1238
1286
|
/** The structural verbs/nouns that mark a near-miss code question (→ keep the
|
|
1239
1287
|
* precise grammar hint, not the friendly nudge). */
|
|
1240
1288
|
const STRUCT_WORDS = new Set([
|
|
@@ -1428,6 +1476,10 @@ const T_ORIENTATION_EMPTY = "orientation-empty";
|
|
|
1428
1476
|
const T_IDENTITY_SELF = "identity-self";
|
|
1429
1477
|
const T_IDENTITY_NOT_LLM = "identity-not-an-llm";
|
|
1430
1478
|
const T_IDENTITY_NO_FEELINGS = "identity-no-feelings";
|
|
1479
|
+
/** Confirms the honest-miss promise itself when a user asks about it directly
|
|
1480
|
+
* ("can you make up an answer if you don't know"). Same "works regardless of
|
|
1481
|
+
* graph state" family as the identity answers just above. */
|
|
1482
|
+
const T_IDENTITY_HONEST_MISS = "identity-honest-miss";
|
|
1431
1483
|
/** THE CONCEPT FORCE (concept.mjs): the three-band answer to a vague "what is a X"
|
|
1432
1484
|
* that names a known concept WITH instances — {definition}/{examples}/{followups}. */
|
|
1433
1485
|
const T_CONCEPT = "concept-force";
|
|
@@ -1497,6 +1549,13 @@ const BYE = new Set([
|
|
|
1497
1549
|
"bye", "goodbye", "quit", "exit", "see ya", "see you", "cya", "later", "farewell",
|
|
1498
1550
|
"peace", "peace out", "im off", "i'm off", "gtg", "gotta go", "catch you later",
|
|
1499
1551
|
"farewell then",
|
|
1552
|
+
// "gtg thx" — the SAME "gtg" farewell above, immediately followed by a
|
|
1553
|
+
// thanks word with no delimiter between them (so farewellOrThanksSignal's
|
|
1554
|
+
// comma/semicolon clause split never sees two clauses to work with).
|
|
1555
|
+
// Whole-phrase entry rather than a general "bye word + thanks word, no
|
|
1556
|
+
// delimiter" mechanism — closed and hand-curated, this exact reported
|
|
1557
|
+
// phrasing only.
|
|
1558
|
+
"gtg thx",
|
|
1500
1559
|
// "good day to you" deliberately does NOT live here: it's a formal-register
|
|
1501
1560
|
// GREETING, not a farewell. foldedBye is checked before GREET in
|
|
1502
1561
|
// conversationalTurn, so having it here would silently end the session on
|
|
@@ -1514,6 +1573,11 @@ const WHY = new Set([
|
|
|
1514
1573
|
const OK_ACK = new Set([
|
|
1515
1574
|
"ok", "okay", "cool", "aight", "fair enough", "got it", "gotcha", "noted",
|
|
1516
1575
|
"sounds good", "sure", "cool cool", "right",
|
|
1576
|
+
// "haha ok fair enough" — a laughter beat leading a two-word ack ("fair
|
|
1577
|
+
// enough") that dismissalSignal's own peeling can't reach: it peels
|
|
1578
|
+
// single-WORD fluff off each end, but "fair enough" is itself two words.
|
|
1579
|
+
// Whole-phrase entry, closed and hand-curated, this exact reported phrasing.
|
|
1580
|
+
"haha ok fair enough",
|
|
1517
1581
|
]);
|
|
1518
1582
|
/** Dismissals — "drop it, no question here" beats. Routed to a warm dismissal
|
|
1519
1583
|
* template, never the identity/orientation blurb (which reads like the tool
|
|
@@ -1620,6 +1684,11 @@ const CLOSING_FILLER_CLAUSES = new Set([
|
|
|
1620
1684
|
"that's everything for now", "that's all for now",
|
|
1621
1685
|
"that's everything i needed", "that's all i needed",
|
|
1622
1686
|
"that's everything for today", "that's all for today",
|
|
1687
|
+
// "thats enough for now" — the same closing-filler shape, worded around
|
|
1688
|
+
// "enough" instead of "everything"/"all", and without the apostrophe a
|
|
1689
|
+
// casual typer routinely drops (conversationalTurn's own `q` never runs an
|
|
1690
|
+
// apostrophe/contraction pass, so both spellings are curated explicitly).
|
|
1691
|
+
"that's enough for now", "thats enough for now",
|
|
1623
1692
|
]);
|
|
1624
1693
|
/** Strip a hedging lead ("i think that's everything for today" → "that's
|
|
1625
1694
|
* everything for today") so a hedged closing clause still matches the closed
|
|
@@ -1876,6 +1945,20 @@ function conversationalTurn(line, ctx) {
|
|
|
1876
1945
|
note(ctx.trace, "lane: conversational — identity/feelings (FEELINGS_PHRASES closed set)");
|
|
1877
1946
|
return mk(t(T_IDENTITY_NO_FEELINGS), { lane: "help" });
|
|
1878
1947
|
}
|
|
1948
|
+
if (HONEST_MISS_PHRASES.some((re) => re.test(raw))) {
|
|
1949
|
+
note(ctx.trace, "goal: identity — a direct probe of the honest-miss promise itself");
|
|
1950
|
+
note(ctx.trace, "lane: conversational — identity/honest-miss (HONEST_MISS_PHRASES closed set)");
|
|
1951
|
+
return mk(t(T_IDENTITY_HONEST_MISS), { lane: "help" });
|
|
1952
|
+
}
|
|
1953
|
+
if (SQL_STATEMENT_RE.test(raw)) {
|
|
1954
|
+
note(ctx.trace, "goal: nonsense input shaped like a SQL statement — a targeted decline, not the identity blurb");
|
|
1955
|
+
note(ctx.trace, "lane: conversational — SQL-statement decline (SQL_STATEMENT_RE)");
|
|
1956
|
+
return mk(
|
|
1957
|
+
"That reads like a SQL statement, not a question about a code graph or taught facts. "
|
|
1958
|
+
+ "Try \"what is a dog\" for vocabulary, or point me at a repo with --repo <path>.",
|
|
1959
|
+
{ lane: "help" },
|
|
1960
|
+
);
|
|
1961
|
+
}
|
|
1879
1962
|
if (ARITHMETIC_RE.test(raw)) {
|
|
1880
1963
|
note(ctx.trace, "goal: arithmetic — not a code/vocabulary question, an honest decline");
|
|
1881
1964
|
note(ctx.trace, "lane: conversational — arithmetic decline (ARITHMETIC_RE)");
|
|
@@ -2113,7 +2196,7 @@ const WALL_MISS_ANYWHERE_RE = /couldn't parse this as a graph question\. Try:/;
|
|
|
2113
2196
|
// bare "X is a Y" declarative the graph parser couldn't handle → route to the
|
|
2114
2197
|
// assert/memory path; when it can't be stored, say what CAN be remembered
|
|
2115
2198
|
// instead of the grammar wall or a silent data loss.
|
|
2116
|
-
const TEACH_RE = /^(?:please\s+)?(?:i\s+(?:want|wanted)\s+you\s+to\s+|i(?:'d|\s+would)\s+like\s+you\s+to\s+)?(?:remember|note|keep in mind|jot down|for the record|fyi|learn)\b(?:\s+(?:this|that|also))?[:,]?\s*(?:that\s+)?(.+?)[.?!]*$/i;
|
|
2199
|
+
const TEACH_RE = /^(?:please\s+)?(?:i\s+(?:want|wanted)\s+you\s+to\s+|i(?:'d|\s+would)\s+like\s+you\s+to\s+)?(?:remember|note|keep in mind|jot down|for the record|fyi|learn|teach(?:\s+me)?)\b(?:\s+(?:this|that|also))?[:,]?\s*(?:that\s+)?(.+?)[.?!]*$/i;
|
|
2117
2200
|
// A trailing sentence-final mark ([.!?]*) is tolerated at the very end: an
|
|
2118
2201
|
// ordinary first turn typed as a full sentence ("every dog is a mammal.")
|
|
2119
2202
|
// otherwise failed this shape test by one character whenever neither ACE nor
|
|
@@ -2862,8 +2945,20 @@ async function unknownSubjectFallback(payload, { memoryDir, sessionId, lexicon }
|
|
|
2862
2945
|
const [, det, subjectRaw, verb, objectRaw] = m;
|
|
2863
2946
|
const { loadLexicon, lookupNoun, lookupAdjective, classify } = await import("../domain/grammar/lexicon.mjs");
|
|
2864
2947
|
const lex = lexicon || loadLexicon();
|
|
2865
|
-
// A known X's own ACE miss is a real miss — never silently reinterpreted
|
|
2866
|
-
|
|
2948
|
+
// A known X's own ACE miss is a real miss — never silently reinterpreted
|
|
2949
|
+
// here. EXCEPT: classify() folds a trailing "-s" the same way resolveNP
|
|
2950
|
+
// does (lexicon.mjs's lookupNoun), so a bare proper name that happens to
|
|
2951
|
+
// end in "s" and collide with an unrelated dictionary noun ("whiskers" ->
|
|
2952
|
+
// "whisker") reads as "already known" under a SINGULAR "is" sentence, where
|
|
2953
|
+
// no plural evidence supports the fold at all (the genuinely-plural "are"
|
|
2954
|
+
// case is handled correctly a few lines down). Refuse only the fold's own
|
|
2955
|
+
// contribution here — an EXACT noun hit (no fold), or any non-noun
|
|
2956
|
+
// classification (a real verb/adjective/proper name/determiner), still
|
|
2957
|
+
// blocks this fallback exactly as before.
|
|
2958
|
+
const subjectClass = classify(subjectRaw, lex);
|
|
2959
|
+
const subjectFoldedNounOnly = subjectClass?.pos === "noun" && !/^are$/i.test(verb)
|
|
2960
|
+
&& (lookupNoun(lex, subjectRaw)?.lemma || "").toLowerCase() !== String(subjectRaw).toLowerCase();
|
|
2961
|
+
if (subjectClass && !subjectFoldedNounOnly) return null;
|
|
2867
2962
|
const quantifier = /^every$/i.test((det || "").trim()) ? "every" : "";
|
|
2868
2963
|
// Singularize the SUBJECT before storage, but ONLY on a genuinely PLURAL
|
|
2869
2964
|
// phrasing ("all men ARE mortal", verb "are"). This
|
|
@@ -3221,12 +3316,26 @@ function participleObject(raw) {
|
|
|
3221
3316
|
/** Does a bare (unwrapped) sentence fit one of the relational teach frames —
|
|
3222
3317
|
* including its negated twin, read through splitTeachNegation the same way the
|
|
3223
3318
|
* general-verb and capability frames read theirs? Used only to admit the
|
|
3224
|
-
* sentence as a teach payload; the dispatch below re-matches and stores.
|
|
3319
|
+
* sentence as a teach payload; the dispatch below re-matches and stores.
|
|
3320
|
+
*
|
|
3321
|
+
* Each regex's own subject capture is a bare word/short NP with no pronoun
|
|
3322
|
+
* exclusion of its own (unlike generalVerbTeach's GENERAL_VERB_NOT_A_VERB_RE
|
|
3323
|
+
* check) — this frame used to be reached only once the bare-sentence pronoun
|
|
3324
|
+
* guard (TEACH_PRONOUN_BARE_RE) had already declined, but that guard is
|
|
3325
|
+
* deliberately narrow (copula + a SHORT complement only), so a pronoun
|
|
3326
|
+
* subject with a longer relational complement ("it is closely connected
|
|
3327
|
+
* with hunting" — the ingest pronoun-carry mechanism's own first, expected-
|
|
3328
|
+
* to-fail attempt) reaches here unprotected. isTeachPronoun is the same
|
|
3329
|
+
* closed check every other mint fallback in this lane uses. */
|
|
3225
3330
|
function matchesRelationalTeachFrame(sentence) {
|
|
3226
3331
|
const { payload } = splitTeachNegation(String(sentence || "").trim());
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3332
|
+
const pp = payload.match(PARTICIPLE_PREP_TEACH_RE);
|
|
3333
|
+
if (pp) return !isTeachPronoun(pp[1]);
|
|
3334
|
+
const np = payload.match(COPULA_NP_PARTICIPLE_TEACH_RE);
|
|
3335
|
+
if (np) return !isTeachPronoun(np[1]);
|
|
3336
|
+
const same = payload.match(SAME_NOUN_TEACH_RE);
|
|
3337
|
+
if (same) return !isTeachPronoun(same[1]) && !isTeachPronoun(same[2]);
|
|
3338
|
+
return false;
|
|
3230
3339
|
}
|
|
3231
3340
|
/** Does a relational-frame sentence name a code-graph entity? "the Router is
|
|
3232
3341
|
* used by every handler" reads as a passive uses-CLAIM the ask engine verifies
|
|
@@ -3343,7 +3452,17 @@ const GENERAL_VERB_ANYWHERE_EXCLUDE_RE = /\b(?:is|are|am|owns|maintains)\b/i;
|
|
|
3343
3452
|
* the legit "mentors" as NOUN, so a POS gate would have regressed a real
|
|
3344
3453
|
* teach; a closed list (templates over general grammar rules) is both more
|
|
3345
3454
|
* reliable here and, being closed, can never widen recognition the way a
|
|
3346
|
-
* probabilistic POS heuristic could.
|
|
3455
|
+
* probabilistic POS heuristic could.
|
|
3456
|
+
*
|
|
3457
|
+
* The interrogative pronouns (what/who/which/where/when/why/how) join the
|
|
3458
|
+
* list for the same reason: a dropped-copula casual fragment ("k what abt
|
|
3459
|
+
* users.mjs", missing the "is" QUESTION_LEAD_RE/hasMidSentenceInterrogative
|
|
3460
|
+
* both expect) leaves the WH-word sitting in the verb slot exactly like "if"
|
|
3461
|
+
* or "in" above — subject "k", verb "what", object "abt users.mjs" — and
|
|
3462
|
+
* the SAME nonsense -s fold ("k WHATs abt users.mjs") mints it as a fact. A
|
|
3463
|
+
* WH-word is never a genuine general-verb-frame verb, so this is the same
|
|
3464
|
+
* pure narrowing the block above already describes, just closing the one
|
|
3465
|
+
* closed class it left out. */
|
|
3347
3466
|
const GENERAL_VERB_NOT_A_VERB_RE = new RegExp(
|
|
3348
3467
|
"^(?:"
|
|
3349
3468
|
// personal/possessive/demonstrative pronouns + determiners (mirrors, and
|
|
@@ -3356,6 +3475,9 @@ const GENERAL_VERB_NOT_A_VERB_RE = new RegExp(
|
|
|
3356
3475
|
+ "|up|down|off|out|above|below|between|among|against|without|within|along|across|behind|beyond|upon|toward|towards|per"
|
|
3357
3476
|
// conjunctions/subordinators
|
|
3358
3477
|
+ "|and|but|or|if|because|although|though|while|when|since|unless|until|whether|so|nor|than|as"
|
|
3478
|
+
// interrogative pronouns/adverbs — never a real verb, only ever the
|
|
3479
|
+
// fronted question-word of a copula-dropped fragment
|
|
3480
|
+
+ "|what|who|whom|whose|which|where|why|how"
|
|
3359
3481
|
+ ")$",
|
|
3360
3482
|
"i",
|
|
3361
3483
|
);
|
|
@@ -3911,20 +4033,38 @@ function habitualGroundingHintText(line, habitual) {
|
|
|
3911
4033
|
* a demonstrated entity ("that is a bug", pointing at something real) is a
|
|
3912
4034
|
* much closer call than "every you is a womble".
|
|
3913
4035
|
*
|
|
3914
|
-
* The verb slot matches ANY word, not just the is/are/am copula
|
|
4036
|
+
* The verb slot matches ANY word, not just the is/are/am copula — a pronoun
|
|
3915
4037
|
* is just as invalid a fact subject under a general verb ("remember you has
|
|
3916
|
-
* a hat", "remember he eats ribs") as it is under "is" —
|
|
3917
|
-
*
|
|
3918
|
-
*
|
|
3919
|
-
*
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
4038
|
+
* a hat", "remember he eats ribs") as it is under "is" — but ONLY once an
|
|
4039
|
+
* explicit "remember"/"note"/"teach me"-style wrapper (TEACH_RE) already
|
|
4040
|
+
* named this an unambiguous teach attempt (TEACH_PRONOUN_WRAPPED_RE, tried
|
|
4041
|
+
* first when `wrapped` is set). A BARE sentence with no such signal gets the
|
|
4042
|
+
* narrower TEACH_PRONOUN_BARE_RE instead: pronoun + copula + a SHORT
|
|
4043
|
+
* complement (one word, one optional leading article) — the exact shape
|
|
4044
|
+
* TEACH_PROPERTY_RE/BARE_DECLARATIVE_RE would themselves recognize from a
|
|
4045
|
+
* legal subject. Without that narrowing, an ordinary opener that merely
|
|
4046
|
+
* STARTS with "I" ("I am new here", "I want to know X") reached this guard
|
|
4047
|
+
* too (teachLane is the LAST lane tried, after every query strategy already
|
|
4048
|
+
* missed) and fired the copula-specific decline over a sentence no frame
|
|
4049
|
+
* would ever have stored regardless of subject — a confusing answer to a
|
|
4050
|
+
* question nobody asked. Those now fall to teachExclusionReason's existing
|
|
4051
|
+
* self-referential exclusion (a standalone "i"/"me" is already a
|
|
4052
|
+
* TEACH_EXCLUDE_META_TOKEN_RE hit) or to generalVerbTeach's own silent
|
|
4053
|
+
* decline (GENERAL_VERB_NOT_A_VERB_RE covers the same closed pronoun set) —
|
|
4054
|
+
* the same clean stand-down a non-pronoun subject already gets. */
|
|
4055
|
+
const TEACH_PRONOUNS_BARE = Object.freeze(["you", "i", "it", "they", "he", "she", "we"]);
|
|
4056
|
+
// The contracted forms ("i'm new here …") bake the copula into one token, so
|
|
4057
|
+
// they get their own branch below rather than a separate copula match.
|
|
4058
|
+
const TEACH_PRONOUNS_CONTRACTED = Object.freeze(["you're", "i'm", "it's", "they're", "he's", "she's", "we're"]);
|
|
4059
|
+
const TEACH_PRONOUNS = Object.freeze([...TEACH_PRONOUNS_CONTRACTED, ...TEACH_PRONOUNS_BARE]);
|
|
4060
|
+
const TEACH_PRONOUN_WRAPPED_RE = new RegExp(`^(?:every\\s+|each\\s+|all\\s+|some\\s+|a few\\s+|a\\s+|an\\s+)?(${TEACH_PRONOUNS.join("|")})\\s+\\S+`, "i");
|
|
4061
|
+
const TEACH_PRONOUN_BARE_RE = new RegExp(
|
|
4062
|
+
"^(?:every\\s+|each\\s+|all\\s+|some\\s+|a few\\s+|a\\s+|an\\s+)?"
|
|
4063
|
+
+ `(?:(${TEACH_PRONOUNS_BARE.join("|")})\\s+(?:is|are|am|was|were)`
|
|
4064
|
+
+ `|(${TEACH_PRONOUNS_CONTRACTED.join("|")}))`
|
|
4065
|
+
+ "\\s+(?:an?\\s+)?[\\w'-]+[.!?]*$",
|
|
4066
|
+
"i",
|
|
4067
|
+
);
|
|
3928
4068
|
/** The same closed set, read as a whole-word membership test: a pronoun is no
|
|
3929
4069
|
* more a legal fact subject when a reader LIFTS one out of a prior answer than
|
|
3930
4070
|
* when a teach frame offers one. */
|
|
@@ -4080,7 +4220,7 @@ async function teachExclusionReason(sentence) {
|
|
|
4080
4220
|
const unpunctuated = s.replace(/[.!?]+\s*$/, "");
|
|
4081
4221
|
if (TEACH_EXCLUDE_IMPERATIVE_LEAD_RE.test(s)
|
|
4082
4222
|
&& !RETRACT_FORGET_RE.test(unpunctuated) && !RETRACT_FORGET_LOCATIVE_RE.test(unpunctuated)) return "imperative";
|
|
4083
|
-
if (TEACH_EXCLUDE_META_TOKEN_RE.test(s) && !
|
|
4223
|
+
if (TEACH_EXCLUDE_META_TOKEN_RE.test(s) && !TEACH_PRONOUN_BARE_RE.test(s)) return "self-referential";
|
|
4084
4224
|
return null;
|
|
4085
4225
|
}
|
|
4086
4226
|
export { teachExclusionReason };
|
|
@@ -4257,10 +4397,10 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
4257
4397
|
// wrapped; trailing punctuation stripped the same way the OWNS/SOME_A_FEW
|
|
4258
4398
|
// lanes below do) before anything else in this function, so a pronoun
|
|
4259
4399
|
// subject NEVER reaches teachSuggestion's "did you mean" hint or
|
|
4260
|
-
// unknownSubjectFallback's direct-write path — see
|
|
4261
|
-
// docblock above for why.
|
|
4400
|
+
// unknownSubjectFallback's direct-write path — see TEACH_PRONOUN_WRAPPED_RE/
|
|
4401
|
+
// TEACH_PRONOUN_BARE_RE's own shared docblock above for why the two differ.
|
|
4262
4402
|
const pronounSrc = (wrapped ?? raw).replace(/[.!?]+\s*$/, "");
|
|
4263
|
-
const pronounMatch = pronounSrc.match(
|
|
4403
|
+
const pronounMatch = pronounSrc.match(wrapped != null ? TEACH_PRONOUN_WRAPPED_RE : TEACH_PRONOUN_BARE_RE);
|
|
4264
4404
|
// A pronoun-led sentence that's ALSO a mid-sentence question ("it uses
|
|
4265
4405
|
// which controller as its base") isn't a pronoun-classification problem at
|
|
4266
4406
|
// all — "it" was never going to be storable either way, so naming the
|
|
@@ -4276,7 +4416,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
4276
4416
|
// declining right here.
|
|
4277
4417
|
if (pronounMatch && !ACTION_SIGNATURE_TEACH_RE.test(pronounSrc)
|
|
4278
4418
|
&& !(await hasMidSentenceInterrogative(pronounSrc))) {
|
|
4279
|
-
const pronoun = pronounMatch[1];
|
|
4419
|
+
const pronoun = pronounMatch[1] || pronounMatch[2];
|
|
4280
4420
|
return {
|
|
4281
4421
|
text: `I can't store a fact about "${pronoun}" as a class — pronouns aren't things I can classify. `
|
|
4282
4422
|
+ `I remember facts in the shape "every X is a Y", where X is a specific noun, not a pronoun. `
|
|
@@ -4288,10 +4428,14 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
4288
4428
|
// NEGATION ("X is not a Y") and RETRACTION ("forget that X is a Y"). Two
|
|
4289
4429
|
// sentences, two intents, and the split is the point: a negative is a source
|
|
4290
4430
|
// DISAGREEING, never an instruction to destroy. Each branch documents itself
|
|
4291
|
-
// below; both are tried here, right after the pronoun guard
|
|
4292
|
-
// subject ("it is not an animal") still falls to that
|
|
4293
|
-
// first (
|
|
4294
|
-
//
|
|
4431
|
+
// below; both are tried here, right after the pronoun guard. A WRAPPED
|
|
4432
|
+
// pronoun subject ("remember that it is not an animal") still falls to that
|
|
4433
|
+
// guard's own decline first. A BARE one ("it is not an animal") reaches this
|
|
4434
|
+
// block instead, but stays safe either way: retractNotMatch's own "gated on
|
|
4435
|
+
// the positive existing" rule below finds no stored "it ⊑ …" fact to
|
|
4436
|
+
// disagree with (a pronoun was never a legal mint subject anywhere in this
|
|
4437
|
+
// lane), so it falls through to the ordinary cascade unstored, same as
|
|
4438
|
+
// today — just without this guard's more specific wording.
|
|
4295
4439
|
const retractSrc = (wrapped ?? raw).replace(/[.!?]+\s*$/, "");
|
|
4296
4440
|
const retractSrcMidQuestion = memoryDir && !QUESTION_LEAD_RE.test(retractSrc)
|
|
4297
4441
|
? await hasMidSentenceInterrogative(retractSrc) : false;
|
|
@@ -8095,13 +8239,14 @@ const IS_ADJECTIVE_PRONOUN_RE = /^(?:it|this|that)$/i;
|
|
|
8095
8239
|
* SUBJECT capture — factReadBack then treats "you"/"you like"/"you secretly
|
|
8096
8240
|
* chatgpt or" as a literal fact subject and offers to teach a fact ABOUT the
|
|
8097
8241
|
* pronoun ("remember that you is happy"), exactly the grammatical category
|
|
8098
|
-
* error
|
|
8099
|
-
* on the teach-lane side. Same pronoun set (you|i|they|he|
|
|
8100
|
-
* here, checked at the START of the subject capture only
|
|
8101
|
-
* the whole capture — a pronoun subject can carry trailing
|
|
8102
|
-
* like"/"you secretly … or", the same
|
|
8103
|
-
*
|
|
8104
|
-
*
|
|
8242
|
+
* error TEACH_PRONOUN_WRAPPED_RE/TEACH_PRONOUN_BARE_RE (above) were already
|
|
8243
|
+
* built to reject on the teach-lane side. Same pronoun set (you|i|they|he|
|
|
8244
|
+
* she|we) reused here, checked at the START of the subject capture only
|
|
8245
|
+
* (not anchored to the whole capture — a pronoun subject can carry trailing
|
|
8246
|
+
* words, "you like"/"you secretly … or", the same class of category error
|
|
8247
|
+
* those two regexes reject on the teach-lane side). "it" is deliberately
|
|
8248
|
+
* EXCLUDED from this set, unlike TEACH_PRONOUN_WRAPPED_RE/
|
|
8249
|
+
* TEACH_PRONOUN_BARE_RE — IS_ADJECTIVE_PRONOUN_RE (just above) already gives "it"
|
|
8105
8250
|
* its own correct, wanted behavior (anaphoric resolution against the
|
|
8106
8251
|
* session's current FOCUS, "is it deprecated" → resolves off focusLabel),
|
|
8107
8252
|
* which this guard must not shadow. Every call site below is expected to
|
|
@@ -10609,9 +10754,13 @@ async function compareAnswer(query, { graph, config, source }) {
|
|
|
10609
10754
|
* "give me a detailed overview of X", wired to src/domain/completions/'s extractive
|
|
10610
10755
|
* multi-sentence pipeline below. Two closed shapes (DETAILED_HOW_WORKS_RE
|
|
10611
10756
|
* tried first, more specific); distinct from DESCRIBE_WRAPPER_RE, which
|
|
10612
|
-
* neither anchors on "give me"/"explain ... in detail".
|
|
10757
|
+
* neither anchors on "give me"/"explain ... in detail". The article before
|
|
10758
|
+
* "detailed" and the "of" before "how" are both OPTIONAL: a non-native
|
|
10759
|
+
* speaker's "give me detailed summary how this application works" carries
|
|
10760
|
+
* the identical intent as the fully-articled form, and dropping either word
|
|
10761
|
+
* changes nothing the pipeline downstream reads — same shape, same term. */
|
|
10613
10762
|
const DETAILED_HOW_WORKS_RE =
|
|
10614
|
-
/^(?:(?:can|could|would)\s+you\s+(?:please\s+)?|please\s+)?(?:give\s+me\s+
|
|
10763
|
+
/^(?:(?:can|could|would)\s+you\s+(?:please\s+)?|please\s+)?(?:give\s+me\s+(?:an?\s+)?detailed\s+(?:summary|overview|explanation)\s+(?:of\s+)?how|explain\s+(?:to\s+me\s+)?in\s+detail\s+how)\s+(.+?)\s+works\s*\??$/i;
|
|
10615
10764
|
|
|
10616
10765
|
const DETAILED_OVERVIEW_RE =
|
|
10617
10766
|
/^(?:(?:can|could|would)\s+you\s+(?:please\s+)?|please\s+)?give\s+me\s+a\s+detailed\s+(?:overview|summary|explanation)\s+of\s+(.+?)\s*\??$/i;
|