@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.
Files changed (65) hide show
  1. package/README.md +2 -1
  2. package/corpus/sprites/src/sprite-facts.jsonl +375 -8
  3. package/package.json +1 -1
  4. package/src/adapters/memory/core.mjs +20 -0
  5. package/src/domain/ask-vocab.mjs +71 -0
  6. package/src/domain/ask.mjs +168 -0
  7. package/src/domain/game-config.mjs +11 -0
  8. package/src/domain/mud-facts.mjs +15 -0
  9. package/src/domain/router/drive.mjs +35 -9
  10. package/src/domain/router/registry.mjs +24 -4
  11. package/src/domain/router/resolver.mjs +102 -40
  12. package/src/domain/scene-compose.mjs +117 -0
  13. package/src/domain/spider-fly-world.mjs +36 -0
  14. package/src/domain/sprite-facts.mjs +0 -0
  15. package/src/domain/sprite-request.mjs +156 -0
  16. package/src/domain/sprite-templates.mjs +161 -14
  17. package/src/services/adventure-editor.mjs +8 -14
  18. package/src/services/adventure-viz.mjs +119 -150
  19. package/src/services/adventure.mjs +97 -35
  20. package/src/services/chat-page-viz.mjs +64 -48
  21. package/src/services/chat.mjs +102 -34
  22. package/src/services/code-explorer-viz.mjs +52 -50
  23. package/src/services/ingest-viz.mjs +32 -74
  24. package/src/services/ledger-viz.mjs +87 -70
  25. package/src/services/memory-panel-viz.mjs +38 -0
  26. package/src/services/mud-editor.mjs +10 -15
  27. package/src/services/mud-turn.mjs +6 -6
  28. package/src/services/mud-viz.mjs +119 -225
  29. package/src/services/p2p-room.mjs +90 -23
  30. package/src/services/plan-pddl.mjs +3 -1
  31. package/src/services/plan-viz.mjs +13 -12
  32. package/src/services/research-viz.mjs +25 -67
  33. package/src/services/spider-fly-turn.mjs +14 -22
  34. package/src/services/spider-fly-viz.mjs +97 -136
  35. package/src/services/spider-fly.mjs +69 -11
  36. package/src/services/sprite-catalog-viz.mjs +274 -224
  37. package/src/services/viz-boot.mjs +71 -0
  38. package/src/services/viz-room-graph.mjs +203 -0
  39. package/src/services/viz-theme.mjs +75 -1
  40. package/src/services/viz-ticker.mjs +22 -0
  41. package/src/surfaces/web/adventure-browser-entry.mjs +62 -47
  42. package/src/surfaces/web/chat-browser-entry.mjs +51 -107
  43. package/src/surfaces/web/code-explorer-browser-entry.mjs +192 -35
  44. package/src/surfaces/web/engine-surface.mjs +82 -0
  45. package/src/surfaces/web/ingest-browser-entry.mjs +16 -17
  46. package/src/surfaces/web/ledger-browser-entry.mjs +24 -56
  47. package/src/surfaces/web/memory-ask-browser-entry.mjs +55 -13
  48. package/src/surfaces/web/memory-ask-browser.bundle.js +128 -125
  49. package/src/surfaces/web/memory-stats.mjs +11 -0
  50. package/src/surfaces/web/mud-browser-entry.mjs +70 -49
  51. package/src/surfaces/web/plan-browser-entry.mjs +39 -50
  52. package/src/surfaces/web/research-browser-entry.mjs +48 -46
  53. package/src/surfaces/web/spider-fly-browser-entry.mjs +76 -40
  54. package/src/surfaces/web/sprites-browser-entry.mjs +28 -32
  55. package/src/surfaces/web/tmct-surface.mjs +147 -0
  56. package/src/surfaces/web/turn-session.mjs +124 -0
  57. package/src/tools/definitions.mjs +30 -0
  58. package/src/tools/handlers/index.mjs +6 -3
  59. package/src/tools/handlers/kit.mjs +19 -2
  60. package/src/tools/handlers/tmct-ask.mjs +11 -6
  61. package/src/tools/handlers/tmct-ingest.mjs +5 -1
  62. package/src/tools/handlers/tmct-related.mjs +4 -4
  63. package/src/tools/handlers/tmct-sprite.mjs +147 -0
  64. package/src/tools/memory-fallthrough.mjs +9 -2
  65. 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 }` — `plan` is the direction sequence that produced
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]) agents[spider].goal = goalLineFor(spider, { subject: fly }, false, "spider-carrying");
912
- if (agents[fly]) agents[fly].goal = `just caught by ${spider} being carried.`;
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
- const writes = [...movementWrites, ...ecology.writes];
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