@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
|
@@ -83,7 +83,7 @@ function webCellIds() {
|
|
|
83
83
|
* `geometry` is `{ parseCellId, cellId, directionDelta }` — the exact three
|
|
84
84
|
* grid-geometry primitives spider-fly-world.mjs exports, injected rather
|
|
85
85
|
* than imported so this function stays `.toString()`-splice safe (the
|
|
86
|
-
* inlined page calls it with `window.
|
|
86
|
+
* inlined page calls it with `window.tmct`'s own re-exports of the
|
|
87
87
|
* same three). Returns the ordered cell-id array (length > 1) or null.
|
|
88
88
|
*/
|
|
89
89
|
export function threadCellsForSpiderPlan(agents, geometry) {
|
|
@@ -558,7 +558,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
558
558
|
// lanes read exactly as much better with wink loaded as they do on every
|
|
559
559
|
// other page. Fired eagerly here so it is likely settled well before a
|
|
560
560
|
// visitor's first ordinary (non-game) question.
|
|
561
|
-
loadWinkVendor({ register: (factory) =>
|
|
561
|
+
loadWinkVendor({ register: (factory) => tmct.page.registerWinkModel(factory) })();
|
|
562
562
|
const el = (id) => document.getElementById(id);
|
|
563
563
|
const boardFrame = el("boardFrame");
|
|
564
564
|
const boardCanvas = el("board");
|
|
@@ -661,12 +661,12 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
661
661
|
// takes, answered by the same pure resolver the tool wraps (sprite-request.mjs,
|
|
662
662
|
// spliced in above) — the page holds the state and nothing else.
|
|
663
663
|
function resolveSpriteFace(spriteRequest) {
|
|
664
|
-
if (!window.
|
|
664
|
+
if (!window.tmct) return "";
|
|
665
665
|
return resolveSpriteRequest(spriteRequest, {
|
|
666
666
|
factRows: (session && session.taxonomyRows) || [],
|
|
667
667
|
templates: SPIDERFLY.spriteTemplates,
|
|
668
|
-
spriteRegistry:
|
|
669
|
-
resolveSpriteAsset:
|
|
668
|
+
spriteRegistry: tmct.page.SPRITE_REGISTRY,
|
|
669
|
+
resolveSpriteAsset: tmct.page.resolveSpriteAsset,
|
|
670
670
|
}).svg || "";
|
|
671
671
|
}
|
|
672
672
|
|
|
@@ -718,7 +718,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
718
718
|
for (const [id, a] of Object.entries(agents)) {
|
|
719
719
|
const cls = agentKindOf(id);
|
|
720
720
|
const node = ensureSpriteEl(id, cls, a);
|
|
721
|
-
const parsed =
|
|
721
|
+
const parsed = tmct.page.parseCellId(a.cell);
|
|
722
722
|
if (!parsed) continue;
|
|
723
723
|
const pct = cellCenterPct(parsed.x, parsed.y);
|
|
724
724
|
node.style.left = pct.leftPct + "%";
|
|
@@ -778,7 +778,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
778
778
|
spriteLayer.appendChild(node);
|
|
779
779
|
corpseEls[id] = node;
|
|
780
780
|
}
|
|
781
|
-
const deathCell =
|
|
781
|
+
const deathCell = tmct.page.parseCellId(corpse.cell);
|
|
782
782
|
if (!deathCell) continue;
|
|
783
783
|
const pct = cellCenterPct(deathCell.x, SPIDERFLY.gridSize);
|
|
784
784
|
node.style.left = pct.leftPct + "%";
|
|
@@ -863,9 +863,9 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
863
863
|
});
|
|
864
864
|
|
|
865
865
|
const threadGeometry = {
|
|
866
|
-
parseCellId: (id) =>
|
|
867
|
-
cellId: (x, y) =>
|
|
868
|
-
directionDelta:
|
|
866
|
+
parseCellId: (id) => tmct.page.parseCellId(id),
|
|
867
|
+
cellId: (x, y) => tmct.page.cellId(x, y),
|
|
868
|
+
directionDelta: tmct.page.DIRECTION_DELTA,
|
|
869
869
|
};
|
|
870
870
|
|
|
871
871
|
function drawBoard(agents, activeWebs) {
|
|
@@ -873,7 +873,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
873
873
|
boardCtx.clearRect(0, 0, w, h);
|
|
874
874
|
boardCtx.fillStyle = cssVar("--taught-soft") || "rgba(46,125,79,.12)";
|
|
875
875
|
for (const wc of SPIDERFLY.webCells) {
|
|
876
|
-
const p =
|
|
876
|
+
const p = tmct.page.parseCellId(wc);
|
|
877
877
|
boardCtx.fillRect((p.x - 1) * cellSize, (p.y - 1) * cellSize, cellSize, cellSize);
|
|
878
878
|
}
|
|
879
879
|
// A spider-built dynamic web is a distinct color from the always-on
|
|
@@ -884,7 +884,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
884
884
|
boardCtx.lineWidth = 1;
|
|
885
885
|
boardCtx.setLineDash([3, 2]);
|
|
886
886
|
for (const web of activeWebs || []) {
|
|
887
|
-
const p =
|
|
887
|
+
const p = tmct.page.parseCellId(web.cell);
|
|
888
888
|
if (!p) continue;
|
|
889
889
|
boardCtx.fillRect((p.x - 1) * cellSize, (p.y - 1) * cellSize, cellSize, cellSize);
|
|
890
890
|
boardCtx.strokeRect((p.x - 1) * cellSize + 0.5, (p.y - 1) * cellSize + 0.5, cellSize - 1, cellSize - 1);
|
|
@@ -904,7 +904,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
904
904
|
boardCtx.beginPath();
|
|
905
905
|
let prev = null;
|
|
906
906
|
thread.forEach((c, i) => {
|
|
907
|
-
const p =
|
|
907
|
+
const p = tmct.page.parseCellId(c);
|
|
908
908
|
const px = (p.x - 0.5) * cellSize, py = (p.y - 0.5) * cellSize;
|
|
909
909
|
if (i === 0) boardCtx.moveTo(px, py); else boardCtx.lineTo(px, py);
|
|
910
910
|
if (prev) threadHits.push({ x: (prev.x + px) / 2, y: (prev.y + py) / 2, step: i });
|
|
@@ -919,19 +919,19 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
919
919
|
if (!povAgentId) return;
|
|
920
920
|
const agent = lastAgents[povAgentId];
|
|
921
921
|
if (!agent) { povAgentId = null; return; }
|
|
922
|
-
const p =
|
|
922
|
+
const p = tmct.page.parseCellId(agent.cell);
|
|
923
923
|
// The live, slider-adjustable per-class radius (falling back to the
|
|
924
924
|
// engine's own shipped default before the session has finished
|
|
925
925
|
// booting) — a POV toggle always reflects whatever vision range this
|
|
926
926
|
// class currently actually has, not a fixed constant.
|
|
927
927
|
const radius = agentKindOf(povAgentId) === "spider"
|
|
928
|
-
? (liveConfig.spiderVisionRadius ??
|
|
929
|
-
: (liveConfig.flyVisionRadius ??
|
|
930
|
-
const visible = new Set(
|
|
928
|
+
? (liveConfig.spiderVisionRadius ?? tmct.page.DEFAULT_VISION_RADIUS)
|
|
929
|
+
: (liveConfig.flyVisionRadius ?? tmct.page.DEFAULT_VISION_RADIUS);
|
|
930
|
+
const visible = new Set(tmct.page.visibleCells(p.x, p.y, radius));
|
|
931
931
|
povCtx.fillStyle = "rgba(0,0,0,.55)";
|
|
932
932
|
for (let gy = 1; gy <= SPIDERFLY.gridSize; gy += 1) {
|
|
933
933
|
for (let gx = 1; gx <= SPIDERFLY.gridSize; gx += 1) {
|
|
934
|
-
if (visible.has(
|
|
934
|
+
if (visible.has(tmct.page.cellId(gx, gy))) continue;
|
|
935
935
|
povCtx.fillRect((gx - 1) * cellSize, (gy - 1) * cellSize, cellSize, cellSize);
|
|
936
936
|
}
|
|
937
937
|
}
|
|
@@ -983,7 +983,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
983
983
|
chatqEl.value = "";
|
|
984
984
|
addChatLine("u", esc(q));
|
|
985
985
|
withLock(async () => {
|
|
986
|
-
const result = await
|
|
986
|
+
const result = await tmct.turn(q);
|
|
987
987
|
addChatLine("a", esc(result.answer).replace(/\\n/g, "<br>"));
|
|
988
988
|
const snap = await session.snapshot();
|
|
989
989
|
redraw(snap.agents, snap.turn, snap.activeWebs);
|
|
@@ -1027,14 +1027,14 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
1027
1027
|
refreshPills();
|
|
1028
1028
|
|
|
1029
1029
|
// ---- deception pills (§A.2.4): a SEPARATE dynamic rail, alongside (never
|
|
1030
|
-
// replacing) the static one above.
|
|
1030
|
+
// replacing) the static one above. tmct.page.pillsForSpiderFly is the
|
|
1031
1031
|
// exact same pure function spider-fly-turn.mjs exports — this page never
|
|
1032
1032
|
// reimplements the true/false claim logic, only renders its output and
|
|
1033
1033
|
// fills #chatq on click, same click-to-fill discipline as every other
|
|
1034
1034
|
// pill on this page (never auto-submits).
|
|
1035
1035
|
function renderDynamicPills() {
|
|
1036
1036
|
if (!session || !Object.keys(lastAgents).length) { dynamicPillsEl.innerHTML = ""; return; }
|
|
1037
|
-
const result =
|
|
1037
|
+
const result = tmct.page.pillsForSpiderFly(lastAgents, selectedAddresseeId, {});
|
|
1038
1038
|
selectedAddresseeId = result.addresseeId;
|
|
1039
1039
|
const addrHtml = result.addressPills.map((p) =>
|
|
1040
1040
|
'<button type="button" class="pill" data-role="dyn-addr" data-id="' + esc(p.id) + '"'
|
|
@@ -1099,7 +1099,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
1099
1099
|
|
|
1100
1100
|
let tuningInitialized = false;
|
|
1101
1101
|
async function boot() {
|
|
1102
|
-
session = await
|
|
1102
|
+
session = await tmct.open();
|
|
1103
1103
|
// A reset mints a brand-new session (fresh board, fresh config) — the
|
|
1104
1104
|
// FIRST boot seeds the sliders from the engine's own shipped defaults;
|
|
1105
1105
|
// every boot after that re-applies whatever the visitor already had the
|
|
@@ -631,7 +631,7 @@ const SPRITE_CHAT = ${embedJson({ rows: dockRows })};
|
|
|
631
631
|
import("./vendor/wink.js"),
|
|
632
632
|
winkTimeout(WINK_LOAD_TIMEOUT_MS, "wink vendor asset load timed out"),
|
|
633
633
|
]);
|
|
634
|
-
|
|
634
|
+
tmct.page.registerWinkModel(() => ({ winkNLP: mod.winkNLP, model: mod.model }));
|
|
635
635
|
} catch (err) {
|
|
636
636
|
console.warn("tmct sprites: the wink vendor asset failed to load, continuing without the lemma/POS tier", err);
|
|
637
637
|
}
|
|
@@ -661,7 +661,7 @@ const SPRITE_CHAT = ${embedJson({ rows: dockRows })};
|
|
|
661
661
|
addDockLine("u", q);
|
|
662
662
|
if (!session) { addDockLine("a miss", "the engine is still loading. Try again in a moment."); return; }
|
|
663
663
|
withLock(async () => {
|
|
664
|
-
const result = await
|
|
664
|
+
const result = await tmct.turn(q);
|
|
665
665
|
const missed = !!(result.record && result.record.miss);
|
|
666
666
|
addDockLine(missed ? "a miss" : "a grounded", result.answer);
|
|
667
667
|
});
|
|
@@ -677,7 +677,7 @@ const SPRITE_CHAT = ${embedJson({ rows: dockRows })};
|
|
|
677
677
|
(async () => {
|
|
678
678
|
try {
|
|
679
679
|
await tryLoadWink();
|
|
680
|
-
session = await
|
|
680
|
+
session = await tmct.open({ factRows: SPRITE_CHAT.rows });
|
|
681
681
|
dockqEl.disabled = false;
|
|
682
682
|
dockStatusEl.textContent = SPRITE_CHAT.rows.length + " sprite facts on record. Ask away, or use a quick question.";
|
|
683
683
|
} catch (err) {
|
|
@@ -865,7 +865,7 @@ ${spriteBundleScript}
|
|
|
865
865
|
function wireSceneComposer() {
|
|
866
866
|
const composeqEl = document.getElementById("composeq");
|
|
867
867
|
if (!composeqEl) return;
|
|
868
|
-
const extractSceneItems =
|
|
868
|
+
const extractSceneItems = tmct.page.extractSceneItems;
|
|
869
869
|
const composeFormEl = document.getElementById("composeForm");
|
|
870
870
|
const composePillsEl = document.getElementById("composePills");
|
|
871
871
|
const sceneRowEl = document.getElementById("sceneRow");
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
// game-shaped falls through to the ordinary conversational layer, exactly
|
|
36
36
|
// like a real CLI session.
|
|
37
37
|
import { createTurnSession } from "./turn-session.mjs";
|
|
38
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
39
|
+
import { graphAsk, enginePlan } from "./engine-surface.mjs";
|
|
38
40
|
import {
|
|
39
41
|
createInMemoryStore, appendFacts, appendRule, loadMemory, readFactRows, removeFacts,
|
|
40
42
|
} from "../../adapters/memory/core.mjs";
|
|
@@ -127,6 +129,7 @@ export async function createAdventureSession(worldPayload, { restoredPayload = n
|
|
|
127
129
|
|
|
128
130
|
return {
|
|
129
131
|
memoryDir,
|
|
132
|
+
graph,
|
|
130
133
|
|
|
131
134
|
/** One auto-play tick: infer the goal, execute exactly one move through
|
|
132
135
|
* adventureTurn (adventure-autoplay.mjs's own contract), thread the
|
|
@@ -192,25 +195,21 @@ export async function createAdventureSession(worldPayload, { restoredPayload = n
|
|
|
192
195
|
};
|
|
193
196
|
}
|
|
194
197
|
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// affordances the chat
|
|
198
|
-
//
|
|
199
|
-
// the
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
worldDigestRows, roomAffordances, foldWorldState, exposedFacts,
|
|
214
|
-
relatedForTerm, classAncestorChain, openPersistedStore,
|
|
215
|
-
directedGridLayout, roomGraphSvg,
|
|
216
|
-
};
|
|
198
|
+
// `tmct.page` keeps what the page draws with and the engine has no
|
|
199
|
+
// plain-English form for: sprite resolution, the digest reader, the room
|
|
200
|
+
// affordances the chat pills read from, the exposure-filtered fold the
|
|
201
|
+
// goal-status panel mirrors, the SKOS neighbourhood and is-a chain behind the
|
|
202
|
+
// edit mode's cursor pills, the persisted-store wrapper, and the manor map's
|
|
203
|
+
// own BFS-grid layout and SVG renderer (neither is `.toString()`-splice-safe:
|
|
204
|
+
// roomGraphSvg needs escapeHtml, directedGridLayout its own exit-delta table).
|
|
205
|
+
publishTmctSurface({
|
|
206
|
+
open: createAdventureSession,
|
|
207
|
+
ask: graphAsk,
|
|
208
|
+
plan: enginePlan,
|
|
209
|
+
page: {
|
|
210
|
+
resolveSpriteForClass, SPRITE_REGISTRY, resolveSpriteAsset,
|
|
211
|
+
worldDigestRows, roomAffordances, foldWorldState, exposedFacts,
|
|
212
|
+
relatedForTerm, classAncestorChain, openPersistedStore,
|
|
213
|
+
directedGridLayout, roomGraphSvg,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
@@ -51,6 +51,8 @@ import { registerLiveReferenceProvider, registerResearchProvider } from "../../a
|
|
|
51
51
|
// decides when to save/load/clear; this entry only carries the wrapper
|
|
52
52
|
// across the bundle boundary.
|
|
53
53
|
import { openPersistedStore } from "./idb-persist.mjs";
|
|
54
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
55
|
+
import { graphAsk, enginePlan } from "./engine-surface.mjs";
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* A browser chat session over the real turn engine.
|
|
@@ -70,7 +72,7 @@ import { openPersistedStore } from "./idb-persist.mjs";
|
|
|
70
72
|
* is module-scope, last write wins, and every session created after this one
|
|
71
73
|
* shares it.
|
|
72
74
|
*
|
|
73
|
-
* Returns { memoryDir, sessionId, turn }. `turn(line)` resolves to
|
|
75
|
+
* Returns { memoryDir, sessionId, graph, turn }. `turn(line)` resolves to
|
|
74
76
|
* { answer, end, record, plan } and threads focus/last/planState between
|
|
75
77
|
* calls exactly as the CLI session does.
|
|
76
78
|
*/
|
|
@@ -112,6 +114,7 @@ export function createChatSession({ seedPayload = null, vocabSeeded = false, liv
|
|
|
112
114
|
return {
|
|
113
115
|
memoryDir,
|
|
114
116
|
sessionId,
|
|
117
|
+
graph,
|
|
115
118
|
get liveReference() { return liveReferenceOn; },
|
|
116
119
|
/** The page's toggle seam: set the live Wikipedia mode for every later turn
|
|
117
120
|
* (the `/wiki on|off|supplement|always` command sets the same state). */
|
|
@@ -144,4 +147,20 @@ export async function researchedFactRows(memoryDir) {
|
|
|
144
147
|
.map((row) => ({ subject: row.subject, predicate: row.predicate, object: row.object }));
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
|
|
150
|
+
// The page reaches the engine through the one shared surface: `tmct.open()`
|
|
151
|
+
// opens this session, `tmct.turn()` runs the dock, `tmct.ask()` puts a
|
|
152
|
+
// question to the session's own graph. What stays on `tmct.page` is what has
|
|
153
|
+
// no plain-English form — the vendor/provider seams the page registers before
|
|
154
|
+
// the first turn, its IndexedDB wrapper, and the two serializers its export
|
|
155
|
+
// and paste-ingest controls run.
|
|
156
|
+
publishTmctSurface({
|
|
157
|
+
open: createChatSession,
|
|
158
|
+
ask: graphAsk,
|
|
159
|
+
plan: enginePlan,
|
|
160
|
+
page: {
|
|
161
|
+
registerWinkModel, registerReferencePackProvider, registerLiveReferenceProvider,
|
|
162
|
+
registerResearchProvider, normFactTerm, vocabExampleHint, memoryStats,
|
|
163
|
+
openPersistedStore, exportFactsJsonl, researchedFactRows,
|
|
164
|
+
splitSentences: splitSentencesPreservingPaths,
|
|
165
|
+
},
|
|
166
|
+
});
|
|
@@ -22,6 +22,8 @@ import * as source from "../../adapters/source.mjs";
|
|
|
22
22
|
import { computeCodeExplorerData, computeCodeLedger, edgePhrase } from "../../services/code-explorer-viz.mjs";
|
|
23
23
|
import { generateCodeHints } from "../../domain/code-explorer-hints.mjs";
|
|
24
24
|
import { createTurnSession } from "./turn-session.mjs";
|
|
25
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
26
|
+
import { graphAsk, enginePlan } from "./engine-surface.mjs";
|
|
25
27
|
import { tmct_ask } from "../../tools/handlers/tmct-ask.mjs";
|
|
26
28
|
import { RELATIONS } from "../../domain/ask-vocab.mjs";
|
|
27
29
|
|
|
@@ -37,7 +39,7 @@ import { RELATIONS } from "../../domain/ask-vocab.mjs";
|
|
|
37
39
|
* payload carries the starter vocabulary. Teaches land in the same in-memory
|
|
38
40
|
* store, so a taught fact never touches disk.
|
|
39
41
|
*
|
|
40
|
-
* Returns { memoryDir, sessionId, turn }, the createChatSession shape the
|
|
42
|
+
* Returns { memoryDir, sessionId, graph, turn }, the createChatSession shape the
|
|
41
43
|
* page's chat expects.
|
|
42
44
|
*/
|
|
43
45
|
export function createCodeExplorerSession({ graphPayload, seedPayload = null, vocabSeeded = false } = {}) {
|
|
@@ -69,6 +71,7 @@ export function createCodeExplorerSession({ graphPayload, seedPayload = null, vo
|
|
|
69
71
|
return {
|
|
70
72
|
memoryDir,
|
|
71
73
|
sessionId,
|
|
74
|
+
graph,
|
|
72
75
|
turn: turnSession.turn,
|
|
73
76
|
};
|
|
74
77
|
}
|
|
@@ -223,17 +226,27 @@ export function askRelatedFacts(graphPayload, term) {
|
|
|
223
226
|
return { term, rows, asked, grounded };
|
|
224
227
|
}
|
|
225
228
|
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
229
|
+
// This page holds a REAL code graph, so `tmct.ask` is the engine answering
|
|
230
|
+
// over it — the same round trip askRelatedFacts already makes twice per
|
|
231
|
+
// relation kind to build the sidebar's rows.
|
|
232
|
+
//
|
|
233
|
+
// `tmct.page` keeps the re-derivation helpers a graph swapped through the
|
|
234
|
+
// desktop picker re-renders with, plus the wink and term-normalizing seams the
|
|
235
|
+
// chat shares with the ledger page. `createCodeExplorerSession` sits there
|
|
236
|
+
// too: this page's client script is still one raw-text block, and it reaches
|
|
237
|
+
// its factory through the bag rather than through `tmct.open()`.
|
|
238
|
+
publishTmctSurface({
|
|
239
|
+
open: createCodeExplorerSession,
|
|
240
|
+
ask: graphAsk,
|
|
241
|
+
plan: enginePlan,
|
|
242
|
+
page: {
|
|
243
|
+
createCodeExplorerSession,
|
|
244
|
+
askRelatedFacts,
|
|
245
|
+
computeCodeExplorerData,
|
|
246
|
+
computeCodeLedger,
|
|
247
|
+
generateCodeHints,
|
|
248
|
+
parseEntities,
|
|
249
|
+
normFactTerm,
|
|
250
|
+
registerWinkModel,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// engine-surface.mjs — the standard `ask` and `plan` routes a browser page
|
|
2
|
+
// wires into publishTmctSurface (tmct-surface.mjs).
|
|
3
|
+
//
|
|
4
|
+
// Both run the SAME machinery the CLI runs, over the session's own in-memory
|
|
5
|
+
// state rather than a repo on disk: `graphAsk` dispatches `tmct_ask` through
|
|
6
|
+
// the tool layer and hands back the structured envelope beside the prose
|
|
7
|
+
// (dispatchToolStructured's `{ content, data }`); `enginePlan` builds the
|
|
8
|
+
// capability planner's context from whatever the session holds and runs one
|
|
9
|
+
// request through it.
|
|
10
|
+
//
|
|
11
|
+
// Kept out of tmct-surface.mjs on purpose. That module is pure wiring and
|
|
12
|
+
// imports nothing, so memory-ask-browser-entry.mjs — the one bundle that is
|
|
13
|
+
// committed and published, and is deliberately small — can publish the same
|
|
14
|
+
// `globalThis.tmct` without pulling the tool layer and the router into its
|
|
15
|
+
// bundle.
|
|
16
|
+
import { dispatchToolStructured } from "../../tools/server.mjs";
|
|
17
|
+
import { capabilityPlanDeps, noCodeGraph } from "../../services/chat.mjs";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* One plain-English question, answered against the graph this session holds,
|
|
21
|
+
* through the same `tmct_ask` tool the CLI dispatches. Returns
|
|
22
|
+
* `{ answer, data, miss }` — `data` is ask()'s own envelope (the parse, the
|
|
23
|
+
* matches it found, the traversal it walked), which is what a panel renders
|
|
24
|
+
* rows from and what makes an answer auditable.
|
|
25
|
+
*
|
|
26
|
+
* A session whose graph is empty answers with an honest miss. That is the real
|
|
27
|
+
* state of a memory-only page: its rows are facts in a store, and ask() reads
|
|
28
|
+
* a graph. spider-fly is the page that closed that gap for its own world, by
|
|
29
|
+
* projecting its live board into a graph (worldRelationGraphPayload) before
|
|
30
|
+
* every turn — the same route is open to any page whose rows have a shape
|
|
31
|
+
* worth asking over.
|
|
32
|
+
*/
|
|
33
|
+
export async function graphAsk(request, options, session) {
|
|
34
|
+
const { content, data } = await dispatchToolStructured("tmct_ask", { query: request }, {
|
|
35
|
+
graph: session.graph,
|
|
36
|
+
memoryBackend: session.memoryDir,
|
|
37
|
+
});
|
|
38
|
+
return { answer: content, data, miss: Boolean(data?.miss) };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* One compound or maintenance-goal request, planned and executed over whatever
|
|
43
|
+
* this session holds — the same capability router `/plan` reaches, with the
|
|
44
|
+
* same memory-only mode a page with no code graph needs (a code-graph
|
|
45
|
+
* capability there refuses by naming the graph it hasn't got).
|
|
46
|
+
*
|
|
47
|
+
* Returns the planner's own result: `{ refused, why, driver, calls, composed,
|
|
48
|
+
* observed }`. No prose is composed here — a page that wants the written
|
|
49
|
+
* answer asks for it in words through `tmct.turn("/plan …")`, which is the
|
|
50
|
+
* same split `dispatchTool` and `dispatchToolStructured` already draw.
|
|
51
|
+
*/
|
|
52
|
+
export async function enginePlan(request, options, session) {
|
|
53
|
+
const graph = session.graph && !noCodeGraph(session.graph) ? session.graph : null;
|
|
54
|
+
const memoryDir = session.memoryDir ?? null;
|
|
55
|
+
if (!graph && !memoryDir) {
|
|
56
|
+
return {
|
|
57
|
+
refused: true,
|
|
58
|
+
why: "nothing to plan over — this session holds neither a code graph nor a memory store",
|
|
59
|
+
driver: null, calls: [], composed: null, observed: null,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const { buildCapabilityPlanCtx, runCapabilityPlan, declaredCapabilityNames } =
|
|
63
|
+
await import("../../domain/router/drive.mjs");
|
|
64
|
+
const ctx = await buildCapabilityPlanCtx({
|
|
65
|
+
...capabilityPlanDeps(), config: null, source: null, graph, memoryDir,
|
|
66
|
+
});
|
|
67
|
+
try {
|
|
68
|
+
const result = await runCapabilityPlan(request, declaredCapabilityNames(), ctx);
|
|
69
|
+
return {
|
|
70
|
+
refused: Boolean(result.refused),
|
|
71
|
+
why: Array.isArray(result.why) ? result.why.join("; ") : (result.why ?? null),
|
|
72
|
+
driver: result.driver ?? null,
|
|
73
|
+
calls: result.calls ?? [],
|
|
74
|
+
composed: result.composed ?? null,
|
|
75
|
+
observed: result.observed ?? null,
|
|
76
|
+
};
|
|
77
|
+
} finally {
|
|
78
|
+
// The taught registrations are per-ctx: dispose them so the next call
|
|
79
|
+
// re-reads the store instead of meeting a stale name collision.
|
|
80
|
+
for (const dispose of ctx.disposers || []) dispose();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -32,6 +32,7 @@ import { loadLexicon } from "../../domain/grammar/lexicon.mjs";
|
|
|
32
32
|
import { registerWinkModel, winkInstance } from "../../adapters/wink-model.mjs";
|
|
33
33
|
import { memoryStats, exportFactsJsonl } from "./memory-stats.mjs";
|
|
34
34
|
import { openPersistedStore } from "./idb-persist.mjs";
|
|
35
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
35
36
|
|
|
36
37
|
// The pronoun subjects a bounded carry substitutes with the last unique
|
|
37
38
|
// grounded subject in the SAME paragraph. Reset at every blank line, so a
|
|
@@ -192,7 +193,15 @@ export function createIngestSession({ seedPayload = null, vocabSeeded = false }
|
|
|
192
193
|
};
|
|
193
194
|
}
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
// This page grounds pasted prose into facts; it holds no conversation, so
|
|
197
|
+
// `tmct.turn` is unwired and says so, and the one call the page makes is
|
|
198
|
+
// `tmct.session.ingest(text)`. `tmct.page` keeps the recognizer entry point
|
|
199
|
+
// the page also drives directly, the wink seam, the stats fold behind its
|
|
200
|
+
// counters, its IndexedDB wrapper and its JSONL export.
|
|
201
|
+
publishTmctSurface({
|
|
202
|
+
open: createIngestSession,
|
|
203
|
+
page: {
|
|
204
|
+
groundTextToFacts, exportFactsJsonl, registerWinkModel, normFactTerm,
|
|
205
|
+
memoryStats, openPersistedStore,
|
|
206
|
+
},
|
|
207
|
+
});
|
|
@@ -27,6 +27,8 @@ import { computeLedgerDataFromPayload } from "../../services/ledger-viz.mjs";
|
|
|
27
27
|
import { digestTermFromPayloadBrowser } from "./digest-client.mjs";
|
|
28
28
|
import { createTurnSession } from "./turn-session.mjs";
|
|
29
29
|
import { exportFactsJsonl } from "./memory-stats.mjs";
|
|
30
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
31
|
+
import { graphAsk, enginePlan } from "./engine-surface.mjs";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* A browser ledger-dock session over the real turn engine — createChatSession's
|
|
@@ -35,7 +37,7 @@ import { exportFactsJsonl } from "./memory-stats.mjs";
|
|
|
35
37
|
* a fact taught through the dock extends the SAME graph the page renders
|
|
36
38
|
* from, never a disconnected one.
|
|
37
39
|
*
|
|
38
|
-
* Returns { memoryDir, sessionId, turn }, identical to createChatSession.
|
|
40
|
+
* Returns { memoryDir, sessionId, graph, turn }, identical to createChatSession.
|
|
39
41
|
* ledger-viz.mjs's own inline script calls computeLedgerDataFromPayload
|
|
40
42
|
* (re-exported below) on `memoryDir.payload` after a turn whose record shows
|
|
41
43
|
* a successful write (`via: "assert"` or `via: "retract"`, `miss: false`) to
|
|
@@ -52,14 +54,20 @@ export function createLedgerSession({ seedPayload = null, vocabSeeded = false }
|
|
|
52
54
|
const sessionId = globalThis.crypto?.randomUUID?.() ?? String(Date.now());
|
|
53
55
|
|
|
54
56
|
const session = createTurnSession({ memoryDir, graph, lexicon, sessionId, vocabHint });
|
|
55
|
-
return { memoryDir, sessionId, turn: session.turn };
|
|
57
|
+
return { memoryDir, sessionId, graph, turn: session.turn };
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
// `tmct.page` keeps what the page's own script renders with and the engine has
|
|
61
|
+
// no plain-English form for: the payload-to-rows/terms/edges/stats derivation
|
|
62
|
+
// the ledger view re-mounts from after a successful teach, the digest reader,
|
|
63
|
+
// the wink and research seams, and the two serializers behind the dock's
|
|
64
|
+
// paste-ingest and JSONL export.
|
|
65
|
+
publishTmctSurface({
|
|
66
|
+
open: createLedgerSession,
|
|
67
|
+
ask: graphAsk,
|
|
68
|
+
plan: enginePlan,
|
|
69
|
+
page: {
|
|
70
|
+
computeLedgerDataFromPayload, normFactTerm, registerWinkModel, registerResearchProvider,
|
|
71
|
+
splitSentences: splitSentencesPreservingPaths, exportFactsJsonl, digestTermFromPayloadBrowser,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
@@ -1,20 +1,62 @@
|
|
|
1
1
|
// memory-ask-browser-entry.mjs — the esbuild entry for the ledger page's
|
|
2
2
|
// chat dock (`tmct viz`, src/services/ledger-viz.mjs).
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// The one COMMITTED browser bundle: `tmct viz` is a real CLI command run
|
|
5
|
+
// against a user's own local .tmct store, so this ships with the package. It
|
|
6
|
+
// deliberately carries the question lanes only (factAnswer/factReadBack) and
|
|
7
|
+
// not the full turn engine — see ledger-browser-entry.mjs's header for the
|
|
8
|
+
// contrast with the demo site's own 1.5MB dock.
|
|
9
|
+
//
|
|
10
|
+
// It publishes the SAME `globalThis.tmct` every other page does, through
|
|
11
|
+
// tmct-surface.mjs, which imports nothing — so this bundle gets the shared
|
|
12
|
+
// contract without the tool layer or the capability router coming with it.
|
|
13
|
+
// `tmct.turn` and `tmct.plan` are unwired here and say so: this dock asks
|
|
14
|
+
// questions, it does not hold a conversation or plan anything.
|
|
9
15
|
import { factAnswer, factReadBack } from "../../services/chat.mjs";
|
|
10
16
|
import { createInMemoryStore, normFactTerm } from "../../adapters/memory/core.mjs";
|
|
11
17
|
import { digestTermFromPayloadBrowser } from "./digest-client.mjs";
|
|
18
|
+
import { publishTmctSurface } from "./tmct-surface.mjs";
|
|
19
|
+
|
|
20
|
+
/** A question-only session over a payload the page already embeds — no fs I/O,
|
|
21
|
+
* no turn engine. Returns `{ memoryDir }`. */
|
|
22
|
+
export function createMemoryAskSession({ payload = null } = {}) {
|
|
23
|
+
const memoryDir = createInMemoryStore();
|
|
24
|
+
if (payload) memoryDir.payload = payload;
|
|
25
|
+
return { memoryDir };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* One question, answered from the page's own embedded store. Runs the same
|
|
30
|
+
* cascade `runAsk` runs — factAnswer first, then factReadBack, which is where
|
|
31
|
+
* the taught-relation chases live ("who is the grandfather of ishmael") that
|
|
32
|
+
* factAnswer's own lanes don't reach.
|
|
33
|
+
*
|
|
34
|
+
* Both are called with `envelope: null, miss: true`, which arms factAnswer's
|
|
35
|
+
* bare-question fallbacks directly and skips the structural-graph parse
|
|
36
|
+
* pipeline this dock has no use for.
|
|
37
|
+
*
|
|
38
|
+
* Returns `{ answer, data, miss }`. `data` is the reader's own result, so a
|
|
39
|
+
* caller still sees the inferred `goal` line it computed on the way.
|
|
40
|
+
*/
|
|
41
|
+
export async function askMemory(request, options, session) {
|
|
42
|
+
let fact = null;
|
|
43
|
+
try { fact = await factAnswer(session.memoryDir, request, null, true, {}); } catch { fact = null; }
|
|
44
|
+
if (!(fact && fact.text)) {
|
|
45
|
+
try { fact = await factReadBack(session.memoryDir, request, null, true, null); } catch { fact = null; }
|
|
46
|
+
}
|
|
47
|
+
if (!(fact && fact.text)) return { answer: null, data: null, miss: true };
|
|
48
|
+
return { answer: fact.text, data: fact, miss: false };
|
|
49
|
+
}
|
|
12
50
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// the
|
|
19
|
-
|
|
20
|
-
|
|
51
|
+
// `tmct.page` keeps the term normalizer the dock refocuses the ledger with,
|
|
52
|
+
// and the client-side digest reader — the narrative the demo ledger's live
|
|
53
|
+
// bundle produces, over the static structure table this page embeds.
|
|
54
|
+
// `fallback`, because the demo ledger page carries this bundle AND the full
|
|
55
|
+
// turn engine at once: this one answers where it is alone, and steps aside
|
|
56
|
+
// wherever the live dock is loaded, whichever order the two arrive in.
|
|
57
|
+
publishTmctSurface({
|
|
58
|
+
open: createMemoryAskSession,
|
|
59
|
+
ask: askMemory,
|
|
60
|
+
page: { normFactTerm, digestTermFromPayloadBrowser },
|
|
61
|
+
fallback: true,
|
|
62
|
+
});
|