@polycode-projects/the-mechanical-code-talker 4.0.1 → 4.1.1
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 +2 -1
- package/corpus/sprites/src/sprite-facts.jsonl +375 -8
- package/package.json +1 -1
- package/src/adapters/memory/core.mjs +20 -0
- package/src/domain/ask-vocab.mjs +71 -0
- package/src/domain/ask.mjs +168 -0
- package/src/domain/game-config.mjs +11 -0
- package/src/domain/mud-facts.mjs +15 -0
- package/src/domain/router/drive.mjs +35 -9
- package/src/domain/router/registry.mjs +24 -4
- package/src/domain/router/resolver.mjs +102 -40
- package/src/domain/scene-compose.mjs +117 -0
- package/src/domain/spider-fly-world.mjs +36 -0
- package/src/domain/sprite-facts.mjs +0 -0
- package/src/domain/sprite-request.mjs +156 -0
- package/src/domain/sprite-templates.mjs +161 -14
- package/src/services/adventure-editor.mjs +8 -14
- package/src/services/adventure-viz.mjs +119 -150
- package/src/services/adventure.mjs +97 -35
- package/src/services/chat-page-viz.mjs +64 -48
- package/src/services/chat.mjs +102 -34
- package/src/services/code-explorer-viz.mjs +52 -50
- package/src/services/ingest-viz.mjs +32 -74
- package/src/services/ledger-viz.mjs +87 -70
- package/src/services/memory-panel-viz.mjs +38 -0
- package/src/services/mud-editor.mjs +10 -15
- package/src/services/mud-turn.mjs +6 -6
- package/src/services/mud-viz.mjs +119 -225
- package/src/services/p2p-room.mjs +90 -23
- package/src/services/plan-pddl.mjs +3 -1
- package/src/services/plan-viz.mjs +13 -12
- package/src/services/research-viz.mjs +25 -67
- package/src/services/spider-fly-turn.mjs +14 -22
- package/src/services/spider-fly-viz.mjs +97 -136
- package/src/services/spider-fly.mjs +69 -11
- package/src/services/sprite-catalog-viz.mjs +274 -224
- package/src/services/viz-boot.mjs +71 -0
- package/src/services/viz-room-graph.mjs +203 -0
- package/src/services/viz-theme.mjs +75 -1
- package/src/services/viz-ticker.mjs +22 -0
- package/src/surfaces/web/adventure-browser-entry.mjs +62 -47
- package/src/surfaces/web/chat-browser-entry.mjs +51 -107
- package/src/surfaces/web/code-explorer-browser-entry.mjs +192 -35
- package/src/surfaces/web/engine-surface.mjs +82 -0
- package/src/surfaces/web/ingest-browser-entry.mjs +16 -17
- package/src/surfaces/web/ledger-browser-entry.mjs +24 -56
- package/src/surfaces/web/memory-ask-browser-entry.mjs +55 -13
- package/src/surfaces/web/memory-ask-browser.bundle.js +128 -125
- package/src/surfaces/web/memory-stats.mjs +11 -0
- package/src/surfaces/web/mud-browser-entry.mjs +70 -49
- package/src/surfaces/web/plan-browser-entry.mjs +39 -50
- package/src/surfaces/web/research-browser-entry.mjs +48 -46
- package/src/surfaces/web/spider-fly-browser-entry.mjs +76 -40
- package/src/surfaces/web/sprites-browser-entry.mjs +28 -32
- package/src/surfaces/web/tmct-surface.mjs +147 -0
- package/src/surfaces/web/turn-session.mjs +124 -0
- package/src/tools/definitions.mjs +30 -0
- package/src/tools/handlers/index.mjs +6 -3
- package/src/tools/handlers/kit.mjs +19 -2
- package/src/tools/handlers/tmct-ask.mjs +11 -6
- package/src/tools/handlers/tmct-ingest.mjs +5 -1
- package/src/tools/handlers/tmct-related.mjs +4 -4
- package/src/tools/handlers/tmct-sprite.mjs +147 -0
- package/src/tools/memory-fallthrough.mjs +9 -2
- package/src/tools/server.mjs +37 -6
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
// doesn't do: it reads the checked-in chat-dock engine bundle.
|
|
16
16
|
|
|
17
17
|
import { loadMemory, readFactRows, findContradictions, normFactTerm } from "../adapters/memory/core.mjs";
|
|
18
|
-
import { THEME_TOKENS_CSS, MONO_STACK, escapeHtml, embedJson } from "./viz-theme.mjs";
|
|
18
|
+
import { THEME_TOKENS_CSS, MONO_STACK, escapeHtml, embedJson, countLabel, TOKENS } from "./viz-theme.mjs";
|
|
19
19
|
import { createTicker, prefersReducedMotion } from "./viz-ticker.mjs";
|
|
20
|
+
import { bfsLevels } from "../domain/planning.mjs";
|
|
21
|
+
import { pluralOf } from "../domain/inflect.mjs";
|
|
20
22
|
import { readFile } from "node:fs/promises";
|
|
21
23
|
import { fileURLToPath } from "node:url";
|
|
22
24
|
import { dirname, join } from "node:path";
|
|
@@ -33,23 +35,33 @@ const DASH_SANS_STACK = `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
|
33
35
|
// themes — a deliberate departure from THEME_TOKENS_CSS's light/dark switch,
|
|
34
36
|
// which every other generated page still follows. --taught/--corpus/--entail/
|
|
35
37
|
// --alert stay the shared semantic provenance tokens; here they're pinned to
|
|
36
|
-
// their dark-theme-friendly values
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
|
|
38
|
+
// their dark-theme-friendly values, read straight off viz-theme.mjs's own
|
|
39
|
+
// exported TOKENS.dark table so this page's palette can never drift from the
|
|
40
|
+
// one every other generated page's dark mode uses. The tier alphas (.35/.65/1)
|
|
41
|
+
// match TOKENS's own tier scale; the four "soft" tints use higher alphas than
|
|
42
|
+
// THEME_TOKENS_CSS's shared soft tokens on purpose — this dashboard chrome
|
|
43
|
+
// wants a more visible tint against its always-dark background than the
|
|
44
|
+
// light/dark-switching pages need, so those four alphas stay page-specific.
|
|
45
|
+
function hexRgb(hex) {
|
|
46
|
+
const n = parseInt(hex.slice(1), 16);
|
|
47
|
+
return `${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}`;
|
|
48
|
+
}
|
|
49
|
+
const DASH_DARK_CHROME_CSS = (() => {
|
|
50
|
+
const t = TOKENS.dark;
|
|
51
|
+
const [taught, corpus, entail] = [hexRgb(t.taught), hexRgb(t.corpus), hexRgb(t.entail)];
|
|
52
|
+
return `
|
|
42
53
|
:root { color-scheme: dark; }
|
|
43
54
|
body {
|
|
44
|
-
--bg:
|
|
45
|
-
--taught:
|
|
46
|
-
--taught-t1: rgba(
|
|
47
|
-
--corpus-t1: rgba(
|
|
48
|
-
--entail-t1: rgba(
|
|
49
|
-
--taught-soft: rgba(
|
|
50
|
-
--entail-soft: rgba(
|
|
55
|
+
--bg: ${t.bg}; --ink: ${t.ink}; --muted: ${t.muted}; --line: ${t.line}; --card: ${t.card};
|
|
56
|
+
--taught: ${t.taught}; --corpus: ${t.corpus}; --entail: ${t.entail}; --alert: ${t.alert};
|
|
57
|
+
--taught-t1: rgba(${taught},.35); --taught-t2: rgba(${taught},.65); --taught-t3: rgba(${taught},1);
|
|
58
|
+
--corpus-t1: rgba(${corpus},.35); --corpus-t2: rgba(${corpus},.65); --corpus-t3: rgba(${corpus},1);
|
|
59
|
+
--entail-t1: rgba(${entail},.35); --entail-t2: rgba(${entail},.65); --entail-t3: rgba(${entail},1);
|
|
60
|
+
--taught-soft: rgba(${taught},.14); --corpus-soft: rgba(${corpus},.14);
|
|
61
|
+
--entail-soft: rgba(${entail},.16); --alert-soft: rgba(${hexRgb(t.alert)},.16);
|
|
51
62
|
}
|
|
52
63
|
`;
|
|
64
|
+
})();
|
|
53
65
|
|
|
54
66
|
/** Read the checked-in browser memory-ask-engine bundle
|
|
55
67
|
* (`src/surfaces/web/memory-ask-browser.bundle.js`) — the real memory-graph answer engine
|
|
@@ -453,7 +465,7 @@ function dashboardHtml(stats, { fresh = false } = {}) {
|
|
|
453
465
|
<div class="tile">
|
|
454
466
|
<span class="tile-label">facts.total</span>
|
|
455
467
|
<span class="tile-value">${total}</span>
|
|
456
|
-
<span class="tile-sub">${terms
|
|
468
|
+
<span class="tile-sub">${countLabel(terms, "term")} tracked</span>
|
|
457
469
|
</div>
|
|
458
470
|
<div class="tile tile-tier">
|
|
459
471
|
<span class="tile-label">facts.by-tier</span>
|
|
@@ -467,7 +479,7 @@ function dashboardHtml(stats, { fresh = false } = {}) {
|
|
|
467
479
|
<div class="tile${qualityCls}">
|
|
468
480
|
<span class="tile-label">data.quality</span>
|
|
469
481
|
<span class="tile-value">${s.contradictionCount || 0}</span>
|
|
470
|
-
<span class="tile-sub"
|
|
482
|
+
<span class="tile-sub">${s.contradictionCount === 1 ? "term" : pluralOf("term")} with more than one answer</span>
|
|
471
483
|
</div>
|
|
472
484
|
</div>
|
|
473
485
|
<div class="barpanels">
|
|
@@ -537,7 +549,7 @@ function sparkCaptionHtml(stats) {
|
|
|
537
549
|
* self-contained document with no external requests. Only
|
|
538
550
|
* scripts/build-demo-site.mjs, which builds the sibling bundle itself
|
|
539
551
|
* first, passes `true`. The dock's own runtime code below ALSO gates on
|
|
540
|
-
* `typeof
|
|
552
|
+
* `typeof tmct !== "undefined"` regardless — the two checks answer
|
|
541
553
|
* different questions (did this render even offer the reference; did the
|
|
542
554
|
* browser actually manage to load it), and both must hold for the live
|
|
543
555
|
* path to run. */
|
|
@@ -545,14 +557,14 @@ export function renderLedgerHtml({ rows, terms, edges, focus, contradictions, wo
|
|
|
545
557
|
const ledgerJson = embedJson({ rows: rows || [], terms: terms || [], edges: edges || [], focus: focus || null, contradictions: contradictions || [], worthALook: worthALook || null, meta: meta || { shown: 0, total: 0, truncated: false }, focusDigest: focusDigest || null, digestStructures: Array.isArray(digestStructures) ? digestStructures : [] });
|
|
546
558
|
const payloadJson = embedJson(payload || { individuals: [], objectProperties: [] });
|
|
547
559
|
const shown = meta?.shown ?? (rows || []).length;
|
|
548
|
-
const title = `tmct ledger — ${shown
|
|
560
|
+
const title = `tmct ledger — ${countLabel(shown, "fact")}${focus ? ` (focus: ${escapeHtml(focus)})` : ""}`;
|
|
549
561
|
const bundleStr = typeof memoryAskBundle === "string" ? memoryAskBundle : "";
|
|
550
562
|
const hasMemChat = bundleStr.length > 0;
|
|
551
563
|
// The placeholder is honest: the canonical exchange only when its terms are
|
|
552
564
|
// really in this payload, otherwise a real term from this graph. Left as
|
|
553
565
|
// the query-only wording even when the live bundle is offered — the dock's
|
|
554
566
|
// own script swaps it for a teach-aware placeholder the moment it confirms
|
|
555
|
-
//
|
|
567
|
+
// the live engine actually loaded (never claimed ahead of that confirmation).
|
|
556
568
|
// The example term skips anything under 3 characters — the SAME floor the
|
|
557
569
|
// dock's own miss-tips apply below — so a graph whose highest-degree term
|
|
558
570
|
// is a short stopword-shaped fragment (init:large corpora carry plenty:
|
|
@@ -563,7 +575,7 @@ export function renderLedgerHtml({ rows, terms, edges, focus, contradictions, wo
|
|
|
563
575
|
? "who is the grandfather of ishmael"
|
|
564
576
|
: (exampleTerm ? `ask the graph… e.g. what is ${exampleTerm.term}` : "ask the graph…");
|
|
565
577
|
// The paste-and-drop ingest panel plus the JSONL export ride the LIVE
|
|
566
|
-
// engine (window.
|
|
578
|
+
// engine (window.tmct): both teach into and read the dock's own
|
|
567
579
|
// session store, so they appear only where that bundle is offered — the
|
|
568
580
|
// demo site, never the CLI's self-contained tmct viz page.
|
|
569
581
|
const ingestHtml = ledgerBundleAvailable
|
|
@@ -833,6 +845,11 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
833
845
|
// escapeHtml/pct by their real names) resolve without a second copy.
|
|
834
846
|
const escapeHtml = esc;
|
|
835
847
|
const pct = ${pct.toString()};
|
|
848
|
+
// pluralOf underlies countLabel's own default-parameter lookup — spliced
|
|
849
|
+
// ahead of it so that lookup resolves the same way it does node-side.
|
|
850
|
+
const pluralOf = ${pluralOf.toString()};
|
|
851
|
+
const countLabel = ${countLabel.toString()};
|
|
852
|
+
const bfsLevels = ${bfsLevels.toString()};
|
|
836
853
|
const tierTileHtml = ${tierTileHtml.toString()};
|
|
837
854
|
const microbarsHtml = ${microbarsHtml.toString()};
|
|
838
855
|
const dashboardHtml = ${dashboardHtml.toString()};
|
|
@@ -871,14 +888,13 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
871
888
|
// exists it points at that session's store, which teach/research grow in
|
|
872
889
|
// place, so a digest of a just-taught term reads the fresh facts.
|
|
873
890
|
let getLivePayload = () => PAYLOAD;
|
|
874
|
-
//
|
|
875
|
-
// ledger's
|
|
876
|
-
//
|
|
877
|
-
//
|
|
891
|
+
// The browser digest helper, from whichever engine this page loaded — the
|
|
892
|
+
// demo ledger's live one or the committed query-only one the CLI viz page
|
|
893
|
+
// inlines. Both publish it in the same place now, so there is one lookup
|
|
894
|
+
// rather than two. Null before either loads, and the focus card then keeps
|
|
895
|
+
// whatever server-computed digest it shipped.
|
|
878
896
|
const digestHelper = () =>
|
|
879
|
-
(typeof
|
|
880
|
-
|| (typeof tmctMemoryAsk !== "undefined" && tmctMemoryAsk && tmctMemoryAsk.digestTermFromPayloadBrowser)
|
|
881
|
-
|| null;
|
|
897
|
+
(typeof tmct !== "undefined" && tmct.page && tmct.page.digestTermFromPayloadBrowser) || null;
|
|
882
898
|
// The digest for one term, computed live in the browser from the embedded
|
|
883
899
|
// structure table, or null when no structures were embedded, no engine has
|
|
884
900
|
// loaded, or the term holds nothing to compose.
|
|
@@ -965,7 +981,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
965
981
|
: "";
|
|
966
982
|
let html = '<div class="focuscard"><div class="term">' + esc(focus) + "</div>" +
|
|
967
983
|
'<div class="klass">' + (klass ? esc(klass.phrase + " " + klass.o) : "no class recorded") + "</div>" +
|
|
968
|
-
'<div class="stats">' + all.length + "
|
|
984
|
+
'<div class="stats">' + countLabel(all.length, "fact") + " · " + countLabel(srcs.size, "source") +
|
|
969
985
|
(dates.length ? " · first " + esc(dates[0].slice(0, 10)) + " · last " + esc(dates[dates.length - 1].slice(0, 10)) : "") + "</div>" + digestHtml + "</div>";
|
|
970
986
|
const bracketed = new Set();
|
|
971
987
|
for (const fam of FAMS) {
|
|
@@ -1028,13 +1044,16 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1028
1044
|
if (!focus) return;
|
|
1029
1045
|
const color = { taught: cssVar("--taught"), corpus: cssVar("--corpus"), entailed: cssVar("--entail"), entail: cssVar("--entail") };
|
|
1030
1046
|
const lineC = cssVar("--line"), inkC = cssVar("--ink");
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1047
|
+
const neighborsOf = (term) => {
|
|
1048
|
+
const out = [];
|
|
1049
|
+
for (const e of LEDGER.edges) {
|
|
1050
|
+
if (e.s === term) out.push(e.o);
|
|
1051
|
+
else if (e.o === term) out.push(e.s);
|
|
1052
|
+
}
|
|
1053
|
+
return out;
|
|
1054
|
+
};
|
|
1055
|
+
const [hop1Level, hop2Level] = bfsLevels(focus, neighborsOf, { maxDepth: 2 });
|
|
1056
|
+
const hop1 = new Set(hop1Level || []), hop2 = new Set(hop2Level || []);
|
|
1038
1057
|
const cx = w / 2, cy = h / 2, r1 = 42, r2 = 68;
|
|
1039
1058
|
const pos = { [focus]: { x: cx, y: cy } };
|
|
1040
1059
|
const place = (set, r) => { const a = [...set].sort(); a.forEach((t, i) => { const ang = (2 * Math.PI * i) / a.length - Math.PI / 2; pos[t] = { x: cx + r * Math.cos(ang), y: cy + r * Math.sin(ang) }; }); };
|
|
@@ -1118,9 +1137,9 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1118
1137
|
}
|
|
1119
1138
|
|
|
1120
1139
|
// ---- the chat dock: LIVE teach+ask over the sibling ledger-browser.bundle.js
|
|
1121
|
-
// (window.
|
|
1122
|
-
// loaded; falls back to the read-only
|
|
1123
|
-
//
|
|
1140
|
+
// (window.tmct) when the demo build offered it AND it actually
|
|
1141
|
+
// loaded; falls back to the read-only engine below, which publishes the
|
|
1142
|
+
// same surface as a stand-in and says so through tmct.fallback.
|
|
1124
1143
|
// bin/tmct.mjs's own \`tmct viz\` output never offers the live bundle
|
|
1125
1144
|
// (renderLedgerHtml's own ledgerBundleAvailable defaults false there), so
|
|
1126
1145
|
// this branch is simply never reachable on a CLI-generated page.
|
|
@@ -1128,7 +1147,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1128
1147
|
const createTicker = ${createTicker.toString()};
|
|
1129
1148
|
const prefersReducedMotion = ${prefersReducedMotion.toString()};
|
|
1130
1149
|
const chatForm = el("chatform");
|
|
1131
|
-
if (chatForm && typeof
|
|
1150
|
+
if (chatForm && typeof tmct !== "undefined" && !tmct.fallback) {
|
|
1132
1151
|
const log = el("chatlog");
|
|
1133
1152
|
const chatqEl = el("chatq");
|
|
1134
1153
|
chatqEl.placeholder = 'ask or teach the graph\\u2026 e.g. "blue is a peg"';
|
|
@@ -1163,7 +1182,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1163
1182
|
import("./vendor/wink.js"),
|
|
1164
1183
|
winkTimeout(WINK_LOAD_TIMEOUT_MS, "wink vendor asset load timed out"),
|
|
1165
1184
|
]);
|
|
1166
|
-
|
|
1185
|
+
tmct.page.registerWinkModel(() => ({ winkNLP: mod.winkNLP, model: mod.model }));
|
|
1167
1186
|
} catch (err) {
|
|
1168
1187
|
// eslint-disable-next-line no-console
|
|
1169
1188
|
console.warn("tmct ledger: the wink vendor asset failed to load, continuing without the lemma/POS tier", err);
|
|
@@ -1176,7 +1195,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1176
1195
|
async function ensureSession() {
|
|
1177
1196
|
if (session) return session;
|
|
1178
1197
|
await tryLoadWink();
|
|
1179
|
-
session = await
|
|
1198
|
+
session = await tmct.open({ seedPayload: PAYLOAD });
|
|
1180
1199
|
// From here the live store is the source of truth for the digest, so a
|
|
1181
1200
|
// digest of a term taught this session reads its fresh facts — the store
|
|
1182
1201
|
// grows in place, so this one closure stays current.
|
|
@@ -1194,15 +1213,15 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1194
1213
|
chatqEl.disabled = true;
|
|
1195
1214
|
withLock(async () => {
|
|
1196
1215
|
try {
|
|
1197
|
-
const
|
|
1198
|
-
const result = await
|
|
1216
|
+
const live = await ensureSession();
|
|
1217
|
+
const result = await tmct.turn(q);
|
|
1199
1218
|
const record = result.record;
|
|
1200
1219
|
const taught = !!record && record.miss === false && (record.via === "assert" || record.via === "retract");
|
|
1201
1220
|
const body = esc(result.answer).replace(/\\n/g, "<br>");
|
|
1202
1221
|
pending.className = "a" + (taught ? " taught" : (record && record.miss ? " miss" : ""));
|
|
1203
1222
|
pending.innerHTML = taught ? '<span class="tag">taught</span>' + body : body;
|
|
1204
1223
|
if (taught) {
|
|
1205
|
-
const fresh =
|
|
1224
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(live.memoryDir.payload, {});
|
|
1206
1225
|
applyLedgerData(fresh);
|
|
1207
1226
|
} else if (!(record && record.miss)) {
|
|
1208
1227
|
// Only a genuine answer (never a miss) tries to resolve a
|
|
@@ -1211,7 +1230,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1211
1230
|
// ordinary English word (e.g. "run", if the graph happens to
|
|
1212
1231
|
// hold it), and resolveAnsweredTerm has no way to tell that
|
|
1213
1232
|
// apart from the term genuinely being discussed.
|
|
1214
|
-
const hit = resolveAnsweredTerm(result.answer, q, LEDGER.terms,
|
|
1233
|
+
const hit = resolveAnsweredTerm(result.answer, q, LEDGER.terms, tmct.page.normFactTerm);
|
|
1215
1234
|
if (hit) refocusWithLabel(hit, q);
|
|
1216
1235
|
}
|
|
1217
1236
|
} catch {
|
|
@@ -1226,7 +1245,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1226
1245
|
|
|
1227
1246
|
// ---- ingest + export: bring your own text, take the graph away ---------
|
|
1228
1247
|
// The ingest panel runs pasted/dropped/browsed text through the SAME dock
|
|
1229
|
-
// session, one sentence at a time (
|
|
1248
|
+
// session, one sentence at a time (tmct.page.splitSentences, then
|
|
1230
1249
|
// s.turn), keeping only the sentences the recognizer grounds — then
|
|
1231
1250
|
// re-derives the whole ledger so the new facts can be examined in place.
|
|
1232
1251
|
// Export serializes that same session store to canonical JSONL.
|
|
@@ -1262,16 +1281,16 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1262
1281
|
withLock(async () => {
|
|
1263
1282
|
try {
|
|
1264
1283
|
const s = await ensureSession();
|
|
1265
|
-
const sentences =
|
|
1284
|
+
const sentences = tmct.page.splitSentences(text);
|
|
1266
1285
|
let grounded = 0;
|
|
1267
1286
|
for (const sentence of sentences) {
|
|
1268
|
-
const r = await
|
|
1287
|
+
const r = await tmct.turn(sentence);
|
|
1269
1288
|
if (r.record && r.record.miss === false && r.record.via === "assert") grounded += 1;
|
|
1270
1289
|
}
|
|
1271
|
-
const fresh =
|
|
1290
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(s.memoryDir.payload, {});
|
|
1272
1291
|
applyLedgerData(fresh);
|
|
1273
1292
|
const skipped = sentences.length - grounded;
|
|
1274
|
-
statusEl.textContent = sentences.length
|
|
1293
|
+
statusEl.textContent = countLabel(sentences.length, "sentence")
|
|
1275
1294
|
+ ", " + grounded + " added" + (skipped ? ", " + skipped + " skipped" : "");
|
|
1276
1295
|
} catch {
|
|
1277
1296
|
statusEl.textContent = "something went wrong ingesting that";
|
|
@@ -1288,7 +1307,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1288
1307
|
withLock(async () => {
|
|
1289
1308
|
try {
|
|
1290
1309
|
const s = await ensureSession();
|
|
1291
|
-
const jsonl = await
|
|
1310
|
+
const jsonl = await tmct.page.exportFactsJsonl(s.memoryDir);
|
|
1292
1311
|
const blob = new Blob([jsonl], { type: "application/x-ndjson" });
|
|
1293
1312
|
const url = URL.createObjectURL(blob);
|
|
1294
1313
|
const link = document.createElement("a");
|
|
@@ -1323,7 +1342,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1323
1342
|
researchPlayBtn.setAttribute("aria-pressed", String(st.playing));
|
|
1324
1343
|
researchStatusEl.textContent = !researchQueue ? ""
|
|
1325
1344
|
: researchQueue.complete
|
|
1326
|
-
? 'research "' + researchQueue.topic + '" complete \\u2014 ' + researchQueue.done.length
|
|
1345
|
+
? 'research "' + researchQueue.topic + '" complete \\u2014 ' + countLabel(researchQueue.done.length, "topic")
|
|
1327
1346
|
: researchQueue.done.length + " done \\u00b7 " + researchQueue.pending.length + " queued";
|
|
1328
1347
|
}
|
|
1329
1348
|
|
|
@@ -1334,8 +1353,8 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1334
1353
|
const pending = addLine("a pending", "researching\\u2026");
|
|
1335
1354
|
return withLock(async () => {
|
|
1336
1355
|
try {
|
|
1337
|
-
const
|
|
1338
|
-
const result = await
|
|
1356
|
+
const live = await ensureSession();
|
|
1357
|
+
const result = await tmct.turn(q);
|
|
1339
1358
|
const missed = !result.record || Boolean(result.record.miss);
|
|
1340
1359
|
pending.className = "a" + (missed ? " miss" : "");
|
|
1341
1360
|
pending.innerHTML = esc(result.answer).replace(/\\n/g, "<br>");
|
|
@@ -1343,7 +1362,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1343
1362
|
researchQueue = result.research;
|
|
1344
1363
|
renderResearchControls();
|
|
1345
1364
|
if (!missed) {
|
|
1346
|
-
const fresh =
|
|
1365
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(live.memoryDir.payload, {});
|
|
1347
1366
|
applyLedgerData(fresh);
|
|
1348
1367
|
}
|
|
1349
1368
|
}
|
|
@@ -1386,9 +1405,11 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1386
1405
|
else researchTicker.play();
|
|
1387
1406
|
});
|
|
1388
1407
|
}
|
|
1389
|
-
} else if (chatForm && typeof
|
|
1390
|
-
|
|
1391
|
-
|
|
1408
|
+
} else if (chatForm && typeof tmct !== "undefined") {
|
|
1409
|
+
// The query-only dock. One session over the page's own embedded payload,
|
|
1410
|
+
// and every line goes to tmct.ask — the cascade that used to be chained
|
|
1411
|
+
// here by hand now lives behind that one call.
|
|
1412
|
+
const opened = tmct.open({ payload: PAYLOAD });
|
|
1392
1413
|
const log = el("chatlog");
|
|
1393
1414
|
const addLine = (cls, html) => {
|
|
1394
1415
|
const d = document.createElement("div");
|
|
@@ -1403,20 +1424,16 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1403
1424
|
input.value = "";
|
|
1404
1425
|
addLine("u", esc(q));
|
|
1405
1426
|
(async () => {
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
try { fact = await tmctMemoryAsk.factReadBack(memHandle, q, null, true, null); } catch { fact = null; }
|
|
1413
|
-
}
|
|
1414
|
-
if (fact && fact.text) {
|
|
1415
|
-
addLine("a", esc(fact.text).replace(/\\n/g, "<br>"));
|
|
1427
|
+
await opened;
|
|
1428
|
+
let answer = null;
|
|
1429
|
+
let data = null;
|
|
1430
|
+
try { ({ answer, data } = await tmct.ask(q)); } catch { answer = null; data = null; }
|
|
1431
|
+
if (answer) {
|
|
1432
|
+
addLine("a", esc(answer).replace(/\\n/g, "<br>"));
|
|
1416
1433
|
// Same formatting contract as chat's withGoalLine: capitalized,
|
|
1417
1434
|
// full-stop-terminated, rendered only when the engine deduced one.
|
|
1418
|
-
if (
|
|
1419
|
-
const hit = resolveAnsweredTerm(
|
|
1435
|
+
if (data && data.goal) addLine("a goal", "Goal (inferred): " + esc(data.goal.charAt(0).toUpperCase() + data.goal.slice(1)) + ".");
|
|
1436
|
+
const hit = resolveAnsweredTerm(answer, q, LEDGER.terms, tmct.page.normFactTerm);
|
|
1420
1437
|
if (hit) refocusWithLabel(hit, q);
|
|
1421
1438
|
} else {
|
|
1422
1439
|
const tips = LEDGER.terms.filter((t) => t.term.length >= 3).slice(0, 2)
|
|
@@ -181,3 +181,41 @@ export function renderStatsPanelInto(panelEl, stats, opts) {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
/** The boot statusline while the big engine assets stream in — byte-identical
|
|
186
|
+
* across research-viz.mjs and ingest-viz.mjs, each commenting that it uses
|
|
187
|
+
* "the same aggregator" as the other. `parts` is an array of { loaded, total }
|
|
188
|
+
* byte counts (total 0 when a response carried no Content-Length); with no
|
|
189
|
+
* usable total the line shows loaded bytes alone rather than inventing a
|
|
190
|
+
* denominator. Self-contained, `.toString()`-splice safe. */
|
|
191
|
+
export function loadProgressLine(parts) {
|
|
192
|
+
const mb = (n) => (n / 1048576).toFixed(1);
|
|
193
|
+
let loaded = 0;
|
|
194
|
+
let total = 0;
|
|
195
|
+
let totalKnown = true;
|
|
196
|
+
for (const p of parts || []) {
|
|
197
|
+
loaded += (p && p.loaded) || 0;
|
|
198
|
+
if (p && p.total > 0) total += p.total;
|
|
199
|
+
else totalKnown = false;
|
|
200
|
+
}
|
|
201
|
+
return totalKnown && total > 0
|
|
202
|
+
? "loading the engine… " + mb(loaded) + " MB / " + mb(total) + " MB"
|
|
203
|
+
: "loading the engine… " + mb(loaded) + " MB";
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** One learned/grounded fact as its three canonical cells, plus whichever of
|
|
207
|
+
* the two per-page provenance fields the caller's fact objects carry: `source`
|
|
208
|
+
* (research.html's source key, e.g. "taught"/"seed:conceptnet" — used for the
|
|
209
|
+
* tone dot) and `provenance` (ingest.html's raw ' | '-joined tag string —
|
|
210
|
+
* shown verbatim). Both default to "" when the fact object doesn't carry
|
|
211
|
+
* that field, so either page reads back only the one it populated. Pure,
|
|
212
|
+
* `.toString()`-splice safe. */
|
|
213
|
+
export function factTripleParts(fact) {
|
|
214
|
+
return {
|
|
215
|
+
subject: String((fact && fact.subject) || ""),
|
|
216
|
+
predicate: String((fact && fact.predicate) || ""),
|
|
217
|
+
object: String((fact && fact.object) || ""),
|
|
218
|
+
source: String((fact && fact.source) || ""),
|
|
219
|
+
provenance: String((fact && fact.provenance) || ""),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
@@ -12,11 +12,14 @@
|
|
|
12
12
|
// point is "change what this world is made of" has to speak the vocabulary the
|
|
13
13
|
// world is actually written in, and neither table is a superset of the other.
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
// adventure-editor.mjs and the
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
15
|
+
// Every export here but wordBeforeCursor is written locally, and is
|
|
16
|
+
// `.toString()`-splice-safe, the same discipline adventure-editor.mjs and the
|
|
17
|
+
// *-viz render-glue functions hold — mud-viz.mjs splices these straight into
|
|
18
|
+
// mud.html's inline script, which captures a function's own source text and
|
|
19
|
+
// nothing it closes over. A splice-safe function carries its whole dependency
|
|
20
|
+
// graph inside its own body. wordBeforeCursor is a re-export of viz-theme.mjs's
|
|
21
|
+
// shared implementation, itself self-contained and splice-safe the same way —
|
|
22
|
+
// this module no longer keeps its own copy of it.
|
|
20
23
|
//
|
|
21
24
|
// Two predicate families sync differently, for the reason adventure-editor.mjs's
|
|
22
25
|
// own header sets out:
|
|
@@ -34,6 +37,8 @@
|
|
|
34
37
|
// that fails to parse this keystroke must never be read as "this fact is
|
|
35
38
|
// gone".
|
|
36
39
|
|
|
40
|
+
export { wordBeforeCursor } from "./viz-theme.mjs";
|
|
41
|
+
|
|
37
42
|
const PLACEMENT_KIND = "placement";
|
|
38
43
|
const OPENNESS_KIND = "openness";
|
|
39
44
|
const MASS_KIND = "mass";
|
|
@@ -301,13 +306,3 @@ export function planMudEditorSync(rows, state, triples) {
|
|
|
301
306
|
}
|
|
302
307
|
return { toAppend, toRemoveIds };
|
|
303
308
|
}
|
|
304
|
-
|
|
305
|
-
/** The word immediately before `cursorPos` in `text` — a run of letters, digits
|
|
306
|
-
* and hyphens, the shape this vocabulary's own terms take ("underground-space",
|
|
307
|
-
* "mole-1"). Empty when the cursor sits after whitespace or punctuation with no
|
|
308
|
-
* word directly behind it. Pure. */
|
|
309
|
-
export function wordBeforeCursor(text, cursorPos) {
|
|
310
|
-
const head = String(text || "").slice(0, cursorPos);
|
|
311
|
-
const m = head.match(/[A-Za-z][A-Za-z0-9-]*$/);
|
|
312
|
-
return m ? m[0].toLowerCase() : "";
|
|
313
|
-
}
|
|
@@ -50,10 +50,12 @@ import { fnv1a32 } from "../domain/hash.mjs";
|
|
|
50
50
|
import { bfsLevels } from "../domain/planning.mjs";
|
|
51
51
|
import { loadMemory, readFactRows } from "../adapters/memory/core.mjs";
|
|
52
52
|
import { DEFAULT_GAME_CONFIG, mudMassDrainPerTurn } from "../domain/game-config.mjs";
|
|
53
|
+
import { predatorSubjects } from "../domain/mud-facts.mjs";
|
|
53
54
|
import {
|
|
54
55
|
foldWorldState, worldActionRows, runWorldCommand, recordTold, recordExamined,
|
|
55
56
|
recordMassDrain, personKnowledgeLines, objectClassChain, diggableDirections,
|
|
56
57
|
isOutOfPlay, outOfPlayReasonOf, outOfPlayPhrase, massDrainPerTurnOf,
|
|
58
|
+
parseSnapshotSubject,
|
|
57
59
|
} from "./adventure.mjs";
|
|
58
60
|
|
|
59
61
|
const FOOD_CLASS = "food";
|
|
@@ -61,7 +63,6 @@ const LATERAL_DIRECTIONS = ["north", "south", "east", "west"];
|
|
|
61
63
|
// Deep enough to cross a whole burrow without letting one character's
|
|
62
64
|
// pathfinder walk a whole grown world every tick.
|
|
63
65
|
const WALK_SEARCH_DEPTH = 8;
|
|
64
|
-
const SNAPSHOT_RE = /^(.+)@turn(\d+)$/;
|
|
65
66
|
|
|
66
67
|
const EXIT_TOWARD_FOOD_CHANCE = 0.5;
|
|
67
68
|
const EDGE_FOLLOW_DIG_CHANCE = 0.25;
|
|
@@ -208,8 +209,8 @@ function roomsStoodIn(rows, character) {
|
|
|
208
209
|
const visited = new Set();
|
|
209
210
|
for (const row of worldActionRows(rows)) {
|
|
210
211
|
if (row.predicate !== "mgx:currently-in") continue;
|
|
211
|
-
const snapshot =
|
|
212
|
-
if ((snapshot ? snapshot
|
|
212
|
+
const snapshot = parseSnapshotSubject(row.subject);
|
|
213
|
+
if ((snapshot ? snapshot.base : row.subject) !== character) continue;
|
|
213
214
|
visited.add(row.object);
|
|
214
215
|
}
|
|
215
216
|
return visited;
|
|
@@ -220,9 +221,8 @@ function roomsStoodIn(rows, character) {
|
|
|
220
221
|
* funnel straight to the den, since the unvisited room nearest the burrow is
|
|
221
222
|
* exactly where the fox lives. The den stays reachable by the food gamble
|
|
222
223
|
* below, which is a gamble and is meant to be. */
|
|
223
|
-
const predatorRooms = (rows, state) => new Set((rows
|
|
224
|
-
.
|
|
225
|
-
.map((r) => state.placements.get(r.subject)?.object)
|
|
224
|
+
const predatorRooms = (rows, state) => new Set(predatorSubjects(rows)
|
|
225
|
+
.map((subject) => state.placements.get(subject)?.object)
|
|
226
226
|
.filter(Boolean));
|
|
227
227
|
|
|
228
228
|
/**
|