@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
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
// `.toString()` — exactly ledger-viz.mjs's own `facetCounts`/
|
|
23
23
|
// `resolveAnsweredTerm` pattern — because they are genuinely UI-only, with no
|
|
24
24
|
// reason to live inside the engine bundle: `createTicker` (viz-ticker.mjs,
|
|
25
|
-
// the shared play/pause/step/reset primitive), `
|
|
25
|
+
// the shared play/pause/step/reset primitive), `agentKindOf` (the shared
|
|
26
|
+
// kind-from-id helper, defined once in spider-fly-world.mjs),
|
|
26
27
|
// `threadCellsForSpiderPlan` (the silk-thread reconstruction), `nextCorpses`
|
|
27
28
|
// (the dying-agent bookkeeping behind the dusty-corner corpse pile) and
|
|
28
29
|
// `facingDegreesFor` (plan-driven sprite orientation) — all kept as real,
|
|
@@ -32,11 +33,14 @@
|
|
|
32
33
|
// bundle's own real ES exports instead, since (unlike ledger-viz, which
|
|
33
34
|
// reuses a FIXED shared bundle it can't extend for one page's own needs) this
|
|
34
35
|
// 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 } from "./viz-theme.mjs";
|
|
36
|
+
import { THEME_TOKENS_CSS, SERIF_STACK, MONO_STACK, escapeHtml, embedJson, embedScriptText, meterBarHtml } from "./viz-theme.mjs";
|
|
36
37
|
import { createTicker } from "./viz-ticker.mjs";
|
|
37
|
-
import {
|
|
38
|
+
import { loadWinkVendor } from "./viz-boot.mjs";
|
|
39
|
+
import { GRID_SIZE, WEB_HOME, WEB_RADIUS, isInWebBlock, cellId, agentKindOf } from "../domain/spider-fly-world.mjs";
|
|
38
40
|
import { FLY_INITIAL_MASS, EGG_LAY_MASS_THRESHOLD } from "./spider-fly.mjs";
|
|
39
|
-
import {
|
|
41
|
+
import { believedFactSentence } from "./spider-fly-turn.mjs";
|
|
42
|
+
import { DEFAULT_GAME_CONFIG, massScaleFor } from "../domain/game-config.mjs";
|
|
43
|
+
import { resolveSpriteRequest } from "../domain/sprite-request.mjs";
|
|
40
44
|
|
|
41
45
|
// A larger cell than this page's first cut (44px) — the board sits in its
|
|
42
46
|
// own full-width block, so a bigger cell keeps it reading as the page's
|
|
@@ -62,13 +66,6 @@ function webCellIds() {
|
|
|
62
66
|
return out;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
/** An agent id's class for sprite/HUD purposes — "spider-2" -> "spider",
|
|
66
|
-
* "fly-10" -> "fly", "egg-1" -> "egg". Self-contained (no outer refs),
|
|
67
|
-
* `.toString()`-splice safe. */
|
|
68
|
-
export function classOfAgentId(id) {
|
|
69
|
-
return String(id).replace(/-\d+$/, "");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
69
|
/**
|
|
73
70
|
* Reconstruct the spider's remaining silk-thread path — the sequence of
|
|
74
71
|
* cell ids from its CURRENT cell (after this tick's one executed step) to
|
|
@@ -86,7 +83,7 @@ export function classOfAgentId(id) {
|
|
|
86
83
|
* `geometry` is `{ parseCellId, cellId, directionDelta }` — the exact three
|
|
87
84
|
* grid-geometry primitives spider-fly-world.mjs exports, injected rather
|
|
88
85
|
* than imported so this function stays `.toString()`-splice safe (the
|
|
89
|
-
* inlined page calls it with `window.
|
|
86
|
+
* inlined page calls it with `window.tmct`'s own re-exports of the
|
|
90
87
|
* same three). Returns the ordered cell-id array (length > 1) or null.
|
|
91
88
|
*/
|
|
92
89
|
export function threadCellsForSpiderPlan(agents, geometry) {
|
|
@@ -128,54 +125,6 @@ export function facingDegreesFor(plan, previousDegrees) {
|
|
|
128
125
|
return previousDegrees ?? 0;
|
|
129
126
|
}
|
|
130
127
|
|
|
131
|
-
/** The instance-level `mgx:feels` emotion for one spider-fly agent this
|
|
132
|
-
* tick — a PURE derivation from state spider-fly.mjs's own `runSpiderFlyTick`
|
|
133
|
-
* already returns on the agent (`goal`/`mass`), never a new persisted fact,
|
|
134
|
-
* the same derive-don't-duplicate posture `hasActiveWebAt` already holds for
|
|
135
|
-
* web state (PLAN_GAMES_UPLIFT_V3.md §B.2.4). Operator-confirmed mapping: a
|
|
136
|
-
* spider that just ate is happy; a spider carrying an uncaught fly or
|
|
137
|
-
* mid-chase (chasing, or co-located and about to catch) is angry (predatory
|
|
138
|
-
* focus); a spider avoiding another spider is scared; a spider holding
|
|
139
|
-
* position or building a web is calm. A fly evading a believed-visible
|
|
140
|
-
* spider is scared, and so is the sharper form of the same fear — just
|
|
141
|
-
* caught, or already being carried; a fly with no threat visible (wandering,
|
|
142
|
-
* or trapped with none in sight) is calm.
|
|
143
|
-
*
|
|
144
|
-
* `agent.goal` is spider-fly.mjs's own fixed-template text (`goalLineFor`'s
|
|
145
|
-
* literal phrasing) — the only reliable per-tick discriminator this
|
|
146
|
-
* function's caller has, and every real goal string that engine can ever
|
|
147
|
-
* produce is matched below by its own distinguishing prefix. A goal this
|
|
148
|
-
* function doesn't recognize (a freshly hatched/spawned agent's "no goal
|
|
149
|
-
* yet", or the transient "X is gone — re-evaluating" scrub once a named
|
|
150
|
-
* agent dies) falls through to "calm" — the 6-word vocabulary's own
|
|
151
|
-
* deliberate no-strong-emotion baseline (sprite-expressions.mjs's B.2.1
|
|
152
|
-
* design), not a guess.
|
|
153
|
-
*
|
|
154
|
-
* `maxMass` is accepted for parity with this page's own per-class mass-bar
|
|
155
|
-
* denominators (`SPIDERFLY.maxSpiderMass`/`maxFlyMass`) but unused today —
|
|
156
|
-
* no state in the operator's own confirmed table keys off a mass ratio,
|
|
157
|
-
* only off the goal text above. Self-contained (no outer refs),
|
|
158
|
-
* `.toString()`-splice safe. */
|
|
159
|
-
export function emotionFor(agent, kind, maxMass) {
|
|
160
|
-
const goal = agent?.goal || "";
|
|
161
|
-
if (kind === "spider") {
|
|
162
|
-
if (goal.startsWith("just ate")) return "happy";
|
|
163
|
-
if (goal.startsWith("carrying") || goal.startsWith("chasing") || goal.startsWith("co-located with")) return "angry";
|
|
164
|
-
if (goal.startsWith("avoiding")) return "scared";
|
|
165
|
-
return "calm";
|
|
166
|
-
}
|
|
167
|
-
if (kind === "fly") {
|
|
168
|
-
if (
|
|
169
|
-
goal.startsWith("evading")
|
|
170
|
-
|| goal.startsWith("being carried by")
|
|
171
|
-
|| goal.startsWith("just caught by")
|
|
172
|
-
|| goal.startsWith("trapped in an active web")
|
|
173
|
-
) return "scared";
|
|
174
|
-
return "calm";
|
|
175
|
-
}
|
|
176
|
-
return "calm";
|
|
177
|
-
}
|
|
178
|
-
|
|
179
128
|
/**
|
|
180
129
|
* The updated corpse set for one redraw (§A.2.5 — visual-only, entirely
|
|
181
130
|
* client-side; the actual starve/eat removal already happened in the
|
|
@@ -200,7 +149,7 @@ export function nextCorpses(prevCorpses, prevAgents, agents, turn, lingerTurns =
|
|
|
200
149
|
if (agents[id] || out[id]) continue;
|
|
201
150
|
const cell = prevAgents[id]?.cell;
|
|
202
151
|
if (!cell) continue;
|
|
203
|
-
out[id] = { cls:
|
|
152
|
+
out[id] = { cls: agentKindOf(id), cell, diedAtTurn: turn };
|
|
204
153
|
}
|
|
205
154
|
return out;
|
|
206
155
|
}
|
|
@@ -409,9 +358,9 @@ ${THEME_TOKENS_CSS}
|
|
|
409
358
|
texture (repeating-linear-gradient) instead of a smooth gradient bar —
|
|
410
359
|
discrete resource units, the same reading-at-a-glance language a 90s
|
|
411
360
|
strategy game's own gold/mana meters use. */
|
|
412
|
-
.
|
|
413
|
-
.
|
|
414
|
-
.
|
|
361
|
+
.meter-track { height: 7px; margin-top: .35rem; background: var(--chrome-well); border: 1px solid var(--chrome-edge-lo); box-shadow: var(--chrome-shadow-inset); border-radius: 1px; overflow: hidden; }
|
|
362
|
+
.meter-fill { height: 100%; background: repeating-linear-gradient(90deg, var(--taught) 0 6px, rgba(0, 0, 0, .25) 6px 7px); }
|
|
363
|
+
.meter-fill.fly { background: repeating-linear-gradient(90deg, var(--fly) 0 6px, rgba(0, 0, 0, .25) 6px 7px); }
|
|
415
364
|
.hud-empty { color: var(--muted); font-size: .85rem; }
|
|
416
365
|
.chatlog { display: flex; flex-direction: column; gap: .4rem; max-height: 220px; overflow-y: auto; margin-bottom: .5rem; }
|
|
417
366
|
.chatlog:empty { display: none; margin-bottom: 0; }
|
|
@@ -591,12 +540,25 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
591
540
|
(function () {
|
|
592
541
|
"use strict";
|
|
593
542
|
const createTicker = ${createTicker.toString()};
|
|
594
|
-
const
|
|
543
|
+
const loadWinkVendor = ${loadWinkVendor.toString()};
|
|
544
|
+
const agentKindOf = ${agentKindOf.toString()};
|
|
595
545
|
const threadCellsForSpiderPlan = ${threadCellsForSpiderPlan.toString()};
|
|
596
546
|
const facingDegreesFor = ${facingDegreesFor.toString()};
|
|
597
|
-
const
|
|
547
|
+
const resolveSpriteRequest = ${resolveSpriteRequest.toString()};
|
|
598
548
|
const nextCorpses = ${nextCorpses.toString()};
|
|
599
|
-
const
|
|
549
|
+
const massScaleFor = ${massScaleFor.toString()};
|
|
550
|
+
const believedFactSentence = ${believedFactSentence.toString()};
|
|
551
|
+
const escapeHtml = ${escapeHtml.toString()};
|
|
552
|
+
const meterBarHtml = ${meterBarHtml.toString()};
|
|
553
|
+
const esc = escapeHtml;
|
|
554
|
+
// The lemma/POS tier every sibling dock loads: this game's OWN commands
|
|
555
|
+
// (tick, address, stop, see) are closed regexes that never need it, but a
|
|
556
|
+
// line that doesn't match one of those still falls through to the
|
|
557
|
+
// ordinary chat/ask/teach lanes (spiderFlyTurn returns null), and those
|
|
558
|
+
// lanes read exactly as much better with wink loaded as they do on every
|
|
559
|
+
// other page. Fired eagerly here so it is likely settled well before a
|
|
560
|
+
// visitor's first ordinary (non-game) question.
|
|
561
|
+
loadWinkVendor({ register: (factory) => tmct.page.registerWinkModel(factory) })();
|
|
600
562
|
const el = (id) => document.getElementById(id);
|
|
601
563
|
const boardFrame = el("boardFrame");
|
|
602
564
|
const boardCanvas = el("board");
|
|
@@ -660,7 +622,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
660
622
|
const goalById = {};
|
|
661
623
|
const spriteEls = {};
|
|
662
624
|
const facingByAgent = {};
|
|
663
|
-
const
|
|
625
|
+
const moodByAgent = {};
|
|
664
626
|
let corpses = {};
|
|
665
627
|
const corpseEls = {};
|
|
666
628
|
let selectedAddresseeId = null;
|
|
@@ -675,30 +637,37 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
675
637
|
function removeStaleSprites(agents) {
|
|
676
638
|
for (const id of Object.keys(spriteEls)) {
|
|
677
639
|
if (!agents[id]) {
|
|
678
|
-
spriteEls[id].remove(); delete spriteEls[id]; delete goalById[id]; delete facingByAgent[id]; delete
|
|
640
|
+
spriteEls[id].remove(); delete spriteEls[id]; delete goalById[id]; delete facingByAgent[id]; delete moodByAgent[id];
|
|
679
641
|
}
|
|
680
642
|
}
|
|
681
643
|
}
|
|
682
644
|
|
|
683
|
-
//
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
688
|
-
//
|
|
689
|
-
//
|
|
690
|
-
//
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
function
|
|
695
|
-
if (cls !== "spider" && cls !== "fly") return
|
|
696
|
-
return
|
|
645
|
+
// The engine writes each agent's mood as a real mgx:feels fact every turn
|
|
646
|
+
// and hands the same word back on the agent, so this page reads it rather
|
|
647
|
+
// than deriving a mood of its own. Only spider/fly carry the mgx:feels
|
|
648
|
+
// parameter (data/sprites-large/*-with-emotion.toml exists for those two
|
|
649
|
+
// classes only) — every other class (egg, and any class this page has no
|
|
650
|
+
// template for at all) asks for no expression at all, resolving through its
|
|
651
|
+
// plain template. A snapshot-sourced agent carries no mood (a chat-driven
|
|
652
|
+
// tick returns text, not the structured tick shape), so the last mood the
|
|
653
|
+
// engine reported stands, exactly as goalById already holds the last goal.
|
|
654
|
+
// The starting board has no reported mood at all, and calm is the mood the
|
|
655
|
+
// engine itself writes for a just-minted agent, so that is the baseline.
|
|
656
|
+
function expressionForAgent(id, agent, cls) {
|
|
657
|
+
if (cls !== "spider" && cls !== "fly") return null;
|
|
658
|
+
return (agent && agent.mood) || moodByAgent[id] || "calm";
|
|
697
659
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
660
|
+
// One sprite request, in the same { class, expression } shape tmct_sprite
|
|
661
|
+
// takes, answered by the same pure resolver the tool wraps (sprite-request.mjs,
|
|
662
|
+
// spliced in above) — the page holds the state and nothing else.
|
|
663
|
+
function resolveSpriteFace(spriteRequest) {
|
|
664
|
+
if (!window.tmct) return "";
|
|
665
|
+
return resolveSpriteRequest(spriteRequest, {
|
|
666
|
+
factRows: (session && session.taxonomyRows) || [],
|
|
667
|
+
templates: SPIDERFLY.spriteTemplates,
|
|
668
|
+
spriteRegistry: tmct.page.SPRITE_REGISTRY,
|
|
669
|
+
resolveSpriteAsset: tmct.page.resolveSpriteAsset,
|
|
670
|
+
}).svg || "";
|
|
702
671
|
}
|
|
703
672
|
|
|
704
673
|
function togglePov(id) {
|
|
@@ -717,18 +686,18 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
717
686
|
node.className = "sprite";
|
|
718
687
|
node.dataset.cls = cls;
|
|
719
688
|
// Property-aware resolution (sprite-templates.mjs's resolveSpriteAsset):
|
|
720
|
-
// a spider/fly's live mgx:feels
|
|
721
|
-
//
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
const
|
|
725
|
-
|
|
689
|
+
// a spider/fly's live mgx:feels mood resolves it through the matching
|
|
690
|
+
// data/sprites-large/*-with-emotion.toml template; every other class
|
|
691
|
+
// (egg) asks for no expression and resolves through its plain class
|
|
692
|
+
// template (or the flat SPRITE_REGISTRY), unchanged.
|
|
693
|
+
const mood = expressionForAgent(id, agent, cls);
|
|
694
|
+
moodByAgent[id] = mood;
|
|
726
695
|
// The sprite SVG lives in its own inner wrapper so plan-driven facing
|
|
727
696
|
// (a CSS rotate on THIS wrapper) never fights the outer .sprite node's
|
|
728
697
|
// own translate(-50%,-50%) positioning transform.
|
|
729
698
|
const face = document.createElement("div");
|
|
730
699
|
face.className = "sprite-face";
|
|
731
|
-
face.innerHTML = resolveSpriteFace(cls,
|
|
700
|
+
face.innerHTML = resolveSpriteFace({ class: cls, expression: mood });
|
|
732
701
|
node.appendChild(face);
|
|
733
702
|
if (!preview) {
|
|
734
703
|
node.tabIndex = 0;
|
|
@@ -747,9 +716,9 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
747
716
|
function applyAgents(agents) {
|
|
748
717
|
removeStaleSprites(agents);
|
|
749
718
|
for (const [id, a] of Object.entries(agents)) {
|
|
750
|
-
const cls =
|
|
719
|
+
const cls = agentKindOf(id);
|
|
751
720
|
const node = ensureSpriteEl(id, cls, a);
|
|
752
|
-
const parsed =
|
|
721
|
+
const parsed = tmct.page.parseCellId(a.cell);
|
|
753
722
|
if (!parsed) continue;
|
|
754
723
|
const pct = cellCenterPct(parsed.x, parsed.y);
|
|
755
724
|
node.style.left = pct.leftPct + "%";
|
|
@@ -763,16 +732,15 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
763
732
|
const face = node.querySelector(".sprite-face");
|
|
764
733
|
if (face) {
|
|
765
734
|
face.style.transform = "rotate(" + facingByAgent[id] + "deg)";
|
|
766
|
-
//
|
|
735
|
+
// Mood changes tick-to-tick (a fly's fear spikes the instant a
|
|
767
736
|
// spider comes into view, a spider's joy fires the instant it eats),
|
|
768
737
|
// so this must re-resolve every redraw, not just at sprite creation —
|
|
769
|
-
// but only actually replace the DOM when the
|
|
738
|
+
// but only actually replace the DOM when the mood word itself
|
|
770
739
|
// changed since last render, never on every tick regardless.
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
face.innerHTML = resolveSpriteFace(cls, propertyFacts);
|
|
740
|
+
const mood = expressionForAgent(id, a, cls);
|
|
741
|
+
if (mood !== moodByAgent[id]) {
|
|
742
|
+
moodByAgent[id] = mood;
|
|
743
|
+
face.innerHTML = resolveSpriteFace({ class: cls, expression: mood });
|
|
776
744
|
}
|
|
777
745
|
}
|
|
778
746
|
if (a.goal) goalById[id] = a.goal;
|
|
@@ -805,12 +773,12 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
805
773
|
// never emotion-parameterized at all (a rotting carcass has none),
|
|
806
774
|
// just its class's own plain template under the grayscale/fade the
|
|
807
775
|
// .sprite.corpse CSS rule already applies.
|
|
808
|
-
face.innerHTML = resolveSpriteFace(corpse.cls
|
|
776
|
+
face.innerHTML = resolveSpriteFace({ class: corpse.cls });
|
|
809
777
|
node.appendChild(face);
|
|
810
778
|
spriteLayer.appendChild(node);
|
|
811
779
|
corpseEls[id] = node;
|
|
812
780
|
}
|
|
813
|
-
const deathCell =
|
|
781
|
+
const deathCell = tmct.page.parseCellId(corpse.cell);
|
|
814
782
|
if (!deathCell) continue;
|
|
815
783
|
const pct = cellCenterPct(deathCell.x, SPIDERFLY.gridSize);
|
|
816
784
|
node.style.left = pct.leftPct + "%";
|
|
@@ -818,13 +786,6 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
818
786
|
}
|
|
819
787
|
}
|
|
820
788
|
|
|
821
|
-
function massBarHtml(cls, mass) {
|
|
822
|
-
const maxMass = cls === "spider" ? SPIDERFLY.maxSpiderMass : cls === "fly" ? SPIDERFLY.maxFlyMass : null;
|
|
823
|
-
if (typeof mass !== "number" || !maxMass) return "";
|
|
824
|
-
const pct = Math.max(0, Math.min(100, (mass / maxMass) * 100));
|
|
825
|
-
return '<div class="mass-track"><div class="mass-fill ' + esc(cls) + '" style="width:' + pct + '%"></div></div>';
|
|
826
|
-
}
|
|
827
|
-
|
|
828
789
|
// The last-created plan as a plain arrow chain ("west → west"), or
|
|
829
790
|
// "holding." when the agent's plan this tick is empty — mirrors exactly
|
|
830
791
|
// what drove this tick's sprite facing (facingDegreesFor reads the same
|
|
@@ -850,15 +811,15 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
850
811
|
|
|
851
812
|
// The full text rendering behind the click-expand panel: one sentence per
|
|
852
813
|
// other candidate the observer currently has a belief about, or "has not
|
|
853
|
-
// been observed" for a candidate it doesn't —
|
|
854
|
-
//
|
|
855
|
-
//
|
|
814
|
+
// been observed" for a candidate it doesn't — believedFactSentence is the
|
|
815
|
+
// SAME function spider-fly-turn.mjs's own chat lane renders "X sees: ..."
|
|
816
|
+
// from, so the chat phrasing and this panel can never drift apart.
|
|
817
|
+
// beliefLineHtml already compresses the same belief map into a single line
|
|
818
|
+
// above; this spells it out in full beside the clicked agent.
|
|
856
819
|
function observedFactsHtml(observerId, belief) {
|
|
857
820
|
const entries = Object.entries(belief || {});
|
|
858
821
|
const lines = entries.length
|
|
859
|
-
? entries.map(([id, cell]) => '<div class="hud-detail-line">'
|
|
860
|
-
+ (cell ? esc(id) + " is at " + esc(cell) + "." : esc(id) + " has not been observed.")
|
|
861
|
-
+ "</div>").join("")
|
|
822
|
+
? entries.map(([id, cell]) => '<div class="hud-detail-line">' + esc(believedFactSentence(id, cell)) + "</div>").join("")
|
|
862
823
|
: '<div class="hud-detail-line">nothing else is on the board yet.</div>';
|
|
863
824
|
return '<div class="hud-detail"><div class="hud-detail-title">' + esc(observerId) + " observes</div>" + lines + "</div>";
|
|
864
825
|
}
|
|
@@ -867,13 +828,13 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
867
828
|
const ids = Object.keys(lastAgents).sort();
|
|
868
829
|
if (!ids.length) { hudEl.innerHTML = '<div class="hud-empty">no agents on the board.</div>'; return; }
|
|
869
830
|
hudEl.innerHTML = ids.map((id) => {
|
|
870
|
-
const cls =
|
|
831
|
+
const cls = agentKindOf(id);
|
|
871
832
|
const a = lastAgents[id];
|
|
872
833
|
const clickable = cls === "spider" || cls === "fly";
|
|
873
834
|
const expanded = clickable && expandedAgents.has(id);
|
|
874
835
|
const main = '<div class="hud-main"><span class="hud-id ' + esc(cls) + '">' + esc(id) + '</span>'
|
|
875
836
|
+ '<span class="hud-goal">' + esc(goalById[id] || "watching\\u2026") + "</span>"
|
|
876
|
-
+
|
|
837
|
+
+ meterBarHtml(cls, a.mass, massScaleFor(cls, { maxSpiderMass: SPIDERFLY.maxSpiderMass, maxFlyMass: SPIDERFLY.maxFlyMass }))
|
|
877
838
|
+ planLineHtml(a.plan)
|
|
878
839
|
+ beliefLineHtml(a.belief)
|
|
879
840
|
+ "</div>";
|
|
@@ -902,9 +863,9 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
902
863
|
});
|
|
903
864
|
|
|
904
865
|
const threadGeometry = {
|
|
905
|
-
parseCellId: (id) =>
|
|
906
|
-
cellId: (x, y) =>
|
|
907
|
-
directionDelta:
|
|
866
|
+
parseCellId: (id) => tmct.page.parseCellId(id),
|
|
867
|
+
cellId: (x, y) => tmct.page.cellId(x, y),
|
|
868
|
+
directionDelta: tmct.page.DIRECTION_DELTA,
|
|
908
869
|
};
|
|
909
870
|
|
|
910
871
|
function drawBoard(agents, activeWebs) {
|
|
@@ -912,7 +873,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
912
873
|
boardCtx.clearRect(0, 0, w, h);
|
|
913
874
|
boardCtx.fillStyle = cssVar("--taught-soft") || "rgba(46,125,79,.12)";
|
|
914
875
|
for (const wc of SPIDERFLY.webCells) {
|
|
915
|
-
const p =
|
|
876
|
+
const p = tmct.page.parseCellId(wc);
|
|
916
877
|
boardCtx.fillRect((p.x - 1) * cellSize, (p.y - 1) * cellSize, cellSize, cellSize);
|
|
917
878
|
}
|
|
918
879
|
// A spider-built dynamic web is a distinct color from the always-on
|
|
@@ -923,7 +884,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
923
884
|
boardCtx.lineWidth = 1;
|
|
924
885
|
boardCtx.setLineDash([3, 2]);
|
|
925
886
|
for (const web of activeWebs || []) {
|
|
926
|
-
const p =
|
|
887
|
+
const p = tmct.page.parseCellId(web.cell);
|
|
927
888
|
if (!p) continue;
|
|
928
889
|
boardCtx.fillRect((p.x - 1) * cellSize, (p.y - 1) * cellSize, cellSize, cellSize);
|
|
929
890
|
boardCtx.strokeRect((p.x - 1) * cellSize + 0.5, (p.y - 1) * cellSize + 0.5, cellSize - 1, cellSize - 1);
|
|
@@ -943,7 +904,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
943
904
|
boardCtx.beginPath();
|
|
944
905
|
let prev = null;
|
|
945
906
|
thread.forEach((c, i) => {
|
|
946
|
-
const p =
|
|
907
|
+
const p = tmct.page.parseCellId(c);
|
|
947
908
|
const px = (p.x - 0.5) * cellSize, py = (p.y - 0.5) * cellSize;
|
|
948
909
|
if (i === 0) boardCtx.moveTo(px, py); else boardCtx.lineTo(px, py);
|
|
949
910
|
if (prev) threadHits.push({ x: (prev.x + px) / 2, y: (prev.y + py) / 2, step: i });
|
|
@@ -958,19 +919,19 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
958
919
|
if (!povAgentId) return;
|
|
959
920
|
const agent = lastAgents[povAgentId];
|
|
960
921
|
if (!agent) { povAgentId = null; return; }
|
|
961
|
-
const p =
|
|
922
|
+
const p = tmct.page.parseCellId(agent.cell);
|
|
962
923
|
// The live, slider-adjustable per-class radius (falling back to the
|
|
963
924
|
// engine's own shipped default before the session has finished
|
|
964
925
|
// booting) — a POV toggle always reflects whatever vision range this
|
|
965
926
|
// class currently actually has, not a fixed constant.
|
|
966
|
-
const radius =
|
|
967
|
-
? (liveConfig.spiderVisionRadius ??
|
|
968
|
-
: (liveConfig.flyVisionRadius ??
|
|
969
|
-
const visible = new Set(
|
|
927
|
+
const radius = agentKindOf(povAgentId) === "spider"
|
|
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));
|
|
970
931
|
povCtx.fillStyle = "rgba(0,0,0,.55)";
|
|
971
932
|
for (let gy = 1; gy <= SPIDERFLY.gridSize; gy += 1) {
|
|
972
933
|
for (let gx = 1; gx <= SPIDERFLY.gridSize; gx += 1) {
|
|
973
|
-
if (visible.has(
|
|
934
|
+
if (visible.has(tmct.page.cellId(gx, gy))) continue;
|
|
974
935
|
povCtx.fillRect((gx - 1) * cellSize, (gy - 1) * cellSize, cellSize, cellSize);
|
|
975
936
|
}
|
|
976
937
|
}
|
|
@@ -1022,7 +983,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
1022
983
|
chatqEl.value = "";
|
|
1023
984
|
addChatLine("u", esc(q));
|
|
1024
985
|
withLock(async () => {
|
|
1025
|
-
const result = await
|
|
986
|
+
const result = await tmct.turn(q);
|
|
1026
987
|
addChatLine("a", esc(result.answer).replace(/\\n/g, "<br>"));
|
|
1027
988
|
const snap = await session.snapshot();
|
|
1028
989
|
redraw(snap.agents, snap.turn, snap.activeWebs);
|
|
@@ -1066,14 +1027,14 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
1066
1027
|
refreshPills();
|
|
1067
1028
|
|
|
1068
1029
|
// ---- deception pills (§A.2.4): a SEPARATE dynamic rail, alongside (never
|
|
1069
|
-
// replacing) the static one above.
|
|
1030
|
+
// replacing) the static one above. tmct.page.pillsForSpiderFly is the
|
|
1070
1031
|
// exact same pure function spider-fly-turn.mjs exports — this page never
|
|
1071
1032
|
// reimplements the true/false claim logic, only renders its output and
|
|
1072
1033
|
// fills #chatq on click, same click-to-fill discipline as every other
|
|
1073
1034
|
// pill on this page (never auto-submits).
|
|
1074
1035
|
function renderDynamicPills() {
|
|
1075
1036
|
if (!session || !Object.keys(lastAgents).length) { dynamicPillsEl.innerHTML = ""; return; }
|
|
1076
|
-
const result =
|
|
1037
|
+
const result = tmct.page.pillsForSpiderFly(lastAgents, selectedAddresseeId, {});
|
|
1077
1038
|
selectedAddresseeId = result.addresseeId;
|
|
1078
1039
|
const addrHtml = result.addressPills.map((p) =>
|
|
1079
1040
|
'<button type="button" class="pill" data-role="dyn-addr" data-id="' + esc(p.id) + '"'
|
|
@@ -1138,7 +1099,7 @@ ${engineBundleJs ? `<script>\n${embedScriptText(engineBundleJs)}\n</script>` : `
|
|
|
1138
1099
|
|
|
1139
1100
|
let tuningInitialized = false;
|
|
1140
1101
|
async function boot() {
|
|
1141
|
-
session = await
|
|
1102
|
+
session = await tmct.open();
|
|
1142
1103
|
// A reset mints a brand-new session (fresh board, fresh config) — the
|
|
1143
1104
|
// FIRST boot seeds the sliders from the engine's own shipped defaults;
|
|
1144
1105
|
// every boot after that re-applies whatever the visitor already had the
|