@polycode-projects/the-mechanical-code-talker 2.6.1 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +46 -1
  2. package/corpus/LICENSES.json +14 -0
  3. package/corpus/child/LICENSE-NOTICE +43 -0
  4. package/corpus/child/README.md +79 -0
  5. package/corpus/child/index.json.gz +0 -0
  6. package/corpus/child/manifest.json +313 -0
  7. package/corpus/child/shards/child-00.jsonl.gz +0 -0
  8. package/corpus/child/shards/child-01.jsonl.gz +0 -0
  9. package/corpus/child/shards/child-02.jsonl.gz +0 -0
  10. package/corpus/child/shards/child-03.jsonl.gz +0 -0
  11. package/corpus/child/shards/child-04.jsonl.gz +0 -0
  12. package/corpus/child/shards/child-05.jsonl.gz +0 -0
  13. package/corpus/child/shards/child-06.jsonl.gz +0 -0
  14. package/corpus/child/shards/child-07.jsonl.gz +0 -0
  15. package/corpus/child/shards/child-08.jsonl.gz +0 -0
  16. package/corpus/child/shards/child-09.jsonl.gz +0 -0
  17. package/corpus/child/shards/child-0a.jsonl.gz +0 -0
  18. package/corpus/child/shards/child-0b.jsonl.gz +0 -0
  19. package/corpus/child/shards/child-0c.jsonl.gz +0 -0
  20. package/corpus/child/shards/child-0d.jsonl.gz +0 -0
  21. package/corpus/child/shards/child-0e.jsonl.gz +0 -0
  22. package/corpus/child/shards/child-0f.jsonl.gz +0 -0
  23. package/corpus/child/shards/child-10.jsonl.gz +0 -0
  24. package/corpus/child/shards/child-11.jsonl.gz +0 -0
  25. package/corpus/child/shards/child-12.jsonl.gz +0 -0
  26. package/corpus/child/shards/child-13.jsonl.gz +0 -0
  27. package/corpus/child/shards/child-14.jsonl.gz +0 -0
  28. package/corpus/child/shards/child-15.jsonl.gz +0 -0
  29. package/corpus/child/shards/child-16.jsonl.gz +0 -0
  30. package/corpus/child/shards/child-17.jsonl.gz +0 -0
  31. package/corpus/child/shards/child-18.jsonl.gz +0 -0
  32. package/corpus/child/shards/child-19.jsonl.gz +0 -0
  33. package/corpus/child/shards/child-1a.jsonl.gz +0 -0
  34. package/corpus/child/shards/child-1b.jsonl.gz +0 -0
  35. package/corpus/child/shards/child-1c.jsonl.gz +0 -0
  36. package/corpus/child/shards/child-1d.jsonl.gz +0 -0
  37. package/corpus/child/shards/child-1e.jsonl.gz +0 -0
  38. package/corpus/child/shards/child-1f.jsonl.gz +0 -0
  39. package/corpus/conceptnet/child-seed.mjs +169 -0
  40. package/corpus/conceptnet/filter-dump.mjs +69 -48
  41. package/corpus/worlds/README.md +26 -0
  42. package/corpus/worlds/index.json.gz +0 -0
  43. package/corpus/worlds/manifest.json +33 -0
  44. package/corpus/worlds/shards/ashcombe-hall.jsonl.gz +0 -0
  45. package/corpus/worlds/src/ashcombe-hall.jsonl +64 -0
  46. package/package.json +4 -1
  47. package/src/adapters/corpus/child-pack.mjs +115 -0
  48. package/src/adapters/corpus/conceptnet-map.toml +7 -0
  49. package/src/adapters/corpus/worlds-pack.mjs +122 -0
  50. package/src/domain/ask-vocab.mjs +10 -2
  51. package/src/domain/ask.mjs +6 -0
  52. package/src/domain/child-pack.mjs +79 -0
  53. package/src/domain/grammar/ace.mjs +77 -0
  54. package/src/domain/grammar/lexicon-core.json +6 -0
  55. package/src/domain/interpret/normalize.mjs +32 -8
  56. package/src/domain/memory/trust.mjs +17 -0
  57. package/src/domain/worlds-pack.mjs +71 -0
  58. package/src/services/adventure.mjs +718 -0
  59. package/src/services/chat.mjs +137 -31
  60. package/src/surfaces/web/memory-ask-browser.bundle.js +555 -46
  61. package/src/tools/definitions.mjs +2 -2
@@ -48,8 +48,11 @@ import { loadLexicon, lookupNoun } from "../domain/grammar/lexicon.mjs";
48
48
  import { pickPhrase } from "../domain/answer-variants.mjs";
49
49
  import { REFERENCE_PACK_NAME, cleanMissReferenceTerm, renderReferenceAnswer, referenceProvenanceTag } from "../domain/reference-pack.mjs";
50
50
  import { getReferencePackProvider } from "../adapters/corpus/reference-pack.mjs";
51
+ import { CHILD_PACK_NAME, childProvenanceTag } from "../domain/child-pack.mjs";
52
+ import { getChildPackProvider } from "../adapters/corpus/child-pack.mjs";
51
53
  import { dialogueActForLane } from "../domain/dialogue-acts.mjs";
52
54
  import { relatedForTerm } from "../domain/skos-view.mjs";
55
+ import { adventureTurn } from "./adventure.mjs";
53
56
 
54
57
  // Composition: the chat surface supplies the domain parser's default lemma/POS
55
58
  // adapter (the browser bundle's ask-nlp stub carries no factory, so this is a
@@ -2793,6 +2796,16 @@ const GENERAL_VERB_DETERMINER_TEACH_RE = new RegExp(
2793
2796
  * stored, and cited, as "len"). */
2794
2797
  const QUANTIFIED_HAS_TEACH_RE = /^(every|each|all)\s+([\w'-]+)\s+(?:has|have)\s+(.+?)[.!?]*$/i;
2795
2798
  const quantifiedHasSubject = (m) => (/^all$/i.test(m[1]) ? singularizeSurface(m[2]) : m[2]);
2799
+ /** The OBJECT side of the same fold: "all dogs have tails" states one tail
2800
+ * per dog, so the plural sentence form's object stores as its singular
2801
+ * ("tail" — the spelling the have-questions and the seeded corpus's own hasA
2802
+ * facts read back). Only the "all" form folds, the same gate the subject
2803
+ * uses: "every"/"each" take singular grammar, so their object's number is
2804
+ * the speaker's own ("every dog has fur"). The last word folds so a
2805
+ * modified object ("all dogs have long tails") keeps its modifier. */
2806
+ const quantifiedHasObject = (m) => (/^all$/i.test(m[1])
2807
+ ? m[3].replace(/[\w'-]+$/, (w) => singularizeSurface(w))
2808
+ : m[3]);
2796
2809
  /** Verbs owned by an earlier, more specific recognizer in this lane — is/are
2797
2810
  * (class-membership/property, above) and owns/maintains (ownership, above).
2798
2811
  * generalVerbTeach declines outright on these so it can never race a more
@@ -2941,7 +2954,7 @@ async function generalVerbTeach(payload) {
2941
2954
  if (quantHas) {
2942
2955
  subjectRaw = quantifiedHasSubject(quantHas);
2943
2956
  verbRaw = "has";
2944
- objectRaw = quantHas[3];
2957
+ objectRaw = quantifiedHasObject(quantHas);
2945
2958
  } else {
2946
2959
  const det = p.match(GENERAL_VERB_DETERMINER_TEACH_RE);
2947
2960
  if (!det) return null; // not a bare-name subject, and no preposition to pin the verb
@@ -9044,14 +9057,15 @@ async function curatedDefinitionAnswer(query, envelope, { memoryDir, lexicon })
9044
9057
  return { text: `${def} (source: corpus/seon)`, term };
9045
9058
  }
9046
9059
 
9047
- // ---- learn-on-miss: the shipped reference pack behind the cleanest miss ----
9060
+ // ---- learn-on-miss: the shipped child + reference packs behind the cleanest miss ----
9048
9061
 
9049
- /** The learn-on-miss gate, shared by the articled miss hook and the bare-form
9050
- * fallback so the two can never disagree. Fires only on the CLEANEST miss: a
9051
- * definition-shaped term the lexicon knows, resolving to no graph entity and
9052
- * no remembered fact — then, and only then, the pack provider is consulted.
9053
- * Null means the turn proceeds byte-identically to a pack-less run. */
9054
- async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, cache }) {
9062
+ /** The learn-on-miss gate, shared by the child-pack hook, the articled
9063
+ * reference hook and the bare-form fallback so the three can never disagree.
9064
+ * Passes only on the CLEANEST miss: a definition-shaped term the lexicon
9065
+ * knows, resolving to no graph entity and no remembered fact — then, and
9066
+ * only then, may a pack provider be consulted. Null means the turn proceeds
9067
+ * byte-identically to a pack-less run. */
9068
+ async function cleanMissPackKey(term, { graph, memoryDir, lexicon, cache }) {
9055
9069
  if (!term || !memoryDir) return null;
9056
9070
  let key = null;
9057
9071
  try { key = cleanMissReferenceTerm(term, lexicon ?? undefined); } catch { key = null; }
@@ -9063,12 +9077,43 @@ async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, c
9063
9077
  variants.add(key);
9064
9078
  const rows = await factRows(memoryDir, cache);
9065
9079
  if (rows.some((f) => variants.has(f.subject) || variants.has(f.object))) return null;
9080
+ return key;
9081
+ }
9082
+
9083
+ /** The reference-pack lookup for an already-gated key: the article, or null
9084
+ * (absent pack, missing term, any read failure — all byte-identical). */
9085
+ async function referencePackAnswerForKey(key, env) {
9066
9086
  let article = null;
9067
9087
  try { article = await getReferencePackProvider(env).lookup(key); } catch { article = null; }
9068
9088
  if (!article) return null;
9069
9089
  return { key, article, text: renderReferenceAnswer(key, article) };
9070
9090
  }
9071
9091
 
9092
+ /** The gate + the reference lookup in one call, for a caller that has a term
9093
+ * rather than a gated key. */
9094
+ async function referencePackMissAnswer(term, { graph, memoryDir, lexicon, env, cache }) {
9095
+ const key = await cleanMissPackKey(term, { graph, memoryDir, lexicon, cache });
9096
+ return key ? referencePackAnswerForKey(key, env) : null;
9097
+ }
9098
+
9099
+ /** The child-pack half of learn-on-miss, for an already-gated key: look the
9100
+ * key up in the shipped child triples pack and append every fact under child
9101
+ * provenance, so the SAME question can be re-asked from the store. Null on a
9102
+ * pack miss or any failure — the turn then proceeds byte-identically. */
9103
+ async function childPackFactsForKey(key, { memoryDir, env, cache }) {
9104
+ let row = null;
9105
+ try { row = await getChildPackProvider(env).lookup(key); } catch { row = null; }
9106
+ if (!row?.facts?.length) return null;
9107
+ try {
9108
+ const { appendFacts } = await import("../adapters/memory/core.mjs");
9109
+ await appendFacts(memoryDir, row.facts.map(({ subject, predicate, object }) => ({
9110
+ subject, predicate, object, provenance: childProvenanceTag(key),
9111
+ })));
9112
+ } catch { return null; }
9113
+ if (cache) cache.rows = null;
9114
+ return { key, count: row.facts.length };
9115
+ }
9116
+
9072
9117
  /** Store the article's first-sentence isa as a subClassOf fact carrying
9073
9118
  * reference provenance — AFTER the cited answer composed, and failure-
9074
9119
  * tolerated: the answer stands whether or not the fact lands. */
@@ -10491,9 +10536,12 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
10491
10536
  // actually stores it, the ORIGINAL ask-engine answer is restored unchanged
10492
10537
  // (see "COLLISION RESTORE" below).
10493
10538
  const trimmedQuery = String(query).trim();
10494
- const relaxedTeachCollision = !!(envelope?.relaxed?.dropped?.length) && !askMiss && memoryDir
10539
+ // Boolean() and not a bare && chain: with no memoryDir the chain would
10540
+ // short-circuit to null, and `miss` below (askMiss || THIS) would record a
10541
+ // null miss flag on every relaxation-rescued turn of a memory-less session.
10542
+ const relaxedTeachCollision = Boolean(!!(envelope?.relaxed?.dropped?.length) && !askMiss && memoryDir
10495
10543
  && !QUESTION_LEAD_RE.test(trimmedQuery) && !/\?\s*$/.test(trimmedQuery)
10496
- && DECLARATIVE_KIND_OF_RE.test(trimmedQuery);
10544
+ && DECLARATIVE_KIND_OF_RE.test(trimmedQuery));
10497
10545
  const preCollisionAnswer = relaxedTeachCollision ? answer : null;
10498
10546
  const miss = askMiss || relaxedTeachCollision;
10499
10547
  // Answer provenance (W1): "composed" is the ask engine's productive band; the
@@ -10788,13 +10836,23 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
10788
10836
  const def = await curatedDefinitionAnswer(factQuery, envelope, { memoryDir, lexicon });
10789
10837
  if (def) bareMetaHit = { text: def.text, replace: true };
10790
10838
  }
10791
- // The reference pack's bare-form fallback, beside the curated one and
10792
- // under the IDENTICAL clean-miss gate the articled hook (4h) applies —
10793
- // "what is otter" reaches the pack exactly as "what is an otter" does.
10839
+ // The learn-on-miss packs' bare-form fallback, beside the curated one
10840
+ // and under the IDENTICAL clean-miss gate the articled hook (4h)
10841
+ // applies — "what is otter" reaches the packs exactly as "what is an
10842
+ // otter" does, child triples first, article prose second.
10794
10843
  if (!bareMetaHit) {
10795
10844
  const refTerm = metaTermOf(factQuery, envelope);
10796
- const ref = refTerm ? await referencePackMissAnswer(refTerm, { graph, memoryDir, lexicon, env, cache }) : null;
10797
- if (ref) bareMetaHit = { text: ref.text, replace: true, reference: ref };
10845
+ const key = refTerm ? await cleanMissPackKey(refTerm, { graph, memoryDir, lexicon, cache }) : null;
10846
+ const learned = key ? await childPackFactsForKey(key, { memoryDir, env, cache }) : null;
10847
+ if (learned) {
10848
+ const fact = (await factAnswer(memoryDir, factQuery, envelope, miss, biasByBundle, cache, newFocus?.label))
10849
+ ?? (await factReadBack(memoryDir, factQuery, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
10850
+ if (fact && !fact.miss) bareMetaHit = { text: fact.text, replace: true, child: learned };
10851
+ }
10852
+ if (!bareMetaHit && key) {
10853
+ const ref = await referencePackAnswerForKey(key, env);
10854
+ if (ref) bareMetaHit = { text: ref.text, replace: true, reference: ref };
10855
+ }
10798
10856
  }
10799
10857
  }
10800
10858
  // A bare "what is X" naming a REAL code-graph entity (not a taught fact,
@@ -10850,8 +10908,13 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
10850
10908
  if (!bareMetaHit.miss) { via = "fact"; recordMiss = false; }
10851
10909
  handled = true;
10852
10910
  if (bareMetaHit.pending) factPending = bareMetaHit.pending;
10853
- note(trace, "lane: (2b) BARE META FACT — \"what is X\" (no article) / \"is X <adjective>\" resolved to a remembered fact before the conversational catch-all could claim it");
10854
- note(trace, "source: .tmct/memory Facts (see /memory for provenance per line)");
10911
+ if (bareMetaHit.child) {
10912
+ note(trace, "lane: (2b) CHILD PACK — a bare \"what is X\" clean miss pulled the term's triples from the shipped child pack into memory, and the question was re-answered from the store");
10913
+ note(trace, `source: child pack ${CHILD_PACK_NAME} — ${bareMetaHit.child.count} fact(s) appended as ${childProvenanceTag(bareMetaHit.child.key)}, answer served from .tmct/memory Facts`);
10914
+ } else {
10915
+ note(trace, "lane: (2b) BARE META FACT — \"what is X\" (no article) / \"is X <adjective>\" resolved to a remembered fact before the conversational catch-all could claim it");
10916
+ note(trace, "source: .tmct/memory Facts (see /memory for provenance per line)");
10917
+ }
10855
10918
  } else if (isConversationalCandidate && habitualGroundingHint) {
10856
10919
  // A bare habitual teach ("penguins swim") naming a subject grounded
10857
10920
  // nowhere: an honest, actionable grounding hint beats the orientation
@@ -11211,22 +11274,40 @@ async function runAsk(query, { config, source, graph, focus, last, templates, me
11211
11274
  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`);
11212
11275
  }
11213
11276
  }
11214
- // (4h) REFERENCE PACK — the cleanest miss consults the shipped reference
11215
- // pack: a definition-shaped term the lexicon knows, no graph entity, no
11216
- // remembered fact. A hit answers with the article's summary, always cited;
11217
- // a null from any gate leaves the turn byte-identical. After the answer
11218
- // composes, the article's first-sentence isa is stored as a subClassOf fact
11219
- // with reference provenance, so the NEXT ask answers from memory.
11277
+ // (4h) LEARN-ON-MISS PACKS — the cleanest miss consults the shipped packs:
11278
+ // a definition-shaped term the lexicon knows, no graph entity, no
11279
+ // remembered fact. The CHILD triples pack goes first (facts before prose):
11280
+ // a hit appends the term's triples under child provenance and the SAME
11281
+ // question is re-asked from the store, so the answer is an ordinary cited
11282
+ // fact answer. Only when the store still cannot answer does the reference
11283
+ // pack's article speak, cited as before. Both packs missing leaves the
11284
+ // honest miss byte-identical.
11220
11285
  if (miss && recordMiss && via === "composed" && memoryDir) {
11221
11286
  const refTerm = metaTermOf(query, envelope);
11222
- const ref = refTerm ? await referencePackMissAnswer(refTerm, { graph, memoryDir, lexicon, env, cache }) : null;
11223
- if (ref) {
11224
- answer = ref.text;
11225
- via = "reference";
11226
- recordMiss = false;
11227
- note(trace, "lane: (4h) REFERENCE PACK — a clean miss on a lexicon term answered from the shipped reference pack, cited");
11228
- note(trace, `source: reference pack ${REFERENCE_PACK_NAME} article "${ref.article.title}" (revid ${ref.article.revid})`);
11229
- await appendReferenceIsaFact(memoryDir, ref.key, ref.article, cache);
11287
+ const key = refTerm ? await cleanMissPackKey(refTerm, { graph, memoryDir, lexicon, cache }) : null;
11288
+ const learned = key ? await childPackFactsForKey(key, { memoryDir, env, cache }) : null;
11289
+ if (learned) {
11290
+ const fact = (await factAnswer(memoryDir, query, envelope, miss, biasByBundle, cache, newFocus?.label))
11291
+ ?? (await factReadBack(memoryDir, query, envelope, miss, graph, newFocus?.label, biasByBundle, cache));
11292
+ if (fact && !fact.miss) {
11293
+ answer = fact.replace ? fact.text : `${answer}\n${fact.text}`;
11294
+ via = "fact";
11295
+ recordMiss = false;
11296
+ if (fact.pending) factPending = fact.pending;
11297
+ note(trace, "lane: (4h) CHILD PACK — a clean miss on a lexicon term pulled the term's triples from the shipped child pack into memory, and the question was re-answered from the store");
11298
+ note(trace, `source: child pack ${CHILD_PACK_NAME} — ${learned.count} fact(s) appended as ${childProvenanceTag(key)}, answer served from .tmct/memory Facts`);
11299
+ }
11300
+ }
11301
+ if (miss && recordMiss && via === "composed" && key) {
11302
+ const ref = await referencePackAnswerForKey(key, env);
11303
+ if (ref) {
11304
+ answer = ref.text;
11305
+ via = "reference";
11306
+ recordMiss = false;
11307
+ note(trace, "lane: (4h) REFERENCE PACK — a clean miss on a lexicon term answered from the shipped reference pack, cited");
11308
+ note(trace, `source: reference pack ${REFERENCE_PACK_NAME} — article "${ref.article.title}" (revid ${ref.article.revid})`);
11309
+ await appendReferenceIsaFact(memoryDir, ref.key, ref.article, cache);
11310
+ }
11230
11311
  }
11231
11312
  }
11232
11313
  // (5) #1 SHORT TAILORED MISS — replace ONLY the engine's full grammar cheat-sheet
@@ -12162,6 +12243,9 @@ function guessNumberTurn(line, { planHolder, env }) {
12162
12243
  return null;
12163
12244
  }
12164
12245
  if (!opening) return null;
12246
+ if (state?.adventure) {
12247
+ return { text: 'we\'re mid-adventure — say "stop playing" to end it before a number game.', lane: "game-inform", note: "GAME — an opening arrived mid-adventure; the slot holds one thing at a time" };
12248
+ }
12165
12249
  const planActive = state && !state.done
12166
12250
  && ((Array.isArray(state.goals) && state.goals.length) || (Array.isArray(state.actions) && state.actions.length));
12167
12251
  if (planActive) {
@@ -12515,6 +12599,28 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
12515
12599
  }
12516
12600
  }
12517
12601
 
12602
+ // THE ADVENTURE — world-loading openers ("play ashcombe hall", "start the
12603
+ // adventure"), the stop command, and (once a world is live) the game's own
12604
+ // turns. Ordered AFTER the guess-number lane so that lane keeps every
12605
+ // guessing-game opener it already owns, and before the conversational
12606
+ // layer for the same reason the game lane is: an opening line would
12607
+ // otherwise read as a declarative or an orientation ask.
12608
+ {
12609
+ const advTurn = await adventureTurn(workingLine, {
12610
+ planHolder, memoryDir, sessionId, env, lexicon, graph, cache: factRowsCache, isPlanFrameLine,
12611
+ });
12612
+ if (advTurn) {
12613
+ note(trace, `lane: ${advTurn.note}`);
12614
+ if (advTurn.goal) note(trace, `goal: ${advTurn.goal}`);
12615
+ const result = plainTurn(workingLine, advTurn.text, { via: "game", miss: !!advTurn.miss, focus });
12616
+ if (advTurn.goal) result.goal = advTurn.goal;
12617
+ result.lane = advTurn.lane;
12618
+ const rec = withLast(result, advTurn.goal ?? "play the adventure");
12619
+ rec.planState = planHolder.state;
12620
+ return rec;
12621
+ }
12622
+ }
12623
+
12518
12624
  // Conversational layer next (greetings, thanks, help, bye, why/say-more) — these
12519
12625
  // resolve no entity and carry their own preserved `last`. Bypasses withLast (a
12520
12626
  // conversational turn is never finish()'d / never becomes a new `last`), so the