@polycode-projects/the-mechanical-code-talker 6.0.20 → 6.0.22
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 +5 -15
- package/bin/tmct.mjs +8 -3
- package/package.json +1 -5
- package/src/adapters/memory/core.mjs +108 -96
- package/src/adapters/memory/corpus-bands.mjs +6 -0
- package/src/domain/agent-traits.mjs +1 -1
- package/src/domain/answer-variants.json +1 -1
- package/src/domain/cli-verbs.mjs +1 -0
- package/src/domain/memory/causal-stability.mjs +22 -5
- package/src/domain/memory/fact-order.mjs +3 -3
- package/src/domain/memory/provenance-time.mjs +35 -0
- package/src/domain/memory/retraction.mjs +3 -5
- package/src/domain/memory/trust.mjs +11 -11
- package/src/domain/news-feed.mjs +1 -1
- package/src/domain/seeded-random.mjs +6 -6
- package/src/services/adventure.mjs +10 -41
- package/src/services/chat-page-viz.mjs +12 -1111
- package/src/services/chat-session.mjs +20 -6
- package/src/services/chat.mjs +269 -197
- package/src/services/extract-facts.mjs +1 -1
- package/src/services/import-file.mjs +1 -1
- package/src/services/mud-viz.mjs +21 -1082
- package/src/services/mudiii-viz.mjs +0 -4
- package/src/services/news.mjs +44 -24
- package/src/services/pill-complete.mjs +5 -8
- package/src/services/predator-prey.mjs +3 -5
- package/src/surfaces/web/memory-ask-browser.bundle.js +107 -107
- package/src/surfaces/web/mud-browser-entry.mjs +8 -48
- package/test-benchmarks/agentbench/README.md +7 -10
- package/src/adapters/p2p/webrtc-transport.mjs +0 -169
- package/src/domain/p2p/facts.mjs +0 -102
- package/src/domain/p2p/peer-id.mjs +0 -47
- package/src/domain/p2p/provenance-relabel.mjs +0 -37
- package/src/domain/p2p/sync-filter.mjs +0 -43
- package/src/domain/p2p/wire.mjs +0 -126
- package/src/services/p2p-room.mjs +0 -848
- package/src/services/share-overlay-viz.mjs +0 -623
- package/src/surfaces/web/p2p-browser-entry.mjs +0 -39
package/src/services/chat.mjs
CHANGED
|
@@ -1741,7 +1741,7 @@ const HONEST_MISS_PHRASES = [
|
|
|
1741
1741
|
];
|
|
1742
1742
|
/** "whats 2+2" — a bare arithmetic expression, not a code/vocabulary question
|
|
1743
1743
|
* at all. With no closed-set match of its own, this fell into the SAME
|
|
1744
|
-
*
|
|
1744
|
+
* ≤3-word catch-all a genuine orientation opener
|
|
1745
1745
|
* ("what's up", "so what is this") uses, giving the non-sequitur identity
|
|
1746
1746
|
* blurb where an honest "I don't do arithmetic" decline belongs. Deliberately
|
|
1747
1747
|
* excludes "-" from the operator set: this domain's OWN dates ("what
|
|
@@ -1759,8 +1759,17 @@ const ARITHMETIC_RE = /\d+\s*[+*/]\s*\d+/;
|
|
|
1759
1759
|
* ("update the readme") is never caught here — "table"/"from"/"into"/"set"
|
|
1760
1760
|
* are the words that make this unambiguously SQL rather than English. */
|
|
1761
1761
|
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;
|
|
1762
|
-
/**
|
|
1763
|
-
*
|
|
1762
|
+
/** Structural verbs and relation nouns the teach lanes must not mint a general
|
|
1763
|
+
* predicate from while the code-graph switch is on. "a commit touches b" and
|
|
1764
|
+
* "a class contains a method" describe the graph's own edge vocabulary, so a
|
|
1765
|
+
* session declared as asking about code would shadow the built-in relation
|
|
1766
|
+
* with a user-taught one of the same name.
|
|
1767
|
+
*
|
|
1768
|
+
* With the switch off these are ordinary English and mint like any other
|
|
1769
|
+
* verb: "ada uses a spoon", "the box contains apples", "a tester tests".
|
|
1770
|
+
* Every reader takes `codeGraphMode` and consults this set only when it is
|
|
1771
|
+
* true, so the declared switch decides and the wording never does. Every
|
|
1772
|
+
* entry is lowercase; the lanes lowercase the candidate verb first. */
|
|
1764
1773
|
const STRUCT_WORDS = new Set([
|
|
1765
1774
|
"import", "imports", "call", "calls", "use", "uses", "define", "defines", "defined",
|
|
1766
1775
|
"class", "classes", "function", "functions", "module", "modules", "method", "methods",
|
|
@@ -1775,35 +1784,48 @@ const STRUCT_WORDS = new Set([
|
|
|
1775
1784
|
"testing", "defining", "touching", "extending", "inheritance", "coverage", "member", "members",
|
|
1776
1785
|
]);
|
|
1777
1786
|
|
|
1778
|
-
/** Is
|
|
1779
|
-
*
|
|
1780
|
-
*
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
/** Does this look like small-talk / an orientation request rather than a
|
|
1786
|
-
* (near-miss) structural question? Greetings & help/identity phrases always
|
|
1787
|
-
* qualify; a very short input with no code-ish token does too. */
|
|
1788
|
-
export function isConversational(query) {
|
|
1787
|
+
/** Is the line one of the enumerated small-talk phrasings — a greeting, a thanks,
|
|
1788
|
+
* an acknowledgement, a "what can you do", a "who are you", an "are you an AI"?
|
|
1789
|
+
* Every one of these is a closed list, so a line qualifies by being a member and
|
|
1790
|
+
* never by how it happens to be spelled. */
|
|
1791
|
+
function matchesSmallTalkPhrase(query) {
|
|
1789
1792
|
const raw = String(query).trim();
|
|
1790
1793
|
const q = raw.toLowerCase().replace(/[.!?]+$/, "").trim();
|
|
1791
1794
|
if (GREET.has(q) || THANKS.has(q) || OK_ACK.has(q)) return true;
|
|
1792
1795
|
if (CAPABILITY_PHRASES.some((re) => re.test(raw))) return true;
|
|
1793
1796
|
if (IDENTITY_PHRASES.some((re) => re.test(raw))) return true;
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1797
|
+
return aiIdentityMatch(raw);
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/** Is the line short enough for the chat lane to treat as an orientation opener
|
|
1801
|
+
* rather than a question? Three words or fewer, counted after contractions are
|
|
1802
|
+
* written out — so a contraction decides the turn on punctuation alone
|
|
1803
|
+
* otherwise: "what's on peg-a" counts 3 and gets the orientation card, "what is
|
|
1804
|
+
* on peg-a" counts 4 and gets the answer.
|
|
1805
|
+
*
|
|
1806
|
+
* The count is the whole reason this is expandContractions and not
|
|
1807
|
+
* normalizeQuery: the fuller pass strips filler, which takes the count DOWN,
|
|
1808
|
+
* and sends "please describe a dog" and "tell me about a dog" to the card
|
|
1809
|
+
* instead. */
|
|
1810
|
+
function isSmallTalkLength(query) {
|
|
1811
|
+
const q = String(query).trim().toLowerCase().replace(/[.!?]+$/, "").trim();
|
|
1812
|
+
return expandContractions(q).split(/\s+/).filter(Boolean).length <= 3;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/** Does this look like small-talk / an orientation request rather than a
|
|
1816
|
+
* structural question? This is the chat lane's rule: the enumerated phrasings
|
|
1817
|
+
* always qualify, and a very short input does too, unless the session declared
|
|
1818
|
+
* code-graph mode. The tool seam uses the phrase lists alone (askFallback) —
|
|
1819
|
+
* a three-word tool request there is a request, not an opener.
|
|
1820
|
+
*
|
|
1821
|
+
* `codeGraphMode` is the session's declared switch (`/code-graph on|off`, or
|
|
1822
|
+
* the `codeGraphMode` option a host sets). Nothing here reads the text for
|
|
1823
|
+
* code-shaped tokens: what the user is asking about is declared, never
|
|
1824
|
+
* guessed. With the switch on, every line is a question about the code graph,
|
|
1825
|
+
* so the length rule stands down. */
|
|
1826
|
+
export function isConversational(query, { codeGraphMode = false } = {}) {
|
|
1827
|
+
if (matchesSmallTalkPhrase(query)) return true;
|
|
1828
|
+
return !codeGraphMode && isSmallTalkLength(query);
|
|
1807
1829
|
}
|
|
1808
1830
|
|
|
1809
1831
|
/** The words that, in the slot of a short "what is X" / "who is X", name the
|
|
@@ -1853,9 +1875,11 @@ const BACKED_TOOLS = new Set([
|
|
|
1853
1875
|
* "untested") → that tool with its argument bound from the exact arg key the
|
|
1854
1876
|
* dispatchTool switch reads (COMMANDS above). Only when the tool is
|
|
1855
1877
|
* declared by the caller.
|
|
1856
|
-
* 2. Otherwise, a
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1878
|
+
* 2. Otherwise, a structural question → tmct_ask{query:…}, when tmct_ask is
|
|
1879
|
+
* declared. Only the enumerated small-talk phrasings
|
|
1880
|
+
* (matchesSmallTalkPhrase) fall through to a text answer instead. The chat
|
|
1881
|
+
* lane's length rule is not applied here: a host asks this seam for a tool
|
|
1882
|
+
* by sending a request, and "what calls fnAlpha" is three words.
|
|
1859
1883
|
*
|
|
1860
1884
|
* Returns { name, input } or null (→ answer as text).
|
|
1861
1885
|
*/
|
|
@@ -1884,10 +1908,11 @@ export function selectTool(text, declaredNames) {
|
|
|
1884
1908
|
return askFallback(t, declaredNames);
|
|
1885
1909
|
}
|
|
1886
1910
|
|
|
1887
|
-
/** The tmct_ask fallback: emit tmct_ask{query}
|
|
1888
|
-
* the
|
|
1911
|
+
/** The tmct_ask fallback: emit tmct_ask{query} when the caller declared tmct_ask
|
|
1912
|
+
* and the line isn't one of the enumerated small-talk phrasings; otherwise null
|
|
1913
|
+
* (→ text answer). */
|
|
1889
1914
|
function askFallback(text, declaredNames) {
|
|
1890
|
-
if (declaredNames.has("tmct_ask") && BACKED_TOOLS.has("tmct_ask") && !
|
|
1915
|
+
if (declaredNames.has("tmct_ask") && BACKED_TOOLS.has("tmct_ask") && !matchesSmallTalkPhrase(text)) {
|
|
1891
1916
|
return { name: "tmct_ask", input: { query: text } };
|
|
1892
1917
|
}
|
|
1893
1918
|
return null;
|
|
@@ -1910,42 +1935,25 @@ export function capabilityPlanDeps() {
|
|
|
1910
1935
|
};
|
|
1911
1936
|
}
|
|
1912
1937
|
|
|
1913
|
-
/**
|
|
1914
|
-
*
|
|
1915
|
-
*
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
|
|
1919
|
-
* lookup that "what is a TaskController" (articled) already resolves through
|
|
1920
|
-
* — never runs. This re-tests the SAME non-CamelCase codeish reasons (paths,
|
|
1921
|
-
* dotted refs, `()` calls, STRUCT_WORDS) looksCodeish already covers, so a
|
|
1922
|
-
* genuine near-miss structural question ("what is foo.bar()", "what is
|
|
1923
|
-
* import") is unaffected. */
|
|
1924
|
-
function isBareCamelCaseMetaQuestion(query) {
|
|
1938
|
+
/** A short bare "what is X?" / "is X <adjective>?" with no article — the shape
|
|
1939
|
+
* the bare-meta-fact lane (2b/2c, further down this file) reads. Used ONLY to
|
|
1940
|
+
* widen that lane's gate, never the generic orientation-card fallback. Lane
|
|
1941
|
+
* (2b) still diverts only on a REAL hit, so a term with nothing behind it
|
|
1942
|
+
* falls through to the ordinary card either way. */
|
|
1943
|
+
function isBareMetaQuestionShape(query) {
|
|
1925
1944
|
const raw = String(query).trim();
|
|
1926
1945
|
const q = raw.toLowerCase().replace(/[.!?]+$/, "").trim();
|
|
1927
|
-
|
|
1928
|
-
if (nonCamelCodeish || q.split(/\s+/).filter(Boolean).length > 3) return false;
|
|
1946
|
+
if (q.split(/\s+/).filter(Boolean).length > 3) return false;
|
|
1929
1947
|
return BARE_WHATIS_RE.test(raw) || IS_ADJECTIVE_YESNO_RE.test(raw);
|
|
1930
1948
|
}
|
|
1931
1949
|
|
|
1932
|
-
/** The
|
|
1933
|
-
* "
|
|
1934
|
-
*
|
|
1935
|
-
*
|
|
1936
|
-
*
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
* label), and that shape is what keeps the exemption at the CamelCase reason
|
|
1940
|
-
* and nothing else: a path, a dotted ref, a `()` call or any multi-word
|
|
1941
|
-
* near-miss structural question ("what is import") can't be one bare word, and
|
|
1942
|
-
* every STRUCT_WORDS member is lowercase, so the CamelCase requirement leaves
|
|
1943
|
-
* them all where they are. Lane (2c) still only diverts on a real, unique
|
|
1944
|
-
* graph hit — an unknown CamelCase word answers exactly as it does now. */
|
|
1945
|
-
function isBareCamelCaseEntityName(query) {
|
|
1946
|
-
const raw = String(query).trim();
|
|
1947
|
-
if (!/^[A-Za-z][A-Za-z0-9]*$/.test(raw)) return false;
|
|
1948
|
-
return /[a-z][A-Z]/.test(raw);
|
|
1950
|
+
/** The WRAPPERLESS form the same lane reads, (2c) only: a bare "TaskController"
|
|
1951
|
+
* or "task" typed on its own, with no "what is X" wrapper around it. One
|
|
1952
|
+
* unbroken word is the whole shape — lane (2c) looks the raw line up as a
|
|
1953
|
+
* graph label and diverts only on a real, unique hit, so a word naming
|
|
1954
|
+
* nothing answers exactly as it did before. */
|
|
1955
|
+
function isBareEntityNameShape(query) {
|
|
1956
|
+
return /^[A-Za-z][A-Za-z0-9]*$/.test(String(query).trim());
|
|
1949
1957
|
}
|
|
1950
1958
|
|
|
1951
1959
|
// ---- the response-template library (W1: templates → render path) ----
|
|
@@ -2175,9 +2183,9 @@ function closedOrCollapsed(q, set, idx) {
|
|
|
2175
2183
|
* - fold an exact word-repeated clause ("bye bye") to one instance before
|
|
2176
2184
|
* matching BYE — informal reduplication for emphasis, not a new phrase
|
|
2177
2185
|
* Still the SAME closed THANKS/BYE sets underneath (same closedOrCollapsed
|
|
2178
|
-
* matcher) — only the SEGMENTATION generalizes. Bounded to short
|
|
2179
|
-
*
|
|
2180
|
-
*
|
|
2186
|
+
* matcher) — only the SEGMENTATION generalizes. Bounded to short lines (same
|
|
2187
|
+
* discipline as isConversational/fuzzyConversationalMatch), and off entirely
|
|
2188
|
+
* in code-graph mode. A single-clause line (no
|
|
2181
2189
|
* comma/semicolon/"and") is left to the exact whole-line checks above/below —
|
|
2182
2190
|
* this only handles the MULTI-clause case those can't. Returns "bye"/"thanks"/
|
|
2183
2191
|
* null; bye wins when a line carries both — a farewell should end the
|
|
@@ -2253,12 +2261,13 @@ const isClosingFillerClause = (c) => CLOSING_FILLER_CLAUSES.has(c) || CLOSING_FI
|
|
|
2253
2261
|
* "thanks for all your help") — stripped before the closed THANKS lookup so the
|
|
2254
2262
|
* bare "thanks" underneath matches. */
|
|
2255
2263
|
const THANKS_HELP_TAIL_RE = /\s+for\s+(?:the\s+|your\s+|all\s+|all\s+the\s+|all\s+your\s+)?help\s*$/i;
|
|
2256
|
-
function farewellOrThanksSignal(
|
|
2264
|
+
function farewellOrThanksSignal(q, codeGraphMode) {
|
|
2265
|
+
if (codeGraphMode) return null;
|
|
2257
2266
|
const words = q.split(/\s+/).filter(Boolean);
|
|
2258
2267
|
// The upper bound is generous because the real safety is the per-clause gate
|
|
2259
2268
|
// below (every non-thanks clause must itself be small-talk-shaped or a curated
|
|
2260
2269
|
// closing-filler clause), not the total word count.
|
|
2261
|
-
if (words.length < 2 || words.length > 16
|
|
2270
|
+
if (words.length < 2 || words.length > 16) return null;
|
|
2262
2271
|
const clauses = conversationalClauses(q);
|
|
2263
2272
|
if (clauses.length < 2) return null; // single-clause lines: the exact whole-line checks own this
|
|
2264
2273
|
// OK_ACK is deliberately NOT a signal here (unlike the exact whole-line check
|
|
@@ -2269,7 +2278,7 @@ function farewellOrThanksSignal(raw, q) {
|
|
|
2269
2278
|
// as a thanks-signal regressed exactly that live case. THANKS itself is more
|
|
2270
2279
|
// specific (genuine gratitude words rarely lead into an unrelated question),
|
|
2271
2280
|
// but still gated below: a THANKS-hit only counts when every OTHER clause is
|
|
2272
|
-
// itself small-talk-shaped (≤3 words
|
|
2281
|
+
// itself small-talk-shaped (≤3 words) — the SAME bound
|
|
2273
2282
|
// isConversational's own catch-all uses — OR a curated closing-filler clause
|
|
2274
2283
|
// (CLOSING_FILLER_CLAUSES, above) — so "cheers, what does X do" is still left
|
|
2275
2284
|
// to the existing THANKS_PREAMBLE_RE lane, never grabbed here.
|
|
@@ -2286,7 +2295,7 @@ function farewellOrThanksSignal(raw, q) {
|
|
|
2286
2295
|
if (byeHit) return "bye";
|
|
2287
2296
|
const thanksHit = thanksClauseIdx >= 0 && clauses.every((c, i) => i === thanksClauseIdx
|
|
2288
2297
|
|| isClosingFillerClause(c)
|
|
2289
|
-
||
|
|
2298
|
+
|| c.split(/\s+/).filter(Boolean).length <= 3);
|
|
2290
2299
|
return thanksHit ? "thanks" : null;
|
|
2291
2300
|
}
|
|
2292
2301
|
|
|
@@ -2294,9 +2303,9 @@ function farewellOrThanksSignal(raw, q) {
|
|
|
2294
2303
|
* line, an ack lead-in peeled off a dismissal, or a short line whose every word
|
|
2295
2304
|
* is laughter / an ack / a single-word dismissal with at least one laughter or
|
|
2296
2305
|
* dismissal word (so a bare "ok"/"sure" still falls to the ack lane, not here).
|
|
2297
|
-
*
|
|
2298
|
-
function dismissalSignal(q) {
|
|
2299
|
-
if (
|
|
2306
|
+
* Stands down entirely in code-graph mode. */
|
|
2307
|
+
function dismissalSignal(q, codeGraphMode) {
|
|
2308
|
+
if (codeGraphMode) return false;
|
|
2300
2309
|
if (DISMISSAL.has(q) || LAUGHTER.has(q)) return true;
|
|
2301
2310
|
const words = q.split(/\s+/).filter(Boolean);
|
|
2302
2311
|
if (words.length < 2 || words.length > 5) return false;
|
|
@@ -2353,14 +2362,17 @@ function expandShorthandContractions(text) {
|
|
|
2353
2362
|
/** UNIQUE within-bound fuzzy match of the whole trimmed line against
|
|
2354
2363
|
* CONVERSATIONAL_PHRASES — the "helo"/"thnx"/"byee" tier, plus (after shorthand
|
|
2355
2364
|
* contraction expansion above) "waht r u"/"wat r u"-style texting shorthand.
|
|
2356
|
-
* Restricted to short (≤4-word),
|
|
2357
|
-
*
|
|
2358
|
-
*
|
|
2359
|
-
|
|
2365
|
+
* Restricted to short (≤4-word) inputs, and stands down entirely in code-graph
|
|
2366
|
+
* mode; a distance tie is refused, never guessed (same discipline as
|
|
2367
|
+
* fuzzyVocabWord). A leading "/" is refused outright: the slash is tmct's own
|
|
2368
|
+
* command sigil, so "/help" is a command the user declared, not a line one
|
|
2369
|
+
* edit away from the small-talk phrase "help". */
|
|
2370
|
+
function fuzzyConversationalMatch(raw, codeGraphMode) {
|
|
2371
|
+
if (codeGraphMode || String(raw).trim().startsWith("/")) return null;
|
|
2360
2372
|
const expanded = expandShorthandContractions(raw);
|
|
2361
2373
|
const q = collapseRuns(expanded.toLowerCase().replace(/[.!?]+$/, "").trim());
|
|
2362
2374
|
const words = q.split(/\s+/).filter(Boolean);
|
|
2363
|
-
if (!words.length || words.length > 4
|
|
2375
|
+
if (!words.length || words.length > 4) return null;
|
|
2364
2376
|
return fuzzyMatchInSet(q, CONVERSATIONAL_PHRASES, Math.min(2, fuzzyBound(q)));
|
|
2365
2377
|
}
|
|
2366
2378
|
|
|
@@ -2406,14 +2418,18 @@ const identitySelfTemplateId = (uiContext, codeDomainActive) =>
|
|
|
2406
2418
|
|
|
2407
2419
|
/** The "here's what you CAN ask" tail on a decline that names no term of its own
|
|
2408
2420
|
* (the SQL-statement and arithmetic shapes below). `ctx.vocabHint` already
|
|
2409
|
-
* carries the session-gated vocabulary/teach pointer
|
|
2410
|
-
*
|
|
2411
|
-
* run
|
|
2421
|
+
* carries the session-gated vocabulary/teach pointer. The repo pointer needs
|
|
2422
|
+
* two things on top: a session that declared code-graph mode, and a terminal
|
|
2423
|
+
* to run the command in. */
|
|
2412
2424
|
const offRampClause = (ctx) => {
|
|
2413
2425
|
const hint = ctx.vocabHint || 'Try "what is a dog" for vocabulary.';
|
|
2414
|
-
return ctx.uiContext === "browser" ? hint : `${hint} Or point me at a repo with --repo <path>.`;
|
|
2426
|
+
return (ctx.uiContext === "browser" || !ctx.codeGraphMode) ? hint : `${hint} Or point me at a repo with --repo <path>.`;
|
|
2415
2427
|
};
|
|
2416
2428
|
|
|
2429
|
+
/** What a decline that names no term of its own says it DOES answer. The code
|
|
2430
|
+
* graph is named only when the session declared code-graph mode. */
|
|
2431
|
+
const answerableSubjects = (ctx) => (ctx.codeGraphMode ? "a code graph or taught facts" : "taught facts");
|
|
2432
|
+
|
|
2417
2433
|
/** Recognise a conversational expression and return a templated turn result, or null
|
|
2418
2434
|
* to fall through to counts/ask. Handled BEFORE slash-commands' non-slash siblings:
|
|
2419
2435
|
* greetings, thanks, help/orientation, farewell (ends the session via `end:true`),
|
|
@@ -2462,7 +2478,7 @@ function conversationalTurn(line, ctx) {
|
|
|
2462
2478
|
// "thanks, bye") — see farewellOrThanksSignal's own docblock. Never fires
|
|
2463
2479
|
// on a single-clause line (those are the exact checks just above/below),
|
|
2464
2480
|
// so this only ADDS coverage, never shadows it.
|
|
2465
|
-
const signal = farewellOrThanksSignal(
|
|
2481
|
+
const signal = farewellOrThanksSignal(q, ctx.codeGraphMode);
|
|
2466
2482
|
if (signal === "bye") {
|
|
2467
2483
|
note(ctx.trace, "goal: casual/social — ending the session (no graph intent)");
|
|
2468
2484
|
note(ctx.trace, "lane: conversational — farewell (multi-clause phrase-shape match)");
|
|
@@ -2520,7 +2536,7 @@ function conversationalTurn(line, ctx) {
|
|
|
2520
2536
|
return mk(t(T_THANKS), { lane: "thanks" });
|
|
2521
2537
|
}
|
|
2522
2538
|
}
|
|
2523
|
-
if (dismissalSignal(q)) {
|
|
2539
|
+
if (dismissalSignal(q, ctx.codeGraphMode)) {
|
|
2524
2540
|
note(ctx.trace, "goal: casual/social — dismissal/laughter, no graph intent");
|
|
2525
2541
|
note(ctx.trace, "lane: conversational — dismissal (DISMISSAL/LAUGHTER closed set)");
|
|
2526
2542
|
return mk(t(T_DISMISSAL), { lane: "thanks" });
|
|
@@ -2544,7 +2560,7 @@ function conversationalTurn(line, ctx) {
|
|
|
2544
2560
|
note(ctx.trace, "goal: nonsense input shaped like a SQL statement — a targeted decline, not the identity blurb");
|
|
2545
2561
|
note(ctx.trace, "lane: conversational — SQL-statement decline (SQL_STATEMENT_RE)");
|
|
2546
2562
|
return mk(
|
|
2547
|
-
`That reads like a SQL statement, not a question about
|
|
2563
|
+
`That reads like a SQL statement, not a question about ${answerableSubjects(ctx)}. ${offRampClause(ctx)}`,
|
|
2548
2564
|
{ lane: "help" },
|
|
2549
2565
|
);
|
|
2550
2566
|
}
|
|
@@ -2552,7 +2568,7 @@ function conversationalTurn(line, ctx) {
|
|
|
2552
2568
|
note(ctx.trace, "goal: arithmetic — not a code/vocabulary question, an honest decline");
|
|
2553
2569
|
note(ctx.trace, "lane: conversational — arithmetic decline (ARITHMETIC_RE)");
|
|
2554
2570
|
return mk(
|
|
2555
|
-
`I don't do arithmetic — I answer questions about
|
|
2571
|
+
`I don't do arithmetic — I answer questions about ${answerableSubjects(ctx)}. ${offRampClause(ctx)}`,
|
|
2556
2572
|
{ lane: "help" },
|
|
2557
2573
|
);
|
|
2558
2574
|
}
|
|
@@ -2583,12 +2599,11 @@ function conversationalTurn(line, ctx) {
|
|
|
2583
2599
|
}
|
|
2584
2600
|
// Fuzzy-typo fallback (A4): every exact/collapsed closed-set lookup above missed —
|
|
2585
2601
|
// try a bounded edit-distance match against the flattened conversational phrase
|
|
2586
|
-
// pool ("helo", "thnx", "wat r u", "byee"), restricted to short
|
|
2587
|
-
//
|
|
2588
|
-
//
|
|
2589
|
-
// process-exit path from a guess before this fix existed.
|
|
2602
|
+
// pool ("helo", "thnx", "wat r u", "byee"), restricted to short input.
|
|
2603
|
+
// Skipped entirely mid-game (see gameActive above) — never reached the CLI's
|
|
2604
|
+
// own process-exit path from a guess before this fix existed.
|
|
2590
2605
|
if (!gameActive) {
|
|
2591
|
-
const fuzzyHit = fuzzyConversationalMatch(raw);
|
|
2606
|
+
const fuzzyHit = fuzzyConversationalMatch(raw, ctx.codeGraphMode);
|
|
2592
2607
|
if (fuzzyHit) {
|
|
2593
2608
|
const bucket = classifyConversational(fuzzyHit);
|
|
2594
2609
|
note(ctx.trace, `goal: casual/social or orientation — fuzzy-typo match "${raw}" → "${fuzzyHit}"`);
|
|
@@ -5127,7 +5142,8 @@ async function subjectIsNounOrPropn(word) {
|
|
|
5127
5142
|
* guards that keep non-relational pasts out:
|
|
5128
5143
|
* - neither side may lead with a determiner ("the build failed yesterday")
|
|
5129
5144
|
* or a closed-class word;
|
|
5130
|
-
* - the verb may not be a closed-class
|
|
5145
|
+
* - the verb may not be a closed-class word, nor a structural one while the
|
|
5146
|
+
* code-graph switch is on;
|
|
5131
5147
|
* - both name heads must POS-tag NOUN/PROPN (the same wink adapter
|
|
5132
5148
|
* subjectIsNounOrPropn uses — "john failed spectacularly" tags its tail
|
|
5133
5149
|
* ADV and declines). No wink → no signal, never a store;
|
|
@@ -5138,7 +5154,7 @@ async function subjectIsNounOrPropn(word) {
|
|
|
5138
5154
|
* path (generalVerbTeach) would mint.
|
|
5139
5155
|
* Returns { subject, verb, base, object }; `base` is what the caller mints
|
|
5140
5156
|
* through generalVerbPredicate. */
|
|
5141
|
-
async function matchRelationalVerbTeach(text) {
|
|
5157
|
+
async function matchRelationalVerbTeach(text, { codeGraphMode = false } = {}) {
|
|
5142
5158
|
const line = String(text || "").trim();
|
|
5143
5159
|
const m = line.match(RELATION_VERB_TEACH_RE);
|
|
5144
5160
|
if (!m) return null;
|
|
@@ -5146,7 +5162,8 @@ async function matchRelationalVerbTeach(text) {
|
|
|
5146
5162
|
const verb = verbRaw.toLowerCase();
|
|
5147
5163
|
const strip = pastVerbBase(verb);
|
|
5148
5164
|
if (!strip) return null;
|
|
5149
|
-
if (GENERAL_VERB_NOT_A_VERB_RE.test(verb)
|
|
5165
|
+
if (GENERAL_VERB_NOT_A_VERB_RE.test(verb)) return null;
|
|
5166
|
+
if (codeGraphMode && STRUCT_WORDS.has(verb)) return null;
|
|
5150
5167
|
const subjWords = subjectRaw.split(/\s+/);
|
|
5151
5168
|
const objWords = objectRaw.split(/\s+/);
|
|
5152
5169
|
for (const head of [subjWords[0], objWords[0]]) {
|
|
@@ -5182,17 +5199,19 @@ async function matchRelationalVerbTeach(text) {
|
|
|
5182
5199
|
* orientation card. This recognizes the shape ONLY well enough to point at
|
|
5183
5200
|
* the wrapped form that does store; it never stores anything itself. Closed
|
|
5184
5201
|
* the same way matchRelationalVerbTeach is: no determiner/closed-class
|
|
5185
|
-
* heads, no
|
|
5186
|
-
* tags NOUN/PROPN/ADJ (wink tags bare
|
|
5202
|
+
* heads, no discourse verb, no structural verb while the code-graph switch is
|
|
5203
|
+
* on, subject POS-tags NOUN/PROPN, object tags NOUN/PROPN/ADJ (wink tags bare
|
|
5204
|
+
* lowercase names like "mary" ADJ;
|
|
5187
5205
|
* a genuine adverb tail — "dog barks loudly" — still declines). */
|
|
5188
|
-
async function bareTeachWrapperNudgeText(text) {
|
|
5206
|
+
async function bareTeachWrapperNudgeText(text, { codeGraphMode = false } = {}) {
|
|
5189
5207
|
const line = String(text || "").trim().replace(/[.!?]+\s*$/, "");
|
|
5190
5208
|
const m = line.match(/^([\w'-]+)\s+([a-z][\w-]*s)\s+([\w'-]+)$/i);
|
|
5191
5209
|
if (!m) return null;
|
|
5192
5210
|
const [, subj, verbRaw, obj] = m;
|
|
5193
5211
|
const verb = verbRaw.toLowerCase();
|
|
5194
5212
|
if (/^(?:is|was|does)$/.test(verb)) return null;
|
|
5195
|
-
if (GENERAL_VERB_NOT_A_VERB_RE.test(verb) ||
|
|
5213
|
+
if (GENERAL_VERB_NOT_A_VERB_RE.test(verb) || HABITUAL_VERB_EXCLUDE.has(verb)) return null;
|
|
5214
|
+
if (codeGraphMode && STRUCT_WORDS.has(verb)) return null;
|
|
5196
5215
|
for (const head of [subj, obj]) {
|
|
5197
5216
|
if (GENERAL_VERB_DETERMINER_RE.test(head) || GENERAL_VERB_NOT_A_VERB_RE.test(head)) return null;
|
|
5198
5217
|
}
|
|
@@ -5253,7 +5272,7 @@ function foldPrepositionIntoPredicate(predicate, objectRaw) {
|
|
|
5253
5272
|
/** Sentence forms to try asserting for a teach payload: the payload as-is, and
|
|
5254
5273
|
* (if it carries no determiner) its "every …" universal — the ACE-OWL shape the
|
|
5255
5274
|
* grammar actually lands. */
|
|
5256
|
-
function assertCandidates(payload) {
|
|
5275
|
+
function assertCandidates(payload, { codeGraphMode = false } = {}) {
|
|
5257
5276
|
const p = String(payload).trim();
|
|
5258
5277
|
const out = [p];
|
|
5259
5278
|
if (!/^(?:every|each|all|a|an)\b/i.test(p)) out.push(`every ${p}`);
|
|
@@ -5279,7 +5298,7 @@ function assertCandidates(payload) {
|
|
|
5279
5298
|
// still has to ground through the teach path (this rewrite, or teachLane's
|
|
5280
5299
|
// grounded-subject direct write), so a subject grounded nowhere
|
|
5281
5300
|
// ("penguins swim" with no prior grounding) stays an honest decline.
|
|
5282
|
-
const habitual = matchBareHabitualTeach(p);
|
|
5301
|
+
const habitual = matchBareHabitualTeach(p, { codeGraphMode });
|
|
5283
5302
|
if (habitual) {
|
|
5284
5303
|
const articleRule = grammarRules().find((r) => r.kind === "article");
|
|
5285
5304
|
const article = articleRule && beginsWithVowelSound(habitual.subject, articleRule) ? "an" : "a";
|
|
@@ -5292,9 +5311,9 @@ function assertCandidates(payload) {
|
|
|
5292
5311
|
* "dogs bark" (plural subject + base verb) and "a dog barks" (articled
|
|
5293
5312
|
* singular + 3sg verb), both meaning the capability fact "a dog can
|
|
5294
5313
|
* bark". Returns {subject, verb} folded to the singular/base forms, or
|
|
5295
|
-
* null. Deliberately closed: structural verbs
|
|
5296
|
-
* are excluded so a truncated code query never reads
|
|
5297
|
-
* claim, and the plural surface's verb must be a BASE form (no
|
|
5314
|
+
* null. Deliberately closed: with the code-graph switch on, structural verbs
|
|
5315
|
+
* (imports/calls/tests …) are excluded so a truncated code query never reads
|
|
5316
|
+
* as a capability claim, and the plural surface's verb must be a BASE form (no
|
|
5298
5317
|
* plural-looking "s" tail — "dogs animals" is not a habitual sentence;
|
|
5299
5318
|
* "pass"/"miss"-style "ss" verbs stay eligible). */
|
|
5300
5319
|
/** Words that sit in the habitual shapes' verb slot without being verbs —
|
|
@@ -5323,15 +5342,16 @@ const LEADING_DISCOURSE_ADVERB_RE = new RegExp(
|
|
|
5323
5342
|
export function stripLeadingDiscourseAdverb(text) {
|
|
5324
5343
|
return String(text || "").trim().replace(LEADING_DISCOURSE_ADVERB_RE, "");
|
|
5325
5344
|
}
|
|
5326
|
-
function matchBareHabitualTeach(text) {
|
|
5345
|
+
function matchBareHabitualTeach(text, { codeGraphMode = false } = {}) {
|
|
5327
5346
|
const t = stripLeadingDiscourseAdverb(String(text || "").trim());
|
|
5347
|
+
const structural = (word) => codeGraphMode && STRUCT_WORDS.has(word);
|
|
5328
5348
|
const plural = t.match(/^(?:all\s+|every\s+)?([\w-]+s)\s+([a-z][\w-]*)[.!?]*$/i);
|
|
5329
|
-
if (plural && !
|
|
5349
|
+
if (plural && !structural(plural[2].toLowerCase()) && !HABITUAL_VERB_EXCLUDE.has(plural[2].toLowerCase()) && !/[^s]s$/i.test(plural[2])) {
|
|
5330
5350
|
const subject = singularizeSurface(plural[1].toLowerCase());
|
|
5331
5351
|
if (subject !== plural[1].toLowerCase()) return { subject, verb: plural[2].toLowerCase() };
|
|
5332
5352
|
}
|
|
5333
5353
|
const singular = t.match(/^an?\s+([\w-]+)\s+([a-z][\w-]*s)[.!?]*$/i);
|
|
5334
|
-
if (singular && !
|
|
5354
|
+
if (singular && !structural(singular[2].toLowerCase()) && !HABITUAL_VERB_EXCLUDE.has(singular[2].toLowerCase())) {
|
|
5335
5355
|
const verb = singularizeSurface(singular[2].toLowerCase());
|
|
5336
5356
|
if (verb !== singular[2].toLowerCase()) return { subject: singular[1].toLowerCase(), verb };
|
|
5337
5357
|
}
|
|
@@ -5353,12 +5373,13 @@ function matchBareHabitualTeach(text) {
|
|
|
5353
5373
|
* capability frame, and "penguins never fly" is a habitual surface that lands
|
|
5354
5374
|
* on generalVerbTeach's own split instead. */
|
|
5355
5375
|
const BARE_CAN_TEACH_RE = /^(?:an?\s+|every\s+|all\s+)?([\w-]+)\s+(can|cannot|can't|can not)\s+([a-z][\w-]*)[.!?]*$/i;
|
|
5356
|
-
function matchBareCanTeach(text) {
|
|
5376
|
+
function matchBareCanTeach(text, { codeGraphMode = false } = {}) {
|
|
5357
5377
|
const m = String(text || "").trim().match(BARE_CAN_TEACH_RE);
|
|
5358
5378
|
if (!m) return null;
|
|
5359
5379
|
const subject = m[1].toLowerCase();
|
|
5360
5380
|
const verb = m[3].toLowerCase();
|
|
5361
|
-
if (
|
|
5381
|
+
if (HABITUAL_VERB_EXCLUDE.has(verb) || GENERAL_VERB_NOT_A_VERB_RE.test(verb)) return null;
|
|
5382
|
+
if (codeGraphMode && STRUCT_WORDS.has(verb)) return null;
|
|
5362
5383
|
if (GENERAL_VERB_DETERMINER_RE.test(subject) || GENERAL_VERB_NOT_A_VERB_RE.test(subject)) return null;
|
|
5363
5384
|
return { subject, verb, negated: m[2].toLowerCase() !== "can" };
|
|
5364
5385
|
}
|
|
@@ -5782,7 +5803,7 @@ async function teachExclusionReason(sentence) {
|
|
|
5782
5803
|
}
|
|
5783
5804
|
export { teachExclusionReason };
|
|
5784
5805
|
|
|
5785
|
-
async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cache = null, planHolder = null, graph = null, gameConfig = DEFAULT_GAME_CONFIG, observedAt = "", dateText = "" }) {
|
|
5806
|
+
async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cache = null, planHolder = null, graph = null, gameConfig = DEFAULT_GAME_CONFIG, observedAt = "", dateText = "", codeGraphMode = false }) {
|
|
5786
5807
|
// THE DATED TEACH FRAME — "<sentence> as of <date>" carries an explicit
|
|
5787
5808
|
// mgx:observedAt past the same shapes this lane already teaches. Tried
|
|
5788
5809
|
// ONCE, recursively, on the suffix-stripped text, the same
|
|
@@ -5796,7 +5817,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
5796
5817
|
const suffix = datedTeachSuffix(String(query));
|
|
5797
5818
|
if (suffix) {
|
|
5798
5819
|
const dated = await teachLane(suffix.stripped, {
|
|
5799
|
-
memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig,
|
|
5820
|
+
memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig, codeGraphMode,
|
|
5800
5821
|
observedAt: suffix.observedAt, dateText: suffix.dateText,
|
|
5801
5822
|
});
|
|
5802
5823
|
if (dated && !dated.miss) return dated;
|
|
@@ -5917,7 +5938,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
5917
5938
|
if (memoryDir && !QUESTION_LEAD_RE.test(conjSrc) && /\s+and\s+/i.test(conjSrc)
|
|
5918
5939
|
&& !(await hasMidSentenceInterrogative(conjSrc))) {
|
|
5919
5940
|
const rewrap = (half) => (wrapped != null ? `remember that ${half}` : half);
|
|
5920
|
-
const recurse = (half) => teachLane(rewrap(half), { memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig, observedAt, dateText });
|
|
5941
|
+
const recurse = (half) => teachLane(rewrap(half), { memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig, observedAt, dateText, codeGraphMode });
|
|
5921
5942
|
const stripNoted = (t) => String(t).replace(/^noted — remembered(?:\s+\d+\s+facts?)?:\s*/i, "").trim();
|
|
5922
5943
|
const shared = conjSrc.match(/^(.+?)\s+and\s+((?:is|are|has|have|can)\b.+)$/i);
|
|
5923
5944
|
const sharedSubject = shared ? shared[1].match(/^(.+?)\s+(?:is|are|has|have|can)\b/i)?.[1]?.trim() : null;
|
|
@@ -6511,7 +6532,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
6511
6532
|
// (name-shaped sides, POS-confirmed nouns, a lemma-confirmed inflected
|
|
6512
6533
|
// past) that keep "the build failed" an honest non-match.
|
|
6513
6534
|
const relVerb = memoryDir && !QUESTION_LEAD_RE.test(ownSrc) && !ownSrcMidQuestion
|
|
6514
|
-
? await matchRelationalVerbTeach(ownSrc) : null;
|
|
6535
|
+
? await matchRelationalVerbTeach(ownSrc, { codeGraphMode }) : null;
|
|
6515
6536
|
if (relVerb) {
|
|
6516
6537
|
const stored = await teachFact(memoryDir, sessionId, {
|
|
6517
6538
|
subject: relVerb.subject, predicate: await generalVerbPredicate(relVerb.base), object: relVerb.object, observedAt, dateText,
|
|
@@ -6864,7 +6885,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
6864
6885
|
// BARE path: "grace mentors alan" — no "remember"/"note" wrapper at all.
|
|
6865
6886
|
// Without this, such a sentence reaches neither this frame NOR an honest
|
|
6866
6887
|
// miss, landing on the raw structural wall instead (or, at exactly <=3
|
|
6867
|
-
// words
|
|
6888
|
+
// words, the UNRELATED isConversational()
|
|
6868
6889
|
// orientation card — see subjectIsNounOrPropn's own docblock for why a
|
|
6869
6890
|
// plain wrapper-required gate can't safely widen to bare sentences on
|
|
6870
6891
|
// shape alone: "tell me a joke" fits the identical SVO shape and must
|
|
@@ -6928,7 +6949,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
6928
6949
|
// instead of letting the general-verb mint below reify the plural
|
|
6929
6950
|
// verbatim (a fact "can a wren hum" could never read back). An
|
|
6930
6951
|
// ungrounded singular falls through unchanged.
|
|
6931
|
-
const canShape = matchBareCanTeach(raw);
|
|
6952
|
+
const canShape = matchBareCanTeach(raw, { codeGraphMode });
|
|
6932
6953
|
const canSingular = canShape ? singularizeSurface(canShape.subject) : null;
|
|
6933
6954
|
if (canShape && canSingular !== canShape.subject) {
|
|
6934
6955
|
let canLex = lexicon;
|
|
@@ -6952,7 +6973,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
6952
6973
|
if (wrapped && /\b(?:is|are)\b/i.test(wrapped)) payload = wrapped;
|
|
6953
6974
|
else if ((BARE_DECLARATIVE_RE.test(raw) || COMPARATIVE_TEACH_RE.test(raw)
|
|
6954
6975
|
|| (matchesRelationalTeachFrame(raw) && !(await relationalFrameNamesGraphEntity(raw, graph)))
|
|
6955
|
-
|| matchBareHabitualTeach(raw) || matchBareCanTeach(raw))
|
|
6976
|
+
|| matchBareHabitualTeach(raw, { codeGraphMode }) || matchBareCanTeach(raw, { codeGraphMode }))
|
|
6956
6977
|
&& !QUESTION_LEAD_RE.test(raw) && !(await hasMidSentenceInterrogative(raw))) payload = raw;
|
|
6957
6978
|
if (!payload) {
|
|
6958
6979
|
// "remember margo eats ribs", re-escaping here through a combination
|
|
@@ -7050,7 +7071,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
7050
7071
|
if (stored) return stored;
|
|
7051
7072
|
}
|
|
7052
7073
|
}
|
|
7053
|
-
for (const cand of assertCandidates(payload)) {
|
|
7074
|
+
for (const cand of assertCandidates(payload, { codeGraphMode })) {
|
|
7054
7075
|
// assertTurn ITSELF records the "every" quantifier (point 3) on a plain
|
|
7055
7076
|
// universal success, so every caller (this loop AND the top-level
|
|
7056
7077
|
// declarative-sentence dispatch in runTurn) gets it uniformly.
|
|
@@ -7065,7 +7086,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
7065
7086
|
// path itself stores. The subject's naive singular is tried too, so the
|
|
7066
7087
|
// explicit plural surface ("penguins can swim") reaches the same stored
|
|
7067
7088
|
// spelling the grounding fact used.
|
|
7068
|
-
const habitualTeach = matchBareHabitualTeach(payload) || matchBareCanTeach(payload);
|
|
7089
|
+
const habitualTeach = matchBareHabitualTeach(payload, { codeGraphMode }) || matchBareCanTeach(payload, { codeGraphMode });
|
|
7069
7090
|
if (habitualTeach) {
|
|
7070
7091
|
let habLex = lexicon;
|
|
7071
7092
|
if (!habLex) { const { loadLexicon } = await import("../domain/grammar/lexicon.mjs"); habLex = loadLexicon(); }
|
|
@@ -7149,7 +7170,7 @@ async function teachLane(query, { memoryDir, sessionId = "", lexicon = null, cac
|
|
|
7149
7170
|
const { parseAce } = await import("../domain/grammar/ace.mjs");
|
|
7150
7171
|
let lex = lexicon;
|
|
7151
7172
|
if (!lex) { const { loadLexicon } = await import("../domain/grammar/lexicon.mjs"); lex = loadLexicon(); }
|
|
7152
|
-
for (const cand of assertCandidates(payload)) {
|
|
7173
|
+
for (const cand of assertCandidates(payload, { codeGraphMode })) {
|
|
7153
7174
|
const parse = parseAce(cand, lex);
|
|
7154
7175
|
if (parse?.residue?.length) { unknown = [...new Set(parse.residue.map((w) => String(w).toLowerCase()))]; break; }
|
|
7155
7176
|
}
|
|
@@ -7495,8 +7516,7 @@ async function metaLane(query, { graph, memoryDir, last = null, templates = null
|
|
|
7495
7516
|
if (moduleOrient) return moduleOrient;
|
|
7496
7517
|
// The sha-authorship form ("who authored a1b2c3d") can be as short as
|
|
7497
7518
|
// THREE words, which the conversational-orientation branch (step 2) would grab
|
|
7498
|
-
// before the author step (4b) is reached
|
|
7499
|
-
// isConversational. The form is closed + unambiguous (7-40 hex chars), so the
|
|
7519
|
+
// before the author step (4b) is reached. The form is closed + unambiguous (7-40 hex chars), so the
|
|
7500
7520
|
// meta lane delegates it to the author lane here. Unknown/ambiguous shas return
|
|
7501
7521
|
// null and fall through unchanged.
|
|
7502
7522
|
if (AUTHOR_SHA_RE.test(q)) return authorLane(q, { graph });
|
|
@@ -7592,9 +7612,9 @@ const PERSONAL_ASSISTANT_NUDGE_RE = new RegExp(
|
|
|
7592
7612
|
* from a prior result set is a capability the engine genuinely doesn't
|
|
7593
7613
|
* have yet (even the fully-spelled "which of those is not X" doesn't
|
|
7594
7614
|
* compile — parsePredicateFilter has no negation branch).
|
|
7595
|
-
* Without this, both fall to the generic orientation card (a short
|
|
7596
|
-
*
|
|
7597
|
-
*
|
|
7615
|
+
* Without this, both fall to the generic orientation card (a short turn trips
|
|
7616
|
+
* isConversational's ≤3-word catch-all) or the raw grammar wall (a longer one,
|
|
7617
|
+
* e.g. a path) — neither names what actually
|
|
7598
7618
|
* went wrong. This is an honest, GUIDING nudge, never a fabricated filtered
|
|
7599
7619
|
* answer and never a bare wall. */
|
|
7600
7620
|
const STACCATO_NEGATION_RE = /^(?:and\s+)?(?:not|except(?:\s+for)?)\s+(.+?)(?:\s+then|\s+though)?[?.!]*$/i;
|
|
@@ -7606,7 +7626,7 @@ const NEGATION_PRONOUN_RE = /^(?:it|that|this|those|them)(?:\s+ones?)?$/i;
|
|
|
7606
7626
|
* Genuinely unanswerable as a real graph query, never fabricated: tmct's
|
|
7607
7627
|
* superlative only ever names the single top (or bottom) match for a metric
|
|
7608
7628
|
* (evalSuperlative) — it has no "runner-up"/"next ranked" or "greater than a
|
|
7609
|
-
* number" capability to reach for. Without this, both a short
|
|
7629
|
+
* number" capability to reach for. Without this, both a short
|
|
7610
7630
|
* phrasing ("more than that") and the wall these route to (once the
|
|
7611
7631
|
* isConversational catch-all is deferred below) fall to the generic
|
|
7612
7632
|
* orientation card or the raw grammar wall — neither says what actually went
|
|
@@ -7831,6 +7851,17 @@ const NO_GRAPH_BOOTSTRAP_WALL_LEAD = "I can't answer that as a code question —
|
|
|
7831
7851
|
/** The no-code-domain sibling of the lead above: same role (the bootstrap
|
|
7832
7852
|
* wall's opening line), no code vocabulary. */
|
|
7833
7853
|
const NEUTRAL_BOOTSTRAP_WALL_LEAD = "I couldn't ground that in anything I know.";
|
|
7854
|
+
/** What a turn says when the session declared code-graph mode and no code
|
|
7855
|
+
* graph stands behind it. The declaration is the caller's, so the unmet
|
|
7856
|
+
* declaration is reported as the error it is, with no remedy command
|
|
7857
|
+
* attached. */
|
|
7858
|
+
const CODE_GRAPH_MISSING_ERROR = "code-graph mode is on, but this session has no code graph behind it.";
|
|
7859
|
+
/** Does this ANSWER speak the code lane's own vocabulary? Every index-miss the
|
|
7860
|
+
* ask engine composes says "found in the index", and the empty-index note
|
|
7861
|
+
* names the code index outright. Read off what we are about to SAY, never off
|
|
7862
|
+
* what the user typed: which subject a session will talk about is declared,
|
|
7863
|
+
* and this only checks that an answer honours the declaration. */
|
|
7864
|
+
const speaksCodeIndex = (answer) => /found in the index|no code index/i.test(String(answer));
|
|
7834
7865
|
|
|
7835
7866
|
/** The orientation-repeat one-liner. The conversational
|
|
7836
7867
|
* orientation branch sits OUTSIDE the composed-only wall-shortening gate (it
|
|
@@ -7904,6 +7935,10 @@ export async function helpText(codeDomainActive = false, helpRows = undefined) {
|
|
|
7904
7935
|
["list <kind>", "list what you've taught under a class (\"list letters\"); \"list facts\" lists memory itself"],
|
|
7905
7936
|
["forget that <X> is a <Y>", "withdraw a fact you taught, and anything derived from it — the phrasing the retract lane reads"],
|
|
7906
7937
|
["/narrate on|off", "verbose developer/debug mode: decision points, matched pattern, results+sources, goal per turn"],
|
|
7938
|
+
// The switch names the code domain, so it lists beside the other
|
|
7939
|
+
// code-domain rows: a session with nothing to index would be reading about
|
|
7940
|
+
// a graph it hasn't got. Typing it still works either way.
|
|
7941
|
+
...(codeDomainActive ? [["/code-graph on|off", "read every line as a question about the code graph (default off, and never inferred from what you type)"]] : []),
|
|
7907
7942
|
["/wiki on|off|supplement|always", "live Wikipedia (default off): on tries en.wikipedia.org when I can't answer (network), cited; supplement also adds a read-out under every grounded vocabulary answer; always widens that to every grounded answer"],
|
|
7908
7943
|
["/wikipedia | /wikidata", "which source \"research <topic>\" fetches from for the rest of the session: Simple English Wikipedia's prose (the default) or Wikidata's structured claims. tmct.toml's [research] source sets the starting value; tmct chat --research-source overrides it per invocation"],
|
|
7909
7944
|
["research <topic> [limit N] [depth D]", "fetch the topic from the session's research source (Simple English Wikipedia by default — /wikidata switches it) (the explicit ask is the network consent), store what it grounds, and queue its linked topics — \"research next\" steps the queue; also status/stop. limit N caps the links queued per topic, depth D how many hops the queue follows (1 by default); a run also stops at its total node budget"],
|
|
@@ -12204,7 +12239,7 @@ async function factReadBackReaders(memoryDir, query, envelope, miss, graph = nul
|
|
|
12204
12239
|
// verdict or an ex-falso clash replaces the miss outright; an exhausted
|
|
12205
12240
|
// budget merges onto whichever of the three recovery texts below still
|
|
12206
12241
|
// renders, so the miss itself stays byte-identical and only gains the
|
|
12207
|
-
// marker
|
|
12242
|
+
// marker infbench reads apart from a parse miss.
|
|
12208
12243
|
const autoProve = await autoProveFallback(memoryDir, cache, rows, subjectWord, subjCandidates, objVariants, kindWord);
|
|
12209
12244
|
if (autoProve?.text) return autoProve;
|
|
12210
12245
|
const askProveBudgetExhausted = !!autoProve?.budgetExhausted;
|
|
@@ -14191,7 +14226,7 @@ async function conceptForceAnswer(query, envelope, { graph, config, source, memo
|
|
|
14191
14226
|
const definition = (await seonDefinitions()).get(term) ?? null;
|
|
14192
14227
|
if (!definition) return null;
|
|
14193
14228
|
// The runChat shell hands the loaded graph straight in; the pure runTurn(config)
|
|
14194
|
-
// path (tests,
|
|
14229
|
+
// path (tests, the bench harnesses) does not, so load it the same way dispatchTool does when
|
|
14195
14230
|
// it's missing. Failure-tolerated: no loadable graph → no concept force.
|
|
14196
14231
|
let g = graph;
|
|
14197
14232
|
if (!g && config && source) {
|
|
@@ -15260,7 +15295,7 @@ const ARCH_OVERVIEW_PHRASES = [
|
|
|
15260
15295
|
new RegExp(`^${ARCH_OVERVIEW_LEAD}(?:(?:an?|the)\\s+)?(?:overview|map|diagram)\\s+${ARCH_OVERVIEW_OF_REPO}(?:\\s+here)?\\??$`, "i"),
|
|
15261
15296
|
];
|
|
15262
15297
|
|
|
15263
|
-
async function runAsk(query, { config, source, graph, focus, last, templates, memoryDir, sessionId = "", lexicon = null, env, trace, vocabHint: sessionVocabHint = null, tel = null, biasByBundle = {}, cache = null, vocabAntecedent = null, planHolder = null, discourseHolder = null, gameConfig = DEFAULT_GAME_CONFIG, liveReference = false, onLiveLookup = null, sourceSkips = null, uiContext = "cli", synthesisBudget = AUTO_SYNTHESIS_BUDGET, codeDomainActive = false }) {
|
|
15298
|
+
async function runAsk(query, { config, source, graph, focus, last, templates, memoryDir, sessionId = "", lexicon = null, env, trace, vocabHint: sessionVocabHint = null, tel = null, biasByBundle = {}, cache = null, vocabAntecedent = null, planHolder = null, discourseHolder = null, gameConfig = DEFAULT_GAME_CONFIG, liveReference = false, onLiveLookup = null, sourceSkips = null, uiContext = "cli", synthesisBudget = AUTO_SYNTHESIS_BUDGET, codeDomainActive = false, codeGraphMode = false, ingested = false }) {
|
|
15264
15299
|
// The session-wide hint names a term this session can PROVE resolves; when
|
|
15265
15300
|
// the question itself named a subject the lexicon knows, the hint names that
|
|
15266
15301
|
// instead, so a miss on "how many eyes does a human have" points at "human"
|
|
@@ -15897,7 +15932,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
15897
15932
|
}
|
|
15898
15933
|
}
|
|
15899
15934
|
// "what about X" with a genuine PRIOR turn to continue is exempt from the
|
|
15900
|
-
// conversational catch-all even when short
|
|
15935
|
+
// conversational catch-all even when short: isConversational()
|
|
15901
15936
|
// can't see that discourseRewrite/describeWrapperAnswer haven't had their
|
|
15902
15937
|
// turn yet. Same exemption for the bare-connective sibling shape ("and
|
|
15903
15938
|
// Widget?", STACCATO_SWAP_RE), gated the SAME way discourseRewrite gates it.
|
|
@@ -15968,7 +16003,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
15968
16003
|
const bareLine = String(query).trim();
|
|
15969
16004
|
const pm = bareLine.match(/^([\w-]+)\s+are\s+([\w-]+)[.!?]*$/i);
|
|
15970
16005
|
const habitual = pm || QUESTION_LEAD_RE.test(bareLine)
|
|
15971
|
-
? null : (matchBareHabitualTeach(bareLine) || matchBareCanTeach(bareLine));
|
|
16006
|
+
? null : (matchBareHabitualTeach(bareLine, { codeGraphMode }) || matchBareCanTeach(bareLine, { codeGraphMode }));
|
|
15972
16007
|
if (pm || habitual) {
|
|
15973
16008
|
try {
|
|
15974
16009
|
const { loadLexicon, lookupNoun, lookupAdjective } = await import("../domain/grammar/lexicon.mjs");
|
|
@@ -16010,8 +16045,8 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16010
16045
|
} catch { /* lexicon unavailable — leave false, the ordinary path decides */ }
|
|
16011
16046
|
} else if (memoryDir && !QUESTION_LEAD_RE.test(bareLine)
|
|
16012
16047
|
&& bareLine.replace(/[.!?]+\s*$/, "").split(/\s+/).filter(Boolean).length <= 3) {
|
|
16013
|
-
if (await matchRelationalVerbTeach(bareLine)) isBareRelationalVerbTeach = true;
|
|
16014
|
-
else bareTeachWrapperNudge = await bareTeachWrapperNudgeText(bareLine);
|
|
16048
|
+
if (await matchRelationalVerbTeach(bareLine, { codeGraphMode })) isBareRelationalVerbTeach = true;
|
|
16049
|
+
else bareTeachWrapperNudge = await bareTeachWrapperNudgeText(bareLine, { codeGraphMode });
|
|
16015
16050
|
}
|
|
16016
16051
|
}
|
|
16017
16052
|
// A vague relation touch ("what about cochange", "tell me about cochange",
|
|
@@ -16048,7 +16083,13 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16048
16083
|
// isConversational's word-count catch-all into the orientation blurb, and
|
|
16049
16084
|
// that blurb (a dispatched turn) then becomes `last`, wiping the very
|
|
16050
16085
|
// antecedent the next pronoun turn needs.
|
|
16051
|
-
|
|
16086
|
+
//
|
|
16087
|
+
// `ingested` is the same kind of declaration as codeGraphMode: a caller
|
|
16088
|
+
// feeding a document a sentence at a time (`tmct extract`, /ingest) says so,
|
|
16089
|
+
// and the short-input catch-all stands down. A sentence out of a file was
|
|
16090
|
+
// never small talk, however few words it runs to.
|
|
16091
|
+
const isConversationalCandidate = conversationalCandidateBaseGate && !vocabAntecedent && !ingested
|
|
16092
|
+
&& isConversational(query, { codeGraphMode });
|
|
16052
16093
|
const liveGameOwnWord = gameOwnWord(query, planHolder);
|
|
16053
16094
|
// "what is X" with NO article ("what is john") is BOTH conversational-shaped
|
|
16054
16095
|
// (isConversational() would claim it) AND a legitimate bare meta/fact-lookup
|
|
@@ -16074,13 +16115,13 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16074
16115
|
const bareNounMatch = correctMisspellings(String(query).trim()).replace(/[?.!]+\s*$/, "").trim()
|
|
16075
16116
|
.match(/^(?:the\s+|a\s+|an\s+)?([a-z][a-z-]*)$/i);
|
|
16076
16117
|
const bareNounShape = !!bareNounMatch;
|
|
16077
|
-
// `
|
|
16078
|
-
// for THIS lane only
|
|
16079
|
-
//
|
|
16080
|
-
//
|
|
16081
|
-
//
|
|
16082
|
-
//
|
|
16083
|
-
const
|
|
16118
|
+
// `isBareMetaQuestionShape` ORs in alongside isConversationalCandidate
|
|
16119
|
+
// for THIS lane only, so a bare "what is TaskController" still reaches the
|
|
16120
|
+
// bare-meta lookup in code-graph mode, where the conversational candidate
|
|
16121
|
+
// gate stands down. Shares the SAME base gate so it's never looser; a term
|
|
16122
|
+
// with no real hit still falls through to the ordinary orientation-card
|
|
16123
|
+
// fallback further down.
|
|
16124
|
+
const isBareMetaQuestionCandidate = conversationalCandidateBaseGate && isBareMetaQuestionShape(gateQuery);
|
|
16084
16125
|
// The SAME divert-only-on-a-real-hit gate covers factAnswer's
|
|
16085
16126
|
// WHAT_USED_FOR_RE/REVERSE_PREDICATE_MARKERS reverse-predicate shapes too —
|
|
16086
16127
|
// for the shortest members of the family ("what wants happiness") it's
|
|
@@ -16102,8 +16143,14 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16102
16143
|
// relations only on a real hit, so a name with no facts still falls to the
|
|
16103
16144
|
// ordinary card.
|
|
16104
16145
|
const whoIsShape = WHO_IS_BARE_RE.test(gateQuery);
|
|
16146
|
+
// "where is ann" is three words, the same length as the vocabulary openers
|
|
16147
|
+
// above, and factReadBack's locative reader surfaces the taught fact only on
|
|
16148
|
+
// a real hit — so "where is up" with nothing behind it still falls to the
|
|
16149
|
+
// ordinary card. Its auxiliary-fronted sibling ("where does ann live")
|
|
16150
|
+
// reads the same stored fact and takes the same gate.
|
|
16151
|
+
const whereIsShape = WHERE_IS_FACT_RE.test(gateQuery) || WHERE_DOES_FACT_RE.test(gateQuery);
|
|
16105
16152
|
let bareMetaHit = null;
|
|
16106
|
-
if ((isConversationalCandidate ||
|
|
16153
|
+
if ((isConversationalCandidate || isBareMetaQuestionCandidate) && (bareWhatisShape || isAdjectiveShape || reversePredicateShape || capabilityAskShape || bareNounShape || whoIsShape || whereIsShape)) {
|
|
16107
16154
|
if (memoryDir) {
|
|
16108
16155
|
// The bare noun asks its own "what is a X" — the readers never see the
|
|
16109
16156
|
// single word, so the vocabulary route is the constructed question's.
|
|
@@ -16175,14 +16222,14 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16175
16222
|
// returns non-null for an EXACT label match, so ordinary small talk is
|
|
16176
16223
|
// unaffected.
|
|
16177
16224
|
//
|
|
16178
|
-
// `
|
|
16179
|
-
//
|
|
16180
|
-
//
|
|
16181
|
-
//
|
|
16182
|
-
//
|
|
16183
|
-
const
|
|
16184
|
-
&&
|
|
16185
|
-
if (!bareMetaHit && (isConversationalCandidate ||
|
|
16225
|
+
// `isBareEntityNameCandidate` ORs in for THIS lane the way
|
|
16226
|
+
// isBareMetaQuestionCandidate does for (2b), so a bare entity name still
|
|
16227
|
+
// reaches the lane in code-graph mode, where the conversational candidate
|
|
16228
|
+
// gate stands down. Same base gate and same `!vocabAntecedent`, so it's
|
|
16229
|
+
// never looser than the gate it joins.
|
|
16230
|
+
const isBareEntityNameCandidate = conversationalCandidateBaseGate && !vocabAntecedent
|
|
16231
|
+
&& isBareEntityNameShape(query);
|
|
16232
|
+
if (!bareMetaHit && (isConversationalCandidate || isBareEntityNameCandidate) && graph) {
|
|
16186
16233
|
const { metaFallbackEntityAnswer } = await import("../domain/ask.mjs");
|
|
16187
16234
|
const fallback = metaFallbackEntityAnswer(graph, String(query).trim());
|
|
16188
16235
|
if (fallback) bareMetaHit = { text: fallback.text, replace: true };
|
|
@@ -16297,12 +16344,11 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16297
16344
|
// wall-shortening gate below, so it needs its own repeat collapse,
|
|
16298
16345
|
// mirroring WALL_REPEAT_ONELINER.
|
|
16299
16346
|
//
|
|
16300
|
-
// `!envelope?.parsed`: isConversational()
|
|
16301
|
-
// has no way to know the query already compiled to a real structural
|
|
16302
|
-
// shape. A pronoun-shortened follow-up ("who touched it")
|
|
16303
|
-
// 3 words
|
|
16304
|
-
//
|
|
16305
|
-
// the generic orientation wall.
|
|
16347
|
+
// `!envelope?.parsed`: isConversational() counts words and nothing else —
|
|
16348
|
+
// it has no way to know the query already compiled to a real structural
|
|
16349
|
+
// AST shape. A pronoun-shortened follow-up ("who touched it") is exactly
|
|
16350
|
+
// 3 words, so without this guard isConversational would discard a correct,
|
|
16351
|
+
// already-composed answer for the generic orientation wall.
|
|
16306
16352
|
const orientation = orientationAnswer(templates, graph, vocabHint, uiContext, codeDomainActive);
|
|
16307
16353
|
const repeat = last?.answer === orientation;
|
|
16308
16354
|
answer = repeat ? ORIENTATION_REPEAT_ONELINER : orientation;
|
|
@@ -16504,7 +16550,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16504
16550
|
// the vocabulary for something the vocabulary had nothing to do with.
|
|
16505
16551
|
let taught = null;
|
|
16506
16552
|
try {
|
|
16507
|
-
taught = await teachLane(query, { memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig });
|
|
16553
|
+
taught = await teachLane(query, { memoryDir, sessionId, lexicon, cache, planHolder, graph, gameConfig, codeGraphMode });
|
|
16508
16554
|
} catch (error) {
|
|
16509
16555
|
if (!isPersistUnavailable(error)) throw error;
|
|
16510
16556
|
taught = persistUnavailableAnswer();
|
|
@@ -16849,47 +16895,48 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16849
16895
|
}
|
|
16850
16896
|
}
|
|
16851
16897
|
// #4 HONEST-EMPTY POLISH — an empty CODE graph: any still-standing engine
|
|
16852
|
-
// dead-end (an honest empty, the short miss, the bootstrap note) carries
|
|
16853
|
-
//
|
|
16854
|
-
//
|
|
16855
|
-
//
|
|
16856
|
-
//
|
|
16857
|
-
//
|
|
16858
|
-
//
|
|
16859
|
-
//
|
|
16860
|
-
//
|
|
16898
|
+
// dead-end (an honest empty, the short miss, the bootstrap note) carries a
|
|
16899
|
+
// teach-forward pointer, unless the turn already names its own recovery (a
|
|
16900
|
+
// self-contained decline, or a teach-offer about to land). Only when
|
|
16901
|
+
// genuinely empty. A live adventure keeps its polish even beside a
|
|
16902
|
+
// teach-offer: the world asides ("look", "talk to the butler") are guidance
|
|
16903
|
+
// the offer can't carry.
|
|
16904
|
+
//
|
|
16905
|
+
// With code-graph mode ON and no code graph behind the session, the turn
|
|
16906
|
+
// reports that as an error. The mode is the caller's declaration, and an
|
|
16907
|
+
// unmet declaration is a failure, not a prompt to go index something. With
|
|
16908
|
+
// the mode OFF the miss stands as written: nothing here reaches for
|
|
16909
|
+
// code-graph wording on a session that never asked for it.
|
|
16861
16910
|
const adventureLive = !!planHolder?.state?.adventure;
|
|
16862
|
-
|
|
16863
|
-
|
|
16911
|
+
const missStanding = recordMiss && (via === "composed" || via === "miss") && !selfContainedMiss;
|
|
16912
|
+
const codeGraphMissing = codeGraphMode && !(graph && moduleCountOf(graph) > 0);
|
|
16913
|
+
if (missStanding && !adventureLive && codeGraphMissing) {
|
|
16914
|
+
answer = CODE_GRAPH_MISSING_ERROR;
|
|
16915
|
+
via = "miss";
|
|
16916
|
+
genericWallMiss = true;
|
|
16917
|
+
teachOffer = null;
|
|
16918
|
+
note(trace, "intermediate: CODE-GRAPH MODE — the switch is on and no code graph stands behind the session, so the turn reports that error");
|
|
16919
|
+
} else if (missStanding && (adventureLive || !teachOffer)
|
|
16864
16920
|
&& noCodeGraph(graph) && !/--repo|tmct init|no code graph/i.test(answer)) {
|
|
16865
16921
|
if (adventureLive) {
|
|
16866
16922
|
answer = `${answer}\n(I don't know that yet — you can teach me: say "remember: <thing> is a <kind>". Or ask the world: "look", "where is the key", "talk to the butler".)`;
|
|
16867
|
-
note(trace, "intermediate: HONEST-EMPTY POLISH — a live adventure miss points at the teach lane and the world asides
|
|
16923
|
+
note(trace, "intermediate: HONEST-EMPTY POLISH — a live adventure miss points at the teach lane and the world asides");
|
|
16868
16924
|
} else if (browser) {
|
|
16869
16925
|
answer = `${answer}\n(I don't know that yet — you can teach me: say "remember: <thing> is a <kind>".)`;
|
|
16870
|
-
note(trace, "intermediate: HONEST-EMPTY POLISH — a browser miss points at the teach lane
|
|
16871
|
-
} else if (
|
|
16872
|
-
|
|
16873
|
-
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
16877
|
-
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
? `I couldn't read that as a question I can answer. ${vocabHint} Type /help for all query shapes.`
|
|
16883
|
-
: shortMissHint(query);
|
|
16884
|
-
via = "miss";
|
|
16885
|
-
genericWallMiss = true;
|
|
16886
|
-
note(trace, "intermediate: HONEST-EMPTY POLISH — no code domain active, so the code-shaped miss declined to the ordinary wall instead of a code-graph pointer");
|
|
16887
|
-
}
|
|
16926
|
+
note(trace, "intermediate: HONEST-EMPTY POLISH — a browser miss points at the teach lane");
|
|
16927
|
+
} else if (speaksCodeIndex(answer)) {
|
|
16928
|
+
// Nobody declared code-graph mode and there is no code graph here, so an
|
|
16929
|
+
// answer that speaks the code lane's own vocabulary is reporting on
|
|
16930
|
+
// something this session was never told to have. Decline to the same wall
|
|
16931
|
+
// a genuinely unparsed line gets.
|
|
16932
|
+
answer = vocabHint
|
|
16933
|
+
? `I couldn't read that as a question I can answer. ${vocabHint} Type /help for all query shapes.`
|
|
16934
|
+
: shortMissHint(query);
|
|
16935
|
+
via = "miss";
|
|
16936
|
+
genericWallMiss = true;
|
|
16937
|
+
note(trace, "intermediate: HONEST-EMPTY POLISH — the miss spoke code-index vocabulary with the switch off, so it declined to the ordinary wall");
|
|
16888
16938
|
} else {
|
|
16889
|
-
|
|
16890
|
-
// help. "who won the 2031 world cup" used to carry the pointer anyway,
|
|
16891
|
-
// which reads as a remedy for a question the remedy cannot touch.
|
|
16892
|
-
note(trace, "intermediate: HONEST-EMPTY POLISH — held back: nothing in the question is code-shaped, so the index/--repo remedy would not apply");
|
|
16939
|
+
note(trace, "intermediate: HONEST-EMPTY POLISH — held back: the miss names no code index, so it stands as written");
|
|
16893
16940
|
}
|
|
16894
16941
|
}
|
|
16895
16942
|
if (teachOffer) {
|
|
@@ -16988,8 +17035,8 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
16988
17035
|
// trailer's own gate. Absent on a point answer and on every miss.
|
|
16989
17036
|
...(enumerationLane && !recordMiss ? { enumerationLane } : {}),
|
|
16990
17037
|
// The automatic /prove fallback's own budget wall — the same marker
|
|
16991
|
-
// /prove's own explicit budget wall stamps, so
|
|
16992
|
-
//
|
|
17038
|
+
// /prove's own explicit budget wall stamps, so infbench can count a
|
|
17039
|
+
// budget miss apart from a parse miss.
|
|
16993
17040
|
...(budgetExhausted ? { budgetExhausted: true } : {}),
|
|
16994
17041
|
};
|
|
16995
17042
|
const logLines = [ts, `> ${query}`, answer, ""];
|
|
@@ -17151,12 +17198,12 @@ export function renderDeclaredGoals(goals) {
|
|
|
17151
17198
|
return lines.join("\n").replace(/\n+$/, "");
|
|
17152
17199
|
}
|
|
17153
17200
|
|
|
17154
|
-
async function runCommand(line, { config, source, graph, focus, memoryDir, trace, narrate = false, liveReference = false, researchSource = null, tel = null, biasByBundle = {}, cache = null, codeDomainActive = false, laneVocab = null, domainPacks = null, lexicon = null, newsState = null, newsConfig = null, newsProviders = null }) {
|
|
17201
|
+
async function runCommand(line, { config, source, graph, focus, memoryDir, trace, narrate = false, liveReference = false, researchSource = null, tel = null, biasByBundle = {}, cache = null, codeDomainActive = false, codeGraphMode = false, laneVocab = null, domainPacks = null, lexicon = null, newsState = null, newsConfig = null, newsProviders = null }) {
|
|
17155
17202
|
const ts = new Date().toISOString();
|
|
17156
17203
|
const sp = line.indexOf(" ");
|
|
17157
17204
|
const name = (sp === -1 ? line.slice(1) : line.slice(1, sp)).toLowerCase();
|
|
17158
17205
|
const argText = (sp === -1 ? "" : line.slice(sp + 1)).trim();
|
|
17159
|
-
const mk = (answer, { resolvedIds = [], miss = false, newFocus = focus, narrateNext, liveReferenceNext, researchSourceNext, newsStateNext } = {}) => ({
|
|
17206
|
+
const mk = (answer, { resolvedIds = [], miss = false, newFocus = focus, narrateNext, liveReferenceNext, codeGraphModeNext, researchSourceNext, newsStateNext } = {}) => ({
|
|
17160
17207
|
answer,
|
|
17161
17208
|
logLines: [ts, `> ${line}`, answer, ""],
|
|
17162
17209
|
record: { type: "turn", ts, query: line, command: name, via: "command", resolvedIds, answeredIds: [], miss },
|
|
@@ -17164,6 +17211,7 @@ async function runCommand(line, { config, source, graph, focus, memoryDir, trace
|
|
|
17164
17211
|
goal: GOAL_BY_COMMAND[name] || "use a specific tool/command directly",
|
|
17165
17212
|
...(narrateNext !== undefined ? { narrate: narrateNext } : {}),
|
|
17166
17213
|
...(liveReferenceNext !== undefined ? { liveReference: liveReferenceNext } : {}),
|
|
17214
|
+
...(codeGraphModeNext !== undefined ? { codeGraphMode: codeGraphModeNext } : {}),
|
|
17167
17215
|
...(researchSourceNext !== undefined ? { researchSource: researchSourceNext } : {}),
|
|
17168
17216
|
...(newsStateNext !== undefined ? { newsState: newsStateNext } : {}),
|
|
17169
17217
|
});
|
|
@@ -17196,6 +17244,30 @@ async function runCommand(line, { config, source, graph, focus, memoryDir, trace
|
|
|
17196
17244
|
return mk(`narrate mode ${next ? "on" : "off"}.`, { narrateNext: next });
|
|
17197
17245
|
}
|
|
17198
17246
|
|
|
17247
|
+
// /code-graph on|off — whether this session reads its lines as questions
|
|
17248
|
+
// about a code graph (session-scoped, the /narrate and /wiki pattern: the new
|
|
17249
|
+
// state rides the turn RESULT as `codeGraphMode`, and each session shell
|
|
17250
|
+
// applies it to its own mutable state). Default OFF. Nothing else turns it
|
|
17251
|
+
// on: no lane infers code intent from the words a user typed, and no loaded
|
|
17252
|
+
// graph flips it by itself. A bare "/code-graph" reports the CURRENT state
|
|
17253
|
+
// and changes nothing.
|
|
17254
|
+
//
|
|
17255
|
+
// Turning it on in a session with no code graph behind it reports that error
|
|
17256
|
+
// and still sets the mode — the switch answers to the caller, not to what
|
|
17257
|
+
// happens to be loaded.
|
|
17258
|
+
if (name === "code-graph") {
|
|
17259
|
+
const arg = argText.toLowerCase();
|
|
17260
|
+
if (arg !== "on" && arg !== "off") {
|
|
17261
|
+
return mk(`code-graph mode is ${codeGraphMode ? "on" : "off"} — /code-graph on or /code-graph off to change it. `
|
|
17262
|
+
+ "When on, I read every line as a question about the code graph. When off, I answer nothing about code.");
|
|
17263
|
+
}
|
|
17264
|
+
const next = arg === "on";
|
|
17265
|
+
if (next && !(graph && moduleCountOf(graph) > 0)) {
|
|
17266
|
+
return mk(`code-graph mode on. ${CODE_GRAPH_MISSING_ERROR}`, { codeGraphModeNext: true, miss: true });
|
|
17267
|
+
}
|
|
17268
|
+
return mk(`code-graph mode ${next ? "on" : "off"}.`, { codeGraphModeNext: next });
|
|
17269
|
+
}
|
|
17270
|
+
|
|
17199
17271
|
// /wiki on|off — the live Wikipedia supplement toggle (session-scoped,
|
|
17200
17272
|
// exactly the /narrate pattern: the new state rides the turn RESULT as
|
|
17201
17273
|
// `liveReference`, and each session shell applies it to its own mutable
|
|
@@ -17590,7 +17662,7 @@ async function runCommand(line, { config, source, graph, focus, memoryDir, trace
|
|
|
17590
17662
|
let factCount = 0;
|
|
17591
17663
|
for (const sentence of sentences) {
|
|
17592
17664
|
const before = readFactRows(await loadMemory(memoryDir));
|
|
17593
|
-
const { record: ingestRecord } = await runTurn(sentence, { config, memoryDir, sessionId: uuidv7() });
|
|
17665
|
+
const { record: ingestRecord } = await runTurn(sentence, { config, memoryDir, sessionId: uuidv7(), ingested: true });
|
|
17594
17666
|
if (ingestRecord?.via !== "assert" || ingestRecord?.miss) continue;
|
|
17595
17667
|
const after = readFactRows(await loadMemory(memoryDir));
|
|
17596
17668
|
const rows = touchedFactRows(before, after);
|
|
@@ -18695,7 +18767,7 @@ export async function runTurn(input, options = {}) {
|
|
|
18695
18767
|
return { ...result, ...(await factsTouchedSince(memoryDir, before)) };
|
|
18696
18768
|
}
|
|
18697
18769
|
|
|
18698
|
-
async function dispatchTurn(input, { config, source = defaultSource, graph = null, focus = null, last = null, memoryDir = null, sessionId = "", env = process.env, lexicon = null, narrate = false, liveReference = false, researchSource = null, onLiveLookup = null, vocabHint = null, tel = null, biasByBundle = {}, factRowsCache: injectedFactRowsCache = null, planState = null, gameConfig = null, uiContext = "cli", synthesisBudget = AUTO_SYNTHESIS_BUDGET, researchState = null, researchConfig = null, newsState = null, newsConfig = null, newsProviders = null, discourse = null, _noSplit = false, actingSubject = "player", codeDomainActive = null, laneVocab = null, domainPacks = null, retrieval = null } = {}) {
|
|
18770
|
+
async function dispatchTurn(input, { config, source = defaultSource, graph = null, focus = null, last = null, memoryDir = null, sessionId = "", env = process.env, lexicon = null, narrate = false, liveReference = false, researchSource = null, onLiveLookup = null, vocabHint = null, tel = null, biasByBundle = {}, factRowsCache: injectedFactRowsCache = null, planState = null, gameConfig = null, uiContext = "cli", synthesisBudget = AUTO_SYNTHESIS_BUDGET, researchState = null, researchConfig = null, newsState = null, newsConfig = null, newsProviders = null, discourse = null, _noSplit = false, actingSubject = "player", codeDomainActive = null, codeGraphMode = false, ingested = false, laneVocab = null, domainPacks = null, retrieval = null } = {}) {
|
|
18699
18771
|
// Every game's tuning knobs (spider-fly's mass economy, guess-the-number's
|
|
18700
18772
|
// bounds, the shared plan lane's search-depth cap) — a caller's own
|
|
18701
18773
|
// gameConfig (chat-session.mjs resolves one per session from tmct.toml)
|
|
@@ -18792,7 +18864,7 @@ async function dispatchTurn(input, { config, source = defaultSource, graph = nul
|
|
|
18792
18864
|
// circuit breaker had already given up on it. A name lands here only when
|
|
18793
18865
|
// the skip changed what served the answer, and the trailer says so.
|
|
18794
18866
|
const sourceSkips = new Set();
|
|
18795
|
-
const ctx = { config, source, graph, focus, last, memoryDir, sessionId, templates, env, lexicon, trace, narrate, liveReference, researchSource, onLiveLookup, sourceSkips, vocabHint: resolvedVocabHint, tel, biasByBundle, cache: factRowsCache, vocabAntecedent, planHolder, discourseHolder, gameConfig: resolvedGameConfig, uiContext, synthesisBudget, codeDomainActive: domainActive, laneVocab: laneVocabValue, domainPacks: domainPacksValue, newsState, newsConfig, newsProviders };
|
|
18867
|
+
const ctx = { config, source, graph, focus, last, memoryDir, sessionId, templates, env, lexicon, trace, narrate, liveReference, researchSource, onLiveLookup, sourceSkips, vocabHint: resolvedVocabHint, tel, biasByBundle, cache: factRowsCache, vocabAntecedent, planHolder, discourseHolder, gameConfig: resolvedGameConfig, uiContext, synthesisBudget, codeDomainActive: domainActive, codeGraphMode, ingested, laneVocab: laneVocabValue, domainPacks: domainPacksValue, newsState, newsConfig, newsProviders };
|
|
18796
18868
|
// A DISPATCHED turn (count / slash-command / ask) becomes the new "last
|
|
18797
18869
|
// answer" that why/say-more re-renders; a conversational turn does not.
|
|
18798
18870
|
// Every dispatched turn's result passes through finish() here — the LAST
|