@polycode-projects/the-mechanical-code-talker 5.0.5 → 5.0.6
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 +72 -19
- package/package.json +1 -1
- package/src/domain/ask.mjs +2 -4
- package/src/domain/codegraph.mjs +23 -9
- package/src/domain/game-config.mjs +12 -0
- package/src/services/adventure-viz.mjs +5 -2
- package/src/services/chat-page-viz.mjs +5 -2
- package/src/services/chat.mjs +104 -16
- package/src/services/code-explorer-viz.mjs +3 -2
- package/src/services/ingest-viz.mjs +5 -4
- package/src/services/ledger-viz.mjs +5 -2
- package/src/services/mud-viz.mjs +4 -3
- package/src/services/mudiii-scene.mjs +136 -12
- package/src/services/mudiii-viz.mjs +540 -64
- package/src/services/p2p-room.mjs +1 -1
- package/src/services/plan-viz.mjs +3 -2
- package/src/services/predator-prey.mjs +65 -10
- package/src/services/research-viz.mjs +5 -4
- package/src/services/spider-fly-viz.mjs +3 -2
- package/src/services/sprite-catalog-viz.mjs +3 -2
- package/src/services/viz-theme.mjs +20 -0
- package/src/surfaces/web/memory-ask-browser.bundle.js +86 -86
- package/src/surfaces/web/mudiii-browser-entry.mjs +70 -34
|
@@ -266,7 +266,7 @@ export function createP2pRoom({
|
|
|
266
266
|
// is a change worth broadcasting even though its provenance never moved.
|
|
267
267
|
let cachedRetractions = [];
|
|
268
268
|
const seenRetractionValueById = new Map();
|
|
269
|
-
const retractionDiffValue = (fact) => `${fact.provenance}
|
|
269
|
+
const retractionDiffValue = (fact) => `${fact.provenance}\u0000${fact.object}`;
|
|
270
270
|
|
|
271
271
|
// Store-touching work runs one job at a time, in arrival order. Every path
|
|
272
272
|
// that reads or writes memoryDir/seenProvenanceById/cachedRows crosses at
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// inline script degrades honestly when that sibling script is absent or
|
|
19
19
|
// fails to load — the live controls disable themselves rather than pretend
|
|
20
20
|
// to work.
|
|
21
|
-
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, countLabel } from "./viz-theme.mjs";
|
|
21
|
+
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, countLabel, demoEyebrowHtml, EYEBROW_LINKS_CSS } from "./viz-theme.mjs";
|
|
22
22
|
import { planToPddl } from "./plan-pddl.mjs";
|
|
23
23
|
|
|
24
24
|
const BOARD_W = 640;
|
|
@@ -334,6 +334,7 @@ button { font: inherit; }
|
|
|
334
334
|
/* ---- head strip: the nameplate ---- */
|
|
335
335
|
.headStrip { display: flex; justify-content: space-between; align-items: flex-end; gap: .6rem; flex-wrap: wrap; padding: .9rem 1.1rem .75rem; background: linear-gradient(180deg, var(--strip2), var(--strip)); border-bottom: 1px solid var(--line); border-radius: 9px 9px 0 0; }
|
|
336
336
|
.eyebrow { font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); margin: 0 0 .35rem; }
|
|
337
|
+
${EYEBROW_LINKS_CSS}
|
|
337
338
|
h1 { font-family: ${SERIF_STACK}; font-size: 1.2rem; font-weight: 600; margin: 0; color: var(--ink); }
|
|
338
339
|
.chip { font-family: ${MONO_STACK}; font-size: .64rem; letter-spacing: .02em; color: var(--muted); border: 1px solid var(--line); border-radius: 3px; padding: .22rem .6rem; background: var(--well); text-align: right; }
|
|
339
340
|
|
|
@@ -432,7 +433,7 @@ h1 { font-family: ${SERIF_STACK}; font-size: 1.2rem; font-weight: 600; margin: 0
|
|
|
432
433
|
<div class="rack">
|
|
433
434
|
<div class="headStrip">
|
|
434
435
|
<div>
|
|
435
|
-
<div class="eyebrow"
|
|
436
|
+
<div class="eyebrow">${demoEyebrowHtml("plan", "plan")}</div>
|
|
436
437
|
<h1 id="pageTitle">${escapeHtml(pageTitle)}</h1>
|
|
437
438
|
</div>
|
|
438
439
|
<span class="chip">blocks archetype · ${pageData.layouts.length} snapshots · plan: findActionPath</span>
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// which is board-size agnostic and models vision as plain Chebyshev distance
|
|
15
15
|
// with no line of sight — a building blocks movement, never sight.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
17
|
+
// Four mechanics are OPT-IN, off unless `config` asks for them by name, so a
|
|
18
18
|
// cast that does not want them sees the same board it always did:
|
|
19
19
|
//
|
|
20
20
|
// - `carryPreyToWeb` splits catching from eating. A predator that shares a
|
|
@@ -27,6 +27,11 @@
|
|
|
27
27
|
// - `layEggs` adds the reproduction stage: an egg (mgx:laid-at-turn) laid in
|
|
28
28
|
// a web once mass crosses a threshold, hatching (mgx:hatched-into) into
|
|
29
29
|
// hatchlings that split the egg's mass.
|
|
30
|
+
// - `blendPreyDecision` replaces the prey's strict evade-then-forage order
|
|
31
|
+
// with one weighted score over both distances (`preyThreatWeight`), so a
|
|
32
|
+
// prey can take a crumb that costs it nothing. Off, prey abandon food the
|
|
33
|
+
// moment anything is in view. scripts/compare-prey-decision.mjs measures
|
|
34
|
+
// the two against each other.
|
|
30
35
|
|
|
31
36
|
import {
|
|
32
37
|
DEFAULT_FACING, TOWN_SQUARE_LAYOUTS,
|
|
@@ -396,6 +401,29 @@ export function greedyToward(fromCell, towardCell, applyActions) {
|
|
|
396
401
|
);
|
|
397
402
|
}
|
|
398
403
|
|
|
404
|
+
/** One-ply greedy over BOTH distances at once, rather than one rung after the
|
|
405
|
+
* other: the reachable cell (or staying put) that maximizes
|
|
406
|
+
*
|
|
407
|
+
* weight * distance-from-`awayFrom` - (1 - weight) * distance-to-`towardCell`
|
|
408
|
+
*
|
|
409
|
+
* At weight 1 this is greedyAway and at weight 0 it is greedyToward. In
|
|
410
|
+
* between it lets an agent take a step toward food that costs it little or no
|
|
411
|
+
* distance from the thing hunting it, which strict priority rungs cannot do.
|
|
412
|
+
*
|
|
413
|
+
* Falls back to whichever single term it still has when the other cell is
|
|
414
|
+
* null, so a caller never has to check first. */
|
|
415
|
+
export function greedyBlend(fromCell, awayFrom, towardCell, applyActions, weight) {
|
|
416
|
+
if (!awayFrom) return greedyToward(fromCell, towardCell, applyActions);
|
|
417
|
+
if (!towardCell) return greedyAway(fromCell, awayFrom, applyActions);
|
|
418
|
+
const w = Number.isFinite(Number(weight)) ? Number(weight) : DEFAULT_GAME_CONFIG.mudiii.preyThreatWeight;
|
|
419
|
+
return bestOneStepBy(
|
|
420
|
+
fromCell, applyActions,
|
|
421
|
+
(cell) => w * chebyshevDistance(cell.x, cell.y, awayFrom.x, awayFrom.y)
|
|
422
|
+
- (1 - w) * chebyshevDistance(cell.x, cell.y, towardCell.x, towardCell.y),
|
|
423
|
+
(score, bestScore) => score > bestScore,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
|
|
399
427
|
/** A seeded, uniform pick among staying put or any one-ply reachable cell.
|
|
400
428
|
* Deterministic and replayable, and it looks random to somebody watching.
|
|
401
429
|
* Both roles' last rung: a motionless predator reads as a broken page. */
|
|
@@ -1053,6 +1081,10 @@ export async function runTownSquareTick(memoryDir, {
|
|
|
1053
1081
|
|
|
1054
1082
|
const carriesPrey = config.carryPreyToWeb === true;
|
|
1055
1083
|
const buildsWebs = config.buildWebs === true;
|
|
1084
|
+
const blendsPreyDecision = config.blendPreyDecision === true;
|
|
1085
|
+
const preyThreatWeight = Number.isFinite(Number(config.preyThreatWeight))
|
|
1086
|
+
? Number(config.preyThreatWeight)
|
|
1087
|
+
: DEFAULT_GAME_CONFIG.mudiii.preyThreatWeight;
|
|
1056
1088
|
const boardNoun = lay.boardNoun ?? "square";
|
|
1057
1089
|
const webbedAt = (c) => hasActiveWebAt(lay, state, k, c.x, c.y, config.webDurationTurns);
|
|
1058
1090
|
// Widened in place as predators spin webs this tick, for the renderer's own
|
|
@@ -1152,18 +1184,41 @@ export async function runTownSquareTick(memoryDir, {
|
|
|
1152
1184
|
goal = goalLine("trapped");
|
|
1153
1185
|
mood = "scared";
|
|
1154
1186
|
} else if (threat) {
|
|
1155
|
-
rung = role === "predator" ? "avoid" : "evade";
|
|
1156
|
-
// A fleeing prey that already knows where food is should flee toward
|
|
1157
|
-
// it, not away from it — among cells that are equally safe, break the
|
|
1158
|
-
// tie toward the nearest believed crumb. Prey-only: the predator's own
|
|
1159
|
-
// avoid rung passes nothing, so its ties still resolve the old way.
|
|
1160
1187
|
const towardFood = role === "prey"
|
|
1161
1188
|
? nearestBelievedTarget(agentId, fromCell, [...foodIds].sort(), state, foodBeliefOpts)
|
|
1162
1189
|
: null;
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1190
|
+
if (blendsPreyDecision && role === "prey" && towardFood) {
|
|
1191
|
+
// The one case the two rungs disagree about: this prey believes a
|
|
1192
|
+
// predator AND food, so a strict order has to pick between them and a
|
|
1193
|
+
// score does not.
|
|
1194
|
+
nextCell = greedyBlend(fromCell, threat.cell, towardFood.cell, applyActions, preyThreatWeight);
|
|
1195
|
+
const closedOnFood = chebyshevDistance(fromCell.x, fromCell.y, towardFood.cell.x, towardFood.cell.y)
|
|
1196
|
+
> chebyshevDistance(nextCell.x, nextCell.y, towardFood.cell.x, towardFood.cell.y);
|
|
1197
|
+
// The score is one number, but the step it buys is still one of two
|
|
1198
|
+
// legible things: this move closed on the crumb, or it did not. The
|
|
1199
|
+
// rung and the goal line say which, so no surface has to render
|
|
1200
|
+
// "mostly evading, somewhat hungry".
|
|
1201
|
+
rung = closedOnFood ? "forage" : "evade";
|
|
1202
|
+
plan = stepPlan(fromCell, nextCell);
|
|
1203
|
+
goal = closedOnFood
|
|
1204
|
+
? goalLine("forage", {
|
|
1205
|
+
subject: towardFood.subject,
|
|
1206
|
+
cell: cellId(towardFood.cell.x, towardFood.cell.y),
|
|
1207
|
+
arrived: nextCell.x === towardFood.cell.x && nextCell.y === towardFood.cell.y,
|
|
1208
|
+
})
|
|
1209
|
+
: goalLine("evade", { subject: threat.subject, cell: cellId(threat.cell.x, threat.cell.y) });
|
|
1210
|
+
mood = closedOnFood ? "calm" : "scared";
|
|
1211
|
+
} else {
|
|
1212
|
+
rung = role === "predator" ? "avoid" : "evade";
|
|
1213
|
+
// A fleeing prey that already knows where food is should flee toward
|
|
1214
|
+
// it, not away from it — among cells that are equally safe, break the
|
|
1215
|
+
// tie toward the nearest believed crumb. Prey-only: the predator's own
|
|
1216
|
+
// avoid rung passes nothing, so its ties still resolve the old way.
|
|
1217
|
+
nextCell = greedyAway(fromCell, threat.cell, applyActions, { towardCell: towardFood?.cell ?? null });
|
|
1218
|
+
plan = stepPlan(fromCell, nextCell);
|
|
1219
|
+
goal = goalLine(rung, { subject: threat.subject, cell: cellId(threat.cell.x, threat.cell.y) });
|
|
1220
|
+
mood = "scared";
|
|
1221
|
+
}
|
|
1167
1222
|
} else {
|
|
1168
1223
|
const quarry = role === "predator"
|
|
1169
1224
|
? nearestBelievedTarget(agentId, fromCell, prey, state, beliefOpts)
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// renderResearchHtml() is pure: no I/O, deterministic output for identical
|
|
20
20
|
// input. scripts/build-demo-site.mjs calls it directly and writes the result to
|
|
21
21
|
// public/research.html, after research-browser.bundle.js already exists.
|
|
22
|
-
import { THEME_TOKENS_CSS, MONO_STACK, escapeHtml } from "./viz-theme.mjs";
|
|
22
|
+
import { THEME_TOKENS_CSS, MONO_STACK, escapeHtml, demoEyebrowHtml, EYEBROW_LINKS_CSS } from "./viz-theme.mjs";
|
|
23
23
|
import { fetchWithProgress, loadProgressLine, factTripleParts } from "./memory-panel-viz.mjs";
|
|
24
24
|
import { createTicker, prefersReducedMotion } from "./viz-ticker.mjs";
|
|
25
25
|
import { loadWinkVendor } from "./viz-boot.mjs";
|
|
@@ -108,7 +108,8 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
108
108
|
header.topbar { display: flex; align-items: flex-start; justify-content: space-between; gap: 1.4rem; flex-wrap: wrap; padding: .6rem 0 1rem; border-bottom: 1px solid var(--line); margin-bottom: 1.3rem; }
|
|
109
109
|
.brand { display: flex; flex-direction: column; gap: .3rem; max-width: 640px; }
|
|
110
110
|
.eyebrow { font-family: ${MONO_STACK}; font-size: .72rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
|
|
111
|
-
|
|
111
|
+
${EYEBROW_LINKS_CSS}
|
|
112
|
+
.subtitle { margin: 0; font-size: .92rem; font-weight: 400; color: var(--ink); opacity: .82; max-width: 58ch; }
|
|
112
113
|
.statuspanel { display: flex; flex-wrap: wrap; gap: .5rem 1.1rem; background: var(--card); border: 1px solid var(--line); border-radius: 6px; padding: .5rem .9rem; }
|
|
113
114
|
.statuspanel .stat { display: flex; flex-direction: column; gap: .14rem; min-width: 7rem; }
|
|
114
115
|
.statuspanel .stat-label { font-family: ${MONO_STACK}; font-size: .6rem; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
|
|
@@ -240,8 +241,8 @@ ${DASH_DARK_CHROME_CSS}
|
|
|
240
241
|
<div class="wrap">
|
|
241
242
|
<header class="topbar">
|
|
242
243
|
<div class="brand">
|
|
243
|
-
<span class="eyebrow"
|
|
244
|
-
<
|
|
244
|
+
<span class="eyebrow">${demoEyebrowHtml("research", "research")}</span>
|
|
245
|
+
<h1 class="subtitle">Grow one graph three ways. Watch what it learns, then ask a question scoped to the sources you trust.</h1>
|
|
245
246
|
</div>
|
|
246
247
|
<div class="statuspanel" id="statusPanel" aria-live="polite">
|
|
247
248
|
<div class="stat stat-facts"><span class="stat-label">facts in the graph</span><span class="stat-value" id="statFacts">—</span></div>
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
// bundle's own real ES exports instead, since (unlike ledger-viz, which
|
|
33
33
|
// reuses a FIXED shared bundle it can't extend for one page's own needs) this
|
|
34
34
|
// page ships its own dedicated bundle and can just export what it needs.
|
|
35
|
-
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, embedScriptText, meterBarHtml } from "./viz-theme.mjs";
|
|
35
|
+
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, embedScriptText, meterBarHtml, demoEyebrowHtml, EYEBROW_LINKS_CSS } from "./viz-theme.mjs";
|
|
36
36
|
import { createTicker } from "./viz-ticker.mjs";
|
|
37
37
|
import { loadWinkVendor } from "./viz-boot.mjs";
|
|
38
38
|
import { GRID_SIZE, WEB_HOME, WEB_RADIUS, isInWebBlock, cellId, agentKindOf } from "../domain/spider-fly-world.mjs";
|
|
@@ -250,6 +250,7 @@ ${THEME_TOKENS_CSS}
|
|
|
250
250
|
.mono { font-family: ${MONO_STACK}; }
|
|
251
251
|
main { max-width: 1120px; margin: 0 auto; padding: 1.4rem 1.2rem 2.2rem; }
|
|
252
252
|
.eyebrow { font-family: ${MONO_STACK}; font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; color: var(--chrome-accent); }
|
|
253
|
+
${EYEBROW_LINKS_CSS}
|
|
253
254
|
h1 { font-size: 1.4rem; margin: .3rem 0 .9rem; text-wrap: balance; }
|
|
254
255
|
button { font: inherit; color: inherit; background: none; cursor: pointer; }
|
|
255
256
|
button:focus-visible, input:focus-visible, .sprite:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
|
|
@@ -443,7 +444,7 @@ ${THEME_TOKENS_CSS}
|
|
|
443
444
|
<main>
|
|
444
445
|
<div class="stage page-head">
|
|
445
446
|
<div class="head-inner">
|
|
446
|
-
<div class="eyebrow"
|
|
447
|
+
<div class="eyebrow">${demoEyebrowHtml("spider-fly", "spider and fly")}</div>
|
|
447
448
|
<h1>Multiple competing planning agents</h1>
|
|
448
449
|
</div>
|
|
449
450
|
<div></div>
|
|
@@ -51,7 +51,7 @@ import { spriteFactRows } from "../domain/sprite-facts.mjs";
|
|
|
51
51
|
import { SEED_TAXONOMY } from "../domain/spider-fly-world.mjs";
|
|
52
52
|
import { loadSlice, loadMap, toFacts, WORDNET_DIR } from "../adapters/corpus/conceptnet.mjs";
|
|
53
53
|
import { join } from "node:path";
|
|
54
|
-
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, embedScriptText } from "./viz-theme.mjs";
|
|
54
|
+
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, embedScriptText, demoEyebrowHtml, EYEBROW_LINKS_CSS } from "./viz-theme.mjs";
|
|
55
55
|
|
|
56
56
|
const DEFAULT_TITLE = "tmct — the sprite library";
|
|
57
57
|
const MAX_CHAIN_DISPLAY = 6;
|
|
@@ -905,6 +905,7 @@ ${THEME_TOKENS_CSS}
|
|
|
905
905
|
.appbar { display: flex; align-items: flex-end; gap: 1rem; margin: 0 -1.2rem; padding: .6rem 1.2rem 0; background: var(--ai-bar); }
|
|
906
906
|
.appbar h1 { font-family: ${MONO_STACK}; font-size: .84rem; font-weight: 600; letter-spacing: .02em; margin: 0; padding: .32rem .8rem .38rem; background: var(--ai-panel); color: var(--ink); border-radius: 4px 4px 0 0; }
|
|
907
907
|
.appbar .doc-sub { font-family: ${MONO_STACK}; font-size: .66rem; letter-spacing: .07em; text-transform: uppercase; color: color-mix(in srgb, var(--ai-bar-ink) 65%, transparent); padding-bottom: .5rem; }
|
|
908
|
+
${EYEBROW_LINKS_CSS}
|
|
908
909
|
.topbar { position: sticky; top: 0; z-index: 2; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .9rem; background: var(--ai-panel); border-bottom: 1px solid var(--ai-edge); margin: 0 -1.2rem 1.4rem; padding: .5rem 1.2rem; }
|
|
909
910
|
.jump { font-family: ${MONO_STACK}; font-size: .7rem; padding: .2rem .6rem; border: 1px solid var(--ai-edge); border-radius: 3px; background: transparent; color: var(--ink); text-decoration: none; }
|
|
910
911
|
.jump:hover { border-color: var(--corpus); color: var(--corpus); }
|
|
@@ -982,7 +983,7 @@ ${dockCss}</style>
|
|
|
982
983
|
<main>
|
|
983
984
|
<header class="appbar">
|
|
984
985
|
<h1>Sprites</h1>
|
|
985
|
-
<span class="doc-sub"
|
|
986
|
+
<span class="doc-sub">${demoEyebrowHtml("sprites", "the sprite library")}</span>
|
|
986
987
|
</header>
|
|
987
988
|
${composerHtml}
|
|
988
989
|
${dockHtml}
|
|
@@ -32,6 +32,26 @@ export function embedScriptText(js) {
|
|
|
32
32
|
return String(js ?? "").replaceAll("</script", "<\\/script");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** The three-link nav every demo page's header opens with: the site's own
|
|
36
|
+
* name (home), the demo's own name (its own page) and "about" (its about
|
|
37
|
+
* page). `page` is the demo's key in scripts/site-pages.mjs's DEMO_PAGES,
|
|
38
|
+
* which is also its filename stem (e.g. "mud" -> mud.html); the about-page
|
|
39
|
+
* suffix below mirrors that same file's `aboutPageOf`, restated rather than
|
|
40
|
+
* imported since scripts/ sits outside src/ and never ships with the
|
|
41
|
+
* published package. Returns the inner markup only — the caller supplies
|
|
42
|
+
* the wrapping element (div/h1/span) and keeps whatever class and
|
|
43
|
+
* neighbouring markup its own header already carries. Style hook:
|
|
44
|
+
* EYEBROW_LINKS_CSS. */
|
|
45
|
+
export function demoEyebrowHtml(page, label) {
|
|
46
|
+
return `<span class="eyebrow-links"><a href="./index.html">tmct</a> · <a href="./${page}.html">${escapeHtml(label)}</a> · <a href="./${page}-about.html">about</a></span>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Styling every page that calls demoEyebrowHtml splices into its own inline
|
|
50
|
+
* <style>: the links read in the eyebrow's own colour (each page sets that
|
|
51
|
+
* colour itself), underlining only on hover/focus. */
|
|
52
|
+
export const EYEBROW_LINKS_CSS = `.eyebrow-links a { color: inherit; text-decoration: none; }
|
|
53
|
+
.eyebrow-links a:hover, .eyebrow-links a:focus-visible { text-decoration: underline; }`;
|
|
54
|
+
|
|
35
55
|
/** A world name read as a name in a scenario dropdown: "mud-garden" ->
|
|
36
56
|
* "mud garden". The world's own hyphenated id is the only thing every caller
|
|
37
57
|
* is guaranteed to have, so a scenario that wants a hand-written label passes
|