@polycode-projects/the-mechanical-code-talker 2.7.12 → 2.7.14

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.
@@ -0,0 +1,88 @@
1
+ // adventure-browser-entry.mjs — the esbuild entry for the adventure's
2
+ // full-screen/home-page pages (public/adventure-browser.bundle.js, built by
3
+ // scripts/build-adventure-bundle.mjs), mirroring
4
+ // spider-fly-browser-entry.mjs's own session-factory shape.
5
+ //
6
+ // Unlike spider-fly's board, Ashcombe Hall's own facts+rules cannot be
7
+ // regenerated in-browser from a pure JS module — its canonical definition is
8
+ // a Node-only JSONL corpus source, read through an fs/gzip provider the
9
+ // browser cannot run. So this session takes the world as data
10
+ // (`worldPayload`, `{ name, facts, rules, opening }`), embedded into the page
11
+ // at build time by scripts/build-demo-site.mjs's own read through the real
12
+ // worlds-pack provider (see adventure-viz.mjs's header for the full
13
+ // rationale) — the bootstrap below then just appends it, exactly the shape
14
+ // openAdventure() itself writes for a real chat session.
15
+ //
16
+ // This session exposes ONLY a raw autoplay tick and a read-only snapshot —
17
+ // no chat dock. Every state-changing command adventure.mjs's own
18
+ // runWorldCommand issues (go/take/open/...) already re-narrates itself
19
+ // through the extractive completions digest on every turn (the
20
+ // "auto-relook"), so this bundle carries the same wink-nlp/completions
21
+ // dependency chain chat.mjs's own runTurn does; a second, lighter path was
22
+ // not available to duck under it. Nothing here calls runTurn, though — the
23
+ // one entry point exercised is adventureTurn itself, via
24
+ // adventure-autoplay.mjs, which is the "auto-play is a caller of the
25
+ // existing interpreter, never a second one" contract this whole feature
26
+ // rests on.
27
+ import {
28
+ createInMemoryStore, appendFacts, appendRule, loadMemory, readFactRows,
29
+ } from "../../adapters/memory/core.mjs";
30
+ import { foldWorldState, worldDigestRows } from "../../services/adventure.mjs";
31
+ import { runAdventureAutoplayTick } from "../../services/adventure-autoplay.mjs";
32
+ import { resolveSpriteForClass, SPRITE_REGISTRY } from "../../domain/sprite-map.mjs";
33
+
34
+ /** A live in-memory adventure this page's ticker drives one auto-play tick
35
+ * at a time. Returns `{ memoryDir, autoplayTick, snapshot }`.
36
+ * `worldPayload.facts`/`.rules` seed the store exactly the way
37
+ * openAdventure() itself does for a real session; `planHolder.state` is set
38
+ * the same way, so adventureTurn treats every subsequent call as a live,
39
+ * already-open world rather than a fresh opening line. */
40
+ export async function createAdventureSession(worldPayload) {
41
+ const memoryDir = createInMemoryStore();
42
+ const tag = `world:${worldPayload.name}`;
43
+ await appendFacts(memoryDir, worldPayload.facts.map((f) => ({
44
+ subject: f.subject, predicate: f.predicate, object: f.object, provenance: tag,
45
+ })));
46
+ for (const rule of worldPayload.rules) {
47
+ await appendRule(memoryDir, { name: rule.name, kind: rule.ruleKind, slots: rule.slots, provenance: tag });
48
+ }
49
+
50
+ const planHolder = { state: { adventure: { world: worldPayload.name } } };
51
+ const sessionId = globalThis.crypto?.randomUUID?.() ?? String(Date.now());
52
+ let exposedRoomIds = new Set();
53
+ const openingRows = readFactRows(await loadMemory(memoryDir));
54
+ const openingHere = foldWorldState(openingRows).placements.get("player")?.object ?? null;
55
+ if (openingHere) exposedRoomIds = new Set([openingHere]);
56
+
57
+ return {
58
+ memoryDir,
59
+
60
+ /** One auto-play tick: infer the goal, execute exactly one move through
61
+ * adventureTurn (adventure-autoplay.mjs's own contract), thread the
62
+ * exposed-room set forward. Returns runAdventureAutoplayTick's own
63
+ * `{ turn, goal, plan, done, stalled }` unmodified. */
64
+ async autoplayTick() {
65
+ const result = await runAdventureAutoplayTick(memoryDir, {
66
+ exposedRoomIds, planHolder, sessionId, env: {},
67
+ });
68
+ exposedRoomIds = result.exposedRoomIds;
69
+ return result;
70
+ },
71
+
72
+ /** A read-only fold of the current room — no engine advance — for the
73
+ * page's own redraw after boot and after every tick. */
74
+ async snapshot() {
75
+ const rows = readFactRows(await loadMemory(memoryDir));
76
+ const state = foldWorldState(rows);
77
+ const here = state.placements.get("player")?.object ?? null;
78
+ return { rows, state, here, turn: state.turnCount };
79
+ },
80
+ };
81
+ }
82
+
83
+ // Re-exported so the page's own rendering script (adventure-viz.mjs) never
84
+ // has to duplicate sprite resolution or the digest reader — the same posture
85
+ // spider-fly-browser-entry.mjs's own globalThis.tmctSpiderFly re-export takes.
86
+ globalThis.tmctAdventure = {
87
+ createAdventureSession, resolveSpriteForClass, SPRITE_REGISTRY, worldDigestRows,
88
+ };
@@ -3071,6 +3071,7 @@ ${shown.join("\n")}${tail}`;
3071
3071
  "nothing",
3072
3072
  "one",
3073
3073
  "any",
3074
+ "anywhere",
3074
3075
  "last",
3075
3076
  // temporal filler ("when was X last touched")
3076
3077
  "usually",
@@ -3370,9 +3371,10 @@ ${shown.join("\n")}${tail}`;
3370
3371
  fuzzyVerb = { from: lcWords[at], to: fuzzyWords[at] };
3371
3372
  }
3372
3373
  }
3373
- if (!verbHit && lcWords.includes("by")) {
3374
+ if (!verbHit) {
3374
3375
  for (let i = 0; i < lcWords.length; i += 1) {
3375
3376
  const k = PASSIVE_PARTICIPLE_TO_KIND[lcWords[i]];
3377
+ if (k && lcWords[i] === "used" && lcWords[i + 1] === "for") continue;
3376
3378
  if (k && lcWords.slice(0, i).some((w) => PASSIVE_AUX.has(w))) {
3377
3379
  verbHit = { kind: k, start: i, end: i + 1 };
3378
3380
  break;
@@ -6195,14 +6197,23 @@ ${options2}
6195
6197
  }
6196
6198
  }
6197
6199
  }
6198
- if (nearest) pool = [...pool, nearest];
6200
+ if (nearest) {
6201
+ pool = [...pool, nearest];
6202
+ if (branches) {
6203
+ const branchResult = traverse(graph, parsed, { pinnedObjMatch: nearest });
6204
+ branches = [...branches, { candidate: nearest, result: branchResult, rendered: render(parsed, branchResult, graph) }];
6205
+ }
6206
+ }
6199
6207
  }
6200
6208
  const noun = pool.length && pool.every((i) => i.class === "Commit") ? "commit" : "module";
6201
6209
  const shown = pool.slice(0, OVERFLOW_CAP).map((i) => i.label);
6202
6210
  const extra2 = pool.length > OVERFLOW_CAP ? `, \u2026and ${pool.length - OVERFLOW_CAP} more` : "";
6203
6211
  const lead = `"${parsed.object}" matches more than one ${noun} ambiguously \u2014 did you mean ${listJoin(shown)}${extra2}? Try one of those. If you're not sure, narrow it to one name.`;
6212
+ const term = String(parsed.object || "");
6213
+ const termRe = term ? new RegExp(`\\b${escapeRegex(term)}\\b`, "gi") : null;
6214
+ const branchText = (b) => termRe ? b.rendered.content.replace(termRe, b.candidate.label) : b.rendered.content;
6204
6215
  const content = branches && branches.length ? `${lead}
6205
- ${branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${b.rendered.content}`).join("\n")}` : lead;
6216
+ ${branches.map((b, i) => `${i + 1}) ${b.candidate.label}: ${branchText(b)}`).join("\n")}` : lead;
6206
6217
  return {
6207
6218
  content,
6208
6219
  miss: false,
@@ -23574,6 +23585,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23574
23585
  // src/services/spider-fly.mjs
23575
23586
  init_planning();
23576
23587
  init_core();
23588
+ init_hash();
23577
23589
 
23578
23590
  // src/services/spider-fly-turn.mjs
23579
23591
  init_core();
@@ -23982,7 +23994,7 @@ ${JSON.stringify(envelope, null, 2)}`;
23982
23994
  const w = String(word || "").trim();
23983
23995
  if (/[a-z]ies$/i.test(w)) return `${w.slice(0, -3)}y`;
23984
23996
  if (/(ses|xes|zes|ches|shes)$/i.test(w)) return w.slice(0, -2);
23985
- if (/[a-z]s$/i.test(w) && !/ss$/i.test(w)) return w.slice(0, -1);
23997
+ if (/[a-z]s$/i.test(w) && !/(?:ss|ous)$/i.test(w)) return w.slice(0, -1);
23986
23998
  return w;
23987
23999
  }
23988
24000
  var TEACH_ADVERB_SKIP_SRC = "(?:(?:usually|often|sometimes|rarely|always|typically|generally|occasionally|frequently|normally|regularly|commonly|mostly|currently|still|also|really|actually)\\s+)?";
@@ -24700,7 +24712,11 @@ ${shown.map(renderFactLine).join("\n")}`,
24700
24712
  const subj = factTermVariants(normFactTerm2, subjTerm);
24701
24713
  const obj = factTermVariants(normFactTerm2, objTerm);
24702
24714
  const hit2 = facts.find((f) => f.predicate === compPredicate && subj.has(f.subject) && obj.has(f.object));
24703
- if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
24715
+ if (hit2) {
24716
+ const reversed = facts.find((f) => f.predicate === compPredicate && subj.has(f.object) && obj.has(f.subject));
24717
+ const caveat = reversed ? ` \u2014 though you also told me the opposite: ${renderFactLine(reversed)}. Both are stored; I won't silently pick one.` : "";
24718
+ return { text: `yes \u2014 ${renderFactLine(hit2)}${caveat}`, replace: true };
24719
+ }
24704
24720
  const known = facts.filter((f) => f.predicate === compPredicate && (subj.has(f.subject) || subj.has(f.object)));
24705
24721
  const shown = known.length ? ` I do know: ${known.slice(0, 3).map(renderFactLine).join("; ")}.` : "";
24706
24722
  return {
@@ -24780,19 +24796,25 @@ ${shown.map(renderFactLine).join("\n")}`,
24780
24796
  );
24781
24797
  const hit2 = hasHit(subj);
24782
24798
  if (hit2) return { text: `yes \u2014 ${renderFactLine(hit2)}`, replace: true };
24783
- let liftFrontier = subj;
24784
- const liftChain = [];
24799
+ let frontier = [{ terms: subj, chain: [] }];
24785
24800
  const liftSeen = /* @__PURE__ */ new Set();
24786
24801
  for (let hop = 0; hop < 4; hop += 1) {
24787
- const step = facts.find((f) => ISA_PREDICATES2.has(f.predicate) && liftFrontier.has(f.subject) && !liftSeen.has(f.object));
24788
- if (!step) break;
24789
- liftSeen.add(step.object);
24790
- liftChain.push(step);
24791
- const lifted = hasHit(factTermVariants(normFactTerm2, step.object));
24792
- if (lifted) {
24793
- return { text: `yes \u2014 ${[...liftChain.map(renderFactLine), renderFactLine(lifted)].join("; ")}`, replace: true };
24802
+ const nextFrontier = [];
24803
+ for (const { terms, chain } of frontier) {
24804
+ const steps = facts.filter((f) => ISA_PREDICATES2.has(f.predicate) && terms.has(f.subject) && !liftSeen.has(f.object));
24805
+ for (const step of steps) {
24806
+ if (liftSeen.has(step.object)) continue;
24807
+ liftSeen.add(step.object);
24808
+ const nextChain = [...chain, step];
24809
+ const lifted = hasHit(factTermVariants(normFactTerm2, step.object));
24810
+ if (lifted) {
24811
+ return { text: `yes \u2014 ${[...nextChain.map(renderFactLine), renderFactLine(lifted)].join("; ")}`, replace: true };
24812
+ }
24813
+ nextFrontier.push({ terms: factTermVariants(normFactTerm2, step.object), chain: nextChain });
24814
+ }
24794
24815
  }
24795
- liftFrontier = factTermVariants(normFactTerm2, step.object);
24816
+ if (!nextFrontier.length) break;
24817
+ frontier = nextFrontier;
24796
24818
  }
24797
24819
  return null;
24798
24820
  }
@@ -25310,6 +25332,38 @@ ${shown.join("\n")}${extra}`, replace: true, ...rest.length ? { pending: { items
25310
25332
  }
25311
25333
  const polarityReply = isaPolarityReply(hit2, negHit || directDisjoint);
25312
25334
  if (polarityReply) return polarityReply;
25335
+ if (disjointRows.length) {
25336
+ const ancestryOf = (seed) => {
25337
+ const closure = new Set(seed);
25338
+ let frontier = new Set(seed);
25339
+ for (let hop = 0; hop < 8 && frontier.size; hop += 1) {
25340
+ const next = /* @__PURE__ */ new Set();
25341
+ for (const [a, b] of mixedSubClassEdges) {
25342
+ if (frontier.has(a) && !closure.has(b)) next.add(b);
25343
+ }
25344
+ if (!next.size) break;
25345
+ for (const t of next) closure.add(t);
25346
+ frontier = next;
25347
+ }
25348
+ return closure;
25349
+ };
25350
+ const objectAncestry = ancestryOf(objVariants);
25351
+ const objViolation = disjointGateViolations.find((vv) => subjCandidates.has(vv.subject) && objectAncestry.has(vv.object) && !ancestryOf([vv.viaClass]).has(vv.object) && !ancestryOf([vv.object]).has(vv.viaClass));
25352
+ if (objViolation) {
25353
+ const posFact = isa.filter((f) => subjCandidates.has(f.subject) && f.object === objViolation.viaClass).sort(byTrust)[0];
25354
+ const disjointFact = disjointRows.find((f) => f.subject === objViolation.viaClass && f.object === objViolation.object || f.subject === objViolation.object && f.object === objViolation.viaClass);
25355
+ const objectNeedsLift = !objVariants.has(objViolation.object);
25356
+ const objFact = objectNeedsLift ? isa.filter((f) => objVariants.has(f.subject) && f.object === objViolation.object).sort(byTrust)[0] : null;
25357
+ if (posFact && disjointFact && (!objectNeedsLift || objFact)) {
25358
+ const kindEcho = stripTrailingDiscourseTag(isaAsk[2]).trim();
25359
+ const chain = [posFact, ...objFact ? [objFact] : []].map(renderFactLine).join("; ");
25360
+ return {
25361
+ text: `no \u2014 ${chain}; and ${factPhrase(disjointFact)}${disjointFact.provenance ? ` (source: ${disjointFact.provenance})` : ""} \u2014 so ${isaSubject} can never be ${indefiniteArticleFor(kindEcho)} ${kindEcho}.`,
25362
+ replace: true
25363
+ };
25364
+ }
25365
+ }
25366
+ }
25313
25367
  const ent = await resolveEntity(graph, isaSubject);
25314
25368
  if (ent) {
25315
25369
  const bridgeSubjects = /* @__PURE__ */ new Map();
@@ -52,7 +52,7 @@ import { loadLexicon } from "../../domain/grammar/lexicon.mjs";
52
52
  import {
53
53
  worldFactRows, WORLD_NAME, WORLD_OPENING, cellId, parseCellId, DIRECTION_DELTA, visibleCells,
54
54
  } from "../../domain/spider-fly-world.mjs";
55
- import { foldSpiderFlyState, runSpiderFlyTick, startSpiderFlyGame, DEFAULT_VISION_RADIUS } from "../../services/spider-fly.mjs";
55
+ import { foldSpiderFlyState, runSpiderFlyTick, startSpiderFlyGame, liveWebs, DEFAULT_VISION_RADIUS } from "../../services/spider-fly.mjs";
56
56
  import { resolveSpriteForClass, SPRITE_REGISTRY } from "../../domain/sprite-map.mjs";
57
57
 
58
58
  /** A live in-memory game the page's ticker and chat dock can both drive.
@@ -77,8 +77,8 @@ export async function createSpiderFlySession({ flyCount = 1 } = {}) {
77
77
  const { facts: startFacts } = await startSpiderFlyGame(memoryDir, { flyCount });
78
78
  const initialAgents = {};
79
79
  for (const f of startFacts) {
80
- if (f.predicate !== "mgx:currently-in") continue;
81
- initialAgents[f.subject] = { cell: f.object };
80
+ if (f.predicate === "mgx:currently-in") initialAgents[f.subject] = { ...initialAgents[f.subject], cell: f.object };
81
+ else if (f.predicate === "mgx:mass") initialAgents[f.subject] = { ...initialAgents[f.subject], mass: Number(f.object) };
82
82
  }
83
83
 
84
84
  const graph = parseEntities({ individuals: [], objectProperties: [] });
@@ -93,7 +93,7 @@ export async function createSpiderFlySession({ flyCount = 1 } = {}) {
93
93
  memoryDir,
94
94
  sessionId,
95
95
  opening: WORLD_OPENING,
96
- initial: { turn: 0, agents: initialAgents },
96
+ initial: { turn: 0, agents: initialAgents, activeWebs: [] },
97
97
  taxonomyRows,
98
98
 
99
99
  /** Run one real engine turn directly. Returns spider-fly.mjs's own
@@ -127,16 +127,18 @@ export async function createSpiderFlySession({ flyCount = 1 } = {}) {
127
127
 
128
128
  /** A read-only fold of the CURRENT board — no engine advance, no goal
129
129
  * lines (see the header comment: only a raw tick() recomputes those).
130
- * Lets the page resync positions/turn count after a chat-driven tick. */
130
+ * Lets the page resync positions/turn count/mass/active webs after a
131
+ * chat-driven tick. Web individuals are never listed as agents (that's
132
+ * spider-1/fly-1/... only) — they surface only through activeWebs. */
131
133
  async snapshot() {
132
134
  const rows = readFactRows(await loadMemory(memoryDir));
133
135
  const state = foldSpiderFlyState(rows);
134
136
  const agents = {};
135
137
  for (const [id, place] of state.placements) {
136
- if (state.removed.has(id)) continue;
137
- agents[id] = { cell: place.cell };
138
+ if (state.removed.has(id) || /^web-\d+$/.test(id)) continue;
139
+ agents[id] = { cell: place.cell, mass: state.mass.get(id)?.value ?? null };
138
140
  }
139
- return { turn: state.turnCount, agents };
141
+ return { turn: state.turnCount, agents, activeWebs: liveWebs(state.webs, state.turnCount) };
140
142
  },
141
143
  };
142
144
  }