@polycode-projects/the-mechanical-code-talker 2.5.3 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polycode-projects/the-mechanical-code-talker",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The Mechanical Code Talker (tmct) — a tolerant, offline, $0 chat surface that guides you toward precision queries about a software repository. ELIZA/PARRY-style but domain-obsessed with code. No model calls; no codebase index of its own.",
|
|
@@ -101,7 +101,13 @@ export function registerReferencePackProvider(provider) {
|
|
|
101
101
|
registeredProvider = provider && typeof provider.lookup === "function" ? provider : null;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
/** The active provider — the registered one, else the lazy fs loader.
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
/** The active provider — the registered one, else the lazy fs loader. An
|
|
105
|
+
* explicit `env` bag (a chat turn's own env, which may carry
|
|
106
|
+
* TMCT_REFERENCE_PACK_DIR) makes the fs loader resolve the pack dir from
|
|
107
|
+
* that bag instead of process.env; with no argument the behavior is
|
|
108
|
+
* unchanged. */
|
|
109
|
+
export function getReferencePackProvider(env) {
|
|
110
|
+
if (registeredProvider) return registeredProvider;
|
|
111
|
+
if (env === undefined) return fsProvider;
|
|
112
|
+
return { lookup: async (normTerm) => loadReferenceArticle(referencePackDir(env), normTerm) };
|
|
107
113
|
}
|
|
@@ -142,6 +142,11 @@ export const LANE_DIALOGUE_ACTS = Object.freeze({
|
|
|
142
142
|
greeting: "initialGreeting",
|
|
143
143
|
thanks: "thanking",
|
|
144
144
|
help: "inform",
|
|
145
|
+
// The guessing game's turns are task-dimension: a reply that discharges
|
|
146
|
+
// the other side's move (a hint, a win, a rebuttal) is an answer; tmct
|
|
147
|
+
// stating its own move (an opening, its next guess) is an inform.
|
|
148
|
+
"game-answer": "answer",
|
|
149
|
+
"game-inform": "inform",
|
|
145
150
|
});
|
|
146
151
|
|
|
147
152
|
/** The dialogue act a router lane resolves to, with its dimension — or null
|
package/src/services/chat.mjs
CHANGED
|
@@ -46,6 +46,10 @@ import { readConstructionFiles } from "../adapters/corpus/construction-banks.mjs
|
|
|
46
46
|
import { fuzzyMatchInSet, fuzzyBound } from "../domain/interpret/fuzzy.mjs";
|
|
47
47
|
import { loadLexicon, lookupNoun } from "../domain/grammar/lexicon.mjs";
|
|
48
48
|
import { pickPhrase } from "../domain/answer-variants.mjs";
|
|
49
|
+
import { REFERENCE_PACK_NAME, cleanMissReferenceTerm, renderReferenceAnswer, referenceProvenanceTag } from "../domain/reference-pack.mjs";
|
|
50
|
+
import { getReferencePackProvider } from "../adapters/corpus/reference-pack.mjs";
|
|
51
|
+
import { dialogueActForLane } from "../domain/dialogue-acts.mjs";
|
|
52
|
+
import { relatedForTerm } from "../domain/skos-view.mjs";
|
|
49
53
|
|
|
50
54
|
// Composition: the chat surface supplies the domain parser's default lemma/POS
|
|
51
55
|
// adapter (the browser bundle's ask-nlp stub carries no factory, so this is a
|
|
@@ -149,6 +153,47 @@ function deduceGoalFromParsed(parsed) {
|
|
|
149
153
|
return "understand a graph relationship";
|
|
150
154
|
}
|
|
151
155
|
|
|
156
|
+
// ---- dialogue acts (ISO 24617-2): a lookup over the lane decision ----
|
|
157
|
+
// A turn result may carry a `lane` string naming the router lane that
|
|
158
|
+
// answered it; withLast (and conversationalTurn's own mk) resolve it through
|
|
159
|
+
// dialogueActForLane and stamp `record.dialogueAct` — a fixed lookup over a
|
|
160
|
+
// decision already made, never a classifier. The honest miss is the row that
|
|
161
|
+
// must never drift: autoNegative in the autoFeedback dimension, tmct
|
|
162
|
+
// reporting its OWN processing failed, not a task answer.
|
|
163
|
+
|
|
164
|
+
/** Stamp the record with the lane's dialogue act (a no-op for an unmapped or
|
|
165
|
+
* absent lane) and put the label in the narrate trace. */
|
|
166
|
+
function attachDialogueAct(result, trace) {
|
|
167
|
+
const act = dialogueActForLane(result?.lane);
|
|
168
|
+
if (act && result?.record) {
|
|
169
|
+
result.record.dialogueAct = act;
|
|
170
|
+
note(trace, `dialogue act: ${act.act} (${act.dimension} dimension, ISO 24617-2)`);
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const PROPOSITIONAL_NODES = new Set(["boolean", "qualifier"]);
|
|
176
|
+
const PROPOSITIONAL_LEAD_RE = /^(?:is|are|am|was|were|does|do|did|can|could|will|would|shall|should|has|have|had|must)\b/i;
|
|
177
|
+
const SET_QUESTION_LEAD_RE = /^(?:what|which|who|whose|where|when|why|how|tell|show|list|define|describe|find|count|name)\b/i;
|
|
178
|
+
|
|
179
|
+
/** The dialogue-act lane for a runAsk turn. A recorded miss is ALWAYS the
|
|
180
|
+
* honest-miss lane, whatever the query shape — feedback about tmct's own
|
|
181
|
+
* processing. An answered turn is labelled by its question shape (yes/no
|
|
182
|
+
* vs set), from the parsed AST when one stood, else the lead word. Null
|
|
183
|
+
* when the turn is neither — the record simply carries no act. */
|
|
184
|
+
function askDialogueLane(parsed, query, recordMiss) {
|
|
185
|
+
if (recordMiss) return "honest-miss";
|
|
186
|
+
if (parsed) {
|
|
187
|
+
if (parsed.node) return PROPOSITIONAL_NODES.has(parsed.node) ? "ask-propositional" : "ask-set";
|
|
188
|
+
if (parsed.shape === "ask") return "ask-propositional";
|
|
189
|
+
if (parsed.shape) return "ask-set";
|
|
190
|
+
}
|
|
191
|
+
const q = String(query).trim();
|
|
192
|
+
if (PROPOSITIONAL_LEAD_RE.test(q)) return "ask-propositional";
|
|
193
|
+
if (SET_QUESTION_LEAD_RE.test(q)) return "ask-set";
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
152
197
|
/** Split the collected trace into buckets by its own leading category tag, so
|
|
153
198
|
* renderNarration can group like with like while the trace array itself stays
|
|
154
199
|
* a flat, chronological narrative — no structured side-channel to keep in
|
|
@@ -1444,16 +1489,17 @@ function conversationalTurn(line, ctx) {
|
|
|
1444
1489
|
const raw = String(line);
|
|
1445
1490
|
const q = raw.toLowerCase().replace(/[.!?]+$/, "").replace(/\s+/g, " ").trim();
|
|
1446
1491
|
const t = (id, slots = {}) => tRender(ctx.templates, id, slots) ?? TEMPLATES_UNAVAILABLE;
|
|
1447
|
-
const mk = (answer, { end = false, miss = false, via = "template" } = {}) => {
|
|
1492
|
+
const mk = (answer, { end = false, miss = false, via = "template", lane = null } = {}) => {
|
|
1448
1493
|
const ts = new Date().toISOString();
|
|
1449
|
-
return {
|
|
1494
|
+
return attachDialogueAct({
|
|
1450
1495
|
answer,
|
|
1451
1496
|
logLines: [ts, `> ${raw}`, answer, ""],
|
|
1452
1497
|
record: { type: "turn", ts, query: raw, conversational: true, via, resolvedIds: [], answeredIds: [], miss },
|
|
1453
1498
|
focus: ctx.focus,
|
|
1454
1499
|
last: ctx.last, // a conversational turn never overwrites the last real answer
|
|
1500
|
+
lane,
|
|
1455
1501
|
...(end ? { end: true } : {}),
|
|
1456
|
-
};
|
|
1502
|
+
}, ctx.trace);
|
|
1457
1503
|
};
|
|
1458
1504
|
if (foldedBye(q)) {
|
|
1459
1505
|
note(ctx.trace, "goal: casual/social — ending the session (no graph intent)");
|
|
@@ -1475,7 +1521,7 @@ function conversationalTurn(line, ctx) {
|
|
|
1475
1521
|
if (signal === "thanks") {
|
|
1476
1522
|
note(ctx.trace, "goal: casual/social — acknowledgement, no graph intent");
|
|
1477
1523
|
note(ctx.trace, "lane: conversational — thanks (multi-clause phrase-shape match)");
|
|
1478
|
-
return mk(t(T_THANKS));
|
|
1524
|
+
return mk(t(T_THANKS), { lane: "thanks" });
|
|
1479
1525
|
}
|
|
1480
1526
|
}
|
|
1481
1527
|
if (WHY.has(q)) {
|
|
@@ -1504,7 +1550,7 @@ function conversationalTurn(line, ctx) {
|
|
|
1504
1550
|
// morning, hello there) keep their wording; only the default greeting swaps.
|
|
1505
1551
|
const id = (!T_GREETING_BY_PHRASE[greetHit] && noCodeGraph(ctx.graph)) ? T_GREETING_EMPTY : (T_GREETING_BY_PHRASE[greetHit] || T_GREETING);
|
|
1506
1552
|
note(ctx.trace, `pattern: template "${id}" (data/templates/responses.jsonl)`);
|
|
1507
|
-
return mk(t(id, { vocabHint: ctx.vocabHint }));
|
|
1553
|
+
return mk(t(id, { vocabHint: ctx.vocabHint }), { lane: "greeting" });
|
|
1508
1554
|
}
|
|
1509
1555
|
}
|
|
1510
1556
|
{
|
|
@@ -1513,23 +1559,23 @@ function conversationalTurn(line, ctx) {
|
|
|
1513
1559
|
note(ctx.trace, "goal: casual/social — acknowledgement, no graph intent");
|
|
1514
1560
|
note(ctx.trace, `lane: conversational — thanks/acknowledgement (${OK_ACK.has(q) ? "OK_ACK" : "THANKS"} closed set${thanksHit === q ? "" : ", elongation-collapsed"})`);
|
|
1515
1561
|
note(ctx.trace, `pattern: template "${T_THANKS}" (data/templates/responses.jsonl)`);
|
|
1516
|
-
return mk(t(T_THANKS));
|
|
1562
|
+
return mk(t(T_THANKS), { lane: "thanks" });
|
|
1517
1563
|
}
|
|
1518
1564
|
}
|
|
1519
1565
|
if (aiIdentityMatch(raw)) {
|
|
1520
1566
|
note(ctx.trace, "goal: identity — is tmct an AI/LLM (a very likely first question)");
|
|
1521
1567
|
note(ctx.trace, "lane: conversational — identity/AI (AI_IDENTITY_PHRASES closed set)");
|
|
1522
|
-
return mk(t(T_IDENTITY_NOT_LLM));
|
|
1568
|
+
return mk(t(T_IDENTITY_NOT_LLM), { lane: "help" });
|
|
1523
1569
|
}
|
|
1524
1570
|
if (FEELINGS_PHRASES.some((re) => re.test(raw))) {
|
|
1525
1571
|
note(ctx.trace, "goal: identity — does tmct have feelings/consciousness (small-talk persona finding)");
|
|
1526
1572
|
note(ctx.trace, "lane: conversational — identity/feelings (FEELINGS_PHRASES closed set)");
|
|
1527
|
-
return mk(t(T_IDENTITY_NO_FEELINGS));
|
|
1573
|
+
return mk(t(T_IDENTITY_NO_FEELINGS), { lane: "help" });
|
|
1528
1574
|
}
|
|
1529
1575
|
if (IDENTITY_PHRASES.some((re) => re.test(raw))) {
|
|
1530
1576
|
note(ctx.trace, "goal: identity — who/what tmct is, not a capability listing");
|
|
1531
1577
|
note(ctx.trace, "lane: conversational — identity (IDENTITY_PHRASES closed set)");
|
|
1532
|
-
return mk(t(T_IDENTITY_SELF));
|
|
1578
|
+
return mk(t(T_IDENTITY_SELF), { lane: "help" });
|
|
1533
1579
|
}
|
|
1534
1580
|
// CAPABILITY_PHRASES' vague-opener entries are self-contained closed
|
|
1535
1581
|
// regexes, but a preamble ahead of one ("right, can you walk me through
|
|
@@ -1541,7 +1587,7 @@ function conversationalTurn(line, ctx) {
|
|
|
1541
1587
|
|| CAPABILITY_PHRASES.some((re) => re.test(applyPreambleFrames(raw))) || ORIENT_OPENERS.has(q)) {
|
|
1542
1588
|
note(ctx.trace, "goal: get oriented — what can tmct answer, how do I start");
|
|
1543
1589
|
note(ctx.trace, "lane: conversational — help/orientation (CAPABILITY_PHRASES/ORIENT_OPENERS / bare help / ?)");
|
|
1544
|
-
return mk(orientationAnswer(ctx.templates, ctx.graph, ctx.vocabHint));
|
|
1590
|
+
return mk(orientationAnswer(ctx.templates, ctx.graph, ctx.vocabHint), { lane: "help" });
|
|
1545
1591
|
}
|
|
1546
1592
|
// Fuzzy-typo fallback (A4): every exact/collapsed closed-set lookup above missed —
|
|
1547
1593
|
// try a bounded edit-distance match against the flattened conversational phrase
|
|
@@ -1554,11 +1600,11 @@ function conversationalTurn(line, ctx) {
|
|
|
1554
1600
|
note(ctx.trace, `goal: casual/social or orientation — fuzzy-typo match "${raw}" → "${fuzzyHit}"`);
|
|
1555
1601
|
note(ctx.trace, `lane: conversational — fuzzy typo tolerance (${bucket})`);
|
|
1556
1602
|
if (bucket === "bye") return mk(t(T_FAREWELL), { end: true });
|
|
1557
|
-
if (bucket === "thanks") return mk(t(T_THANKS));
|
|
1558
|
-
if (bucket === "identity") return mk(t(T_IDENTITY_SELF));
|
|
1559
|
-
if (bucket === "capability") return mk(orientationAnswer(ctx.templates, ctx.graph, ctx.vocabHint));
|
|
1603
|
+
if (bucket === "thanks") return mk(t(T_THANKS), { lane: "thanks" });
|
|
1604
|
+
if (bucket === "identity") return mk(t(T_IDENTITY_SELF), { lane: "help" });
|
|
1605
|
+
if (bucket === "capability") return mk(orientationAnswer(ctx.templates, ctx.graph, ctx.vocabHint), { lane: "help" });
|
|
1560
1606
|
const id = (!T_GREETING_BY_PHRASE[fuzzyHit] && noCodeGraph(ctx.graph)) ? T_GREETING_EMPTY : (T_GREETING_BY_PHRASE[fuzzyHit] || T_GREETING);
|
|
1561
|
-
return mk(t(id, { vocabHint: ctx.vocabHint }));
|
|
1607
|
+
return mk(t(id, { vocabHint: ctx.vocabHint }), { lane: "greeting" });
|
|
1562
1608
|
}
|
|
1563
1609
|
}
|
|
1564
1610
|
return null;
|
|
@@ -8783,6 +8829,46 @@ async function curatedDefinitionAnswer(query, envelope, { memoryDir, lexicon })
|
|
|
8783
8829
|
return { text: `${def} (source: corpus/seon)`, term };
|
|
8784
8830
|
}
|
|
8785
8831
|
|
|
8832
|
+
// ---- learn-on-miss: the shipped reference pack behind the cleanest miss ----
|
|
8833
|
+
|
|
8834
|
+
/** The learn-on-miss gate, shared by the articled miss hook and the bare-form
|
|
8835
|
+
* fallback so the two can never disagree. Fires only on the CLEANEST miss: a
|
|
8836
|
+
* definition-shaped term the lexicon knows, resolving to no graph entity and
|
|
8837
|
+
* no remembered fact — then, and only then, the pack provider is consulted.
|
|
8838
|
+
* Null means the turn proceeds byte-identically to a pack-less run. */
|
|
8839
|
+
async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, cache }) {
|
|
8840
|
+
if (!term || !memoryDir) return null;
|
|
8841
|
+
let key = null;
|
|
8842
|
+
try { key = cleanMissReferenceTerm(term, lexicon ?? undefined); } catch { key = null; }
|
|
8843
|
+
if (!key) return null;
|
|
8844
|
+
if (await resolveEntity(graph, term)) return null;
|
|
8845
|
+
let normFactTerm;
|
|
8846
|
+
try { ({ normFactTerm } = await import("../adapters/memory/core.mjs")); } catch { return null; }
|
|
8847
|
+
const variants = factTermVariants(normFactTerm, term);
|
|
8848
|
+
variants.add(key);
|
|
8849
|
+
const rows = await factRows(memoryDir, cache);
|
|
8850
|
+
if (rows.some((f) => variants.has(f.subject) || variants.has(f.object))) return null;
|
|
8851
|
+
let article = null;
|
|
8852
|
+
try { article = await getReferencePackProvider(env).lookup(key); } catch { article = null; }
|
|
8853
|
+
if (!article) return null;
|
|
8854
|
+
return { key, article, text: renderReferenceAnswer(key, article) };
|
|
8855
|
+
}
|
|
8856
|
+
|
|
8857
|
+
/** Store the article's first-sentence isa as a subClassOf fact carrying
|
|
8858
|
+
* reference provenance — AFTER the cited answer composed, and failure-
|
|
8859
|
+
* tolerated: the answer stands whether or not the fact lands. */
|
|
8860
|
+
async function appendReferenceIsaFact(memoryDir, key, article, cache) {
|
|
8861
|
+
if (!article?.isa) return;
|
|
8862
|
+
try {
|
|
8863
|
+
const { appendFact } = await import("../adapters/memory/core.mjs");
|
|
8864
|
+
await appendFact(memoryDir, {
|
|
8865
|
+
subject: key, predicate: "rdfs:subClassOf", object: article.isa,
|
|
8866
|
+
provenance: referenceProvenanceTag(article),
|
|
8867
|
+
});
|
|
8868
|
+
if (cache) cache.rows = null;
|
|
8869
|
+
} catch { /* tolerated — the cited answer is already composed */ }
|
|
8870
|
+
}
|
|
8871
|
+
|
|
8786
8872
|
/** The concept term a vague "what is a X" / "tell me about X" / "what does X mean" /
|
|
8787
8873
|
* "define X" asks about — metaTermOf's forms plus the "tell me about …" opener that
|
|
8788
8874
|
* the graph parser reads as a count. Null when the line isn't such a touch. The
|
|
@@ -9473,7 +9559,7 @@ async function planLaneAnswer(query, { memoryDir, planHolder, sessionId = "", })
|
|
|
9473
9559
|
const n = heldGoals.length;
|
|
9474
9560
|
return {
|
|
9475
9561
|
text: `${added ? "noted" : "already noted"} — the goal is that ${tails.join(" and ")}.${n > 1 ? ` (${n} goals held)` : ""} Say "solve it" when the state is taught.`,
|
|
9476
|
-
via: "plan", deduced: "record the goal state for a later plan",
|
|
9562
|
+
via: "plan", lane: "goal", deduced: "record the goal state for a later plan",
|
|
9477
9563
|
note: added
|
|
9478
9564
|
? `GOAL frame — ${added === 1 ? "goal spec" : `${added} goal specs`} accumulated on the session plan slot`
|
|
9479
9565
|
: "GOAL frame — the same goal spec was already held, so it folded onto the existing one",
|
|
@@ -9613,7 +9699,7 @@ async function planLaneAnswer(query, { memoryDir, planHolder, sessionId = "", })
|
|
|
9613
9699
|
`${ordering.length ? ` and ${ordering.length} ordering fact${ordering.length === 1 ? "" : "s"}` : ""}. ` +
|
|
9614
9700
|
`Say "next" to make move 1, or ask "what moves are legal now".${assumptionNote}`;
|
|
9615
9701
|
return {
|
|
9616
|
-
text, via: "plan",
|
|
9702
|
+
text, via: "plan", lane: "imperative",
|
|
9617
9703
|
deduced: `plan a move sequence from the current state to the goal (${n} move${n === 1 ? "" : "s"})`,
|
|
9618
9704
|
note: "plan lane — compileDomain + findActionPath over the taught rules; plan held on the session slot",
|
|
9619
9705
|
plan,
|
|
@@ -9982,6 +10068,10 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
9982
10068
|
// facts/recall (a fact EXTENDS a non-miss schema hit too — NOT miss-gated),
|
|
9983
10069
|
// (4) TEACH lane (would-miss), (5) the short tailored miss (would-miss).
|
|
9984
10070
|
let handled = false;
|
|
10071
|
+
// The dialogue-act lane, when a lane below knows better than the final
|
|
10072
|
+
// question-shape lookup (a plan frame is a request/instruct, a stored
|
|
10073
|
+
// teach is an inform, whatever the surface punctuation looked like).
|
|
10074
|
+
let dialogueLaneOverride = null;
|
|
9985
10075
|
// (0) "what else is X" — recognized off the RAW query text, before every
|
|
9986
10076
|
// other lane below (all of which read `envelope`, already relaxed/reparsed
|
|
9987
10077
|
// by ask()'s noise-strip cascade, which silently drops "else"). via is set
|
|
@@ -10021,6 +10111,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10021
10111
|
const planLane = await planLaneAnswer(query, { memoryDir, planHolder, sessionId });
|
|
10022
10112
|
if (planLane) {
|
|
10023
10113
|
answer = planLane.text; via = planLane.via; recordMiss = false; handled = true;
|
|
10114
|
+
if (planLane.lane) dialogueLaneOverride = planLane.lane;
|
|
10024
10115
|
if (planLane.plan) planResult = planLane.plan;
|
|
10025
10116
|
if (planLane.deduced) {
|
|
10026
10117
|
deduced = planLane.deduced;
|
|
@@ -10214,6 +10305,14 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10214
10305
|
const def = await curatedDefinitionAnswer(gateQuery, envelope, { memoryDir, lexicon });
|
|
10215
10306
|
if (def) bareMetaHit = { text: def.text, replace: true };
|
|
10216
10307
|
}
|
|
10308
|
+
// The reference pack's bare-form fallback, beside the curated one and
|
|
10309
|
+
// under the IDENTICAL clean-miss gate the articled hook (4h) applies —
|
|
10310
|
+
// "what is otter" reaches the pack exactly as "what is an otter" does.
|
|
10311
|
+
if (!bareMetaHit) {
|
|
10312
|
+
const refTerm = metaTermOf(gateQuery, envelope);
|
|
10313
|
+
const ref = refTerm ? await referencePackMissAnswer(refTerm, { graph, memoryDir, lexicon, env, cache }) : null;
|
|
10314
|
+
if (ref) bareMetaHit = { text: ref.text, replace: true, reference: ref };
|
|
10315
|
+
}
|
|
10217
10316
|
}
|
|
10218
10317
|
// A bare "what is X" naming a REAL code-graph entity (not a taught fact,
|
|
10219
10318
|
// not a curated corpus term) needs the SAME race fixed too.
|
|
@@ -10249,7 +10348,18 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10249
10348
|
if (fallback) bareMetaHit = { text: fallback.text, replace: true };
|
|
10250
10349
|
}
|
|
10251
10350
|
const coldPronounDecline = focus?.label ? null : coldPronounDeclineText(query);
|
|
10252
|
-
if (bareMetaHit) {
|
|
10351
|
+
if (bareMetaHit?.reference) {
|
|
10352
|
+
// The bare-form reference hit mirrors (4h): the cited answer replaces the
|
|
10353
|
+
// miss, the turn is no longer recorded as one, and the article's isa is
|
|
10354
|
+
// stored after the answer composes.
|
|
10355
|
+
answer = bareMetaHit.text;
|
|
10356
|
+
via = "reference";
|
|
10357
|
+
recordMiss = false;
|
|
10358
|
+
handled = true;
|
|
10359
|
+
note(trace, "lane: (2b) REFERENCE PACK — a bare \"what is X\" clean miss answered from the shipped reference pack, cited");
|
|
10360
|
+
note(trace, `source: reference pack ${REFERENCE_PACK_NAME} — article "${bareMetaHit.reference.article.title}" (revid ${bareMetaHit.reference.article.revid})`);
|
|
10361
|
+
await appendReferenceIsaFact(memoryDir, bareMetaHit.reference.key, bareMetaHit.reference.article, cache);
|
|
10362
|
+
} else if (bareMetaHit) {
|
|
10253
10363
|
answer = bareMetaHit.replace ? bareMetaHit.text : `${answer}\n${bareMetaHit.text}`;
|
|
10254
10364
|
// Same discipline as lane (3): a fact-lane return flagged `miss` is an
|
|
10255
10365
|
// honest miss in better words — the turn record keeps miss=true and via
|
|
@@ -10459,6 +10569,7 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10459
10569
|
const taught = await teachLane(query, { memoryDir, sessionId, lexicon, cache });
|
|
10460
10570
|
if (taught) {
|
|
10461
10571
|
answer = taught.text; via = taught.via; recordMiss = taught.miss;
|
|
10572
|
+
if (!taught.miss) dialogueLaneOverride = "teach";
|
|
10462
10573
|
note(trace, `lane: (4) TEACH — TEACH_RE/OWNS_TEACH_RE/BARE_DECLARATIVE_RE matched, ${taught.miss ? "but the payload could not be stored" : "reified into .tmct/memory"}`);
|
|
10463
10574
|
// `deduced` was computed straight off envelope.parsed alone, but the
|
|
10464
10575
|
// structural grammar has no business parsing a teach-shaped sentence at
|
|
@@ -10603,6 +10714,24 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10603
10714
|
note(trace, `lane: (4g) FUZZY-VERB DECLINE — "${from}" only became a verb through the edit-distance repair tier ("${to}"), and the two words are different verbs, so the repaired sentence's graph answer is dropped rather than shown as an answer to what was typed`);
|
|
10604
10715
|
}
|
|
10605
10716
|
}
|
|
10717
|
+
// (4h) REFERENCE PACK — the cleanest miss consults the shipped reference
|
|
10718
|
+
// pack: a definition-shaped term the lexicon knows, no graph entity, no
|
|
10719
|
+
// remembered fact. A hit answers with the article's summary, always cited;
|
|
10720
|
+
// a null from any gate leaves the turn byte-identical. After the answer
|
|
10721
|
+
// composes, the article's first-sentence isa is stored as a subClassOf fact
|
|
10722
|
+
// with reference provenance, so the NEXT ask answers from memory.
|
|
10723
|
+
if (miss && recordMiss && via === "composed" && memoryDir) {
|
|
10724
|
+
const refTerm = metaTermOf(query, envelope);
|
|
10725
|
+
const ref = refTerm ? await referencePackMissAnswer(refTerm, { graph, memoryDir, lexicon, env, cache }) : null;
|
|
10726
|
+
if (ref) {
|
|
10727
|
+
answer = ref.text;
|
|
10728
|
+
via = "reference";
|
|
10729
|
+
recordMiss = false;
|
|
10730
|
+
note(trace, "lane: (4h) REFERENCE PACK — a clean miss on a lexicon term answered from the shipped reference pack, cited");
|
|
10731
|
+
note(trace, `source: reference pack ${REFERENCE_PACK_NAME} — article "${ref.article.title}" (revid ${ref.article.revid})`);
|
|
10732
|
+
await appendReferenceIsaFact(memoryDir, ref.key, ref.article, cache);
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10606
10735
|
// (5) #1 SHORT TAILORED MISS — replace ONLY the engine's full grammar cheat-sheet
|
|
10607
10736
|
// wall (WALL_MISS_RE). Receipt-bearing misses keep their specific wording.
|
|
10608
10737
|
// WALL KINDNESS: a second consecutive wall collapses to a one-liner whose
|
|
@@ -10733,7 +10862,10 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
|
|
|
10733
10862
|
// `goal`: the SAME deduced string the debug trace's own "goal:" line
|
|
10734
10863
|
// carries. Only runAsk ever sets this field, so the always-on goal line is
|
|
10735
10864
|
// scoped to real ask-engine turns by construction.
|
|
10736
|
-
|
|
10865
|
+
// `lane`: the dialogue-act lane — a lane's own override, else the
|
|
10866
|
+
// question-shape lookup — resolved to an ISO act by runTurn's withLast.
|
|
10867
|
+
const lane = dialogueLaneOverride ?? askDialogueLane(envelope?.parsed, query, recordMiss);
|
|
10868
|
+
return { answer, logLines, record, focus: newFocus, detail, effectiveQuery, goal: deduced, lane, ...(planResult ? { plan: planResult } : {}) };
|
|
10737
10869
|
}
|
|
10738
10870
|
|
|
10739
10871
|
/** A non-ask, non-dispatch chat turn (count answer, /stats) — the same
|
|
@@ -11287,6 +11419,251 @@ function morePage(query, { last, focus }) {
|
|
|
11287
11419
|
return turn;
|
|
11288
11420
|
}
|
|
11289
11421
|
|
|
11422
|
+
// ---- the SKOS view: synonym/related-word questions over the store ----
|
|
11423
|
+
// "another word for X" / "synonyms of X" / "what is related to X" read the
|
|
11424
|
+
// store's mgx:synonym / mgx:relatedTo / mgx:similarTo facts through
|
|
11425
|
+
// buildSkosConceptView's minted concepts (relatedForTerm). Routed ahead of
|
|
11426
|
+
// the generic parse, which reads these phrasings as something else entirely.
|
|
11427
|
+
// A term that mints no concept — unknown, or with no synonym/related facts —
|
|
11428
|
+
// misses honestly, naming the term, never a guessed neighbour.
|
|
11429
|
+
const SKOS_SYNONYM_RE = /^(?:another\s+word\s+for|other\s+words\s+for|synonyms?\s+(?:of|for)|what\s+is\s+a\s+synonym\s+(?:of|for))\s+(?:an?\s+|the\s+)?(.+?)[?.!\s]*$/i;
|
|
11430
|
+
const SKOS_RELATED_RE = /^(?:what\s+is\s+related\s+to|what\s+relates\s+to|what\s+words\s+are\s+related\s+to)\s+(?:an?\s+|the\s+)?(.+?)[?.!\s]*$/i;
|
|
11431
|
+
|
|
11432
|
+
/** The SKOS-view answer for a synonym/related question, or null when the
|
|
11433
|
+
* line is not one. A matched line always answers — a hit lists the group's
|
|
11434
|
+
* other labels and the related concepts; anything else is the honest miss. */
|
|
11435
|
+
async function skosRelatedAnswer(memoryDir, query, cache) {
|
|
11436
|
+
if (!memoryDir) return null;
|
|
11437
|
+
const q = expandContractions(String(query).trim());
|
|
11438
|
+
const syn = q.match(SKOS_SYNONYM_RE);
|
|
11439
|
+
const rel = syn ? null : q.match(SKOS_RELATED_RE);
|
|
11440
|
+
if (!syn && !rel) return null;
|
|
11441
|
+
const term = (syn ?? rel)[1].trim();
|
|
11442
|
+
let hood = null;
|
|
11443
|
+
try { hood = relatedForTerm(await factRows(memoryDir, cache), term); } catch { hood = null; }
|
|
11444
|
+
const parts = [];
|
|
11445
|
+
if (hood?.synonyms?.length) parts.push(`another word for ${term}: ${joinList(hood.synonyms)}`);
|
|
11446
|
+
const relatedLabels = (hood?.related ?? []).map((c) => c.prefLabel);
|
|
11447
|
+
if (relatedLabels.length) parts.push(`related: ${joinList(relatedLabels)}`);
|
|
11448
|
+
if (!parts.length) {
|
|
11449
|
+
return { term, miss: true, text: `I don't know any synonyms or related words for "${term}" yet.` };
|
|
11450
|
+
}
|
|
11451
|
+
return { term, miss: false, text: `${parts.join("; ")} (source: remembered synonym/related facts, read as SKOS)` };
|
|
11452
|
+
}
|
|
11453
|
+
|
|
11454
|
+
// ---- guess-the-number: a closed-loop game over hidden state ----
|
|
11455
|
+
// Two modes on one mechanism. In GUESSER mode the human holds a secret and
|
|
11456
|
+
// tmct searches: a belief interval {lo, hi} narrowed by bisection, one
|
|
11457
|
+
// observation ("higher"/"lower"/"correct") folded in per turn. In THINKER
|
|
11458
|
+
// mode tmct commits a secret up front and each turn is a stateless
|
|
11459
|
+
// comparison against it. The game payload rides the session's plan slot as
|
|
11460
|
+
// a tagged sub-object ({ game: {...} }), so a plan frame and a game never
|
|
11461
|
+
// share the slot — each declines to start while the other is active.
|
|
11462
|
+
|
|
11463
|
+
/** "between A and B" / "up to N" anywhere in an opening line. Loose token
|
|
11464
|
+
* captures (\S+) so a non-numeric bound is SEEN and declined rather than
|
|
11465
|
+
* silently defaulted. */
|
|
11466
|
+
const GAME_BOUNDS_CLAUSE_RE = /\b(?:between\s+(\S+)\s+and\s+(\S+)|up\s+to\s+(\S+))\b/i;
|
|
11467
|
+
const GAME_BOUND_MAX = 1_000_000_000;
|
|
11468
|
+
|
|
11469
|
+
/** The bounds an opening line states — { lo, hi } (default 1–100), or
|
|
11470
|
+
* { problem } naming why the stated range is unplayable. */
|
|
11471
|
+
function parseGameBounds(text) {
|
|
11472
|
+
const m = String(text).match(GAME_BOUNDS_CLAUSE_RE);
|
|
11473
|
+
if (!m) return { lo: 1, hi: 100 };
|
|
11474
|
+
const tokens = (m[3] !== undefined ? ["1", m[3]] : [m[1], m[2]])
|
|
11475
|
+
.map((t) => String(t).replace(/[,.?!]+$/, ""));
|
|
11476
|
+
if (!tokens.every((t) => /^-?\d+$/.test(t))) {
|
|
11477
|
+
return { problem: 'I can only play with whole-number bounds — say "between 1 and 100".' };
|
|
11478
|
+
}
|
|
11479
|
+
const lo = Number(tokens[0]);
|
|
11480
|
+
const hi = Number(tokens[1]);
|
|
11481
|
+
if (Math.abs(lo) > GAME_BOUND_MAX || Math.abs(hi) > GAME_BOUND_MAX) {
|
|
11482
|
+
return { problem: `that range is too big for a fair game — keep both bounds within ${GAME_BOUND_MAX.toLocaleString("en-US")}.` };
|
|
11483
|
+
}
|
|
11484
|
+
if (hi < lo) return { problem: `no number is between ${lo} and ${hi} — that range is empty. Put the smaller bound first.` };
|
|
11485
|
+
if (hi === lo) return { problem: `between ${lo} and ${hi} leaves exactly one number, so there is nothing to guess. Pick a wider range.` };
|
|
11486
|
+
return { lo, hi };
|
|
11487
|
+
}
|
|
11488
|
+
|
|
11489
|
+
// Opening moves, both modes, as closed-set leads + a tail that may only carry
|
|
11490
|
+
// the bounds clause and the closing invitation words — any other tail is a
|
|
11491
|
+
// real sentence and falls through to the ordinary lanes.
|
|
11492
|
+
const GUESSER_OPEN_LEAD_RE = /^(?:i\s*(?:'m|am)\s+thinking\s+of\s+a\s+number|guess\s+my\s+number|guess\s+the\s+number\s+i\s*(?:'m|am)\s+thinking\s+of|guess\s+a\s+number\s+(?:between\s+\S+\s+and\s+\S+\s+|up\s+to\s+\S+\s+)?and\s+i\s*(?:'ll|\s+will)\s+tell\s+you\s+(?:if\s+it\s*(?:'s|\s+is)\s+)?higher\s+or\s+lower)\b(.*)$/i;
|
|
11493
|
+
const THINKER_OPEN_LEAD_RE = /^(?:think\s+of\s+a\s+number)\b(.*)$/i;
|
|
11494
|
+
const GUESSER_OPEN_TAIL_RE = /^[\s,.!?—-]*(?:and\s+)?(?:you\s+)?(?:can\s+|have\s+to\s+|try\s+to\s+)?(?:guess(?:\s+it|\s+what\s+it\s+is)?)?[\s,.!?—-]*$/i;
|
|
11495
|
+
const THINKER_OPEN_TAIL_RE = /^[\s,.!?—-]*(?:and\s+)?(?:i\s*(?:'ll|\s+will)\s+(?:try\s+to\s+)?guess(?:\s+it)?|i\s+guess)?[\s,.!?—-]*$/i;
|
|
11496
|
+
|
|
11497
|
+
/** An opening move — { mode, bounds } — or null. */
|
|
11498
|
+
function matchGameOpening(line) {
|
|
11499
|
+
const l = String(line).trim();
|
|
11500
|
+
const guesser = l.match(GUESSER_OPEN_LEAD_RE);
|
|
11501
|
+
if (guesser && GUESSER_OPEN_TAIL_RE.test(guesser[1].replace(GAME_BOUNDS_CLAUSE_RE, " "))) {
|
|
11502
|
+
return { mode: "guesser", bounds: parseGameBounds(l) };
|
|
11503
|
+
}
|
|
11504
|
+
const thinker = l.match(THINKER_OPEN_LEAD_RE);
|
|
11505
|
+
if (thinker && THINKER_OPEN_TAIL_RE.test(thinker[1].replace(GAME_BOUNDS_CLAUSE_RE, " "))) {
|
|
11506
|
+
return { mode: "thinker", bounds: parseGameBounds(l) };
|
|
11507
|
+
}
|
|
11508
|
+
return null;
|
|
11509
|
+
}
|
|
11510
|
+
|
|
11511
|
+
// Continuation replies, gated STRICTLY on an active game (the same discipline
|
|
11512
|
+
// MORE_RE applies to a held pending remainder): with no game standing none of
|
|
11513
|
+
// these are ever consulted, and mid-game any line that matches none of them
|
|
11514
|
+
// is an ordinary aside — answered by the normal lanes, game untouched.
|
|
11515
|
+
const GAME_STOP_RE = /^(?:ok[,\s]+)?(?:i\s+give\s+up|give\s+up|i\s+quit(?:\s+the\s+game)?|stop\s+(?:the\s+game|playing)|end\s+the\s+game)[.!?\s]*$/i;
|
|
11516
|
+
const GAME_REVEAL_RE = /^(?:just\s+tell\s+me|(?:just\s+)?tell\s+me\s+the\s+(?:number|answer)|what(?:'s|\s+is)\s+(?:the|your)\s+(?:secret\s+)?number|reveal\s+(?:it|the\s+number)|show\s+me\s+the\s+number)[.!?\s]*$/i;
|
|
11517
|
+
const GAME_OBS_HIGHER_RE = /^(?:no[,\s]+)?(?:higher|too\s+low|too\s+small|bigger|greater|go\s+higher|it(?:'s|\s+is)\s+higher)[.!?\s]*$/i;
|
|
11518
|
+
const GAME_OBS_LOWER_RE = /^(?:no[,\s]+)?(?:lower|too\s+high|too\s+big|smaller|less|go\s+lower|it(?:'s|\s+is)\s+lower)[.!?\s]*$/i;
|
|
11519
|
+
const GAME_OBS_CORRECT_RE = /^(?:yes|yep|yeah|correct|you\s+got\s+it|you\s+guessed\s+it|that(?:'s|\s+is)\s+it|that(?:'s|\s+is)\s+right|got\s+it|spot\s+on)[.!?\s]*$/i;
|
|
11520
|
+
const GAME_GUESS_RE = /^(?:is\s+it\s+)?(-?\d{1,12})\s*\??[.!?\s]*$/;
|
|
11521
|
+
const GAME_FALSE_CORRECT_RE = /^(?:but\s+)?you\s+(?:already\s+)?said\s+(?:it\s+was\s+)?(?:correct|right)\b/i;
|
|
11522
|
+
|
|
11523
|
+
/** A natural-language plan frame — the shapes planLaneAnswer owns. Mid-game
|
|
11524
|
+
* these get the one-at-a-time decline instead of clobbering the slot. */
|
|
11525
|
+
function isPlanFrameLine(line) {
|
|
11526
|
+
return GOAL_TEACH_RE.test(line) || GOAL_TEACH_INFINITIVE_RE.test(line)
|
|
11527
|
+
|| GOAL_TEACH_VERBLESS_RE.test(line) || GOAL_TEACH_NP_RE.test(line)
|
|
11528
|
+
|| GOAL_TEACH_IMPERATIVE_RE.test(line) || GOAL_TEACH_CONJUNCTION_RE.test(line)
|
|
11529
|
+
|| PLAN_SOLVE_RE.test(line) || LEGAL_MOVES_RE.test(line);
|
|
11530
|
+
}
|
|
11531
|
+
|
|
11532
|
+
/** The per-turn goal line, table-driven off the live game state. */
|
|
11533
|
+
function gameGoal(game) {
|
|
11534
|
+
if (game.mode === "guesser") return `narrow down your number — currently between ${game.lo} and ${game.hi}`;
|
|
11535
|
+
if (!game.lastHint) return "let you find my secret number I've committed to";
|
|
11536
|
+
return `let you find my secret number — said "${game.lastHint}" so it's ${game.lastHint === "higher" ? "above" : "below"} your last guess`;
|
|
11537
|
+
}
|
|
11538
|
+
|
|
11539
|
+
/** One guesser-mode observation folded into the belief interval, or a
|
|
11540
|
+
* thinker-mode guess compared against the secret. Mutates planHolder.state
|
|
11541
|
+
* (the same slot the plan lane owns) and returns { text, goal?, lane, note },
|
|
11542
|
+
* or null when the line is not a game reply. */
|
|
11543
|
+
function gameContinuationAnswer(line, game, planHolder) {
|
|
11544
|
+
const endGame = () => { planHolder.state = null; };
|
|
11545
|
+
if (game.mode === "guesser") {
|
|
11546
|
+
if (GAME_STOP_RE.test(line)) {
|
|
11547
|
+
endGame();
|
|
11548
|
+
return { text: 'OK, stopping — I never found it. Say "guess my number" any time to play again.', lane: "game-inform", note: "GAME — the game ended on request; the belief interval is discarded" };
|
|
11549
|
+
}
|
|
11550
|
+
if (GAME_OBS_CORRECT_RE.test(line)) {
|
|
11551
|
+
const { guess, guesses } = game;
|
|
11552
|
+
endGame();
|
|
11553
|
+
return { text: `Got it — your number is ${guess}, found in ${guesses} guess${guesses === 1 ? "" : "es"}. Want to play again?`, lane: "game-answer", note: "GAME — the guess was confirmed; game over, won" };
|
|
11554
|
+
}
|
|
11555
|
+
const higher = GAME_OBS_HIGHER_RE.test(line);
|
|
11556
|
+
const lower = !higher && GAME_OBS_LOWER_RE.test(line);
|
|
11557
|
+
if (!higher && !lower) return null;
|
|
11558
|
+
const prior = game.guess;
|
|
11559
|
+
const next = { ...game };
|
|
11560
|
+
if (higher) { next.lo = prior + 1; next.loSetBy = { guess: prior }; }
|
|
11561
|
+
else { next.hi = prior - 1; next.hiSetBy = { guess: prior }; }
|
|
11562
|
+
if (next.lo > next.hi) {
|
|
11563
|
+
// The interval is EMPTY: no number satisfies every observation given,
|
|
11564
|
+
// so name the two observations that cannot both hold and stop guessing
|
|
11565
|
+
// — never a fabricated next guess over a premise known to be false.
|
|
11566
|
+
endGame();
|
|
11567
|
+
const earlier = higher
|
|
11568
|
+
? (game.hiSetBy ? `lower than ${game.hiSetBy.guess}` : `it's between ${game.lo0} and ${game.hi0}`)
|
|
11569
|
+
: (game.loSetBy ? `higher than ${game.loSetBy.guess}` : `it's between ${game.lo0} and ${game.hi0}`);
|
|
11570
|
+
const now = `${higher ? "higher" : "lower"} than ${prior}`;
|
|
11571
|
+
return {
|
|
11572
|
+
text: `That's not possible — you said ${earlier}, and now ${now}, but no number can be both. One of those answers must be wrong. Say "guess my number" to restart.`,
|
|
11573
|
+
lane: "game-answer",
|
|
11574
|
+
note: "GAME — the observations emptied the belief interval; refused to keep guessing under a false premise",
|
|
11575
|
+
};
|
|
11576
|
+
}
|
|
11577
|
+
next.guess = Math.floor((next.lo + next.hi) / 2);
|
|
11578
|
+
next.guesses = game.guesses + 1;
|
|
11579
|
+
planHolder.state = { game: next };
|
|
11580
|
+
return {
|
|
11581
|
+
text: `My guess: ${next.guess}. Say higher, lower, or correct.`,
|
|
11582
|
+
goal: gameGoal(next),
|
|
11583
|
+
lane: "game-inform",
|
|
11584
|
+
note: `GAME — folded "${higher ? "higher" : "lower"}" into the interval and bisected it again`,
|
|
11585
|
+
};
|
|
11586
|
+
}
|
|
11587
|
+
// Thinker mode: tmct holds the ground truth, so every reply is a plain
|
|
11588
|
+
// comparison — and the hint record is authoritative against false claims.
|
|
11589
|
+
if (GAME_STOP_RE.test(line) || GAME_REVEAL_RE.test(line)) {
|
|
11590
|
+
const { secret } = game;
|
|
11591
|
+
endGame();
|
|
11592
|
+
return { text: `The number was ${secret}. Want to play again?`, lane: "game-answer", note: "GAME — revealed the secret on request; game over" };
|
|
11593
|
+
}
|
|
11594
|
+
if (GAME_FALSE_CORRECT_RE.test(line)) {
|
|
11595
|
+
const record = game.lastHint
|
|
11596
|
+
? `my last hint was "${game.lastHint}", after your guess of ${game.lastGuess}`
|
|
11597
|
+
: "you haven't guessed yet";
|
|
11598
|
+
return { text: `I haven't said "correct" yet — ${record}. Keep guessing.`, goal: gameGoal(game), lane: "game-answer", note: "GAME — rebutted a false \"you said correct\" from the game's own hint record" };
|
|
11599
|
+
}
|
|
11600
|
+
const m = String(line).trim().match(GAME_GUESS_RE);
|
|
11601
|
+
if (!m) return null;
|
|
11602
|
+
const guess = Number.parseInt(m[1], 10);
|
|
11603
|
+
if (guess < game.lo0 || guess > game.hi0) {
|
|
11604
|
+
return { text: `${guess} is outside the ${game.lo0} to ${game.hi0} range we agreed — try a number in range.`, goal: gameGoal(game), lane: "game-answer", note: "GAME — an out-of-range guess; declined rather than comparing outside the agreed bounds" };
|
|
11605
|
+
}
|
|
11606
|
+
const next = { ...game, guesses: game.guesses + 1, lastGuess: guess };
|
|
11607
|
+
if (guess === game.secret) {
|
|
11608
|
+
endGame();
|
|
11609
|
+
return { text: `Correct — you got it in ${next.guesses} guess${next.guesses === 1 ? "" : "es"}! The number was ${guess}. Want to play again?`, lane: "game-answer", note: "GAME — the guess matched the secret; game over, won" };
|
|
11610
|
+
}
|
|
11611
|
+
next.lastHint = guess < game.secret ? "higher" : "lower";
|
|
11612
|
+
planHolder.state = { game: next };
|
|
11613
|
+
return { text: `${next.lastHint} — guess again.`, goal: gameGoal(next), lane: "game-answer", note: `GAME — compared the guess against the committed secret: ${next.lastHint}` };
|
|
11614
|
+
}
|
|
11615
|
+
|
|
11616
|
+
/** The whole game lane for one turn: continuations first (active game only),
|
|
11617
|
+
* then opening moves, with the one-at-a-time declines both ways across the
|
|
11618
|
+
* shared plan slot. Null when the turn is not the game's to answer. */
|
|
11619
|
+
function guessNumberTurn(line, { planHolder, env }) {
|
|
11620
|
+
const state = planHolder?.state ?? null;
|
|
11621
|
+
const game = state?.game ?? null;
|
|
11622
|
+
const opening = matchGameOpening(line);
|
|
11623
|
+
if (game) {
|
|
11624
|
+
const continuation = gameContinuationAnswer(line, game, planHolder);
|
|
11625
|
+
if (continuation) return continuation;
|
|
11626
|
+
if (opening) {
|
|
11627
|
+
return { text: `we're already playing — I'm ${game.mode === "guesser" ? "guessing your number" : "holding a secret number"}. Say "I give up" to end this game first.`, lane: "game-inform", note: "GAME — an opening arrived mid-game; declined, the running game stands" };
|
|
11628
|
+
}
|
|
11629
|
+
if (isPlanFrameLine(line)) {
|
|
11630
|
+
return { text: 'a guess-the-number game is active — say "I give up" to end it, then set your goal.', lane: "game-inform", note: "GAME — a plan frame arrived mid-game; the slot holds one thing at a time" };
|
|
11631
|
+
}
|
|
11632
|
+
return null;
|
|
11633
|
+
}
|
|
11634
|
+
if (!opening) return null;
|
|
11635
|
+
const planActive = state && !state.done
|
|
11636
|
+
&& ((Array.isArray(state.goals) && state.goals.length) || (Array.isArray(state.actions) && state.actions.length));
|
|
11637
|
+
if (planActive) {
|
|
11638
|
+
return { text: "a plan is in progress — finish it or start a fresh goal before we play guess-the-number.", lane: "game-inform", note: "GAME — an opening arrived while a plan frame is active; the slot holds one thing at a time" };
|
|
11639
|
+
}
|
|
11640
|
+
if (opening.bounds.problem) {
|
|
11641
|
+
return { text: opening.bounds.problem, lane: "game-inform", note: "GAME — the opening stated an unplayable range; declined honestly" };
|
|
11642
|
+
}
|
|
11643
|
+
const { lo, hi } = opening.bounds;
|
|
11644
|
+
if (opening.mode === "guesser") {
|
|
11645
|
+
const guess = Math.floor((lo + hi) / 2);
|
|
11646
|
+
planHolder.state = { game: { mode: "guesser", lo0: lo, hi0: hi, lo, hi, guess, guesses: 1, loSetBy: null, hiSetBy: null } };
|
|
11647
|
+
return {
|
|
11648
|
+
text: `OK — you're thinking of a number between ${lo} and ${hi}; I'll guess it. My guess: ${guess}. Say higher, lower, or correct.`,
|
|
11649
|
+
goal: `narrow down your number — currently between ${lo} and ${hi}`,
|
|
11650
|
+
lane: "game-inform",
|
|
11651
|
+
note: "GAME — guesser mode opened; the belief interval starts at the agreed bounds and the first guess is its midpoint",
|
|
11652
|
+
};
|
|
11653
|
+
}
|
|
11654
|
+
const envSecret = Number.parseInt(String(env?.TMCT_GAME_SECRET ?? ""), 10);
|
|
11655
|
+
const secret = Number.isSafeInteger(envSecret) && envSecret >= lo && envSecret <= hi
|
|
11656
|
+
? envSecret
|
|
11657
|
+
: lo + Math.floor(Math.random() * (hi - lo + 1));
|
|
11658
|
+
planHolder.state = { game: { mode: "thinker", lo0: lo, hi0: hi, secret, guesses: 0, lastHint: null, lastGuess: null } };
|
|
11659
|
+
return {
|
|
11660
|
+
text: `Done — I've thought of a number between ${lo} and ${hi}. Guess it, and I'll say higher, lower, or correct.`,
|
|
11661
|
+
goal: "let you find my secret number I've committed to",
|
|
11662
|
+
lane: "game-inform",
|
|
11663
|
+
note: "GAME — thinker mode opened; the secret is committed for the whole game",
|
|
11664
|
+
};
|
|
11665
|
+
}
|
|
11666
|
+
|
|
11290
11667
|
// "I want you to search for Widget" / "I'd like you to search for Widget" —
|
|
11291
11668
|
// a closed-set indirect-request wrapper, checked VERY early. Without this it
|
|
11292
11669
|
// is mis-swallowed by GENERAL_VERB_TEACH_RE as a bare teach triple (subject
|
|
@@ -11501,7 +11878,7 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
11501
11878
|
// what the shell prints. The narrate block is applied AFTER `last` is
|
|
11502
11879
|
// captured from the PRE-narration finished result.
|
|
11503
11880
|
const withLast = (result, fallbackGoal = "unclear — no goal signal for this turn type") => {
|
|
11504
|
-
const finished = finish(result, { graph });
|
|
11881
|
+
const finished = attachDialogueAct(finish(result, { graph }), trace);
|
|
11505
11882
|
// Every dispatch path below built its own record off `workingLine` (the
|
|
11506
11883
|
// indirect-request wrapper stripped and/or the discontiguous-frame
|
|
11507
11884
|
// rewrite applied) — restore the ORIGINAL raw `line` into record.query
|
|
@@ -11541,6 +11918,26 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
11541
11918
|
const bareCmd = asBareCommand(workingLine);
|
|
11542
11919
|
if (bareCmd) return withLast(await runCommand(bareCmd, ctx), "use a specific tool/command directly");
|
|
11543
11920
|
|
|
11921
|
+
// GUESS-THE-NUMBER — opening moves, and (with a game standing) the
|
|
11922
|
+
// closed-set continuation replies. Checked before the conversational layer
|
|
11923
|
+
// because the guesser-mode observations ("yes", "got it") share words with
|
|
11924
|
+
// the acknowledgement sets, and before assertTurn/runAsk because an opening
|
|
11925
|
+
// line would otherwise read as a declarative to remember. A mid-game line
|
|
11926
|
+
// matching no game shape returns null here and the game stands untouched.
|
|
11927
|
+
{
|
|
11928
|
+
const gameTurn = guessNumberTurn(workingLine, { planHolder, env });
|
|
11929
|
+
if (gameTurn) {
|
|
11930
|
+
note(trace, `lane: ${gameTurn.note}`);
|
|
11931
|
+
if (gameTurn.goal) note(trace, `goal: ${gameTurn.goal}`);
|
|
11932
|
+
const result = plainTurn(workingLine, gameTurn.text, { via: "game", focus });
|
|
11933
|
+
if (gameTurn.goal) result.goal = gameTurn.goal;
|
|
11934
|
+
result.lane = gameTurn.lane;
|
|
11935
|
+
const rec = withLast(result, gameTurn.goal ?? "play the guessing game");
|
|
11936
|
+
rec.planState = planHolder.state;
|
|
11937
|
+
return rec;
|
|
11938
|
+
}
|
|
11939
|
+
}
|
|
11940
|
+
|
|
11544
11941
|
// Conversational layer next (greetings, thanks, help, bye, why/say-more) — these
|
|
11545
11942
|
// resolve no entity and carry their own preserved `last`. Bypasses withLast (a
|
|
11546
11943
|
// conversational turn is never finish()'d / never becomes a new `last`), so the
|
|
@@ -11558,7 +11955,9 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
11558
11955
|
const step = await executePlanStep(planHolder, { memoryDir, sessionId });
|
|
11559
11956
|
note(trace, `goal: ${step.deduced}`);
|
|
11560
11957
|
note(trace, "lane: PLAN NEXT — executed the active plan's next move as an @stepK snapshot write");
|
|
11561
|
-
const
|
|
11958
|
+
const stepTurn = plainTurn(workingLine, step.text, { via: "plan", focus });
|
|
11959
|
+
stepTurn.lane = "imperative";
|
|
11960
|
+
const rec = withLast(stepTurn, step.deduced);
|
|
11562
11961
|
rec.planState = planHolder.state;
|
|
11563
11962
|
return rec;
|
|
11564
11963
|
}
|
|
@@ -11669,6 +12068,7 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
11669
12068
|
if (asserted) {
|
|
11670
12069
|
note(trace, "goal: teach/remember a new fact (declarative ACE sentence)");
|
|
11671
12070
|
note(trace, "lane: assertTurn — grammar/ace.mjs parseAce matched a full triple with no residue");
|
|
12071
|
+
asserted.lane = "teach";
|
|
11672
12072
|
return withLast(asserted, "teach/remember a new fact");
|
|
11673
12073
|
}
|
|
11674
12074
|
// Bare declarative taxonomy (hyphenated-instance membership, article-led
|
|
@@ -11678,7 +12078,26 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
11678
12078
|
if (taxonomy) {
|
|
11679
12079
|
note(trace, "goal: teach/remember a new fact (bare declarative taxonomy)");
|
|
11680
12080
|
note(trace, "lane: bareTaxonomyTeach — hyphenated-instance or article-led kind-of declarative, stored before the ask engine could parse it as a question");
|
|
11681
|
-
|
|
12081
|
+
const taxonomyTurn = plainTurn(workingLine, taxonomy.text, { via: taxonomy.via, miss: taxonomy.miss, focus });
|
|
12082
|
+
if (!taxonomy.miss) taxonomyTurn.lane = "teach";
|
|
12083
|
+
return withLast(taxonomyTurn, "teach/remember a new fact");
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12086
|
+
// Synonym/related-word questions read the store through the SKOS view.
|
|
12087
|
+
// Routed before the ask engine: the generic parse reads "another word for
|
|
12088
|
+
// X" as a bare object search and "what is related to X" through
|
|
12089
|
+
// BARE_WHATIS_RE, both wrong lanes for this question.
|
|
12090
|
+
if (memoryDir) {
|
|
12091
|
+
const skos = await skosRelatedAnswer(memoryDir, workingLine, factRowsCache);
|
|
12092
|
+
if (skos) {
|
|
12093
|
+
const goal = `surface the remembered synonym/related-word neighbourhood of "${skos.term}"`;
|
|
12094
|
+
note(trace, `goal: ${goal}`);
|
|
12095
|
+
note(trace, `lane: SKOS VIEW — a synonym/related question ${skos.miss ? "matched but the store holds no such facts (honest miss)" : "answered from the store's relation facts"}`);
|
|
12096
|
+
if (!skos.miss) note(trace, "source: .tmct/memory Facts (mgx:synonym/mgx:relatedTo/mgx:similarTo, read as skos:altLabel/skos:related)");
|
|
12097
|
+
const skosTurn = plainTurn(workingLine, skos.text, { via: skos.miss ? "miss" : "fact", miss: skos.miss, focus });
|
|
12098
|
+
if (!skos.miss) skosTurn.goal = goal;
|
|
12099
|
+
skosTurn.lane = skos.miss ? "honest-miss" : "ask-set";
|
|
12100
|
+
return withLast(skosTurn, goal);
|
|
11682
12101
|
}
|
|
11683
12102
|
}
|
|
11684
12103
|
// MEMORY-STORE counts first ("how many facts / utterances do you know") — the
|
package/src/services/index.mjs
CHANGED
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
// the ask() orchestration.
|
|
8
8
|
|
|
9
9
|
// Chat surface (also reachable as the `./chat` subpath export).
|
|
10
|
-
|
|
10
|
+
// createSession is the library's session sink — the same focus/last/planState
|
|
11
|
+
// threading and memory-backend seam every shell shares; runTurn is the pure
|
|
12
|
+
// single-turn engine underneath it.
|
|
13
|
+
export { runChat, createSession, runTurn, COMMANDS, answerCount, renderStats } from "./chat.mjs";
|
|
11
14
|
|
|
12
15
|
// Grammar / NL-over-graph primitives.
|
|
13
16
|
export { ask, resolveObject } from "../domain/ask.mjs";
|
|
@@ -21376,6 +21376,189 @@ ${codeblock}`, options);
|
|
|
21376
21376
|
}
|
|
21377
21377
|
});
|
|
21378
21378
|
|
|
21379
|
+
// src/domain/concept.mjs
|
|
21380
|
+
var CONCEPT_CLASS, CLASS_NOUN, FOLLOWUP_SHAPES, RELATION_TERM, RELATION_KINDS, RELATION_RENDER, RELATION_FOLLOWUP_SHAPES;
|
|
21381
|
+
var init_concept = __esm({
|
|
21382
|
+
"src/domain/concept.mjs"() {
|
|
21383
|
+
init_ask();
|
|
21384
|
+
init_codegraph();
|
|
21385
|
+
CONCEPT_CLASS = Object.freeze({
|
|
21386
|
+
class: "Class",
|
|
21387
|
+
module: "Module",
|
|
21388
|
+
function: "Function",
|
|
21389
|
+
method: "Method",
|
|
21390
|
+
attribute: "Attribute",
|
|
21391
|
+
variable: "GlobalVariable",
|
|
21392
|
+
constant: "GlobalVariable",
|
|
21393
|
+
commit: "Commit"
|
|
21394
|
+
});
|
|
21395
|
+
CLASS_NOUN = Object.freeze({
|
|
21396
|
+
Class: ["class", "classes"],
|
|
21397
|
+
Module: ["module", "modules"],
|
|
21398
|
+
Function: ["function", "functions"],
|
|
21399
|
+
Method: ["method", "methods"],
|
|
21400
|
+
Attribute: ["attribute", "attributes"],
|
|
21401
|
+
GlobalVariable: ["variable", "variables"],
|
|
21402
|
+
Commit: ["commit", "commits"]
|
|
21403
|
+
});
|
|
21404
|
+
FOLLOWUP_SHAPES = Object.freeze({
|
|
21405
|
+
Class: [
|
|
21406
|
+
(x) => `which classes inherit from ${x}`,
|
|
21407
|
+
(x) => `what does ${x} contain`,
|
|
21408
|
+
(x) => `where is ${x} defined`
|
|
21409
|
+
],
|
|
21410
|
+
Module: [
|
|
21411
|
+
(x) => `what does ${x} import`,
|
|
21412
|
+
(x) => `which modules import ${x}`,
|
|
21413
|
+
(x) => `where is ${x} defined`
|
|
21414
|
+
],
|
|
21415
|
+
Function: [
|
|
21416
|
+
(x) => `what calls ${x}`,
|
|
21417
|
+
(x) => `what does ${x} call`,
|
|
21418
|
+
(x) => `where is ${x} defined`
|
|
21419
|
+
],
|
|
21420
|
+
Method: [
|
|
21421
|
+
(x) => `which class contains ${x}`,
|
|
21422
|
+
(x) => `what calls ${x}`,
|
|
21423
|
+
(x) => `where is ${x} defined`
|
|
21424
|
+
],
|
|
21425
|
+
Attribute: [
|
|
21426
|
+
(x) => `which class contains ${x}`,
|
|
21427
|
+
(x) => `where is ${x} defined`
|
|
21428
|
+
],
|
|
21429
|
+
GlobalVariable: [
|
|
21430
|
+
(x) => `where is ${x} defined`,
|
|
21431
|
+
(x) => `where is ${x} mentioned`
|
|
21432
|
+
],
|
|
21433
|
+
Commit: [
|
|
21434
|
+
(x) => `what did commit ${x} touch`,
|
|
21435
|
+
(x) => `when did ${x} change`
|
|
21436
|
+
]
|
|
21437
|
+
});
|
|
21438
|
+
RELATION_TERM = Object.freeze({
|
|
21439
|
+
import: "imports",
|
|
21440
|
+
imports: "imports",
|
|
21441
|
+
importing: "imports",
|
|
21442
|
+
imported: "imports",
|
|
21443
|
+
call: "calls",
|
|
21444
|
+
calls: "calls",
|
|
21445
|
+
calling: "calls",
|
|
21446
|
+
called: "calls",
|
|
21447
|
+
invoke: "calls",
|
|
21448
|
+
invokes: "calls",
|
|
21449
|
+
invoking: "calls",
|
|
21450
|
+
contain: "contains",
|
|
21451
|
+
contains: "contains",
|
|
21452
|
+
containing: "contains",
|
|
21453
|
+
containment: "contains",
|
|
21454
|
+
member: "contains",
|
|
21455
|
+
members: "contains",
|
|
21456
|
+
inherit: "inherits",
|
|
21457
|
+
inherits: "inherits",
|
|
21458
|
+
inheriting: "inherits",
|
|
21459
|
+
inheritance: "inherits",
|
|
21460
|
+
extend: "inherits",
|
|
21461
|
+
extends: "inherits",
|
|
21462
|
+
extending: "inherits",
|
|
21463
|
+
subclass: "inherits",
|
|
21464
|
+
subclasses: "inherits",
|
|
21465
|
+
subclassing: "inherits",
|
|
21466
|
+
test: "tests",
|
|
21467
|
+
tests: "tests",
|
|
21468
|
+
testing: "tests",
|
|
21469
|
+
tested: "tests",
|
|
21470
|
+
coverage: "tests",
|
|
21471
|
+
define: "defines",
|
|
21472
|
+
defines: "defines",
|
|
21473
|
+
defining: "defines",
|
|
21474
|
+
defined: "defines",
|
|
21475
|
+
definition: "defines",
|
|
21476
|
+
definitions: "defines",
|
|
21477
|
+
declaration: "defines",
|
|
21478
|
+
touch: "touches",
|
|
21479
|
+
touches: "touches",
|
|
21480
|
+
touching: "touches",
|
|
21481
|
+
touched: "touches",
|
|
21482
|
+
cochange: "cochange",
|
|
21483
|
+
"co-change": "cochange",
|
|
21484
|
+
"change-coupling": "cochange",
|
|
21485
|
+
coupled: "cochange",
|
|
21486
|
+
// "export"/"exports" is also a curated seon lexicon noun, but that meta reading
|
|
21487
|
+
// only owns "what does export mean" — no conflict with this vague-touch table.
|
|
21488
|
+
export: "reexports",
|
|
21489
|
+
exports: "reexports",
|
|
21490
|
+
exporting: "reexports",
|
|
21491
|
+
exported: "reexports",
|
|
21492
|
+
reexport: "reexports",
|
|
21493
|
+
reexports: "reexports",
|
|
21494
|
+
reexporting: "reexports",
|
|
21495
|
+
"re-export": "reexports",
|
|
21496
|
+
"re-exports": "reexports",
|
|
21497
|
+
"re-exporting": "reexports"
|
|
21498
|
+
});
|
|
21499
|
+
RELATION_KINDS = Object.freeze({
|
|
21500
|
+
imports: ["imports"],
|
|
21501
|
+
calls: ["calls", "callsSymbol"],
|
|
21502
|
+
contains: ["contains"],
|
|
21503
|
+
inherits: ["inherits"],
|
|
21504
|
+
tests: ["tests"],
|
|
21505
|
+
defines: ["defines"],
|
|
21506
|
+
touches: ["touches", "touchesSymbol"],
|
|
21507
|
+
cochange: ["cochange"],
|
|
21508
|
+
reexports: ["reexports"]
|
|
21509
|
+
});
|
|
21510
|
+
RELATION_RENDER = Object.freeze({
|
|
21511
|
+
imports: { verb: "imports", edgeNoun: "import" },
|
|
21512
|
+
calls: { verb: "calls", edgeNoun: "call" },
|
|
21513
|
+
contains: { verb: "contains", edgeNoun: "containment" },
|
|
21514
|
+
inherits: { verb: "inherits from", edgeNoun: "inheritance" },
|
|
21515
|
+
tests: { verb: "tests", edgeNoun: "test" },
|
|
21516
|
+
defines: { verb: "defines", edgeNoun: "definition" },
|
|
21517
|
+
touches: { verb: "touches", edgeNoun: "touch" },
|
|
21518
|
+
cochange: { verb: "changes together with", edgeNoun: "change-coupling" },
|
|
21519
|
+
reexports: { verb: "re-exports", edgeNoun: "re-export" }
|
|
21520
|
+
});
|
|
21521
|
+
RELATION_FOLLOWUP_SHAPES = Object.freeze({
|
|
21522
|
+
imports: [
|
|
21523
|
+
{ side: "obj", make: (x) => `which modules import ${x}` },
|
|
21524
|
+
{ side: "subj", make: (x) => `what does ${x} import` }
|
|
21525
|
+
],
|
|
21526
|
+
calls: [
|
|
21527
|
+
{ side: "obj", make: (x) => `what calls ${x}` },
|
|
21528
|
+
{ side: "subj", make: (x) => `what does ${x} call` }
|
|
21529
|
+
],
|
|
21530
|
+
contains: [
|
|
21531
|
+
{ side: "subj", make: (x) => `what does ${x} contain` },
|
|
21532
|
+
{ side: "obj", make: (x) => `which class contains ${x}` }
|
|
21533
|
+
],
|
|
21534
|
+
inherits: [
|
|
21535
|
+
{ side: "obj", make: (x) => `which classes inherit from ${x}` },
|
|
21536
|
+
{ side: "subj", make: (x) => `where is ${x} defined` }
|
|
21537
|
+
],
|
|
21538
|
+
tests: [
|
|
21539
|
+
{ side: "obj", make: (x) => `what tests ${x}` },
|
|
21540
|
+
{ side: "obj", make: (x) => `where is ${x} defined` }
|
|
21541
|
+
],
|
|
21542
|
+
defines: [
|
|
21543
|
+
{ side: "obj", make: (x) => `where is ${x} defined` },
|
|
21544
|
+
{ side: "subj", make: (x) => `what does ${x} contain` }
|
|
21545
|
+
],
|
|
21546
|
+
touches: [
|
|
21547
|
+
{ side: "obj", make: (x) => `when did ${x} change` },
|
|
21548
|
+
{ side: "subj", make: (x) => `what did commit ${x} touch` }
|
|
21549
|
+
],
|
|
21550
|
+
cochange: [
|
|
21551
|
+
{ side: "obj", make: (x) => `where is ${x} defined` },
|
|
21552
|
+
{ side: "subj", make: (x) => `which modules import ${x}` }
|
|
21553
|
+
],
|
|
21554
|
+
reexports: [
|
|
21555
|
+
{ side: "subj", make: (x) => `what does ${x} export` },
|
|
21556
|
+
{ side: "obj", make: (x) => `where is ${x} defined` }
|
|
21557
|
+
]
|
|
21558
|
+
});
|
|
21559
|
+
}
|
|
21560
|
+
});
|
|
21561
|
+
|
|
21379
21562
|
// src/adapters/toml-config.mjs
|
|
21380
21563
|
var init_toml_config = __esm({
|
|
21381
21564
|
"src/adapters/toml-config.mjs"() {
|
|
@@ -21432,7 +21615,7 @@ ${codeblock}`, options);
|
|
|
21432
21615
|
});
|
|
21433
21616
|
|
|
21434
21617
|
// src/adapters/corpus/conceptnet.mjs
|
|
21435
|
-
var
|
|
21618
|
+
var import_meta5, PKG_ROOT3, SLICE_FILE, MAP_FILE, SEON_CONCEPTS_FILE, SEON_DEFINITIONS_FILE, TIER2_DIR, TIER2_MANIFEST_FILE, WORDNET_DIR, WORDNET_MANIFEST_FILE;
|
|
21436
21619
|
var init_conceptnet = __esm({
|
|
21437
21620
|
"src/adapters/corpus/conceptnet.mjs"() {
|
|
21438
21621
|
init_node_fs();
|
|
@@ -21442,15 +21625,15 @@ ${codeblock}`, options);
|
|
|
21442
21625
|
init_node_path();
|
|
21443
21626
|
init_dist();
|
|
21444
21627
|
init_core();
|
|
21445
|
-
|
|
21446
|
-
|
|
21447
|
-
SLICE_FILE = join(
|
|
21448
|
-
MAP_FILE = join(
|
|
21449
|
-
SEON_CONCEPTS_FILE = join(
|
|
21450
|
-
SEON_DEFINITIONS_FILE = join(
|
|
21451
|
-
TIER2_DIR = join(
|
|
21628
|
+
import_meta5 = {};
|
|
21629
|
+
PKG_ROOT3 = join(dirname(fileURLToPath2(import_meta5.url)), "..", "..", "..");
|
|
21630
|
+
SLICE_FILE = join(PKG_ROOT3, "corpus", "conceptnet", "slice.jsonl");
|
|
21631
|
+
MAP_FILE = join(PKG_ROOT3, "src", "adapters", "corpus", "conceptnet-map.toml");
|
|
21632
|
+
SEON_CONCEPTS_FILE = join(PKG_ROOT3, "corpus", "seon", "concepts.jsonl");
|
|
21633
|
+
SEON_DEFINITIONS_FILE = join(PKG_ROOT3, "corpus", "seon", "definitions.jsonl");
|
|
21634
|
+
TIER2_DIR = join(PKG_ROOT3, "corpus", "tier2");
|
|
21452
21635
|
TIER2_MANIFEST_FILE = join(TIER2_DIR, "manifest.json");
|
|
21453
|
-
WORDNET_DIR = join(
|
|
21636
|
+
WORDNET_DIR = join(PKG_ROOT3, "corpus", "wordnet");
|
|
21454
21637
|
WORDNET_MANIFEST_FILE = join(WORDNET_DIR, "manifest.json");
|
|
21455
21638
|
}
|
|
21456
21639
|
});
|
|
@@ -21544,7 +21727,7 @@ ${codeblock}`, options);
|
|
|
21544
21727
|
}
|
|
21545
21728
|
};
|
|
21546
21729
|
}
|
|
21547
|
-
var
|
|
21730
|
+
var import_meta6, NAMENET_DIR, EXTENSION_KINDS, CONCEPTNET_PREFER, BUILTIN_EXTENSIONS;
|
|
21548
21731
|
var init_extensions = __esm({
|
|
21549
21732
|
"src/services/extensions.mjs"() {
|
|
21550
21733
|
init_node_path();
|
|
@@ -21552,8 +21735,8 @@ ${codeblock}`, options);
|
|
|
21552
21735
|
init_node_url();
|
|
21553
21736
|
init_toml_config();
|
|
21554
21737
|
init_conceptnet();
|
|
21555
|
-
|
|
21556
|
-
NAMENET_DIR = join(dirname(fileURLToPath2(
|
|
21738
|
+
import_meta6 = {};
|
|
21739
|
+
NAMENET_DIR = join(dirname(fileURLToPath2(import_meta6.url)), "..", "..", "corpus", "namenet");
|
|
21557
21740
|
EXTENSION_KINDS = Object.freeze(["corpus", "lexicon", "templates", "pack", "ontology"]);
|
|
21558
21741
|
CONCEPTNET_PREFER = ["rdfs:subClassOf", "rdf:type", "mgx:usedFor", "mgx:partOf", "mgx:capableOf"];
|
|
21559
21742
|
BUILTIN_EXTENSIONS = Object.freeze(builtinExtensions());
|
|
@@ -22633,6 +22816,134 @@ ${JSON.stringify(envelope, null, 2)}`;
|
|
|
22633
22816
|
init_fuzzy();
|
|
22634
22817
|
init_lexicon();
|
|
22635
22818
|
|
|
22819
|
+
// src/domain/reference-pack.mjs
|
|
22820
|
+
init_hash();
|
|
22821
|
+
init_lexicon();
|
|
22822
|
+
init_concept();
|
|
22823
|
+
|
|
22824
|
+
// src/adapters/corpus/reference-pack.mjs
|
|
22825
|
+
init_node_fs();
|
|
22826
|
+
init_node_url();
|
|
22827
|
+
init_node_path();
|
|
22828
|
+
var import_meta4 = {};
|
|
22829
|
+
var PKG_ROOT2 = join(dirname(fileURLToPath2(import_meta4.url)), "..", "..", "..");
|
|
22830
|
+
|
|
22831
|
+
// src/domain/dialogue-acts.mjs
|
|
22832
|
+
var DIALOGUE_ACT_DIMENSIONS = Object.freeze([
|
|
22833
|
+
"task",
|
|
22834
|
+
"autoFeedback",
|
|
22835
|
+
"alloFeedback",
|
|
22836
|
+
"turnManagement",
|
|
22837
|
+
"timeManagement",
|
|
22838
|
+
"discourseStructuring",
|
|
22839
|
+
"ownCommunicationManagement",
|
|
22840
|
+
"partnerCommunicationManagement",
|
|
22841
|
+
"socialObligationsManagement",
|
|
22842
|
+
"contactManagement"
|
|
22843
|
+
]);
|
|
22844
|
+
var DIALOGUE_ACTS = Object.freeze({
|
|
22845
|
+
propositionalQuestion: Object.freeze({
|
|
22846
|
+
dimension: "task",
|
|
22847
|
+
gloss: "a yes/no question about whether a proposition holds ('does X import Y?')"
|
|
22848
|
+
}),
|
|
22849
|
+
checkQuestion: Object.freeze({
|
|
22850
|
+
dimension: "task",
|
|
22851
|
+
gloss: "a yes/no question whose asker already expects the answer yes ('..., right?')"
|
|
22852
|
+
}),
|
|
22853
|
+
setQuestion: Object.freeze({
|
|
22854
|
+
dimension: "task",
|
|
22855
|
+
gloss: "a wh-question asking for the members of a set ('what does X import?')"
|
|
22856
|
+
}),
|
|
22857
|
+
choiceQuestion: Object.freeze({
|
|
22858
|
+
dimension: "task",
|
|
22859
|
+
gloss: "a question asking which of the listed alternatives holds ('is X a module or a class?')"
|
|
22860
|
+
}),
|
|
22861
|
+
inform: Object.freeze({
|
|
22862
|
+
dimension: "task",
|
|
22863
|
+
gloss: "a declarative telling the addressee something (a teach turn; also tmct explaining its own function)"
|
|
22864
|
+
}),
|
|
22865
|
+
answer: Object.freeze({
|
|
22866
|
+
dimension: "task",
|
|
22867
|
+
gloss: "an inform that discharges a question just asked (an answer grounded in the graph)"
|
|
22868
|
+
}),
|
|
22869
|
+
confirm: Object.freeze({
|
|
22870
|
+
dimension: "task",
|
|
22871
|
+
gloss: "an answer 'yes' to a check question"
|
|
22872
|
+
}),
|
|
22873
|
+
disconfirm: Object.freeze({
|
|
22874
|
+
dimension: "task",
|
|
22875
|
+
gloss: "an answer 'no' to a check question"
|
|
22876
|
+
}),
|
|
22877
|
+
agreement: Object.freeze({
|
|
22878
|
+
dimension: "task",
|
|
22879
|
+
gloss: "an inform stating that the speaker holds what the addressee just stated to be true"
|
|
22880
|
+
}),
|
|
22881
|
+
disagreement: Object.freeze({
|
|
22882
|
+
dimension: "task",
|
|
22883
|
+
gloss: "an inform stating that the speaker holds what the addressee just stated to be false"
|
|
22884
|
+
}),
|
|
22885
|
+
correction: Object.freeze({
|
|
22886
|
+
dimension: "task",
|
|
22887
|
+
gloss: "a disagreement that also supplies the replacement ('no, I meant Y')"
|
|
22888
|
+
}),
|
|
22889
|
+
request: Object.freeze({
|
|
22890
|
+
dimension: "task",
|
|
22891
|
+
gloss: "asks the addressee to perform an action ('solve it' \u2014 a goal turn)"
|
|
22892
|
+
}),
|
|
22893
|
+
instruct: Object.freeze({
|
|
22894
|
+
dimension: "task",
|
|
22895
|
+
gloss: "a request the addressee is expected to carry out without negotiation (a bare imperative)"
|
|
22896
|
+
}),
|
|
22897
|
+
suggestion: Object.freeze({
|
|
22898
|
+
dimension: "task",
|
|
22899
|
+
gloss: "puts an action forward as advisable without claiming authority over the addressee"
|
|
22900
|
+
}),
|
|
22901
|
+
offer: Object.freeze({
|
|
22902
|
+
dimension: "task",
|
|
22903
|
+
gloss: "commits the speaker to an action, conditional on the addressee wanting it"
|
|
22904
|
+
}),
|
|
22905
|
+
autoPositive: Object.freeze({
|
|
22906
|
+
dimension: "autoFeedback",
|
|
22907
|
+
gloss: "the sender reports its own processing of the previous turn succeeded (an acknowledgement)"
|
|
22908
|
+
}),
|
|
22909
|
+
autoNegative: Object.freeze({
|
|
22910
|
+
dimension: "autoFeedback",
|
|
22911
|
+
gloss: "the sender reports its own processing of the previous turn failed \u2014 tmct's honest miss"
|
|
22912
|
+
}),
|
|
22913
|
+
initialGreeting: Object.freeze({
|
|
22914
|
+
dimension: "socialObligationsManagement",
|
|
22915
|
+
gloss: "opens an exchange of greetings ('hi')"
|
|
22916
|
+
}),
|
|
22917
|
+
returnGreeting: Object.freeze({
|
|
22918
|
+
dimension: "socialObligationsManagement",
|
|
22919
|
+
gloss: "answers a greeting with a greeting"
|
|
22920
|
+
}),
|
|
22921
|
+
thanking: Object.freeze({
|
|
22922
|
+
dimension: "socialObligationsManagement",
|
|
22923
|
+
gloss: "expresses gratitude for something the addressee did"
|
|
22924
|
+
}),
|
|
22925
|
+
apology: Object.freeze({
|
|
22926
|
+
dimension: "socialObligationsManagement",
|
|
22927
|
+
gloss: "expresses regret for something the speaker did"
|
|
22928
|
+
})
|
|
22929
|
+
});
|
|
22930
|
+
var LANE_DIALOGUE_ACTS = Object.freeze({
|
|
22931
|
+
teach: "inform",
|
|
22932
|
+
"ask-set": "setQuestion",
|
|
22933
|
+
"ask-propositional": "propositionalQuestion",
|
|
22934
|
+
goal: "request",
|
|
22935
|
+
imperative: "instruct",
|
|
22936
|
+
"honest-miss": "autoNegative",
|
|
22937
|
+
greeting: "initialGreeting",
|
|
22938
|
+
thanks: "thanking",
|
|
22939
|
+
help: "inform",
|
|
22940
|
+
// The guessing game's turns are task-dimension: a reply that discharges
|
|
22941
|
+
// the other side's move (a hint, a win, a rebuttal) is an answer; tmct
|
|
22942
|
+
// stating its own move (an opening, its next guess) is an inform.
|
|
22943
|
+
"game-answer": "answer",
|
|
22944
|
+
"game-inform": "inform"
|
|
22945
|
+
});
|
|
22946
|
+
|
|
22636
22947
|
// src/services/chat-session.mjs
|
|
22637
22948
|
init_node_path();
|
|
22638
22949
|
init_node_fs();
|