@polycode-projects/the-mechanical-code-talker 2.7.3 → 2.7.5

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.
@@ -9675,6 +9675,8 @@ ${bodyText}` : graphText, tier });
9675
9675
  return { id: `src:provider:${desc.name}`, type: "provider" };
9676
9676
  case "corpus":
9677
9677
  return { id: `src:corpus:${desc.name}`, type: "corpus" };
9678
+ case "corpusWeak":
9679
+ return { id: `src:corpus-weak:${desc.name}`, type: "corpusWeak" };
9678
9680
  // One Source per pack article (the @revid stays in the article segment),
9679
9681
  // so two facts from the same article corroborate nothing extra.
9680
9682
  case "reference":
@@ -10580,6 +10582,94 @@ CREATE INDEX IF NOT EXISTS edges_by_prop ON edges(prop);
10580
10582
  }
10581
10583
  });
10582
10584
 
10585
+ // src/domain/skos-view.mjs
10586
+ function buildSkosConceptView(rows, { conceptBase = "concept:", relationMap = DEFAULT_RELATION_MAP } = {}) {
10587
+ const synonymPreds = new Set(relationMap.synonym || []);
10588
+ const relatedPreds = new Set(relationMap.related || []);
10589
+ const parent = /* @__PURE__ */ new Map();
10590
+ const ensure = (t) => {
10591
+ if (!parent.has(t)) parent.set(t, t);
10592
+ };
10593
+ const find = (x) => {
10594
+ let r = x;
10595
+ while (parent.get(r) !== r) r = parent.get(r);
10596
+ while (parent.get(x) !== r) {
10597
+ const next = parent.get(x);
10598
+ parent.set(x, r);
10599
+ x = next;
10600
+ }
10601
+ return r;
10602
+ };
10603
+ const union = (a, b) => {
10604
+ const ra = find(a), rb = find(b);
10605
+ if (ra === rb) return;
10606
+ if (ra < rb) parent.set(rb, ra);
10607
+ else parent.set(ra, rb);
10608
+ };
10609
+ const relatedRaw = [];
10610
+ for (const r of rows) {
10611
+ const p = r.predicate;
10612
+ if (!synonymPreds.has(p) && !relatedPreds.has(p)) continue;
10613
+ const s = normFactTerm(r.subject), o = normFactTerm(r.object);
10614
+ if (!s || !o) continue;
10615
+ ensure(s);
10616
+ ensure(o);
10617
+ if (synonymPreds.has(p)) union(s, o);
10618
+ else relatedRaw.push({ s, o });
10619
+ }
10620
+ const iriFor = (rep) => conceptBase + rep.replace(/ /g, "_");
10621
+ const componentTerms = /* @__PURE__ */ new Map();
10622
+ for (const t of parent.keys()) {
10623
+ const rep = find(t);
10624
+ if (!componentTerms.has(rep)) componentTerms.set(rep, /* @__PURE__ */ new Set());
10625
+ componentTerms.get(rep).add(t);
10626
+ }
10627
+ const concepts = [];
10628
+ for (const [rep, terms] of componentTerms) {
10629
+ const sorted = [...terms].sort();
10630
+ concepts.push({ id: iriFor(rep), prefLabel: rep, altLabels: sorted.filter((t) => t !== rep) });
10631
+ }
10632
+ concepts.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
10633
+ const seen = /* @__PURE__ */ new Set();
10634
+ const related = [];
10635
+ for (const { s, o } of relatedRaw) {
10636
+ const cs = iriFor(find(s)), co = iriFor(find(o));
10637
+ if (cs === co) continue;
10638
+ const key = cs < co ? `${cs}\0${co}` : `${co}\0${cs}`;
10639
+ if (seen.has(key)) continue;
10640
+ seen.add(key);
10641
+ related.push({ subject: cs, object: co });
10642
+ }
10643
+ related.sort((a, b) => `${a.subject}${a.object}` < `${b.subject}${b.object}` ? -1 : 1);
10644
+ const conceptIdForTerm = (term) => {
10645
+ const t = normFactTerm(term);
10646
+ return parent.has(t) ? iriFor(find(t)) : null;
10647
+ };
10648
+ return { concepts, related, conceptIdForTerm, namespace: SKOS_NS };
10649
+ }
10650
+ function relatedForTerm(rows, term, options = {}) {
10651
+ const view = buildSkosConceptView(rows, options);
10652
+ const conceptId = view.conceptIdForTerm(term);
10653
+ if (!conceptId) return null;
10654
+ const byId = new Map(view.concepts.map((c) => [c.id, c]));
10655
+ const concept = byId.get(conceptId);
10656
+ const queried = normFactTerm(term);
10657
+ const synonyms = [concept.prefLabel, ...concept.altLabels].filter((label) => label !== queried);
10658
+ const related = view.related.filter((r) => r.subject === conceptId || r.object === conceptId).map((r) => byId.get(r.subject === conceptId ? r.object : r.subject)).filter(Boolean);
10659
+ return { conceptId, prefLabel: concept.prefLabel, altLabels: concept.altLabels, synonyms, related };
10660
+ }
10661
+ var SKOS_NS, DEFAULT_RELATION_MAP;
10662
+ var init_skos_view = __esm({
10663
+ "src/domain/skos-view.mjs"() {
10664
+ init_hash();
10665
+ SKOS_NS = "http://www.w3.org/2004/02/skos/core#";
10666
+ DEFAULT_RELATION_MAP = {
10667
+ synonym: ["mgx:synonym"],
10668
+ related: ["mgx:relatedTo", "mgx:similarTo"]
10669
+ };
10670
+ }
10671
+ });
10672
+
10583
10673
  // adapter-stub-ask-nlp.mjs:../adapters/ask-nlp.mjs
10584
10674
  var nlpAdapter;
10585
10675
  var init_ask_nlp = __esm({
@@ -22968,91 +23058,7 @@ ${hint}` : ""}${cand}`;
22968
23058
 
22969
23059
  // src/tools/handlers/tmct-related.mjs
22970
23060
  init_config();
22971
-
22972
- // src/domain/skos-view.mjs
22973
- init_hash();
22974
- var SKOS_NS = "http://www.w3.org/2004/02/skos/core#";
22975
- var DEFAULT_RELATION_MAP = {
22976
- synonym: ["mgx:synonym"],
22977
- related: ["mgx:relatedTo", "mgx:similarTo"]
22978
- };
22979
- function buildSkosConceptView(rows, { conceptBase = "concept:", relationMap = DEFAULT_RELATION_MAP } = {}) {
22980
- const synonymPreds = new Set(relationMap.synonym || []);
22981
- const relatedPreds = new Set(relationMap.related || []);
22982
- const parent = /* @__PURE__ */ new Map();
22983
- const ensure = (t) => {
22984
- if (!parent.has(t)) parent.set(t, t);
22985
- };
22986
- const find = (x) => {
22987
- let r = x;
22988
- while (parent.get(r) !== r) r = parent.get(r);
22989
- while (parent.get(x) !== r) {
22990
- const next = parent.get(x);
22991
- parent.set(x, r);
22992
- x = next;
22993
- }
22994
- return r;
22995
- };
22996
- const union = (a, b) => {
22997
- const ra = find(a), rb = find(b);
22998
- if (ra === rb) return;
22999
- if (ra < rb) parent.set(rb, ra);
23000
- else parent.set(ra, rb);
23001
- };
23002
- const relatedRaw = [];
23003
- for (const r of rows) {
23004
- const p = r.predicate;
23005
- if (!synonymPreds.has(p) && !relatedPreds.has(p)) continue;
23006
- const s = normFactTerm(r.subject), o = normFactTerm(r.object);
23007
- if (!s || !o) continue;
23008
- ensure(s);
23009
- ensure(o);
23010
- if (synonymPreds.has(p)) union(s, o);
23011
- else relatedRaw.push({ s, o });
23012
- }
23013
- const iriFor = (rep) => conceptBase + rep.replace(/ /g, "_");
23014
- const componentTerms = /* @__PURE__ */ new Map();
23015
- for (const t of parent.keys()) {
23016
- const rep = find(t);
23017
- if (!componentTerms.has(rep)) componentTerms.set(rep, /* @__PURE__ */ new Set());
23018
- componentTerms.get(rep).add(t);
23019
- }
23020
- const concepts = [];
23021
- for (const [rep, terms] of componentTerms) {
23022
- const sorted = [...terms].sort();
23023
- concepts.push({ id: iriFor(rep), prefLabel: rep, altLabels: sorted.filter((t) => t !== rep) });
23024
- }
23025
- concepts.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
23026
- const seen = /* @__PURE__ */ new Set();
23027
- const related = [];
23028
- for (const { s, o } of relatedRaw) {
23029
- const cs = iriFor(find(s)), co = iriFor(find(o));
23030
- if (cs === co) continue;
23031
- const key = cs < co ? `${cs}\0${co}` : `${co}\0${cs}`;
23032
- if (seen.has(key)) continue;
23033
- seen.add(key);
23034
- related.push({ subject: cs, object: co });
23035
- }
23036
- related.sort((a, b) => `${a.subject}${a.object}` < `${b.subject}${b.object}` ? -1 : 1);
23037
- const conceptIdForTerm = (term) => {
23038
- const t = normFactTerm(term);
23039
- return parent.has(t) ? iriFor(find(t)) : null;
23040
- };
23041
- return { concepts, related, conceptIdForTerm, namespace: SKOS_NS };
23042
- }
23043
- function relatedForTerm(rows, term, options = {}) {
23044
- const view = buildSkosConceptView(rows, options);
23045
- const conceptId = view.conceptIdForTerm(term);
23046
- if (!conceptId) return null;
23047
- const byId = new Map(view.concepts.map((c) => [c.id, c]));
23048
- const concept = byId.get(conceptId);
23049
- const queried = normFactTerm(term);
23050
- const synonyms = [concept.prefLabel, ...concept.altLabels].filter((label) => label !== queried);
23051
- const related = view.related.filter((r) => r.subject === conceptId || r.object === conceptId).map((r) => byId.get(r.subject === conceptId ? r.object : r.subject)).filter(Boolean);
23052
- return { conceptId, prefLabel: concept.prefLabel, altLabels: concept.altLabels, synonyms, related };
23053
- }
23054
-
23055
- // src/tools/handlers/tmct-related.mjs
23061
+ init_skos_view();
23056
23062
  init_memory_fallthrough();
23057
23063
  async function tmct_related(args, { config }) {
23058
23064
  const term = requiredArg(args, "term");
@@ -23396,6 +23402,9 @@ ${JSON.stringify(envelope, null, 2)}`;
23396
23402
  "game-inform": "inform"
23397
23403
  });
23398
23404
 
23405
+ // src/services/chat.mjs
23406
+ init_skos_view();
23407
+
23399
23408
  // src/domain/worlds-pack.mjs
23400
23409
  var WORLD_NAME_RE = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
23401
23410
  var WORLD_RULE_KINDS = Object.freeze([
@@ -23509,6 +23518,35 @@ ${JSON.stringify(envelope, null, 2)}`;
23509
23518
  init_core();
23510
23519
  init_completions();
23511
23520
 
23521
+ // src/domain/spider-fly-world.mjs
23522
+ var WEB_HOME = Object.freeze({ x: 2, y: 2 });
23523
+ var DIRECTION_DELTA = Object.freeze({
23524
+ north: Object.freeze({ dx: 0, dy: -1 }),
23525
+ south: Object.freeze({ dx: 0, dy: 1 }),
23526
+ east: Object.freeze({ dx: 1, dy: 0 }),
23527
+ west: Object.freeze({ dx: -1, dy: 0 })
23528
+ });
23529
+ var SEED_TAXONOMY = Object.freeze([
23530
+ Object.freeze(["poodle", "dog"]),
23531
+ Object.freeze(["sheepdog", "dog"]),
23532
+ Object.freeze(["dog", "animal"]),
23533
+ Object.freeze(["spider", "arachnid"]),
23534
+ Object.freeze(["arachnid", "animal"]),
23535
+ Object.freeze(["fly", "insect"]),
23536
+ Object.freeze(["insect", "animal"])
23537
+ ]);
23538
+
23539
+ // src/services/spider-fly.mjs
23540
+ init_planning();
23541
+ init_core();
23542
+
23543
+ // src/services/spider-fly-turn.mjs
23544
+ init_core();
23545
+ var SPIDER_FLY_TOLD_RE = new RegExp(
23546
+ "^@(spider|fly)(?:-(\\d+))?[,:]?\\s+the\\s+(spider|fly)(?:-(\\d+))?\\s+is\\s+(?:(north|south|east|west)|at\\s+(cell-\\d+-\\d+))[.!?\\s]*$",
23547
+ "i"
23548
+ );
23549
+
23512
23550
  // src/services/chat-session.mjs
23513
23551
  init_node_path();
23514
23552
  init_node_fs();
@@ -0,0 +1,152 @@
1
+ // spider-fly-browser-entry.mjs — the esbuild entry for the spider-and-fly
2
+ // full-screen/home-page pages (public/spider-fly-browser.bundle.js, built by
3
+ // scripts/build-spider-fly-bundle.mjs).
4
+ //
5
+ // Exposes ONE session factory over the real engine, `createSpiderFlySession`,
6
+ // mirroring chat-browser-entry.mjs's `createChatSession` shape exactly (same
7
+ // underlying runTurn, same minus-every-filesystem-side-effect posture — no
8
+ // transcript log, no sidecar, no graph upsert) with two additions specific to
9
+ // this game's rendering needs:
10
+ //
11
+ // - `session.tick()` runs ONE real engine turn directly
12
+ // (spider-fly.mjs's runSpiderFlyTick), unmediated by chat text, so the
13
+ // page's own ticker gets back the structured `{ turn, agents, ecology }`
14
+ // shape it needs to redraw the board and the HUD's goal lines. This is
15
+ // the "page's ticker calls a turn" half of the brief.
16
+ // - `session.turn(line)` runs the FULL chat turn engine (chat.mjs's
17
+ // runTurn — the exact dispatch the CLI and the home page's own chat run,
18
+ // with the spider-fly lane already wired in), so the in-page chat dock
19
+ // supports the addressed teach-frame ("@spider the fly is east"), the
20
+ // bare "tick" command, and any ordinary fallthrough question ("where is
21
+ // the spider") exactly as the CLI does. This is the "chat dock runs
22
+ // runTurn" half.
23
+ // - `session.snapshot()` is a READ-ONLY fold (spider-fly.mjs's own
24
+ // foldSpiderFlyState, no engine advance) so the page can resync agent
25
+ // positions after a CHAT-driven tick/address turn (whose reply is text,
26
+ // not the structured tick() shape) without double-advancing the turn.
27
+ // It carries no `.goal` — the chat reply text itself already narrates
28
+ // that turn's outcome; only a raw tick() refreshes the HUD's goal lines.
29
+ //
30
+ // tick(), turn() and snapshot() all read/write the SAME in-memory store
31
+ // (`memoryDir`), so the board and the chat dock never disagree about the
32
+ // game's state. A caller that lets a play button and a chat submit fire
33
+ // concurrently must serialize its own calls against this session — this
34
+ // module runs each call to completion but does not queue overlapping ones
35
+ // itself (see spider-fly-viz.mjs's own inlined `withLock` wrapper).
36
+ //
37
+ // The world bootstrap never touches the worlds-pack fetch/provider machinery
38
+ // spider-fly-turn.mjs's openSpiderFlyGame uses (that path needs a Node fs
39
+ // read or a registered fetch provider, neither of which this bundle carries):
40
+ // spider-fly-world.mjs's worldFactRows()/startSpiderFlyGame are already pure/
41
+ // in-memory, so the browser bootstraps the identical board directly from
42
+ // them. The world's rule rows (worldRuleRows) are skipped on purpose —
43
+ // spider-fly.mjs's own header comment confirms grid movement never reads
44
+ // them back (hand-written pathfinding over has-exit-* facts, not the taught
45
+ // action-rule DSL), so nothing here depends on them being loaded.
46
+ import { runTurn } from "../../services/chat.mjs";
47
+ import {
48
+ createInMemoryStore, normFactTerm, appendFacts, loadMemory, readFactRows,
49
+ } from "../../adapters/memory/core.mjs";
50
+ import { parseEntities } from "../../domain/codegraph.mjs";
51
+ import { loadLexicon } from "../../domain/grammar/lexicon.mjs";
52
+ import {
53
+ worldFactRows, WORLD_NAME, WORLD_OPENING, cellId, parseCellId, DIRECTION_DELTA, visibleCells,
54
+ } from "../../domain/spider-fly-world.mjs";
55
+ import { foldSpiderFlyState, runSpiderFlyTick, startSpiderFlyGame, DEFAULT_VISION_RADIUS } from "../../services/spider-fly.mjs";
56
+ import { resolveSpriteForClass, SPRITE_REGISTRY } from "../../domain/sprite-map.mjs";
57
+
58
+ /** A live in-memory game the page's ticker and chat dock can both drive.
59
+ * Returns { memoryDir, sessionId, opening, initial, taxonomyRows, tick,
60
+ * turn, snapshot }. `initial` is the freshly-bootstrapped board's starting
61
+ * agents ({ [id]: { cell } }, turn 0, no goal computed yet — the CLI's own
62
+ * opener shows the same static starting board before any real tick runs).
63
+ * `taxonomyRows` is the world's static rdfs:subClassOf rows, for
64
+ * resolveSpriteForClass — immutable for the life of the session, so it is
65
+ * computed once here rather than re-read from memory on every render. */
66
+ export async function createSpiderFlySession({ flyCount = 1 } = {}) {
67
+ const memoryDir = createInMemoryStore();
68
+ const tag = `world:${WORLD_NAME}`;
69
+ const worldRows = [...worldFactRows()];
70
+ await appendFacts(memoryDir, worldRows.map((f) => ({
71
+ subject: f.subject, predicate: f.predicate, object: f.object, provenance: tag,
72
+ })));
73
+ const taxonomyRows = worldRows
74
+ .filter((f) => f.predicate === "rdfs:subClassOf")
75
+ .map((f) => ({ subject: f.subject, predicate: f.predicate, object: f.object }));
76
+
77
+ const { facts: startFacts } = await startSpiderFlyGame(memoryDir, { flyCount });
78
+ const initialAgents = {};
79
+ for (const f of startFacts) {
80
+ if (f.predicate !== "mgx:currently-in") continue;
81
+ initialAgents[f.subject] = { cell: f.object };
82
+ }
83
+
84
+ const graph = parseEntities({ individuals: [], objectProperties: [] });
85
+ const lexicon = loadLexicon();
86
+ const sessionId = globalThis.crypto?.randomUUID?.() ?? String(Date.now());
87
+
88
+ let focus = null;
89
+ let last = null;
90
+ let planState = { spiderFly: { turn: 0 } };
91
+
92
+ return {
93
+ memoryDir,
94
+ sessionId,
95
+ opening: WORLD_OPENING,
96
+ initial: { turn: 0, agents: initialAgents },
97
+ taxonomyRows,
98
+
99
+ /** Run one real engine turn directly. Returns spider-fly.mjs's own
100
+ * { turn, agents, ecology } shape unmodified. */
101
+ async tick() {
102
+ const result = await runSpiderFlyTick(memoryDir);
103
+ planState = { spiderFly: { turn: result.turn } };
104
+ return result;
105
+ },
106
+
107
+ /** One dispatched chat turn — the SAME runTurn the CLI and the home
108
+ * page's own chat run, over this session's own memoryDir. A throwing
109
+ * runTurn must never kill the session — the page has no other chance
110
+ * to show this turn's answer. */
111
+ async turn(line) {
112
+ let result;
113
+ try {
114
+ result = await runTurn(line, {
115
+ config: null, source: null, graph, focus, last, memoryDir, sessionId,
116
+ env: {}, lexicon, vocabHint: "", planState,
117
+ });
118
+ } catch (e) {
119
+ const message = e instanceof Error ? e.message : String(e);
120
+ return { answer: `Something went wrong answering that (${message}). Try rephrasing, or /help.`, end: false, record: null, plan: null };
121
+ }
122
+ focus = result.focus;
123
+ last = result.last;
124
+ if ("planState" in result) planState = result.planState;
125
+ return { answer: result.answer, end: Boolean(result.end), record: result.record ?? null, plan: result.plan ?? null };
126
+ },
127
+
128
+ /** A read-only fold of the CURRENT board — no engine advance, no goal
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. */
131
+ async snapshot() {
132
+ const rows = readFactRows(await loadMemory(memoryDir));
133
+ const state = foldSpiderFlyState(rows);
134
+ const agents = {};
135
+ for (const [id, place] of state.placements) {
136
+ if (state.removed.has(id)) continue;
137
+ agents[id] = { cell: place.cell };
138
+ }
139
+ return { turn: state.turnCount, agents };
140
+ },
141
+ };
142
+ }
143
+
144
+ // cellId/parseCellId/DIRECTION_DELTA/visibleCells/DEFAULT_VISION_RADIUS are
145
+ // re-exported so the page's own rendering script (spider-fly-viz.mjs) never
146
+ // has to duplicate grid geometry or the vision-radius default: reconstructing
147
+ // a spider's remaining silk-thread path from its returned direction list, and
148
+ // computing the POV overlay's visible-cell mask, both need them.
149
+ globalThis.tmctSpiderFly = {
150
+ createSpiderFlySession, normFactTerm, resolveSpriteForClass, SPRITE_REGISTRY,
151
+ cellId, parseCellId, DIRECTION_DELTA, visibleCells, DEFAULT_VISION_RADIUS,
152
+ };