@polycode-projects/the-mechanical-code-talker 2.10.5 → 2.11.0

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 (40) hide show
  1. package/README.md +2 -2
  2. package/corpus/sprites/src/sprite-facts.jsonl +18 -0
  3. package/corpus/worlds/manifest.json +5 -5
  4. package/corpus/worlds/shards/ashcombe-hall.jsonl.gz +0 -0
  5. package/corpus/worlds/src/ashcombe-hall.jsonl +27 -0
  6. package/data/sprites/book-icon.toml +12 -0
  7. package/data/sprites/cellar-icon.toml +12 -0
  8. package/data/sprites/drawing-room-icon.toml +13 -0
  9. package/data/sprites/garden-icon.toml +12 -0
  10. package/data/sprites/kitchen-icon.toml +13 -0
  11. package/data/sprites/library-icon.toml +12 -0
  12. package/data/sprites/pan-icon.toml +11 -0
  13. package/data/sprites/study-icon.toml +12 -0
  14. package/package.json +5 -2
  15. package/src/adapters/corpus/wikipedia-live.mjs +182 -26
  16. package/src/adapters/corpus/worlds-pack.mjs +8 -2
  17. package/src/adapters/toml-config.mjs +6 -0
  18. package/src/domain/memory/trust.mjs +11 -0
  19. package/src/domain/worlds-pack.mjs +50 -0
  20. package/src/services/adventure-autoplay.mjs +5 -2
  21. package/src/services/adventure-viz.mjs +301 -33
  22. package/src/services/adventure.mjs +162 -14
  23. package/src/services/chat-page-viz.mjs +265 -189
  24. package/src/services/chat-session.mjs +15 -5
  25. package/src/services/chat.mjs +286 -43
  26. package/src/services/code-explorer-viz.mjs +183 -75
  27. package/src/services/extract-facts.mjs +118 -28
  28. package/src/services/ingest-viz.mjs +328 -79
  29. package/src/services/ledger-viz.mjs +99 -0
  30. package/src/services/memory-panel-viz.mjs +159 -0
  31. package/src/services/research.mjs +266 -0
  32. package/src/services/sentences.mjs +19 -0
  33. package/src/services/spider-fly-viz.mjs +21 -5
  34. package/src/surfaces/web/adventure-browser-entry.mjs +9 -5
  35. package/src/surfaces/web/chat-browser-entry.mjs +28 -11
  36. package/src/surfaces/web/code-explorer-browser-entry.mjs +27 -11
  37. package/src/surfaces/web/ingest-browser-entry.mjs +123 -41
  38. package/src/surfaces/web/ledger-browser-entry.mjs +10 -4
  39. package/src/surfaces/web/memory-ask-browser.bundle.js +112 -112
  40. package/src/surfaces/web/memory-stats.mjs +53 -0
@@ -15,7 +15,7 @@ import { readFileSync } from "node:fs";
15
15
  import { gunzipSync } from "node:zlib";
16
16
  import { fileURLToPath } from "node:url";
17
17
  import { dirname, join } from "node:path";
18
- import { isWorldsIndexEntry, isWorldRow, isWorldFactRow, isWorldRuleRow, isWorldMetaRow } from "../../domain/worlds-pack.mjs";
18
+ import { isWorldsIndexEntry, isWorldRow, isWorldFactRow, isWorldRuleRow, isWorldMetaRow, expandWorldDefaultContents } from "../../domain/worlds-pack.mjs";
19
19
 
20
20
  const PKG_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
21
21
 
@@ -84,7 +84,13 @@ export function loadWorld(dir, worldName) {
84
84
  else if (isWorldMetaRow(row) && !meta) meta = row;
85
85
  } catch { /* tolerated: a bad line loses one row, not the world */ }
86
86
  }
87
- if (facts.length || rules.length || meta) payload = { name: worldName, facts, rules, meta };
87
+ // Class-default contents (library -> a book, kitchen -> a pan) are
88
+ // materialized here, the one choke point every loader path runs through
89
+ // (openAdventure, the site build, the tests) — never baked into the
90
+ // shipped shard, which stays a byte-copy of its hand-authored source.
91
+ if (facts.length || rules.length || meta) {
92
+ payload = { name: worldName, facts: expandWorldDefaultContents(facts), rules, meta };
93
+ }
88
94
  }
89
95
  worldCacheByKey.set(key, payload);
90
96
  return payload;
@@ -125,6 +125,12 @@ export async function normalizeConfig(raw, { configDir } = {}) {
125
125
  if (src.games !== undefined) cfg.games = src.games;
126
126
  if (src.planning !== undefined) cfg.planning = src.planning;
127
127
 
128
+ // Research-lane knobs (src/services/research.mjs): sparse PASS-THROUGH,
129
+ // same discipline as [games.*] — the raw `[research]` table
130
+ // (fanout_limit / depth_limit / min_interval_ms, snake_case) rides through
131
+ // unmodified; clamping and default-filling is resolveResearchConfig's job.
132
+ if (src.research !== undefined) cfg.research = src.research;
133
+
128
134
  const idx = src.index || {};
129
135
  const index = {};
130
136
  if (idx.languages !== undefined) index.languages = idx.languages;
@@ -77,6 +77,17 @@ export function provenanceTagToSource(tag) {
77
77
  const pack = rest.slice(0, colon) || "unknown";
78
78
  return { kind: referenceKindFor(pack), pack, article: rest.slice(colon + 1) };
79
79
  }
80
+ // research:<topic>@<depth> — the research lane's Simple English Wikipedia
81
+ // loads. Live-fetched at query time like the wikipedia-live pack, so it
82
+ // scores at the same referenceLive prior, below every curated pack. Parsed
83
+ // from the FULL tag (a topic may contain spaces); the depth segment records
84
+ // how far the fan-out reached and is not part of the Source identity.
85
+ if (t.startsWith("research:")) {
86
+ const rest = t.slice("research:".length);
87
+ const at = rest.lastIndexOf("@");
88
+ const topic = (at >= 0 ? rest.slice(0, at) : rest).trim();
89
+ return { kind: "referenceLive", pack: "research", article: topic || "unknown" };
90
+ }
80
91
  const head = t.split(/\s+/)[0]; // drop trailing " /r/IsA" etc.
81
92
  if (head.startsWith("corpus-weak:")) return { kind: "corpusWeak", name: head.slice("corpus-weak:".length) || "unknown" };
82
93
  if (head.startsWith("corpus:")) return { kind: "corpus", name: head.slice("corpus:".length) || "unknown" };
@@ -69,3 +69,53 @@ export function isWorldRow(row) {
69
69
  export function worldProvenanceTag(worldName) {
70
70
  return `world:${worldName}`;
71
71
  }
72
+
73
+ const DEFAULT_CONTAINS_PREDICATE = "mgx:default-contains";
74
+
75
+ /** Materialize a world's class-default contents: for every
76
+ * `<room> mgx:default-contains <class>` fact, mint one placed, portable
77
+ * instance of that class in that room unless the room already holds one. A
78
+ * minted book gets `rdf:type <class>` (so it resolves its own sprite),
79
+ * `rdf:type portable` (so the take family accepts it), and
80
+ * `mgx:located-in <room>` (so it shows up in the room and can be carried
81
+ * out). Pure and deterministic: defaults are visited in sorted order, and a
82
+ * collision mints `<class>-2`, `<class>-3`… so re-running never renames an
83
+ * earlier instance. Rows keep whatever world/kind wrapper the input rows
84
+ * carry, so both a loaded shard (world rows) and a bare triple list expand
85
+ * the same way. */
86
+ export function expandWorldDefaultContents(facts) {
87
+ const rows = facts || [];
88
+ const defaults = rows
89
+ .filter((r) => r.predicate === DEFAULT_CONTAINS_PREDICATE)
90
+ .sort((a, b) => `${a.subject}\0${a.object}`.localeCompare(`${b.subject}\0${b.object}`));
91
+ if (!defaults.length) return rows;
92
+
93
+ const instanceIds = new Set(rows.filter((r) => r.predicate === "rdf:type").map((r) => r.subject));
94
+ const placedIn = new Map();
95
+ for (const r of rows) {
96
+ if (r.predicate === "mgx:located-in") placedIn.set(r.subject, r.object);
97
+ }
98
+ const roomAlreadyHas = (klass, room) =>
99
+ rows.some((r) => r.predicate === "rdf:type" && r.object === klass && placedIn.get(r.subject) === room);
100
+
101
+ const wrapper = rows.find((r) => typeof r.world === "string" && r.kind === "fact");
102
+ const wrap = (subject, predicate, object) => ({
103
+ ...(wrapper ? { world: wrapper.world, kind: "fact" } : {}),
104
+ subject, predicate, object,
105
+ });
106
+
107
+ const minted = [];
108
+ for (const d of defaults) {
109
+ const room = d.subject;
110
+ const klass = d.object;
111
+ if (roomAlreadyHas(klass, room)) continue;
112
+ let id = klass;
113
+ let n = 1;
114
+ while (instanceIds.has(id)) { n += 1; id = `${klass}-${n}`; }
115
+ instanceIds.add(id);
116
+ minted.push(wrap(id, "rdf:type", klass));
117
+ minted.push(wrap(id, "rdf:type", "portable"));
118
+ minted.push(wrap(id, "mgx:located-in", room));
119
+ }
120
+ return minted.length ? [...rows, ...minted] : rows;
121
+ }
@@ -26,7 +26,7 @@
26
26
  // fact simply has nothing for this module to infer a goal toward.
27
27
  import { findActionPath } from "../domain/planning.mjs";
28
28
  import { loadMemory, readFactRows } from "../adapters/memory/core.mjs";
29
- import { foldWorldState, adventureTurn } from "./adventure.mjs";
29
+ import { foldWorldState, adventureTurn, worldActionRows } from "./adventure.mjs";
30
30
 
31
31
  const SNAPSHOT_RE = /^(.+)@turn(\d+)$/;
32
32
  const baseSubjectOf = (subject) => SNAPSHOT_RE.exec(subject)?.[1] ?? subject;
@@ -138,7 +138,10 @@ async function stepTowardThenAct({
138
138
  */
139
139
  export async function runAdventureAutoplayTick(memoryDir, opts = {}) {
140
140
  const { exposedRoomIds, planHolder, sessionId = "", env = {}, graph = null, cache = null } = opts;
141
- const rows = readFactRows(await loadMemory(memoryDir));
141
+ // Auto-play reasons over the game world only — the same isolation the manual
142
+ // command path uses, so a fact the player taught mid-game never steers the
143
+ // planner toward a room the world never placed anything in.
144
+ const rows = worldActionRows(readFactRows(await loadMemory(memoryDir)));
142
145
  const state = foldWorldState(rows);
143
146
  const here = state.placements.get("player")?.object ?? null;
144
147
  const exposed = new Set(exposedRoomIds || []);