@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.
- package/README.md +21 -0
- package/bin/tmct.mjs +63 -2
- package/package.json +3 -2
- package/src/adapters/memory/core.mjs +23 -0
- package/src/domain/ask-vocab.mjs +19 -0
- package/src/domain/ask.mjs +8 -1
- package/src/domain/interpret/strategies/keywords.mjs +30 -1
- package/src/domain/memory/capability.mjs +15 -11
- package/src/domain/router/drive.mjs +36 -17
- package/src/domain/router/resolver.mjs +63 -17
- package/src/domain/spider-fly-world.mjs +2 -2
- package/src/domain/sprite-templates.mjs +19 -7
- package/src/domain/syllogise.mjs +16 -6
- package/src/domain/town-square-world.mjs +1 -1
- package/src/services/adventure.mjs +8 -1
- package/src/services/chat-page-viz.mjs +118 -23
- package/src/services/chat-session.mjs +60 -10
- package/src/services/chat.mjs +228 -24
- package/src/services/extract-facts.mjs +47 -7
- package/src/services/ingest-viz.mjs +108 -25
- package/src/services/ledger-viz.mjs +4 -2
- package/src/services/memory-panel-viz.mjs +44 -0
- package/src/services/mud-viz.mjs +17 -0
- package/src/services/mudiii-scene.mjs +271 -24
- package/src/services/mudiii-turn.mjs +65 -9
- package/src/services/mudiii-viz.mjs +302 -125
- package/src/services/plan-viz.mjs +23 -2
- package/src/services/predator-prey.mjs +82 -33
- package/src/services/research-viz.mjs +12 -19
- package/src/services/spider-fly-turn.mjs +7 -1
- package/src/services/spider-fly-viz.mjs +10 -3
- package/src/services/viz-ticker.mjs +15 -2
- package/src/surfaces/http/server-http.mjs +90 -13
- package/src/surfaces/web/memory-ask-browser.bundle.js +125 -125
- package/src/surfaces/web/mud-browser-entry.mjs +33 -1
- package/src/surfaces/web/tmct-surface.mjs +18 -6
- package/src/tools/handlers/tmct-ask.mjs +15 -2
- 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
|
-
//
|
|
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),
|
|
31
|
-
// record, `{at, ms}` (chat, adventure).
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
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
|
-
|
|
13
|
-
|
|
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
|
+
}
|
package/src/tools/server.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|