@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
|
@@ -579,7 +579,10 @@ export function runEcologyPass({
|
|
|
579
579
|
* the shipped (reusable, static) world pack itself. A no-op when spider-1
|
|
580
580
|
* already exists (idempotent — safe to call from a caller unsure whether
|
|
581
581
|
* the game has already started). `config` (default
|
|
582
|
-
* DEFAULT_GAME_CONFIG.spiderFly) supplies the starting masses.
|
|
582
|
+
* DEFAULT_GAME_CONFIG.spiderFly) supplies the starting masses. Every minted
|
|
583
|
+
* agent also gets a starting mgx:feels mood (`calm` — nothing has a goal
|
|
584
|
+
* yet), so a mood fact exists for an individual from the moment the
|
|
585
|
+
* individual does. */
|
|
583
586
|
export async function startSpiderFlyGame(memoryDir, { flyCount = 1, config = DEFAULT_GAME_CONFIG.spiderFly } = {}) {
|
|
584
587
|
const state = foldSpiderFlyState(readFactRows(await loadMemory(memoryDir)));
|
|
585
588
|
if (state.placements.has("spider-1")) return { started: false, facts: [] };
|
|
@@ -588,6 +591,7 @@ export async function startSpiderFlyGame(memoryDir, { flyCount = 1, config = DEF
|
|
|
588
591
|
const facts = [
|
|
589
592
|
{ subject: "spider-1", predicate: "mgx:currently-in", object: cellId(WEB_HOME.x, WEB_HOME.y) },
|
|
590
593
|
{ subject: "spider-1", predicate: "mgx:mass", object: String(config.spiderInitialMass) },
|
|
594
|
+
{ subject: "spider-1", predicate: "mgx:feels", object: "calm" },
|
|
591
595
|
];
|
|
592
596
|
const occupied = new Set([cellId(WEB_HOME.x, WEB_HOME.y)]);
|
|
593
597
|
for (let i = 0; i < flyCount; i += 1) {
|
|
@@ -597,11 +601,34 @@ export async function startSpiderFlyGame(memoryDir, { flyCount = 1, config = DEF
|
|
|
597
601
|
occupied.add(cell);
|
|
598
602
|
facts.push({ subject: flyId, predicate: "mgx:currently-in", object: cell });
|
|
599
603
|
facts.push({ subject: flyId, predicate: "mgx:mass", object: String(config.flyInitialMass) });
|
|
604
|
+
facts.push({ subject: flyId, predicate: "mgx:feels", object: "calm" });
|
|
600
605
|
}
|
|
601
606
|
await appendFacts(memoryDir, facts.map((f) => ({ ...f, provenance: worldProvenanceTag(WORLD_NAME) })));
|
|
602
607
|
return { started: true, facts };
|
|
603
608
|
}
|
|
604
609
|
|
|
610
|
+
// ---- the goal line and the mood word -----------------------------------------
|
|
611
|
+
// Every branch of the tick's own priority chain assigns a `mood` word beside
|
|
612
|
+
// the `goal` sentence it renders, and that word is appended as a real
|
|
613
|
+
// mgx:feels fact for the turn (runSpiderFlyTick's moodWrites), the same
|
|
614
|
+
// per-agent-per-turn treatment mgx:currently-in and mgx:mass already get.
|
|
615
|
+
// Two reasons it is a fact rather than something a renderer re-derives. A
|
|
616
|
+
// question about "a happy spider" has to bind against the store. And this
|
|
617
|
+
// branch is the one that knows the mood, so recovering it downstream means
|
|
618
|
+
// re-reading the prose this file already wrote.
|
|
619
|
+
//
|
|
620
|
+
// The words are a subset of sprite-expressions.mjs's own six-word
|
|
621
|
+
// EXPRESSION_PALETTE (`sad` and `surprised` have no state in this game's goal
|
|
622
|
+
// chain). Operator-confirmed mapping: a spider that just ate is happy; a
|
|
623
|
+
// spider carrying a fly or mid-chase is angry (predatory focus); a spider
|
|
624
|
+
// avoiding another spider is scared; a spider holding position or building a
|
|
625
|
+
// web is calm. A fly evading a believed-visible spider is scared, and so is
|
|
626
|
+
// the sharper form of the same fear — just caught, being carried, or trapped
|
|
627
|
+
// in a web; a fly with nothing in sight is calm. `calm` is also the
|
|
628
|
+
// no-strong-emotion baseline for an agent with no goal yet: freshly hatched,
|
|
629
|
+
// freshly spawned, or re-evaluating because the agent its goal named died
|
|
630
|
+
// this tick.
|
|
631
|
+
|
|
605
632
|
function goalLineFor(subject, believed, arrived, kind) {
|
|
606
633
|
if (kind === "spider-carrying") return `carrying ${believed.subject} toward the web.`;
|
|
607
634
|
if (kind === "spider-carrying-delivered") return `carrying ${believed.subject} — already in the web, delivering it now.`;
|
|
@@ -678,8 +705,11 @@ export function liveWebs(websMap, turn, webDurationTurns = WEB_DURATION_TURNS) {
|
|
|
678
705
|
* it.
|
|
679
706
|
*
|
|
680
707
|
* Returns `{ turn, writes, agents, ecology, activeWebs }`: `agents` is keyed
|
|
681
|
-
* by every live spider/fly subject after this tick, each `{ cell, goal,
|
|
682
|
-
* plan, mass, belief }` — `
|
|
708
|
+
* by every live spider/fly subject after this tick, each `{ cell, goal, mood,
|
|
709
|
+
* plan, mass, belief }` — `mood` is the branch's own mood word (see the goal
|
|
710
|
+
* line and mood word section above), also appended as this turn's mgx:feels
|
|
711
|
+
* fact, so a renderer reads the structured word rather than parsing `goal`
|
|
712
|
+
* back apart; `plan` is the direction sequence that produced
|
|
683
713
|
* THIS tick's move (a full multi-step search result when one was found,
|
|
684
714
|
* else a length-1 array for a single greedy step, else `[]` when the agent
|
|
685
715
|
* held still — `plan[0]` is always the direction actually taken, the facing
|
|
@@ -744,15 +774,18 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
744
774
|
let nextCell;
|
|
745
775
|
let plan;
|
|
746
776
|
let goal;
|
|
777
|
+
let mood;
|
|
747
778
|
if (isCarrying && alreadyDelivered) {
|
|
748
779
|
nextCell = spiderCell;
|
|
749
780
|
plan = [];
|
|
750
781
|
goal = goalLineFor(spiderId, { subject: carriedFlyId }, false, "spider-carrying-delivered");
|
|
782
|
+
mood = "angry";
|
|
751
783
|
} else if (isCarrying) {
|
|
752
784
|
const path = planSpiderPathToWeb(spiderCell, applyActions, state, k, config.webDurationTurns);
|
|
753
785
|
if (path && path.actions.length) { nextCell = path.states[1]; plan = path.actions; }
|
|
754
786
|
else { nextCell = greedySpiderApproach(spiderCell, WEB_HOME, applyActions); plan = stepPlan(spiderCell, nextCell); }
|
|
755
787
|
goal = goalLineFor(spiderId, { subject: carriedFlyId }, false, "spider-carrying");
|
|
788
|
+
mood = "angry";
|
|
756
789
|
} else {
|
|
757
790
|
// Priority 1: avoid any OTHER live spider believed visible.
|
|
758
791
|
const avoidTarget = nearestBelievedTarget(spiderId, spiderCell, otherSpiders, state, { visionRadius: config.spiderVisionRadius, toldFacts });
|
|
@@ -760,6 +793,7 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
760
793
|
nextCell = greedySpiderAvoid(spiderCell, avoidTarget.cell, applyActions);
|
|
761
794
|
plan = stepPlan(spiderCell, nextCell);
|
|
762
795
|
goal = goalLineFor(spiderId, avoidTarget, false, "spider-avoid");
|
|
796
|
+
mood = "scared";
|
|
763
797
|
} else {
|
|
764
798
|
// Priority 2: chase a believed-visible fly, exactly as before.
|
|
765
799
|
const target = nearestBelievedTarget(spiderId, spiderCell, flies, state, { visionRadius: config.spiderVisionRadius, toldFacts });
|
|
@@ -782,11 +816,13 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
782
816
|
// has-exit-* edges have no diagonal hop).
|
|
783
817
|
const arrived = nextCell.x === target.cell.x && nextCell.y === target.cell.y;
|
|
784
818
|
goal = goalLineFor(spiderId, target, arrived, "spider");
|
|
819
|
+
mood = "angry";
|
|
785
820
|
} else {
|
|
786
821
|
// Priority 3: hold position, and build/refresh a web there unless an
|
|
787
822
|
// unexpired web already covers this exact cell.
|
|
788
823
|
nextCell = spiderCell;
|
|
789
824
|
plan = [];
|
|
825
|
+
mood = "calm";
|
|
790
826
|
const heldCellId = cellId(spiderCell.x, spiderCell.y);
|
|
791
827
|
if (!hasActiveWebAt(spiderCell.x, spiderCell.y, state, k, config.webDurationTurns)) {
|
|
792
828
|
const webId = `web-${nextWebNum}`;
|
|
@@ -805,7 +841,7 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
805
841
|
postMovePlacements.set(spiderId, nextCell);
|
|
806
842
|
movementWrites.push({ subject: `${spiderId}@turn${k}`, predicate: "mgx:currently-in", object: cellId(nextCell.x, nextCell.y) });
|
|
807
843
|
movementWrites.push({ subject: `${spiderId}@turn${k}`, predicate: "mgx:mass", object: String(newMass) });
|
|
808
|
-
agents[spiderId] = { cell: cellId(nextCell.x, nextCell.y), goal, plan, mass: newMass, belief };
|
|
844
|
+
agents[spiderId] = { cell: cellId(nextCell.x, nextCell.y), goal, mood, plan, mass: newMass, belief };
|
|
809
845
|
}
|
|
810
846
|
|
|
811
847
|
// A fly currently carried by a still-live spider (state.carrying, keyed by
|
|
@@ -827,10 +863,12 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
827
863
|
let nextCell;
|
|
828
864
|
let plan;
|
|
829
865
|
let goal;
|
|
866
|
+
let mood;
|
|
830
867
|
if (captorId) {
|
|
831
868
|
nextCell = postMovePlacements.get(captorId);
|
|
832
869
|
plan = [];
|
|
833
870
|
goal = `being carried by ${captorId}.`;
|
|
871
|
+
mood = "scared";
|
|
834
872
|
} else {
|
|
835
873
|
const believedSpider = nearestBelievedTarget(flyId, flyCell, spiders, state, { visionRadius: config.flyVisionRadius, toldFacts });
|
|
836
874
|
const webbed = hasActiveWebAt(flyCell.x, flyCell.y, state, k, config.webDurationTurns);
|
|
@@ -838,10 +876,12 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
838
876
|
nextCell = flyCell;
|
|
839
877
|
plan = [];
|
|
840
878
|
goal = "trapped in an active web — can't move.";
|
|
879
|
+
mood = "scared";
|
|
841
880
|
} else {
|
|
842
881
|
nextCell = greedyFlyMove(flyCell, believedSpider?.cell ?? null, applyActions, k, flyId);
|
|
843
882
|
plan = stepPlan(flyCell, nextCell);
|
|
844
883
|
goal = goalLineFor(flyId, believedSpider, true, "fly");
|
|
884
|
+
mood = believedSpider ? "scared" : "calm";
|
|
845
885
|
}
|
|
846
886
|
}
|
|
847
887
|
const belief = beliefSnapshotFor(flyId, captorId ? nextCell : flyCell, [...spiders, ...flies.filter((id) => id !== flyId)], state, { visionRadius: config.flyVisionRadius, toldFacts });
|
|
@@ -852,7 +892,7 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
852
892
|
const newMass = Math.max(0, priorMass - config.flyMassDecrementPerTurn);
|
|
853
893
|
postMoveMassByFly.set(flyId, newMass);
|
|
854
894
|
movementWrites.push({ subject: `${flyId}@turn${k}`, predicate: "mgx:mass", object: String(newMass) });
|
|
855
|
-
agents[flyId] = { cell: cellId(nextCell.x, nextCell.y), goal, plan, mass: newMass, belief };
|
|
895
|
+
agents[flyId] = { cell: cellId(nextCell.x, nextCell.y), goal, mood, plan, mass: newMass, belief };
|
|
856
896
|
}
|
|
857
897
|
|
|
858
898
|
const ecology = runEcologyPass({ state, postMovePlacements, postMoveMassByFly, postMoveMassBySpider, turn: k, config });
|
|
@@ -871,6 +911,7 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
871
911
|
for (const deadId of diedThisTick) {
|
|
872
912
|
if (new RegExp(`${deadId}(?!\\d)`).test(agents[id].goal)) {
|
|
873
913
|
agents[id].goal = `${deadId} is gone — re-evaluating.`;
|
|
914
|
+
agents[id].mood = "calm";
|
|
874
915
|
break;
|
|
875
916
|
}
|
|
876
917
|
}
|
|
@@ -897,6 +938,7 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
897
938
|
for (const [spider, flyIds] of eatenBySpider) {
|
|
898
939
|
if (agents[spider]) {
|
|
899
940
|
agents[spider].goal = `just ate ${flyIds.join(" and ")} in the web.`;
|
|
941
|
+
agents[spider].mood = "happy";
|
|
900
942
|
agents[spider].mass = ecology.events.massAfterEating.get(spider) ?? agents[spider].mass;
|
|
901
943
|
}
|
|
902
944
|
}
|
|
@@ -908,8 +950,14 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
908
950
|
// fly's own evade/wander text neither one mentions the catch.
|
|
909
951
|
for (const { spider, fly } of ecology.events.caught) {
|
|
910
952
|
if (eatenBySpider.has(spider)) continue;
|
|
911
|
-
if (agents[spider])
|
|
912
|
-
|
|
953
|
+
if (agents[spider]) {
|
|
954
|
+
agents[spider].goal = goalLineFor(spider, { subject: fly }, false, "spider-carrying");
|
|
955
|
+
agents[spider].mood = "angry";
|
|
956
|
+
}
|
|
957
|
+
if (agents[fly]) {
|
|
958
|
+
agents[fly].goal = `just caught by ${spider} — being carried.`;
|
|
959
|
+
agents[fly].mood = "scared";
|
|
960
|
+
}
|
|
913
961
|
}
|
|
914
962
|
// A hatched spider or a spawned fly is minted by the ecology pass, which
|
|
915
963
|
// runs AFTER the movement loops above already built `agents` from the
|
|
@@ -921,13 +969,23 @@ export async function runSpiderFlyTick(memoryDir, opts = {}) {
|
|
|
921
969
|
// agents[] entry, sharing the egg's own cell.
|
|
922
970
|
for (const h of ecology.events.hatched) {
|
|
923
971
|
for (const { spider, mass } of h.spiders) {
|
|
924
|
-
agents[spider] = { cell: h.cell, goal: "just hatched — no goal yet.", plan: [], mass, belief: {} };
|
|
972
|
+
agents[spider] = { cell: h.cell, goal: "just hatched — no goal yet.", mood: "calm", plan: [], mass, belief: {} };
|
|
925
973
|
}
|
|
926
974
|
}
|
|
927
975
|
if (ecology.events.spawned && ecology.events.spawnedCell) {
|
|
928
|
-
agents[ecology.events.spawned] = { cell: ecology.events.spawnedCell, goal: "just arrived — no goal yet.", plan: [], mass: config.flyInitialMass, belief: {} };
|
|
929
|
-
}
|
|
930
|
-
|
|
976
|
+
agents[ecology.events.spawned] = { cell: ecology.events.spawnedCell, goal: "just arrived — no goal yet.", mood: "calm", plan: [], mass: config.flyInitialMass, belief: {} };
|
|
977
|
+
}
|
|
978
|
+
// Each remaining agent's mood goes on record as this turn's own mgx:feels
|
|
979
|
+
// fact, exactly the way its mgx:currently-in placement did. Built here,
|
|
980
|
+
// after the ecology pass, so the fact carries the mood the tick ENDED on
|
|
981
|
+
// (a spider that just ate is happy, not still angry mid-chase) and covers
|
|
982
|
+
// the hatchlings and the spawned fly the ecology pass just minted. An
|
|
983
|
+
// agent eaten or starved this tick is already out of `agents`, so nothing
|
|
984
|
+
// records a mood for it.
|
|
985
|
+
const moodWrites = Object.entries(agents)
|
|
986
|
+
.filter(([, agent]) => agent.mood)
|
|
987
|
+
.map(([id, agent]) => ({ subject: `${id}@turn${k}`, predicate: "mgx:feels", object: agent.mood }));
|
|
988
|
+
const writes = [...movementWrites, ...ecology.writes, ...moodWrites];
|
|
931
989
|
const provenance = `${worldProvenanceTag(WORLD_NAME)}:turn${k}`;
|
|
932
990
|
await appendFacts(memoryDir, writes.map((f) => ({ ...f, provenance })));
|
|
933
991
|
|