@polycode-projects/the-mechanical-code-talker 5.0.6 → 5.0.8

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 (38) hide show
  1. package/README.md +21 -0
  2. package/bin/tmct.mjs +63 -2
  3. package/package.json +3 -2
  4. package/src/adapters/memory/core.mjs +23 -0
  5. package/src/domain/ask-vocab.mjs +19 -0
  6. package/src/domain/ask.mjs +8 -1
  7. package/src/domain/interpret/strategies/keywords.mjs +30 -1
  8. package/src/domain/memory/capability.mjs +15 -11
  9. package/src/domain/router/drive.mjs +36 -17
  10. package/src/domain/router/resolver.mjs +63 -17
  11. package/src/domain/spider-fly-world.mjs +2 -2
  12. package/src/domain/sprite-templates.mjs +19 -7
  13. package/src/domain/syllogise.mjs +16 -6
  14. package/src/domain/town-square-world.mjs +1 -1
  15. package/src/services/adventure.mjs +8 -1
  16. package/src/services/chat-page-viz.mjs +118 -23
  17. package/src/services/chat-session.mjs +60 -10
  18. package/src/services/chat.mjs +228 -24
  19. package/src/services/extract-facts.mjs +47 -7
  20. package/src/services/ingest-viz.mjs +108 -25
  21. package/src/services/ledger-viz.mjs +4 -2
  22. package/src/services/memory-panel-viz.mjs +44 -0
  23. package/src/services/mud-viz.mjs +17 -0
  24. package/src/services/mudiii-scene.mjs +271 -24
  25. package/src/services/mudiii-turn.mjs +65 -9
  26. package/src/services/mudiii-viz.mjs +302 -125
  27. package/src/services/plan-viz.mjs +23 -2
  28. package/src/services/predator-prey.mjs +82 -33
  29. package/src/services/research-viz.mjs +12 -19
  30. package/src/services/spider-fly-turn.mjs +7 -1
  31. package/src/services/spider-fly-viz.mjs +10 -3
  32. package/src/services/viz-ticker.mjs +15 -2
  33. package/src/surfaces/http/server-http.mjs +90 -13
  34. package/src/surfaces/web/memory-ask-browser.bundle.js +125 -125
  35. package/src/surfaces/web/mud-browser-entry.mjs +33 -1
  36. package/src/surfaces/web/tmct-surface.mjs +18 -6
  37. package/src/tools/handlers/tmct-ask.mjs +15 -2
  38. package/src/tools/server.mjs +31 -2
@@ -36,7 +36,7 @@ import { memoryFactGraphPayload } from "../../domain/memory-facts.mjs";
36
36
  import { loadLexicon } from "../../domain/grammar/lexicon.mjs";
37
37
  import {
38
38
  foldWorldState, worldActionRows, worldDigestRows, roomAffordances,
39
- personKnowledgeLines, personKnownFoodLines,
39
+ personKnowledgeLines, personKnownFoodLines, objectClassChain, recordExamined,
40
40
  diggableDirections, castInRoom, displayNameOf, isOutOfPlay, outOfPlayReasonOf, outOfPlayPhrase,
41
41
  roomKindOf, isMudStatePredicate, worldEpochFact, snapshotSubject,
42
42
  } from "../../services/adventure.mjs";
@@ -52,6 +52,12 @@ import { createTurnSession } from "./turn-session.mjs";
52
52
  import { publishTmctSurface } from "./tmct-surface.mjs";
53
53
  import { graphAsk, enginePlan } from "./engine-surface.mjs";
54
54
 
55
+ // The class chain a room-mate object needs to reach for a starting character
56
+ // to be seeded as already knowing about it — mirrors mud-turn.mjs's own
57
+ // private FOOD_CLASS, kept as this file's own copy for the same reason that
58
+ // file gives: nothing here reaches into another module's private constant.
59
+ const MUD_STARTING_FOOD_CLASS = "food";
60
+
55
61
  /** A live, shared mud world several characters can each act in. `worldPayload`
56
62
  * is `{ name, facts, rules, opening }` — the same shape adventure-browser-
57
63
  * entry.mjs's own worldPayload takes, read once at build time through the
@@ -131,6 +137,32 @@ export async function createMudSession(worldPayload, { characters = [], epoch =
131
137
  return state.placements.get(character)?.object ?? null;
132
138
  }
133
139
 
140
+ // Every character already stands somewhere in the world this store was
141
+ // just seeded with, and food sitting loose in ITS OWN starting room is
142
+ // something it would see just by being there — the room's own "You can:"
143
+ // affordances and the pouch already read straight off this same seeded
144
+ // state, no play button required. Without this, personKnownFoodLines
145
+ // reads pure testimony (the mgx:knows-about facts recordTold/recordExamined
146
+ // write from the scripted tick loop), so "what food do you know about"
147
+ // stayed empty until the sim had ticked at least once, even with a carrot
148
+ // sitting in plain view. One recordExamined per character per food item
149
+ // already in its own room, written once here at open, closes that gap —
150
+ // a character standing three rooms away from every carrot on the board
151
+ // still starts knowing nothing, the honest answer for it.
152
+ {
153
+ const { rows, state } = await readWorld();
154
+ for (const character of characters) {
155
+ const room = state.placements.get(character)?.object ?? null;
156
+ if (!room) continue;
157
+ for (const [thing, place] of state.placements) {
158
+ if (thing === character) continue;
159
+ if (place.predicate !== "mgx:located-in" || place.object !== room) continue;
160
+ if (!objectClassChain(rows, thing).includes(MUD_STARTING_FOOD_CLASS)) continue;
161
+ await recordExamined(memoryDir, { observer: character, thing, k: 0, epoch });
162
+ }
163
+ }
164
+ }
165
+
134
166
  const windows = {};
135
167
  for (const character of characters) {
136
168
  // Deliberately per-closure, never on a shared object: a window's own
@@ -25,13 +25,25 @@
25
25
  // them apart at a glance, which is exactly what eleven flat bags made
26
26
  // impossible.
27
27
  //
28
- // Two more members show up on some pages, by convention rather than by
28
+ // Three more members show up on some pages, by convention rather than by
29
29
  // anything this function returns: `tmct.ready` is the page's own boot
30
- // promise (chat, ingest), and `tmct.lastSave` is its last background-save
31
- // record, `{at, ms}` (chat, adventure). Neither can be threaded through
32
- // `publishTmctSurface(...)` itselfboot is still running when this
33
- // function returns, so the page's own script sets each once it reaches
34
- // that point (see chat-page-viz.mjs, ingest-viz.mjs, adventure-viz.mjs).
30
+ // promise (chat, ingest), `tmct.lastSave` is its last background-save
31
+ // record, `{at, ms}` (chat, adventure), and `tmct.seed` is what the starter
32
+ // memory is doing `{state, facts}` through "loading", "indexing", "ready",
33
+ // "failed" (with `error`) and "skipped" (chat, ingest, research).
34
+ //
35
+ // `tmct.seed` is deliberately not the same question as `tmct.ready`. Boot
36
+ // finishing and the memory arriving are separate events, and a page whose
37
+ // seed failed still boots, still opens, and still answers — from a smaller
38
+ // store, which is the open-world assumption behaving correctly rather than a
39
+ // degraded mode. What the seed record adds is the ability to SAY so: a pill
40
+ // that reports real load progress, and a test that can tell a failed download
41
+ // apart from a store that is empty on purpose. Both look like zero facts.
42
+ //
43
+ // None can be threaded through `publishTmctSurface(...)` itself — boot is
44
+ // still running when this function returns, so the page's own script sets
45
+ // each once it reaches that point (see chat-page-viz.mjs, ingest-viz.mjs,
46
+ // adventure-viz.mjs).
35
47
  // Each page's older bare global — window.tmctChatReady, tmctIngestReady,
36
48
  // tmctChatLastSave, tmctAdventureLastSave — keeps working unchanged;
37
49
  // `tmct.ready` / `tmct.lastSave` just reach the same value under the one
@@ -1,5 +1,11 @@
1
1
  // tmct_ask — a plain-English structural question answered from the graph in one
2
2
  // mechanical, zero-model-call round-trip. See src/domain/ask.mjs.
3
+ //
4
+ // SYNCHRONOUS, and callers rely on it: the browser's code explorer imports this
5
+ // function directly and reads the envelope off the return, twice per relation
6
+ // kind, to build its sidebar. Anything that has to await — the memory fallback a
7
+ // cold surface supplies — composes AROUND this at dispatch, never inside it.
8
+ // See askWithMemoryFallback in ../server.mjs.
3
9
 
4
10
  import { ask } from "../../domain/ask.mjs";
5
11
  import { requiredArg, toolResult } from "./kit.mjs";
@@ -9,11 +15,18 @@ import { requiredArg, toolResult } from "./kit.mjs";
9
15
  * that still read that string split it on the one constant rather than their own copy. */
10
16
  export const ASK_ENVELOPE_DELIM = "\n\n---tmct_ask---\n";
11
17
 
12
- export function tmct_ask(args, { graph }) {
13
- const { content, tmct_ask: envelope } = ask(graph, requiredArg(args, "query"));
18
+ /** One answer + envelope as a tool result. Shared with the memory fallback, so
19
+ * both spellings of an answered ask carry the envelope in the same three
20
+ * places (prose, structured data, and the in-band string). */
21
+ export function askToolResult(content, envelope) {
14
22
  return toolResult({
15
23
  content,
16
24
  data: envelope,
17
25
  text: `${content}${ASK_ENVELOPE_DELIM}${JSON.stringify(envelope, null, 2)}`,
18
26
  });
19
27
  }
28
+
29
+ export function tmct_ask(args, { graph }) {
30
+ const { content, tmct_ask: envelope } = ask(graph, requiredArg(args, "query"));
31
+ return askToolResult(content, envelope);
32
+ }
@@ -23,6 +23,7 @@ import { ask } from "../domain/ask.mjs";
23
23
  import { createGraphService } from "../adapters/providers/graph-service.mjs";
24
24
  import { loadGraph } from "./graph-load.mjs";
25
25
  import { HANDLERS } from "./handlers/index.mjs";
26
+ import { askToolResult } from "./handlers/tmct-ask.mjs";
26
27
  import { isToolResult } from "./handlers/kit.mjs";
27
28
  import { setDefaultNlpAdapter } from "../domain/interpret/nlp-registry.mjs";
28
29
  import { setConstructionBanks } from "../domain/interpret/strategies/constructions.mjs";
@@ -50,7 +51,7 @@ export const TOOLS = HOT_TOOLS.map(({ name, agentDescription, inputSchema }) =>
50
51
  inputSchema,
51
52
  }));
52
53
 
53
- async function runHandler(name, args, { config, source = defaultSource, tel = null, ingest = null, memoryBackend = null, graph: suppliedGraph = null } = {}) {
54
+ async function runHandler(name, args, { config, source = defaultSource, tel = null, ingest = null, memoryBackend = null, factLookup = null, graph: suppliedGraph = null } = {}) {
54
55
  // Reject an unknown tool BEFORE touching the graph — an unknown name never
55
56
  // triggers a load. hasOwn, so an inherited name ("constructor", "toString")
56
57
  // is unknown rather than a callable found on the prototype chain.
@@ -78,7 +79,35 @@ async function runHandler(name, args, { config, source = defaultSource, tel = nu
78
79
  // isn't there.
79
80
  const repoRoot = config?.graphFile ? dirname(dirname(config.graphFile)) : null;
80
81
  const svc = createGraphService(graph, { sourceAccess: Boolean(repoRoot), repoRoot, readFile, tel, ask });
81
- return handle(args, { graph, svc, config, repoRoot, memoryBackend });
82
+ const out = handle(args, { graph, svc, config, repoRoot, memoryBackend });
83
+ return name === "tmct_ask" ? askWithMemoryFallback(out, args, factLookup) : out;
84
+ }
85
+
86
+ /** Offer a graph miss to the caller's own memory reader before the miss stands.
87
+ * `factLookup` is a seam of the same kind as `ingest`: the conversational
88
+ * store's vocabulary reader lives in the SERVICE layer, which this layer sits
89
+ * under and must not import, so a cold caller holding a repo's memory hands it
90
+ * in. A caller that supplies none keeps the graph-only answer, byte for byte.
91
+ *
92
+ * Composed HERE rather than inside the handler because the handler is a
93
+ * synchronous entry the browser's code explorer calls directly — an `async`
94
+ * handler hands that caller a promise whose `.data` is undefined, and its
95
+ * sidebar silently draws nothing. Dispatch already awaits, so the awaiting
96
+ * belongs here. */
97
+ async function askWithMemoryFallback(out, args, factLookup) {
98
+ const envelope = out?.data;
99
+ if (!envelope?.miss || typeof factLookup !== "function") return out;
100
+ let fromMemory = null;
101
+ try { fromMemory = await factLookup(String(args?.query ?? ""), envelope); } catch { fromMemory = null; }
102
+ if (!fromMemory?.text) return out;
103
+ // A reader hit flagged `miss` is the same refusal in better words, so the
104
+ // envelope keeps its miss and only the prose improves.
105
+ const content = fromMemory.replace ? fromMemory.text : `${out.content}\n${fromMemory.text}`;
106
+ return askToolResult(content, {
107
+ ...envelope,
108
+ miss: Boolean(fromMemory.miss),
109
+ matchedVia: fromMemory.miss ? envelope.matchedVia : "memory",
110
+ });
82
111
  }
83
112
 
84
113
  /** The caller-facing string for one tool call. A handler that returns a structured