@polycode-projects/the-mechanical-code-talker 4.1.0 → 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/package.json +1 -1
- package/src/domain/sprite-request.mjs +1 -1
- package/src/services/adventure-viz.mjs +38 -38
- package/src/services/chat-page-viz.mjs +32 -32
- package/src/services/chat.mjs +1 -1
- package/src/services/code-explorer-viz.mjs +1 -1
- package/src/services/ingest-viz.mjs +19 -19
- package/src/services/ledger-viz.mjs +40 -43
- package/src/services/mud-viz.mjs +33 -28
- package/src/services/plan-viz.mjs +9 -9
- package/src/services/research-viz.mjs +17 -17
- package/src/services/spider-fly-viz.mjs +22 -22
- package/src/services/sprite-catalog-viz.mjs +4 -4
- package/src/surfaces/web/adventure-browser-entry.mjs +21 -22
- package/src/surfaces/web/chat-browser-entry.mjs +21 -2
- package/src/surfaces/web/code-explorer-browser-entry.mjs +28 -15
- package/src/surfaces/web/engine-surface.mjs +82 -0
- package/src/surfaces/web/ingest-browser-entry.mjs +13 -4
- package/src/surfaces/web/ledger-browser-entry.mjs +18 -10
- package/src/surfaces/web/memory-ask-browser-entry.mjs +55 -13
- package/src/surfaces/web/memory-ask-browser.bundle.js +54 -54
- package/src/surfaces/web/mud-browser-entry.mjs +42 -21
- package/src/surfaces/web/plan-browser-entry.mjs +18 -11
- package/src/surfaces/web/research-browser-entry.mjs +22 -5
- package/src/surfaces/web/spider-fly-browser-entry.mjs +32 -13
- package/src/surfaces/web/sprites-browser-entry.mjs +15 -6
- package/src/surfaces/web/tmct-surface.mjs +147 -0
- package/src/surfaces/web/turn-session.mjs +6 -2
- package/src/tools/server.mjs +13 -6
|
@@ -549,7 +549,7 @@ function sparkCaptionHtml(stats) {
|
|
|
549
549
|
* self-contained document with no external requests. Only
|
|
550
550
|
* scripts/build-demo-site.mjs, which builds the sibling bundle itself
|
|
551
551
|
* first, passes `true`. The dock's own runtime code below ALSO gates on
|
|
552
|
-
* `typeof
|
|
552
|
+
* `typeof tmct !== "undefined"` regardless — the two checks answer
|
|
553
553
|
* different questions (did this render even offer the reference; did the
|
|
554
554
|
* browser actually manage to load it), and both must hold for the live
|
|
555
555
|
* path to run. */
|
|
@@ -564,7 +564,7 @@ export function renderLedgerHtml({ rows, terms, edges, focus, contradictions, wo
|
|
|
564
564
|
// really in this payload, otherwise a real term from this graph. Left as
|
|
565
565
|
// the query-only wording even when the live bundle is offered — the dock's
|
|
566
566
|
// own script swaps it for a teach-aware placeholder the moment it confirms
|
|
567
|
-
//
|
|
567
|
+
// the live engine actually loaded (never claimed ahead of that confirmation).
|
|
568
568
|
// The example term skips anything under 3 characters — the SAME floor the
|
|
569
569
|
// dock's own miss-tips apply below — so a graph whose highest-degree term
|
|
570
570
|
// is a short stopword-shaped fragment (init:large corpora carry plenty:
|
|
@@ -575,7 +575,7 @@ export function renderLedgerHtml({ rows, terms, edges, focus, contradictions, wo
|
|
|
575
575
|
? "who is the grandfather of ishmael"
|
|
576
576
|
: (exampleTerm ? `ask the graph… e.g. what is ${exampleTerm.term}` : "ask the graph…");
|
|
577
577
|
// The paste-and-drop ingest panel plus the JSONL export ride the LIVE
|
|
578
|
-
// engine (window.
|
|
578
|
+
// engine (window.tmct): both teach into and read the dock's own
|
|
579
579
|
// session store, so they appear only where that bundle is offered — the
|
|
580
580
|
// demo site, never the CLI's self-contained tmct viz page.
|
|
581
581
|
const ingestHtml = ledgerBundleAvailable
|
|
@@ -888,14 +888,13 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
888
888
|
// exists it points at that session's store, which teach/research grow in
|
|
889
889
|
// place, so a digest of a just-taught term reads the fresh facts.
|
|
890
890
|
let getLivePayload = () => PAYLOAD;
|
|
891
|
-
//
|
|
892
|
-
// ledger's
|
|
893
|
-
//
|
|
894
|
-
//
|
|
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.
|
|
895
896
|
const digestHelper = () =>
|
|
896
|
-
(typeof
|
|
897
|
-
|| (typeof tmctMemoryAsk !== "undefined" && tmctMemoryAsk && tmctMemoryAsk.digestTermFromPayloadBrowser)
|
|
898
|
-
|| null;
|
|
897
|
+
(typeof tmct !== "undefined" && tmct.page && tmct.page.digestTermFromPayloadBrowser) || null;
|
|
899
898
|
// The digest for one term, computed live in the browser from the embedded
|
|
900
899
|
// structure table, or null when no structures were embedded, no engine has
|
|
901
900
|
// loaded, or the term holds nothing to compose.
|
|
@@ -1138,9 +1137,9 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1138
1137
|
}
|
|
1139
1138
|
|
|
1140
1139
|
// ---- the chat dock: LIVE teach+ask over the sibling ledger-browser.bundle.js
|
|
1141
|
-
// (window.
|
|
1142
|
-
// loaded; falls back to the read-only
|
|
1143
|
-
//
|
|
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.
|
|
1144
1143
|
// bin/tmct.mjs's own \`tmct viz\` output never offers the live bundle
|
|
1145
1144
|
// (renderLedgerHtml's own ledgerBundleAvailable defaults false there), so
|
|
1146
1145
|
// this branch is simply never reachable on a CLI-generated page.
|
|
@@ -1148,7 +1147,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1148
1147
|
const createTicker = ${createTicker.toString()};
|
|
1149
1148
|
const prefersReducedMotion = ${prefersReducedMotion.toString()};
|
|
1150
1149
|
const chatForm = el("chatform");
|
|
1151
|
-
if (chatForm && typeof
|
|
1150
|
+
if (chatForm && typeof tmct !== "undefined" && !tmct.fallback) {
|
|
1152
1151
|
const log = el("chatlog");
|
|
1153
1152
|
const chatqEl = el("chatq");
|
|
1154
1153
|
chatqEl.placeholder = 'ask or teach the graph\\u2026 e.g. "blue is a peg"';
|
|
@@ -1183,7 +1182,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1183
1182
|
import("./vendor/wink.js"),
|
|
1184
1183
|
winkTimeout(WINK_LOAD_TIMEOUT_MS, "wink vendor asset load timed out"),
|
|
1185
1184
|
]);
|
|
1186
|
-
|
|
1185
|
+
tmct.page.registerWinkModel(() => ({ winkNLP: mod.winkNLP, model: mod.model }));
|
|
1187
1186
|
} catch (err) {
|
|
1188
1187
|
// eslint-disable-next-line no-console
|
|
1189
1188
|
console.warn("tmct ledger: the wink vendor asset failed to load, continuing without the lemma/POS tier", err);
|
|
@@ -1196,7 +1195,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1196
1195
|
async function ensureSession() {
|
|
1197
1196
|
if (session) return session;
|
|
1198
1197
|
await tryLoadWink();
|
|
1199
|
-
session = await
|
|
1198
|
+
session = await tmct.open({ seedPayload: PAYLOAD });
|
|
1200
1199
|
// From here the live store is the source of truth for the digest, so a
|
|
1201
1200
|
// digest of a term taught this session reads its fresh facts — the store
|
|
1202
1201
|
// grows in place, so this one closure stays current.
|
|
@@ -1214,15 +1213,15 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1214
1213
|
chatqEl.disabled = true;
|
|
1215
1214
|
withLock(async () => {
|
|
1216
1215
|
try {
|
|
1217
|
-
const
|
|
1218
|
-
const result = await
|
|
1216
|
+
const live = await ensureSession();
|
|
1217
|
+
const result = await tmct.turn(q);
|
|
1219
1218
|
const record = result.record;
|
|
1220
1219
|
const taught = !!record && record.miss === false && (record.via === "assert" || record.via === "retract");
|
|
1221
1220
|
const body = esc(result.answer).replace(/\\n/g, "<br>");
|
|
1222
1221
|
pending.className = "a" + (taught ? " taught" : (record && record.miss ? " miss" : ""));
|
|
1223
1222
|
pending.innerHTML = taught ? '<span class="tag">taught</span>' + body : body;
|
|
1224
1223
|
if (taught) {
|
|
1225
|
-
const fresh =
|
|
1224
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(live.memoryDir.payload, {});
|
|
1226
1225
|
applyLedgerData(fresh);
|
|
1227
1226
|
} else if (!(record && record.miss)) {
|
|
1228
1227
|
// Only a genuine answer (never a miss) tries to resolve a
|
|
@@ -1231,7 +1230,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1231
1230
|
// ordinary English word (e.g. "run", if the graph happens to
|
|
1232
1231
|
// hold it), and resolveAnsweredTerm has no way to tell that
|
|
1233
1232
|
// apart from the term genuinely being discussed.
|
|
1234
|
-
const hit = resolveAnsweredTerm(result.answer, q, LEDGER.terms,
|
|
1233
|
+
const hit = resolveAnsweredTerm(result.answer, q, LEDGER.terms, tmct.page.normFactTerm);
|
|
1235
1234
|
if (hit) refocusWithLabel(hit, q);
|
|
1236
1235
|
}
|
|
1237
1236
|
} catch {
|
|
@@ -1246,7 +1245,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1246
1245
|
|
|
1247
1246
|
// ---- ingest + export: bring your own text, take the graph away ---------
|
|
1248
1247
|
// The ingest panel runs pasted/dropped/browsed text through the SAME dock
|
|
1249
|
-
// session, one sentence at a time (
|
|
1248
|
+
// session, one sentence at a time (tmct.page.splitSentences, then
|
|
1250
1249
|
// s.turn), keeping only the sentences the recognizer grounds — then
|
|
1251
1250
|
// re-derives the whole ledger so the new facts can be examined in place.
|
|
1252
1251
|
// Export serializes that same session store to canonical JSONL.
|
|
@@ -1282,13 +1281,13 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1282
1281
|
withLock(async () => {
|
|
1283
1282
|
try {
|
|
1284
1283
|
const s = await ensureSession();
|
|
1285
|
-
const sentences =
|
|
1284
|
+
const sentences = tmct.page.splitSentences(text);
|
|
1286
1285
|
let grounded = 0;
|
|
1287
1286
|
for (const sentence of sentences) {
|
|
1288
|
-
const r = await
|
|
1287
|
+
const r = await tmct.turn(sentence);
|
|
1289
1288
|
if (r.record && r.record.miss === false && r.record.via === "assert") grounded += 1;
|
|
1290
1289
|
}
|
|
1291
|
-
const fresh =
|
|
1290
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(s.memoryDir.payload, {});
|
|
1292
1291
|
applyLedgerData(fresh);
|
|
1293
1292
|
const skipped = sentences.length - grounded;
|
|
1294
1293
|
statusEl.textContent = countLabel(sentences.length, "sentence")
|
|
@@ -1308,7 +1307,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1308
1307
|
withLock(async () => {
|
|
1309
1308
|
try {
|
|
1310
1309
|
const s = await ensureSession();
|
|
1311
|
-
const jsonl = await
|
|
1310
|
+
const jsonl = await tmct.page.exportFactsJsonl(s.memoryDir);
|
|
1312
1311
|
const blob = new Blob([jsonl], { type: "application/x-ndjson" });
|
|
1313
1312
|
const url = URL.createObjectURL(blob);
|
|
1314
1313
|
const link = document.createElement("a");
|
|
@@ -1354,8 +1353,8 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1354
1353
|
const pending = addLine("a pending", "researching\\u2026");
|
|
1355
1354
|
return withLock(async () => {
|
|
1356
1355
|
try {
|
|
1357
|
-
const
|
|
1358
|
-
const result = await
|
|
1356
|
+
const live = await ensureSession();
|
|
1357
|
+
const result = await tmct.turn(q);
|
|
1359
1358
|
const missed = !result.record || Boolean(result.record.miss);
|
|
1360
1359
|
pending.className = "a" + (missed ? " miss" : "");
|
|
1361
1360
|
pending.innerHTML = esc(result.answer).replace(/\\n/g, "<br>");
|
|
@@ -1363,7 +1362,7 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1363
1362
|
researchQueue = result.research;
|
|
1364
1363
|
renderResearchControls();
|
|
1365
1364
|
if (!missed) {
|
|
1366
|
-
const fresh =
|
|
1365
|
+
const fresh = tmct.page.computeLedgerDataFromPayload(live.memoryDir.payload, {});
|
|
1367
1366
|
applyLedgerData(fresh);
|
|
1368
1367
|
}
|
|
1369
1368
|
}
|
|
@@ -1406,9 +1405,11 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1406
1405
|
else researchTicker.play();
|
|
1407
1406
|
});
|
|
1408
1407
|
}
|
|
1409
|
-
} else if (chatForm && typeof
|
|
1410
|
-
|
|
1411
|
-
|
|
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 });
|
|
1412
1413
|
const log = el("chatlog");
|
|
1413
1414
|
const addLine = (cls, html) => {
|
|
1414
1415
|
const d = document.createElement("div");
|
|
@@ -1423,20 +1424,16 @@ ${ledgerBundleAvailable ? `<script src="./ledger-browser.bundle.js"></script>` :
|
|
|
1423
1424
|
input.value = "";
|
|
1424
1425
|
addLine("u", esc(q));
|
|
1425
1426
|
(async () => {
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
try { fact = await tmctMemoryAsk.factReadBack(memHandle, q, null, true, null); } catch { fact = null; }
|
|
1433
|
-
}
|
|
1434
|
-
if (fact && fact.text) {
|
|
1435
|
-
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>"));
|
|
1436
1433
|
// Same formatting contract as chat's withGoalLine: capitalized,
|
|
1437
1434
|
// full-stop-terminated, rendered only when the engine deduced one.
|
|
1438
|
-
if (
|
|
1439
|
-
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);
|
|
1440
1437
|
if (hit) refocusWithLabel(hit, q);
|
|
1441
1438
|
} else {
|
|
1442
1439
|
const tips = LEDGER.terms.filter((t) => t.term.length >= 3).slice(0, 2)
|
package/src/services/mud-viz.mjs
CHANGED
|
@@ -184,7 +184,7 @@ export function burrowGraph(state, roomIds, root = "garden") {
|
|
|
184
184
|
* allows that has no written exit yet. Above ground there is nothing to
|
|
185
185
|
* tunnel sideways THROUGH, so a garden digs straight down and no other way;
|
|
186
186
|
* underground, all six are open soil. The engine is the authority on this —
|
|
187
|
-
* the page prefers `
|
|
187
|
+
* the page prefers `tmct.page.diggableDirections` whenever the bundle exposes
|
|
188
188
|
* one and only falls back to the rule below. Pure, self-contained
|
|
189
189
|
* (roomKindForRoom is spliced alongside it). */
|
|
190
190
|
export function diggableDirections(rows, state, here) {
|
|
@@ -1339,7 +1339,7 @@ function pageScript() {
|
|
|
1339
1339
|
// world's ending sentence comes from the engine either way, never a second
|
|
1340
1340
|
// wording of the same event.
|
|
1341
1341
|
appendChat(character, "a", note
|
|
1342
|
-
|| (window.
|
|
1342
|
+
|| (window.tmct.page.outOfPlayPhrase(character, fate) + ". It takes no more turns."));
|
|
1343
1343
|
playFateScene(character, fate);
|
|
1344
1344
|
}
|
|
1345
1345
|
|
|
@@ -1372,7 +1372,7 @@ function pageScript() {
|
|
|
1372
1372
|
const rows = lastSnapshot.rows;
|
|
1373
1373
|
const state = lastSnapshot.state;
|
|
1374
1374
|
renderRoomView(character, rows, state, den, allRoomIds(rows),
|
|
1375
|
-
window.
|
|
1375
|
+
window.tmct.page.castInRoom(rows, state, den, character));
|
|
1376
1376
|
|
|
1377
1377
|
const room = el(w + "-room");
|
|
1378
1378
|
const lunger = room.querySelector('.floor-others .sprite-card[data-subject="' + predator + '"]');
|
|
@@ -1462,7 +1462,7 @@ function pageScript() {
|
|
|
1462
1462
|
return { answer: "", end: false };
|
|
1463
1463
|
});
|
|
1464
1464
|
}
|
|
1465
|
-
return serializeTick(function () { return
|
|
1465
|
+
return serializeTick(function () { return tmct.turn(line, { as: character }); }).then(async function (res) {
|
|
1466
1466
|
appendChat(character, "a", res.answer);
|
|
1467
1467
|
await broadcastLocalChange();
|
|
1468
1468
|
renderSoon();
|
|
@@ -1556,8 +1556,8 @@ function pageScript() {
|
|
|
1556
1556
|
}
|
|
1557
1557
|
|
|
1558
1558
|
function spriteSvgFor(species, rows, instanceKey) {
|
|
1559
|
-
if (window.
|
|
1560
|
-
return window.
|
|
1559
|
+
if (window.tmct && window.tmct.page.resolveSpriteAsset) {
|
|
1560
|
+
return window.tmct.page.resolveSpriteAsset(species, rows, [], DATA.spriteTemplates, window.tmct.page.SPRITE_REGISTRY, { instanceKey: instanceKey });
|
|
1561
1561
|
}
|
|
1562
1562
|
return "";
|
|
1563
1563
|
}
|
|
@@ -1570,10 +1570,10 @@ function pageScript() {
|
|
|
1570
1570
|
// "animal": everything hung on this wall is a thing, and a thing with no
|
|
1571
1571
|
// sprite should read as a plain parcel rather than a creature.
|
|
1572
1572
|
function objectSvgFor(subject, rows) {
|
|
1573
|
-
if (window.
|
|
1574
|
-
return window.
|
|
1573
|
+
if (window.tmct && window.tmct.page.resolveSpriteAsset) {
|
|
1574
|
+
return window.tmct.page.resolveSpriteAsset(
|
|
1575
1575
|
subject, spriteAncestryRows(rows, subject), factsForSubject(rows, subject),
|
|
1576
|
-
DATA.spriteTemplates, window.
|
|
1576
|
+
DATA.spriteTemplates, window.tmct.page.SPRITE_REGISTRY,
|
|
1577
1577
|
{ rootFallback: "portable", instanceKey: subject },
|
|
1578
1578
|
);
|
|
1579
1579
|
}
|
|
@@ -1736,7 +1736,7 @@ function pageScript() {
|
|
|
1736
1736
|
// than the pane offers to talk to.
|
|
1737
1737
|
function roomCaptionFor(rows, state, here, character, roomMates) {
|
|
1738
1738
|
const parts = [];
|
|
1739
|
-
const view = window.
|
|
1739
|
+
const view = window.tmct.page.worldDigestRows(rows, state, character);
|
|
1740
1740
|
const lines = view.filter(function (r) { return r.subject.toLowerCase() === here.toLowerCase(); })
|
|
1741
1741
|
.map(function (r) { return r.subject + " " + r.predicate + " " + r.object + "."; });
|
|
1742
1742
|
parts.push(lines.length ? lines.join(" ") : "You are in the " + here + ".");
|
|
@@ -1755,7 +1755,7 @@ function pageScript() {
|
|
|
1755
1755
|
// direction with neither draws nothing, so the ring never offers a command
|
|
1756
1756
|
// the world would refuse.
|
|
1757
1757
|
function diggableFor(rows, state, here) {
|
|
1758
|
-
const fromEngine = window.
|
|
1758
|
+
const fromEngine = window.tmct && window.tmct.page.diggableDirections;
|
|
1759
1759
|
return fromEngine ? fromEngine(rows, state, here) : diggableDirections(rows, state, here);
|
|
1760
1760
|
}
|
|
1761
1761
|
|
|
@@ -1782,7 +1782,7 @@ function pageScript() {
|
|
|
1782
1782
|
// it. Exits appear in both the row and the compass ring on purpose: the ring
|
|
1783
1783
|
// says WHERE a way out points, the row says what it is called.
|
|
1784
1784
|
function affordancesFor(rows, state, here, character) {
|
|
1785
|
-
const fromEngine = window.
|
|
1785
|
+
const fromEngine = window.tmct && window.tmct.page.roomAffordances;
|
|
1786
1786
|
return fromEngine ? fromEngine(rows, state, here, character) : [];
|
|
1787
1787
|
}
|
|
1788
1788
|
|
|
@@ -1838,7 +1838,7 @@ function pageScript() {
|
|
|
1838
1838
|
// can say "carrot" while every verb still resolves the distinct id
|
|
1839
1839
|
// ("carrot-1"). itemLabel is the fallback for an id carrying no such fact.
|
|
1840
1840
|
function labelForItem(rows, subject, roomIds) {
|
|
1841
|
-
const fromEngine = window.
|
|
1841
|
+
const fromEngine = window.tmct && window.tmct.page.displayNameOf;
|
|
1842
1842
|
const name = fromEngine ? fromEngine(rows, subject) : null;
|
|
1843
1843
|
return name && name !== subject ? name : itemLabel(subject, roomIds);
|
|
1844
1844
|
}
|
|
@@ -1878,7 +1878,7 @@ function pageScript() {
|
|
|
1878
1878
|
// the survey they annotate, and each one's own dot already carries its name.
|
|
1879
1879
|
const keys = cast.map(function (c) {
|
|
1880
1880
|
const room = state.placements.get(c) ? state.placements.get(c).object : "?";
|
|
1881
|
-
const fate = window.
|
|
1881
|
+
const fate = window.tmct.page.outOfPlayReasonOf(state, c);
|
|
1882
1882
|
const where = fate ? fate : "in " + esc(room);
|
|
1883
1883
|
return '<span class="key-actor"><span class="key-dot" style="background:' + colorFor(c) + '"></span>'
|
|
1884
1884
|
+ esc(speciesOfCharacter(c)) + " " + where + "</span>";
|
|
@@ -1939,7 +1939,7 @@ function pageScript() {
|
|
|
1939
1939
|
}
|
|
1940
1940
|
}
|
|
1941
1941
|
for (const character of everyone()) {
|
|
1942
|
-
const reason = window.
|
|
1942
|
+
const reason = window.tmct.page.outOfPlayReasonOf(snap.state, character);
|
|
1943
1943
|
if (reason) markOutOfPlay(character, reason, null);
|
|
1944
1944
|
}
|
|
1945
1945
|
renderWorldMap(snap.rows, snap.state, roomIds);
|
|
@@ -1954,7 +1954,7 @@ function pageScript() {
|
|
|
1954
1954
|
const place = snap.state.placements.get(character);
|
|
1955
1955
|
const here = place ? place.object : null;
|
|
1956
1956
|
if (!here) continue;
|
|
1957
|
-
const roomMates = window.
|
|
1957
|
+
const roomMates = window.tmct.page.castInRoom(snap.rows, snap.state, here, character);
|
|
1958
1958
|
renderRoomView(character, snap.rows, snap.state, here, roomIds, roomMates);
|
|
1959
1959
|
renderDirections(character, snap.rows, snap.state, here);
|
|
1960
1960
|
renderChatPills(character, snap.rows, snap.state, here, roomIds);
|
|
@@ -2072,9 +2072,9 @@ function pageScript() {
|
|
|
2072
2072
|
function renderSuggestionPills() {
|
|
2073
2073
|
const box = el("editorPills");
|
|
2074
2074
|
const term = wordBeforeCursor(el("editorText").value, el("editorText").selectionStart);
|
|
2075
|
-
if (!term || !window.
|
|
2076
|
-
const related = window.
|
|
2077
|
-
const chain = window.
|
|
2075
|
+
if (!term || !window.tmct) { box.innerHTML = ""; return; }
|
|
2076
|
+
const related = window.tmct.page.relatedForTerm ? window.tmct.page.relatedForTerm(allStoreRows, term) : null;
|
|
2077
|
+
const chain = window.tmct.page.classAncestorChain ? window.tmct.page.classAncestorChain(term, allStoreRows) : [];
|
|
2078
2078
|
const seen = { };
|
|
2079
2079
|
seen[term] = true;
|
|
2080
2080
|
const out = [];
|
|
@@ -2113,7 +2113,7 @@ function pageScript() {
|
|
|
2113
2113
|
const snap = await session.snapshot();
|
|
2114
2114
|
allStoreRows = snap.rows;
|
|
2115
2115
|
editRows = worldOnlyRows(snap.rows);
|
|
2116
|
-
editState = window.
|
|
2116
|
+
editState = window.tmct.page.foldWorldState(window.tmct.page.worldActionRows(editRows));
|
|
2117
2117
|
if (result.unrecognized.length) {
|
|
2118
2118
|
const lines = result.unrecognized.map(function (u) { return u.line; }).join(", ");
|
|
2119
2119
|
status.className = "edit-status pending";
|
|
@@ -2169,7 +2169,7 @@ function pageScript() {
|
|
|
2169
2169
|
const snap = await session.snapshot();
|
|
2170
2170
|
allStoreRows = snap.rows;
|
|
2171
2171
|
editRows = worldOnlyRows(snap.rows);
|
|
2172
|
-
editState = window.
|
|
2172
|
+
editState = window.tmct.page.foldWorldState(window.tmct.page.worldActionRows(editRows));
|
|
2173
2173
|
el("editorText").value = renderMudEditorText(editRows, editState);
|
|
2174
2174
|
el("editorStatus").className = "edit-status";
|
|
2175
2175
|
el("editorStatus").textContent = "";
|
|
@@ -2249,8 +2249,8 @@ function pageScript() {
|
|
|
2249
2249
|
// engine calls live world state, plus the P2P layer's own four predicates.
|
|
2250
2250
|
// The check is handed over rather than imported, so the sync filter never
|
|
2251
2251
|
// learns the world engine's private predicate names.
|
|
2252
|
-
const extraPredicates = (window.
|
|
2253
|
-
const isState = window.
|
|
2252
|
+
const extraPredicates = (window.tmct.page.P2P_PREDICATES || []).slice();
|
|
2253
|
+
const isState = window.tmct.page.isMudStatePredicate;
|
|
2254
2254
|
room = mod.createP2pRoom({
|
|
2255
2255
|
memoryDir: session.memoryDir,
|
|
2256
2256
|
myPeerId: myPeerId,
|
|
@@ -2803,15 +2803,21 @@ function pageScript() {
|
|
|
2803
2803
|
// so the roster belongs to the scenario rather than to the page.
|
|
2804
2804
|
roster = rosterOf(scenario());
|
|
2805
2805
|
rootRoom = rootRoomOf(scenario());
|
|
2806
|
-
cast = window.
|
|
2807
|
-
const pool = window.
|
|
2806
|
+
cast = window.tmct.page.pickMudRoster(roster, { count: chosenPlayerCount() });
|
|
2807
|
+
const pool = window.tmct.page.expandMudRoster(roster, cast.length + npcCount)
|
|
2808
2808
|
.filter(function (id) { return cast.indexOf(id) === -1; });
|
|
2809
|
-
npcs = window.
|
|
2809
|
+
npcs = window.tmct.page.pickMudRoster(pool, { count: npcCount });
|
|
2810
2810
|
slots = DATA.paneSlots.slice(0, cast.length);
|
|
2811
2811
|
showPlayerCount(cast.length);
|
|
2812
2812
|
showNpcCount(npcs.length);
|
|
2813
2813
|
renderStage();
|
|
2814
|
-
|
|
2814
|
+
// slotOf has to match cast from the same synchronous stretch that just
|
|
2815
|
+
// rebuilt the DOM for it \\u2014 a render triggered anywhere in the awaits
|
|
2816
|
+
// below (rebind's own re-sync can trigger one) must never see this
|
|
2817
|
+
// scenario's cast paired with the LAST scenario's pane ids, which is what
|
|
2818
|
+
// a null pane-element lookup in renderAll means when it happens.
|
|
2819
|
+
for (let i = 0; i < cast.length; i += 1) slotOf[cast[i]] = slots[i];
|
|
2820
|
+
const opened = await window.tmct.open(scenario().worldPayload, { characters: everyone(), epoch: nextEpoch });
|
|
2815
2821
|
if (seq !== bootSeq) return;
|
|
2816
2822
|
session = opened;
|
|
2817
2823
|
if (liveRoom) {
|
|
@@ -2828,7 +2834,6 @@ function pageScript() {
|
|
|
2828
2834
|
dropRoom("the link didn't survive the recast \\u2014 share again to link up.");
|
|
2829
2835
|
}
|
|
2830
2836
|
}
|
|
2831
|
-
for (let i = 0; i < cast.length; i += 1) slotOf[cast[i]] = slots[i];
|
|
2832
2837
|
bindPanes();
|
|
2833
2838
|
for (const character of cast) {
|
|
2834
2839
|
const w = paneIdFor(character);
|
|
@@ -665,7 +665,7 @@ const PLAN = ${embedded};
|
|
|
665
665
|
mountPlan(PLAN);
|
|
666
666
|
|
|
667
667
|
// ---- live re-solve: disk-count/max-depth controls + the chat-assert dock
|
|
668
|
-
// over the sibling plan-browser.bundle.js (window.
|
|
668
|
+
// over the sibling plan-browser.bundle.js (window.tmct). Degrades
|
|
669
669
|
// honestly when the bundle failed to load or wasn't built alongside this
|
|
670
670
|
// page (e.g. a plain renderPlanHtml() call with no bundle nearby) — the
|
|
671
671
|
// baked-in replay above already stands on its own either way.
|
|
@@ -678,7 +678,7 @@ const PLAN = ${embedded};
|
|
|
678
678
|
const chatqEl = document.getElementById("chatq");
|
|
679
679
|
const chatpillsEl = document.getElementById("chatpills");
|
|
680
680
|
|
|
681
|
-
const liveAvailable = typeof
|
|
681
|
+
const liveAvailable = typeof tmct !== "undefined" && typeof tmct.open === "function";
|
|
682
682
|
if (!liveAvailable) {
|
|
683
683
|
liveStatusEl.textContent = "live re-solve unavailable here — showing the baked-in replay only.";
|
|
684
684
|
liveStatusEl.classList.add("isError");
|
|
@@ -712,7 +712,7 @@ const PLAN = ${embedded};
|
|
|
712
712
|
import("./vendor/wink.js"),
|
|
713
713
|
winkTimeout(WINK_LOAD_TIMEOUT_MS, "wink vendor asset load timed out"),
|
|
714
714
|
]);
|
|
715
|
-
|
|
715
|
+
tmct.page.registerWinkModel(() => ({ winkNLP: mod.winkNLP, model: mod.model }));
|
|
716
716
|
} catch (err) {
|
|
717
717
|
// eslint-disable-next-line no-console
|
|
718
718
|
console.warn("tmct plan: the wink vendor asset failed to load, continuing without the lemma/POS tier", err);
|
|
@@ -729,9 +729,9 @@ const PLAN = ${embedded};
|
|
|
729
729
|
}
|
|
730
730
|
function applyPlan(freshPlan) {
|
|
731
731
|
if (!freshPlan) return;
|
|
732
|
-
const { rendersAs, sizeOrder } =
|
|
733
|
-
mountPlan(
|
|
734
|
-
if (pddlEl) pddlEl.textContent =
|
|
732
|
+
const { rendersAs, sizeOrder } = tmct.page.renderInputsFromPlan(freshPlan);
|
|
733
|
+
mountPlan(tmct.page.planToPageData({ plan: freshPlan, rendersAs, sizeOrder }));
|
|
734
|
+
if (pddlEl) pddlEl.textContent = tmct.page.planToPddl(freshPlan);
|
|
735
735
|
// The <title>/<h1> were baked from the INITIAL plan's own goal text —
|
|
736
736
|
// a live re-solve toward a different goal (a fresh puzzle, or a
|
|
737
737
|
// taught goal revision) must not leave them stating the old one.
|
|
@@ -742,7 +742,7 @@ const PLAN = ${embedded};
|
|
|
742
742
|
async function ensureSession() {
|
|
743
743
|
if (session) return session;
|
|
744
744
|
await tryLoadWink();
|
|
745
|
-
session = await
|
|
745
|
+
session = await tmct.open({
|
|
746
746
|
diskCount: Math.max(1, Math.min(7, parseInt(diskCountEl.value, 10) || 3)),
|
|
747
747
|
maxDepth: Math.max(1, parseInt(maxDepthEl.value, 10) || 300),
|
|
748
748
|
});
|
|
@@ -757,7 +757,7 @@ const PLAN = ${embedded};
|
|
|
757
757
|
liveStatusEl.textContent = "solving a " + n + "-disk puzzle…";
|
|
758
758
|
liveStatusEl.classList.remove("isError");
|
|
759
759
|
await tryLoadWink();
|
|
760
|
-
session = await
|
|
760
|
+
session = await tmct.open({ diskCount: n, maxDepth: d });
|
|
761
761
|
if (session.plan) {
|
|
762
762
|
applyPlan(session.plan);
|
|
763
763
|
liveStatusEl.textContent = "live — " + countLabel(n, "disk", "disks") + ", max depth " + d + ".";
|
|
@@ -777,7 +777,7 @@ const PLAN = ${embedded};
|
|
|
777
777
|
withLock(async () => {
|
|
778
778
|
const s = await ensureSession();
|
|
779
779
|
const maxDepth = Math.max(1, parseInt(maxDepthEl.value, 10) || 300);
|
|
780
|
-
const result = await
|
|
780
|
+
const result = await tmct.turn(q, { maxDepth });
|
|
781
781
|
addChatLine("a", result.answer);
|
|
782
782
|
if (result.plan) applyPlan(result.plan);
|
|
783
783
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// document shaped exactly like ingest-viz.mjs/chat-page-viz.mjs's own
|
|
3
3
|
// page-builders — one inlined <style> importing viz-theme.mjs's shared tokens,
|
|
4
4
|
// behaviour as an inlined IIFE — running the research engine
|
|
5
|
-
// (research-browser.bundle.js's globalThis.
|
|
5
|
+
// (research-browser.bundle.js's globalThis.tmct) by same-origin
|
|
6
6
|
// relative paths.
|
|
7
7
|
//
|
|
8
8
|
// One in-memory graph grows three ways, each visible on the page:
|
|
@@ -392,7 +392,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
392
392
|
if (progressActive) statEngineEl.textContent = loadProgressLine(Object.values(progressParts));
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
const loadWink = loadWinkVendor({ register: (factory) => window.
|
|
395
|
+
const loadWink = loadWinkVendor({ register: (factory) => window.tmct.page.registerWinkModel(factory) });
|
|
396
396
|
|
|
397
397
|
let seedPayload = null;
|
|
398
398
|
let seedFacts = 0;
|
|
@@ -406,8 +406,8 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
406
406
|
console.warn("tmct research: chat-seed.json unavailable — starting unseeded", err);
|
|
407
407
|
}
|
|
408
408
|
}
|
|
409
|
-
function newSession() {
|
|
410
|
-
return window.
|
|
409
|
+
async function newSession() {
|
|
410
|
+
return window.tmct.open({ seedPayload: cloneMemoryPayload(seedPayload), vocabSeeded: Boolean(seedPayload), digestStructures: DIGEST_STRUCTURES });
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
// The curated reference pack provider, same fetch seam chat.html registers,
|
|
@@ -432,7 +432,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
432
432
|
};
|
|
433
433
|
|
|
434
434
|
async function boot() {
|
|
435
|
-
if (!window.
|
|
435
|
+
if (!window.tmct) {
|
|
436
436
|
engineNoteEl.hidden = false;
|
|
437
437
|
engineNoteEl.textContent = "The research engine did not load. Run npm run demo:build, then reload this page.";
|
|
438
438
|
statSeedEl.textContent = "—";
|
|
@@ -441,8 +441,8 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
441
441
|
}
|
|
442
442
|
const [winkStatus] = await Promise.all([loadWink(), fetchSeed()]);
|
|
443
443
|
progressActive = false;
|
|
444
|
-
window.
|
|
445
|
-
session = newSession();
|
|
444
|
+
window.tmct.page.registerReferencePackProvider(fetchPackProvider);
|
|
445
|
+
session = await newSession();
|
|
446
446
|
const winkPart = winkStatus === "loaded" ? "wink-nlp loaded" : "wink-nlp unavailable, curated tiers only";
|
|
447
447
|
statSeedEl.textContent = seedPayload ? seedFacts.toLocaleString() + " facts" : "no seed";
|
|
448
448
|
statEngineEl.textContent = winkPart + " · ready";
|
|
@@ -465,7 +465,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
465
465
|
async function refresh() {
|
|
466
466
|
if (!session) return;
|
|
467
467
|
let snap;
|
|
468
|
-
try { snap = await window.
|
|
468
|
+
try { snap = await window.tmct.page.researchSnapshot(session.memoryDir, session.sessionIds); }
|
|
469
469
|
catch (err) { console.warn("tmct research: snapshot failed", err); return; }
|
|
470
470
|
const total = (snap.sources || []).reduce((sum, source) => sum + (Number(source.count) || 0), 0);
|
|
471
471
|
statFactsEl.textContent = total.toLocaleString();
|
|
@@ -626,15 +626,15 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
626
626
|
answerEl.className = "";
|
|
627
627
|
answerEl.textContent = "thinking…";
|
|
628
628
|
let res;
|
|
629
|
-
try { res = await
|
|
630
|
-
catch (err) { res = {
|
|
631
|
-
if (res.miss || !res.
|
|
629
|
+
try { res = await tmct.ask(q, { sources: allChecked ? null : checked }); }
|
|
630
|
+
catch (err) { res = { answer: "", miss: true }; }
|
|
631
|
+
if (res.miss || !res.answer) {
|
|
632
632
|
answerEl.className = "miss";
|
|
633
633
|
const scope = allChecked ? "any checked source" : "the " + checked.length + " checked source" + (checked.length === 1 ? "" : "s");
|
|
634
634
|
answerEl.textContent = "No grounded answer from " + scope + ". It abstains rather than guess.";
|
|
635
635
|
} else {
|
|
636
636
|
answerEl.className = "";
|
|
637
|
-
answerEl.textContent = res.
|
|
637
|
+
answerEl.textContent = res.answer;
|
|
638
638
|
}
|
|
639
639
|
}
|
|
640
640
|
el("askGo").addEventListener("click", ask);
|
|
@@ -705,7 +705,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
705
705
|
const note = el("teachNote");
|
|
706
706
|
note.textContent = "…";
|
|
707
707
|
let res;
|
|
708
|
-
try { res = await
|
|
708
|
+
try { res = await tmct.turn(q); } catch { res = null; }
|
|
709
709
|
if (res && res.record && res.record.via === "assert" && !res.record.miss) {
|
|
710
710
|
note.textContent = "stored: " + (res.answer || "remembered.");
|
|
711
711
|
el("teachInput").value = "";
|
|
@@ -786,7 +786,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
786
786
|
async function researchStep(line) {
|
|
787
787
|
if (!session) return;
|
|
788
788
|
let res;
|
|
789
|
-
try { res = await
|
|
789
|
+
try { res = await tmct.turn(line); } catch { res = null; }
|
|
790
790
|
if (res && res.research !== undefined) {
|
|
791
791
|
researchQueue = res.research;
|
|
792
792
|
renderResearchControls();
|
|
@@ -817,9 +817,9 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
817
817
|
|
|
818
818
|
// ---- tools --------------------------------------------------------------
|
|
819
819
|
el("exportFacts").addEventListener("click", async () => {
|
|
820
|
-
if (!session || !window.
|
|
820
|
+
if (!session || !window.tmct.page.exportFactsJsonl) return;
|
|
821
821
|
let jsonl;
|
|
822
|
-
try { jsonl = await window.
|
|
822
|
+
try { jsonl = await window.tmct.page.exportFactsJsonl(session.memoryDir); }
|
|
823
823
|
catch { return; }
|
|
824
824
|
const blob = new Blob([jsonl], { type: "application/x-ndjson" });
|
|
825
825
|
const url = URL.createObjectURL(blob);
|
|
@@ -832,7 +832,7 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
832
832
|
researchTicker.pause();
|
|
833
833
|
researchQueue = null;
|
|
834
834
|
checkedSources.clear();
|
|
835
|
-
session = newSession();
|
|
835
|
+
session = await newSession();
|
|
836
836
|
el("teachNote").textContent = "";
|
|
837
837
|
el("ingestNote").textContent = "";
|
|
838
838
|
el("researchNote").textContent = "";
|