simfile 0.0.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 (249) hide show
  1. package/README.md +143 -0
  2. package/dist/cli/index.d.ts +3 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +348 -0
  5. package/dist/coverage/index.d.ts +2 -0
  6. package/dist/coverage/index.d.ts.map +1 -0
  7. package/dist/coverage/index.js +1 -0
  8. package/dist/coverage/matrix.d.ts +21 -0
  9. package/dist/coverage/matrix.d.ts.map +1 -0
  10. package/dist/coverage/matrix.js +173 -0
  11. package/dist/coverage/render.d.ts +3 -0
  12. package/dist/coverage/render.d.ts.map +1 -0
  13. package/dist/coverage/render.js +26 -0
  14. package/dist/index.d.ts +8 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +7 -0
  17. package/dist/kernel/duration.d.ts +9 -0
  18. package/dist/kernel/duration.d.ts.map +1 -0
  19. package/dist/kernel/duration.js +44 -0
  20. package/dist/kernel/index.d.ts +4 -0
  21. package/dist/kernel/index.d.ts.map +1 -0
  22. package/dist/kernel/index.js +3 -0
  23. package/dist/kernel/range.d.ts +6 -0
  24. package/dist/kernel/range.d.ts.map +1 -0
  25. package/dist/kernel/range.js +16 -0
  26. package/dist/kernel/stochastic.d.ts +10 -0
  27. package/dist/kernel/stochastic.d.ts.map +1 -0
  28. package/dist/kernel/stochastic.js +25 -0
  29. package/dist/ledger/index.d.ts +4 -0
  30. package/dist/ledger/index.d.ts.map +1 -0
  31. package/dist/ledger/index.js +3 -0
  32. package/dist/ledger/markers.d.ts +46 -0
  33. package/dist/ledger/markers.d.ts.map +1 -0
  34. package/dist/ledger/markers.js +108 -0
  35. package/dist/ledger/stable.d.ts +65 -0
  36. package/dist/ledger/stable.d.ts.map +1 -0
  37. package/dist/ledger/stable.js +78 -0
  38. package/dist/ledger/validation.d.ts +16 -0
  39. package/dist/ledger/validation.d.ts.map +1 -0
  40. package/dist/ledger/validation.js +127 -0
  41. package/dist/moltnet/index.d.ts +3 -0
  42. package/dist/moltnet/index.d.ts.map +1 -0
  43. package/dist/moltnet/index.js +2 -0
  44. package/dist/moltnet/transcript-export.d.ts +34 -0
  45. package/dist/moltnet/transcript-export.d.ts.map +1 -0
  46. package/dist/moltnet/transcript-export.js +68 -0
  47. package/dist/moltnet/world-participant.d.ts +77 -0
  48. package/dist/moltnet/world-participant.d.ts.map +1 -0
  49. package/dist/moltnet/world-participant.js +221 -0
  50. package/dist/observe/artifacts.d.ts +16 -0
  51. package/dist/observe/artifacts.d.ts.map +1 -0
  52. package/dist/observe/artifacts.js +31 -0
  53. package/dist/observe/causalStreams.d.ts +19 -0
  54. package/dist/observe/causalStreams.d.ts.map +1 -0
  55. package/dist/observe/causalStreams.js +38 -0
  56. package/dist/observe/compute.d.ts +17 -0
  57. package/dist/observe/compute.d.ts.map +1 -0
  58. package/dist/observe/compute.js +97 -0
  59. package/dist/observe/index.d.ts +11 -0
  60. package/dist/observe/index.d.ts.map +1 -0
  61. package/dist/observe/index.js +10 -0
  62. package/dist/observe/manifest.d.ts +79 -0
  63. package/dist/observe/manifest.d.ts.map +1 -0
  64. package/dist/observe/manifest.js +87 -0
  65. package/dist/observe/memoryBanks.d.ts +38 -0
  66. package/dist/observe/memoryBanks.d.ts.map +1 -0
  67. package/dist/observe/memoryBanks.js +86 -0
  68. package/dist/observe/observe.d.ts +32 -0
  69. package/dist/observe/observe.d.ts.map +1 -0
  70. package/dist/observe/observe.js +73 -0
  71. package/dist/observe/report.d.ts +165 -0
  72. package/dist/observe/report.d.ts.map +1 -0
  73. package/dist/observe/report.js +138 -0
  74. package/dist/observe/seedSpread.d.ts +49 -0
  75. package/dist/observe/seedSpread.d.ts.map +1 -0
  76. package/dist/observe/seedSpread.js +262 -0
  77. package/dist/observe/seedSpreadArtifacts.d.ts +49 -0
  78. package/dist/observe/seedSpreadArtifacts.d.ts.map +1 -0
  79. package/dist/observe/seedSpreadArtifacts.js +118 -0
  80. package/dist/observe/spreadMatcher.d.ts +45 -0
  81. package/dist/observe/spreadMatcher.d.ts.map +1 -0
  82. package/dist/observe/spreadMatcher.js +124 -0
  83. package/dist/report/index.d.ts +4 -0
  84. package/dist/report/index.d.ts.map +1 -0
  85. package/dist/report/index.js +3 -0
  86. package/dist/report/probeArtifact.d.ts +26 -0
  87. package/dist/report/probeArtifact.d.ts.map +1 -0
  88. package/dist/report/probeArtifact.js +119 -0
  89. package/dist/report/probes.d.ts +52 -0
  90. package/dist/report/probes.d.ts.map +1 -0
  91. package/dist/report/probes.js +164 -0
  92. package/dist/report/score.d.ts +27 -0
  93. package/dist/report/score.d.ts.map +1 -0
  94. package/dist/report/score.js +14 -0
  95. package/dist/report/transcripts.d.ts +12 -0
  96. package/dist/report/transcripts.d.ts.map +1 -0
  97. package/dist/report/transcripts.js +17 -0
  98. package/dist/runtime/causal-fixture.d.ts +26 -0
  99. package/dist/runtime/causal-fixture.d.ts.map +1 -0
  100. package/dist/runtime/causal-fixture.js +36 -0
  101. package/dist/runtime/clock.d.ts +25 -0
  102. package/dist/runtime/clock.d.ts.map +1 -0
  103. package/dist/runtime/clock.js +63 -0
  104. package/dist/runtime/condition.d.ts +55 -0
  105. package/dist/runtime/condition.d.ts.map +1 -0
  106. package/dist/runtime/condition.js +122 -0
  107. package/dist/runtime/emit-causal-fixture.d.ts +2 -0
  108. package/dist/runtime/emit-causal-fixture.d.ts.map +1 -0
  109. package/dist/runtime/emit-causal-fixture.js +45 -0
  110. package/dist/runtime/expression.d.ts +11 -0
  111. package/dist/runtime/expression.d.ts.map +1 -0
  112. package/dist/runtime/expression.js +305 -0
  113. package/dist/runtime/numeric.d.ts +7 -0
  114. package/dist/runtime/numeric.d.ts.map +1 -0
  115. package/dist/runtime/numeric.js +11 -0
  116. package/dist/runtime/rule-actions.d.ts +5 -0
  117. package/dist/runtime/rule-actions.d.ts.map +1 -0
  118. package/dist/runtime/rule-actions.js +82 -0
  119. package/dist/runtime/run-record.d.ts +73 -0
  120. package/dist/runtime/run-record.d.ts.map +1 -0
  121. package/dist/runtime/run-record.js +179 -0
  122. package/dist/runtime/step-tick.d.ts +43 -0
  123. package/dist/runtime/step-tick.d.ts.map +1 -0
  124. package/dist/runtime/step-tick.js +177 -0
  125. package/dist/runtime/trace-compile.d.ts +66 -0
  126. package/dist/runtime/trace-compile.d.ts.map +1 -0
  127. package/dist/runtime/trace-compile.js +157 -0
  128. package/dist/runtime/trace-export.d.ts +5 -0
  129. package/dist/runtime/trace-export.d.ts.map +1 -0
  130. package/dist/runtime/trace-export.js +4 -0
  131. package/dist/runtime/trace-run.d.ts +15 -0
  132. package/dist/runtime/trace-run.d.ts.map +1 -0
  133. package/dist/runtime/trace-run.js +92 -0
  134. package/dist/runtime/trace.d.ts +10 -0
  135. package/dist/runtime/trace.d.ts.map +1 -0
  136. package/dist/runtime/trace.js +9 -0
  137. package/dist/runtime/types.d.ts +61 -0
  138. package/dist/runtime/types.d.ts.map +1 -0
  139. package/dist/runtime/types.js +1 -0
  140. package/dist/runtime/viewer-trace.d.ts +64 -0
  141. package/dist/runtime/viewer-trace.d.ts.map +1 -0
  142. package/dist/runtime/viewer-trace.js +164 -0
  143. package/dist/runtime/world-act.d.ts +55 -0
  144. package/dist/runtime/world-act.d.ts.map +1 -0
  145. package/dist/runtime/world-act.js +156 -0
  146. package/dist/schema/binding.d.ts +23 -0
  147. package/dist/schema/binding.d.ts.map +1 -0
  148. package/dist/schema/binding.js +249 -0
  149. package/dist/schema/index.d.ts +5 -0
  150. package/dist/schema/index.d.ts.map +1 -0
  151. package/dist/schema/index.js +4 -0
  152. package/dist/schema/model.d.ts +244 -0
  153. package/dist/schema/model.d.ts.map +1 -0
  154. package/dist/schema/model.js +222 -0
  155. package/dist/schema/parse.d.ts +10 -0
  156. package/dist/schema/parse.d.ts.map +1 -0
  157. package/dist/schema/parse.js +15 -0
  158. package/dist/schema/semantic.d.ts +3 -0
  159. package/dist/schema/semantic.d.ts.map +1 -0
  160. package/dist/schema/semantic.js +135 -0
  161. package/dist/sims/composeRunManifest.d.ts +54 -0
  162. package/dist/sims/composeRunManifest.d.ts.map +1 -0
  163. package/dist/sims/composeRunManifest.js +40 -0
  164. package/dist/sims/composedJungianSimDriver.d.ts +50 -0
  165. package/dist/sims/composedJungianSimDriver.d.ts.map +1 -0
  166. package/dist/sims/composedJungianSimDriver.js +199 -0
  167. package/dist/sims/composedOfficeSimDriver.d.ts +53 -0
  168. package/dist/sims/composedOfficeSimDriver.d.ts.map +1 -0
  169. package/dist/sims/composedOfficeSimDriver.js +162 -0
  170. package/dist/sims/exchangeWait.d.ts +56 -0
  171. package/dist/sims/exchangeWait.d.ts.map +1 -0
  172. package/dist/sims/exchangeWait.js +66 -0
  173. package/dist/sims/index.d.ts +14 -0
  174. package/dist/sims/index.d.ts.map +1 -0
  175. package/dist/sims/index.js +13 -0
  176. package/dist/sims/moltnetRoomClient.d.ts +47 -0
  177. package/dist/sims/moltnetRoomClient.d.ts.map +1 -0
  178. package/dist/sims/moltnetRoomClient.js +41 -0
  179. package/dist/sims/poll.d.ts +19 -0
  180. package/dist/sims/poll.d.ts.map +1 -0
  181. package/dist/sims/poll.js +23 -0
  182. package/dist/sims/spawnfileCli.d.ts +49 -0
  183. package/dist/sims/spawnfileCli.d.ts.map +1 -0
  184. package/dist/sims/spawnfileCli.js +90 -0
  185. package/dist/sims/spawnfileReceipts.d.ts +76 -0
  186. package/dist/sims/spawnfileReceipts.d.ts.map +1 -0
  187. package/dist/sims/spawnfileReceipts.js +107 -0
  188. package/dist/sims/worldDrivenOfficeSimDriver.d.ts +59 -0
  189. package/dist/sims/worldDrivenOfficeSimDriver.d.ts.map +1 -0
  190. package/dist/sims/worldDrivenOfficeSimDriver.js +182 -0
  191. package/dist/sims/worldLedgerWriter.d.ts +32 -0
  192. package/dist/sims/worldLedgerWriter.d.ts.map +1 -0
  193. package/dist/sims/worldLedgerWriter.js +39 -0
  194. package/dist/sims/worldSeedLint.d.ts +42 -0
  195. package/dist/sims/worldSeedLint.d.ts.map +1 -0
  196. package/dist/sims/worldSeedLint.js +73 -0
  197. package/dist/sims/worldTickIngest.d.ts +27 -0
  198. package/dist/sims/worldTickIngest.d.ts.map +1 -0
  199. package/dist/sims/worldTickIngest.js +35 -0
  200. package/dist/sims/worldTickLoop.d.ts +64 -0
  201. package/dist/sims/worldTickLoop.d.ts.map +1 -0
  202. package/dist/sims/worldTickLoop.js +122 -0
  203. package/dist/view/engineProvenance.d.ts +46 -0
  204. package/dist/view/engineProvenance.d.ts.map +1 -0
  205. package/dist/view/engineProvenance.js +76 -0
  206. package/dist/view/index.d.ts +14 -0
  207. package/dist/view/index.d.ts.map +1 -0
  208. package/dist/view/index.js +107 -0
  209. package/dist/view/membranes.d.ts +14 -0
  210. package/dist/view/membranes.d.ts.map +1 -0
  211. package/dist/view/membranes.js +127 -0
  212. package/dist/view/runDetect.d.ts +2 -0
  213. package/dist/view/runDetect.d.ts.map +1 -0
  214. package/dist/view/runDetect.js +47 -0
  215. package/dist/view/runRawArtifacts.d.ts +48 -0
  216. package/dist/view/runRawArtifacts.d.ts.map +1 -0
  217. package/dist/view/runRawArtifacts.js +143 -0
  218. package/dist/view/runTimeline.d.ts +24 -0
  219. package/dist/view/runTimeline.d.ts.map +1 -0
  220. package/dist/view/runTimeline.js +204 -0
  221. package/dist/view/runTimelineRecords.d.ts +42 -0
  222. package/dist/view/runTimelineRecords.d.ts.map +1 -0
  223. package/dist/view/runTimelineRecords.js +244 -0
  224. package/dist/view/runTimelineRefs.d.ts +27 -0
  225. package/dist/view/runTimelineRefs.d.ts.map +1 -0
  226. package/dist/view/runTimelineRefs.js +31 -0
  227. package/dist/view/runTimelineTypes.d.ts +99 -0
  228. package/dist/view/runTimelineTypes.d.ts.map +1 -0
  229. package/dist/view/runTimelineTypes.js +1 -0
  230. package/dist/view/runViewModel.d.ts +21 -0
  231. package/dist/view/runViewModel.d.ts.map +1 -0
  232. package/dist/view/runViewModel.js +66 -0
  233. package/dist/view/runViewModelCompute.d.ts +24 -0
  234. package/dist/view/runViewModelCompute.d.ts.map +1 -0
  235. package/dist/view/runViewModelCompute.js +157 -0
  236. package/dist/view/runViewModelTypes.d.ts +177 -0
  237. package/dist/view/runViewModelTypes.d.ts.map +1 -0
  238. package/dist/view/runViewModelTypes.js +1 -0
  239. package/dist/view/runWorldTrace.d.ts +97 -0
  240. package/dist/view/runWorldTrace.d.ts.map +1 -0
  241. package/dist/view/runWorldTrace.js +88 -0
  242. package/dist/view/server.d.ts +12 -0
  243. package/dist/view/server.d.ts.map +1 -0
  244. package/dist/view/server.js +306 -0
  245. package/package.json +65 -0
  246. package/web/dist/assets/index-CJHLjfsG.js +49 -0
  247. package/web/dist/assets/index-DJOsPsU8.css +1 -0
  248. package/web/dist/index.html +13 -0
  249. package/web/dist/models/AnimatedMushnub.glb +0 -0
@@ -0,0 +1,182 @@
1
+ import { readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { exportMoltnetTranscript, roomTranscriptTargetFromScope } from "../moltnet/transcript-export.js";
4
+ import { parseSimfileSource } from "../schema/parse.js";
5
+ import { parseClockSpec } from "../runtime/clock.js";
6
+ import { compileRuntime } from "../runtime/trace-compile.js";
7
+ import { clampAndRound } from "../runtime/numeric.js";
8
+ import { composeRunManifest, sha256HexOfBuffer } from "./composeRunManifest.js";
9
+ import { DEFAULT_QUIET_GRACE_MS, resolveTargetTurns } from "./exchangeWait.js";
10
+ import { allMembersConnected, getMoltnetRoom, listMoltnetAgents, moltnetHealthy } from "./moltnetRoomClient.js";
11
+ import { defaultSleep, pollUntilReady } from "./poll.js";
12
+ import { runSpawnfileArtifactsExport, runSpawnfileDown, runSpawnfileUp } from "./spawnfileCli.js";
13
+ import { createWorldLedgerWriter } from "./worldLedgerWriter.js";
14
+ import { assertWorldRuleContentClean, buildSeedDeclarationFromMemoryDoc } from "./worldSeedLint.js";
15
+ import { runLiveTickLoop } from "./worldTickLoop.js";
16
+ const DEFAULT_NETWORK_ID = "office_lab";
17
+ const DEFAULT_ROOM_ID = "office-room";
18
+ const DEFAULT_AGENT_IDS = ["eleanor", "sam"];
19
+ const DEFAULT_SEED_AGENT_ID = "eleanor";
20
+ const DEFAULT_TOKEN_SET = ["Rosa Delgado"];
21
+ const DEFAULT_TIMEOUT_MS = 240_000;
22
+ const DEFAULT_POLL_INTERVAL_MS = 2_000;
23
+ const DEFAULT_MAX_TICKS = 200;
24
+ const collapseEngineDisclosure = (engines) => {
25
+ const distinct = [...new Set((engines ?? []).map((entry) => entry.engine))].sort();
26
+ return distinct.length === 0 ? "unknown" : distinct.join("+");
27
+ };
28
+ const waitForMoltnetReady = async (baseUrl, roomId, agentIds, pollOptions) => {
29
+ await pollUntilReady("moltnet health", pollOptions, async () => ((await moltnetHealthy(baseUrl)) ? true : null));
30
+ await pollUntilReady(`moltnet room ${roomId} membership`, pollOptions, async () => {
31
+ const room = await getMoltnetRoom(baseUrl, roomId);
32
+ const expected = [...agentIds].sort();
33
+ const actual = [...room.members].sort();
34
+ return expected.length === actual.length && expected.every((id, index) => actual[index] === id) ? room : null;
35
+ });
36
+ await pollUntilReady(`moltnet bridge attachments for ${roomId}`, pollOptions, async () => {
37
+ const agents = await listMoltnetAgents(baseUrl);
38
+ return allMembersConnected(agents, roomId, agentIds) ? agents : null;
39
+ });
40
+ };
41
+ const relativeArtifactPath = (runDir, absolutePath) => path.relative(runDir, absolutePath).split(path.sep).join("/");
42
+ const hashedArtifact = async (runDir, absolutePath) => ({
43
+ path: relativeArtifactPath(runDir, absolutePath),
44
+ sha256: sha256HexOfBuffer(await readFile(absolutePath))
45
+ });
46
+ const writeTranscriptArtifact = async (runDir, transcript) => {
47
+ const absolutePath = path.join(runDir, "raw", "moltnet", "transcript.json");
48
+ await writeFile(absolutePath, `${JSON.stringify(transcript, null, 2)}\n`, "utf8");
49
+ return hashedArtifact(runDir, absolutePath);
50
+ };
51
+ export const runWorldDrivenOfficeSim = async (options) => {
52
+ const cliContext = { spawnfileBin: options.spawnfileBin };
53
+ const networkId = options.networkId ?? DEFAULT_NETWORK_ID;
54
+ const roomId = options.roomId ?? DEFAULT_ROOM_ID;
55
+ const agentIds = options.agentIds ?? DEFAULT_AGENT_IDS;
56
+ const seedAgentId = options.seedAgentId ?? DEFAULT_SEED_AGENT_ID;
57
+ const tokenSet = options.tokenSet ?? DEFAULT_TOKEN_SET;
58
+ const memoryDocPath = options.memoryDocPath ?? path.join(options.orgPath, "agents", seedAgentId, "MEMORY.md");
59
+ const targetTurns = resolveTargetTurns(options.targetTurns);
60
+ const quietGraceMs = options.quietGraceMs ?? DEFAULT_QUIET_GRACE_MS;
61
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
62
+ const pollOptions = {
63
+ intervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
64
+ sleep: defaultSleep,
65
+ timeoutMs
66
+ };
67
+ const maxTicks = options.maxTicks ?? DEFAULT_MAX_TICKS;
68
+ const now = options.now ?? (() => new Date());
69
+ const worldSource = await readFile(options.worldPath, "utf8");
70
+ const { simfile } = parseSimfileSource(worldSource, { path: options.worldPath });
71
+ assertWorldRuleContentClean(simfile, tokenSet);
72
+ const upReceipt = await runSpawnfileUp(cliContext, {
73
+ orgPath: options.orgPath,
74
+ containerName: options.containerName,
75
+ deploymentName: options.deploymentName,
76
+ compiledOutputDirectory: options.compiledOutputDirectory
77
+ });
78
+ if (!upReceipt.run_id) {
79
+ throw new Error("spawnfile up-receipt had no run_id; cannot assemble a run directory");
80
+ }
81
+ const baseUrl = upReceipt.readiness.moltnet_base_url;
82
+ if (!baseUrl) {
83
+ throw new Error("spawnfile up-receipt had no moltnet_base_url; is human_ingress enabled on the network?");
84
+ }
85
+ const runId = upReceipt.run_id;
86
+ const runDir = path.join(options.runsRootDirectory, runId);
87
+ const createdAt = now().toISOString();
88
+ let loopResult;
89
+ try {
90
+ await waitForMoltnetReady(baseUrl, roomId, agentIds, pollOptions);
91
+ const clock = parseClockSpec(simfile.clock);
92
+ const runtime = compileRuntime(simfile);
93
+ const precision = 6;
94
+ const state = {};
95
+ for (const [id, value] of Object.entries(runtime.initialState)) {
96
+ const range = runtime.ranges.get(id);
97
+ if (!range) {
98
+ throw new Error(`variable ${id} missing range`);
99
+ }
100
+ state[id] = clampAndRound(value, range, precision);
101
+ }
102
+ const ledgerWriter = await createWorldLedgerWriter(runDir, runId);
103
+ loopResult = await runLiveTickLoop({
104
+ baseUrl,
105
+ networkId,
106
+ roomId,
107
+ roomScope: `room:${networkId}:${roomId}`,
108
+ agentIds,
109
+ clock,
110
+ runtime,
111
+ simfile,
112
+ state,
113
+ precision,
114
+ runId,
115
+ seed: simfile.clock.seed,
116
+ ledgerWriter,
117
+ pollOptions,
118
+ targetTurns,
119
+ quietGraceMs,
120
+ timeoutMs,
121
+ maxTicks
122
+ });
123
+ const exportResult = await runSpawnfileArtifactsExport(cliContext, {
124
+ orgPath: options.orgPath,
125
+ deploymentName: options.deploymentName,
126
+ compiledOutputDirectory: options.compiledOutputDirectory,
127
+ destinationDirectory: runDir
128
+ });
129
+ const transcript = await exportMoltnetTranscript({
130
+ baseUrl,
131
+ targets: [roomTranscriptTargetFromScope(`room:${networkId}:${roomId}`)]
132
+ });
133
+ const transcriptArtifact = await writeTranscriptArtifact(runDir, transcript);
134
+ await runSpawnfileDown(cliContext, {
135
+ orgPath: options.orgPath,
136
+ deploymentName: options.deploymentName,
137
+ compiledOutputDirectory: options.compiledOutputDirectory,
138
+ removeVolumes: options.removeVolumesOnDown ?? true
139
+ });
140
+ const worldLedgerWriter = ledgerWriter;
141
+ const worldArtifacts = await Promise.all([
142
+ hashedArtifact(runDir, worldLedgerWriter.causalJsonlPath),
143
+ hashedArtifact(runDir, worldLedgerWriter.telemetryJsonPath),
144
+ hashedArtifact(runDir, worldLedgerWriter.ingestedMessagesPath)
145
+ ]);
146
+ const seedDeclaration = await buildSeedDeclarationFromMemoryDoc({
147
+ memoryDocPath,
148
+ tokenSet,
149
+ seedAgent: seedAgentId,
150
+ seedEpoch: createdAt
151
+ });
152
+ const world = { network_id: networkId, room_id: roomId, members: [...agentIds].sort() };
153
+ const manifest = composeRunManifest({
154
+ runId,
155
+ createdAt,
156
+ fingerprint: upReceipt.fingerprint,
157
+ engine: collapseEngineDisclosure(upReceipt.engines),
158
+ world,
159
+ exportedArtifacts: exportResult.index.files,
160
+ extraArtifacts: [transcriptArtifact, ...worldArtifacts],
161
+ seedDeclaration
162
+ });
163
+ await writeFile(path.join(runDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
164
+ }
165
+ catch (error) {
166
+ await runSpawnfileDown(cliContext, {
167
+ orgPath: options.orgPath,
168
+ deploymentName: options.deploymentName,
169
+ compiledOutputDirectory: options.compiledOutputDirectory,
170
+ removeVolumes: options.removeVolumesOnDown ?? true
171
+ }).catch(() => undefined);
172
+ throw error;
173
+ }
174
+ return {
175
+ runDir,
176
+ runId,
177
+ endedReason: loopResult.endedReason,
178
+ ticksRun: loopResult.ticksRun,
179
+ markerFired: loopResult.markerFired,
180
+ agentTurnCount: loopResult.agentTurnCount
181
+ };
182
+ };
@@ -0,0 +1,32 @@
1
+ import type { RuntimeTraceEvent, RuntimeVariableSample } from "../runtime/types.js";
2
+ export interface WorldLedgerTickInput {
3
+ tick: number;
4
+ tickEvents: readonly RuntimeTraceEvent[];
5
+ sample: RuntimeVariableSample;
6
+ /** Every Moltnet room message id folded into this tick — the determinism
7
+ * contract: without recording exactly which messages a tick ingested,
8
+ * "the world replays byte-identically" would be unfalsifiable. */
9
+ ingestedMessageIds: readonly string[];
10
+ }
11
+ export interface WorldLedgerWriter {
12
+ causalJsonlPath: string;
13
+ telemetryJsonPath: string;
14
+ ingestedMessagesPath: string;
15
+ appendTick: (input: WorldLedgerTickInput) => Promise<void>;
16
+ }
17
+ /**
18
+ * Live-loop counterpart of `run-record.ts`'s batch artifact writer, scoped
19
+ * to just the world authority's own stream: appends every tick's minted
20
+ * events to `raw/world/causal.jsonl` in the `noopolis.causal-event.v1` wire
21
+ * shape (via `causal-fixture.ts`'s `toCausalFixtureRecord` — the same
22
+ * mapping B92's conformance harness already validates), grows
23
+ * `world/telemetry.json` with each tick's variable sample, and appends the
24
+ * tick's ingested-message-id list to `world/ingested-messages.jsonl`. All
25
+ * three are real appends (one `appendFile`/rewrite per tick call, not a
26
+ * single write at the end) so the live loop's own on-disk ledger reflects
27
+ * ticks as they happen, matching what the composed driver is meant to be
28
+ * doing here: driving the run, not just producing a side artifact after
29
+ * the fact.
30
+ */
31
+ export declare const createWorldLedgerWriter: (runDir: string, runId: string) => Promise<WorldLedgerWriter>;
32
+ //# sourceMappingURL=worldLedgerWriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worldLedgerWriter.d.ts","sourceRoot":"","sources":["../../src/sims/worldLedgerWriter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACzC,MAAM,EAAE,qBAAqB,CAAC;IAC9B;;sEAEkE;IAClE,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,uBAAuB,GAAU,QAAQ,MAAM,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,iBAAiB,CAoCtG,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { appendFile, mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { buildCausalFixtureRecords } from "../runtime/causal-fixture.js";
4
+ /**
5
+ * Live-loop counterpart of `run-record.ts`'s batch artifact writer, scoped
6
+ * to just the world authority's own stream: appends every tick's minted
7
+ * events to `raw/world/causal.jsonl` in the `noopolis.causal-event.v1` wire
8
+ * shape (via `causal-fixture.ts`'s `toCausalFixtureRecord` — the same
9
+ * mapping B92's conformance harness already validates), grows
10
+ * `world/telemetry.json` with each tick's variable sample, and appends the
11
+ * tick's ingested-message-id list to `world/ingested-messages.jsonl`. All
12
+ * three are real appends (one `appendFile`/rewrite per tick call, not a
13
+ * single write at the end) so the live loop's own on-disk ledger reflects
14
+ * ticks as they happen, matching what the composed driver is meant to be
15
+ * doing here: driving the run, not just producing a side artifact after
16
+ * the fact.
17
+ */
18
+ export const createWorldLedgerWriter = async (runDir, runId) => {
19
+ const worldRawDir = path.join(runDir, "raw", "world");
20
+ const worldDir = path.join(runDir, "world");
21
+ await mkdir(worldRawDir, { recursive: true });
22
+ await mkdir(worldDir, { recursive: true });
23
+ const causalJsonlPath = path.join(worldRawDir, "causal.jsonl");
24
+ const telemetryJsonPath = path.join(worldDir, "telemetry.json");
25
+ const ingestedMessagesPath = path.join(worldDir, "ingested-messages.jsonl");
26
+ await writeFile(causalJsonlPath, "", "utf8");
27
+ await writeFile(ingestedMessagesPath, "", "utf8");
28
+ const samples = [];
29
+ const appendTick = async (input) => {
30
+ if (input.tickEvents.length > 0) {
31
+ const lines = buildCausalFixtureRecords(input.tickEvents).map((record) => JSON.stringify(record));
32
+ await appendFile(causalJsonlPath, `${lines.join("\n")}\n`, "utf8");
33
+ }
34
+ samples.push(input.sample);
35
+ await writeFile(telemetryJsonPath, `${JSON.stringify({ run_id: runId, samples, version: "simfile.telemetry.v1" }, null, 2)}\n`, "utf8");
36
+ await appendFile(ingestedMessagesPath, `${JSON.stringify({ tick: input.tick, message_ids: [...input.ingestedMessageIds] })}\n`, "utf8");
37
+ };
38
+ return { causalJsonlPath, telemetryJsonPath, ingestedMessagesPath, appendTick };
39
+ };
@@ -0,0 +1,42 @@
1
+ import type { SeedDeclaration } from "../observe/manifest.js";
2
+ import type { Simfile } from "../schema/model.js";
3
+ /**
4
+ * Every `moltnet:message`/`moltnet:dm` action content string across every
5
+ * world rule (the kickoff included) — the full surface the token lint below
6
+ * checks. Kept as its own export so a test can assert the lint actually
7
+ * looked at every rule, not just the one named `kickoff`.
8
+ */
9
+ export declare const worldRuleMessageContents: (simfile: Simfile) => {
10
+ ruleId: string;
11
+ content: string;
12
+ }[];
13
+ /**
14
+ * Lints every world rule's message content against the memetics
15
+ * `token_set` at run start. A hit here means the WORLD ITSELF would speak a
16
+ * seed token — if that ever happened, any later `marker.seen` on that token
17
+ * would be meaningless (the world can't measure spread of a fact it just
18
+ * announced itself). Returns violation strings; an empty array means the
19
+ * world is clean. Case-insensitive, same as the marker matcher
20
+ * (`../ledger/markers.ts`'s `containsAlias`).
21
+ */
22
+ export declare const lintWorldRuleContentAgainstTokenSet: (simfile: Simfile, tokenSet: readonly string[]) => string[];
23
+ /** Throws with every violation listed if the world would leak a seed token. */
24
+ export declare const assertWorldRuleContentClean: (simfile: Simfile, tokenSet: readonly string[]) => void;
25
+ export interface BuildSeedDeclarationOptions {
26
+ /** Absolute path to the seed agent's doc-seeded memory file. */
27
+ memoryDocPath: string;
28
+ tokenSet: readonly string[];
29
+ seedAgent: string;
30
+ seedEpoch: string;
31
+ }
32
+ /**
33
+ * Builds the `seed_declaration` manifest field (Decision-13) from the
34
+ * agent's own `workspace.docs.memory` file rather than hand-typing the
35
+ * secret line's hash twice: locates the one line containing every
36
+ * `token_set` entry, hashes that exact (trimmed) line, and stamps
37
+ * `matcher_policy: "exact"` / `entry_channel: "doc-seeded"`. Ground truth
38
+ * for the seed lives in this manifest field, never inferred from the
39
+ * substrate — see `../observe/manifest.ts`'s `SeedDeclaration` doc comment.
40
+ */
41
+ export declare const buildSeedDeclarationFromMemoryDoc: (options: BuildSeedDeclarationOptions) => Promise<SeedDeclaration>;
42
+ //# sourceMappingURL=worldSeedLint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worldSeedLint.d.ts","sourceRoot":"","sources":["../../src/sims/worldSeedLint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAIlD;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,OAAO,KAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAU9F,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mCAAmC,GAC9C,SAAS,OAAO,EAChB,UAAU,SAAS,MAAM,EAAE,KAC1B,MAAM,EAWR,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,2BAA2B,GAAI,SAAS,OAAO,EAAE,UAAU,SAAS,MAAM,EAAE,KAAG,IAK3F,CAAC;AAEF,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,iCAAiC,GAC5C,SAAS,2BAA2B,KACnC,OAAO,CAAC,eAAe,CAkBzB,CAAC"}
@@ -0,0 +1,73 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFile } from "node:fs/promises";
3
+ const sha256Hex = (value) => createHash("sha256").update(value, "utf8").digest("hex");
4
+ /**
5
+ * Every `moltnet:message`/`moltnet:dm` action content string across every
6
+ * world rule (the kickoff included) — the full surface the token lint below
7
+ * checks. Kept as its own export so a test can assert the lint actually
8
+ * looked at every rule, not just the one named `kickoff`.
9
+ */
10
+ export const worldRuleMessageContents = (simfile) => {
11
+ const contents = [];
12
+ for (const [ruleId, rule] of Object.entries(simfile.rules)) {
13
+ for (const action of rule.do) {
14
+ if (action.action === "moltnet:message" || action.action === "moltnet:dm") {
15
+ contents.push({ ruleId, content: action.content });
16
+ }
17
+ }
18
+ }
19
+ return contents;
20
+ };
21
+ /**
22
+ * Lints every world rule's message content against the memetics
23
+ * `token_set` at run start. A hit here means the WORLD ITSELF would speak a
24
+ * seed token — if that ever happened, any later `marker.seen` on that token
25
+ * would be meaningless (the world can't measure spread of a fact it just
26
+ * announced itself). Returns violation strings; an empty array means the
27
+ * world is clean. Case-insensitive, same as the marker matcher
28
+ * (`../ledger/markers.ts`'s `containsAlias`).
29
+ */
30
+ export const lintWorldRuleContentAgainstTokenSet = (simfile, tokenSet) => {
31
+ const violations = [];
32
+ for (const { ruleId, content } of worldRuleMessageContents(simfile)) {
33
+ const lowerContent = content.toLowerCase();
34
+ for (const token of tokenSet) {
35
+ if (lowerContent.includes(token.toLowerCase())) {
36
+ violations.push(`rule "${ruleId}" message content contains seed token "${token}": ${content}`);
37
+ }
38
+ }
39
+ }
40
+ return violations;
41
+ };
42
+ /** Throws with every violation listed if the world would leak a seed token. */
43
+ export const assertWorldRuleContentClean = (simfile, tokenSet) => {
44
+ const violations = lintWorldRuleContentAgainstTokenSet(simfile, tokenSet);
45
+ if (violations.length > 0) {
46
+ throw new Error(`world rule content lint failed — the world would emit a seed token itself:\n${violations.join("\n")}`);
47
+ }
48
+ };
49
+ /**
50
+ * Builds the `seed_declaration` manifest field (Decision-13) from the
51
+ * agent's own `workspace.docs.memory` file rather than hand-typing the
52
+ * secret line's hash twice: locates the one line containing every
53
+ * `token_set` entry, hashes that exact (trimmed) line, and stamps
54
+ * `matcher_policy: "exact"` / `entry_channel: "doc-seeded"`. Ground truth
55
+ * for the seed lives in this manifest field, never inferred from the
56
+ * substrate — see `../observe/manifest.ts`'s `SeedDeclaration` doc comment.
57
+ */
58
+ export const buildSeedDeclarationFromMemoryDoc = async (options) => {
59
+ const text = await readFile(options.memoryDocPath, "utf8");
60
+ const lines = text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
61
+ const seedLine = lines.find((line) => options.tokenSet.every((token) => line.includes(token)));
62
+ if (!seedLine) {
63
+ throw new Error(`no line in ${options.memoryDocPath} contains every token_set entry (${options.tokenSet.join(", ")})`);
64
+ }
65
+ return {
66
+ content_hash: sha256Hex(seedLine),
67
+ token_set: [...options.tokenSet],
68
+ matcher_policy: "exact",
69
+ seed_agent: options.seedAgent,
70
+ seed_epoch: options.seedEpoch,
71
+ entry_channel: "doc-seeded"
72
+ };
73
+ };
@@ -0,0 +1,27 @@
1
+ import type { LedgerEvent } from "../ledger/markers.js";
2
+ import type { MoltnetRoomMessage } from "./moltnetRoomClient.js";
3
+ /**
4
+ * Lowers one Moltnet room message to the `LedgerEvent` shape `scanMarkers`
5
+ * (`../ledger/markers.ts`) expects, so the live world-driven loop can scan
6
+ * real agent utterances for marker tokens exactly like the batch
7
+ * `runSimfileTrace` scans its own minted events. `kind` here
8
+ * ("moltnet.message") is a display-only label for this ingestion path — it
9
+ * is never merged into `SimfileEventKind`'s own kind space and never minted
10
+ * as a world-ledger event itself; only the `marker.seen` event a hit
11
+ * produces gets written to `raw/world/causal.jsonl`.
12
+ */
13
+ export declare const moltnetMessageToLedgerEvent: (message: MoltnetRoomMessage, roomScope: string, simTime: number) => LedgerEvent;
14
+ export interface IngestNewRoomMessagesResult {
15
+ newMessages: MoltnetRoomMessage[];
16
+ ingestedIds: string[];
17
+ }
18
+ /**
19
+ * Fetches the room's current message list and returns only the messages not
20
+ * already present in `cursor` (mutated in place: registering an id here is
21
+ * the ingestion cursor for the whole live loop — a message is folded into
22
+ * exactly one tick, never re-scanned on a later poll). This is the ONLY read
23
+ * the live loop performs against Moltnet each tick; delivery of world-minted
24
+ * events is a separate write path (`../moltnet/world-participant.ts`).
25
+ */
26
+ export declare const ingestNewRoomMessages: (baseUrl: string, roomId: string, cursor: Set<string>, listMessages: (baseUrl: string, roomId: string, limit: number) => Promise<MoltnetRoomMessage[]>) => Promise<IngestNewRoomMessagesResult>;
27
+ //# sourceMappingURL=worldTickIngest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worldTickIngest.d.ts","sourceRoot":"","sources":["../../src/sims/worldTickIngest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACtC,SAAS,kBAAkB,EAC3B,WAAW,MAAM,EACjB,SAAS,MAAM,KACd,WAQD,CAAC;AAEH,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,QAAQ,GAAG,CAAC,MAAM,CAAC,EACnB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,EAAE,CAAC,KAC9F,OAAO,CAAC,2BAA2B,CAOrC,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Lowers one Moltnet room message to the `LedgerEvent` shape `scanMarkers`
3
+ * (`../ledger/markers.ts`) expects, so the live world-driven loop can scan
4
+ * real agent utterances for marker tokens exactly like the batch
5
+ * `runSimfileTrace` scans its own minted events. `kind` here
6
+ * ("moltnet.message") is a display-only label for this ingestion path — it
7
+ * is never merged into `SimfileEventKind`'s own kind space and never minted
8
+ * as a world-ledger event itself; only the `marker.seen` event a hit
9
+ * produces gets written to `raw/world/causal.jsonl`.
10
+ */
11
+ export const moltnetMessageToLedgerEvent = (message, roomScope, simTime) => ({
12
+ event_id: message.id,
13
+ kind: "moltnet.message",
14
+ sim_time: simTime,
15
+ actor: message.from.id,
16
+ target: roomScope,
17
+ scope: roomScope,
18
+ payload: { content: (message.parts ?? []).map((part) => part.text ?? "").join(" ") }
19
+ });
20
+ /**
21
+ * Fetches the room's current message list and returns only the messages not
22
+ * already present in `cursor` (mutated in place: registering an id here is
23
+ * the ingestion cursor for the whole live loop — a message is folded into
24
+ * exactly one tick, never re-scanned on a later poll). This is the ONLY read
25
+ * the live loop performs against Moltnet each tick; delivery of world-minted
26
+ * events is a separate write path (`../moltnet/world-participant.ts`).
27
+ */
28
+ export const ingestNewRoomMessages = async (baseUrl, roomId, cursor, listMessages) => {
29
+ const messages = await listMessages(baseUrl, roomId, 200);
30
+ const newMessages = messages.filter((message) => !cursor.has(message.id));
31
+ for (const message of newMessages) {
32
+ cursor.add(message.id);
33
+ }
34
+ return { newMessages, ingestedIds: newMessages.map((message) => message.id) };
35
+ };
@@ -0,0 +1,64 @@
1
+ import type { Simfile } from "../schema/model.js";
2
+ import { type ClockRuntime } from "../runtime/clock.js";
3
+ import type { CompiledTraceRuntime } from "../runtime/trace-compile.js";
4
+ import { type ExchangeEndReason } from "./exchangeWait.js";
5
+ import type { WorldLedgerWriter } from "./worldLedgerWriter.js";
6
+ export interface LiveTickLoopContext {
7
+ baseUrl: string;
8
+ networkId: string;
9
+ roomId: string;
10
+ roomScope: string;
11
+ agentIds: readonly string[];
12
+ clock: ClockRuntime;
13
+ runtime: CompiledTraceRuntime;
14
+ simfile: Simfile;
15
+ state: Record<string, number>;
16
+ precision: number;
17
+ runId: string;
18
+ seed: string;
19
+ ledgerWriter: WorldLedgerWriter;
20
+ pollOptions: {
21
+ intervalMs: number;
22
+ sleep: (ms: number) => Promise<void>;
23
+ timeoutMs: number;
24
+ };
25
+ targetTurns: number;
26
+ quietGraceMs: number;
27
+ timeoutMs: number;
28
+ maxTicks: number;
29
+ }
30
+ export interface LiveTickLoopResult {
31
+ endedReason: ExchangeEndReason;
32
+ ticksRun: number;
33
+ markerFired: boolean;
34
+ agentTurnCount: number;
35
+ }
36
+ /**
37
+ * The live loop itself (memetics increment (a)): one iteration per
38
+ * wall-clock `clock.tick`. Ingest -> step -> scan -> deliver -> append, then
39
+ * decide stop/continue with the same pure `evaluateExchangeCompletion`
40
+ * decision the batch composed driver uses for its own exchange wait — never
41
+ * a coax/resend, per this package's derail-guard rule
42
+ * (`composedOfficeSimDriver.ts`'s own doc comment; unchanged here).
43
+ *
44
+ * - Ingest: `ingestNewRoomMessages` GETs the room and returns only messages
45
+ * not yet folded into a tick (the cursor IS the determinism contract).
46
+ * - Step: `stepSimfileTick` advances the world exactly one tick (clock
47
+ * resolve -> world-acts -> generators -> derived -> rules).
48
+ * - Scan: `scanMarkers` runs LIVE over this tick's own newly ingested
49
+ * messages (never the whole transcript, never the world's own minted
50
+ * events) so a `marker.seen` hit is always evidence of a REAL agent
51
+ * utterance. Its `cause_event_ids` is this tick's own `clock.sync` only —
52
+ * the moltnet message id is carried as `source_event_id` in payload, a
53
+ * display/measurement join, never a synthesized causal link — so stele
54
+ * reconciliation over the world stream stays complete.
55
+ * - Deliver: any rule-emitted `world.message`/`world.dm`/`wake.recommended`
56
+ * from this tick goes out through the existing moltnet world-participant
57
+ * bridge, unchanged.
58
+ * - Append: the tick's events (plus any minted `marker.seen`), its variable
59
+ * sample, and the list of ingested message ids all land in
60
+ * `raw/world/causal.jsonl` / `world/telemetry.json` /
61
+ * `world/ingested-messages.jsonl` via `ledgerWriter`.
62
+ */
63
+ export declare const runLiveTickLoop: (ctx: LiveTickLoopContext) => Promise<LiveTickLoopResult>;
64
+ //# sourceMappingURL=worldTickLoop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worldTickLoop.d.ts","sourceRoot":"","sources":["../../src/sims/worldTickLoop.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAKxE,OAAO,EAA8B,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGvF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,iBAAiB,CAAC;IAChC,WAAW,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,eAAe,GAAU,KAAK,mBAAmB,KAAG,OAAO,CAAC,kBAAkB,CAiH1F,CAAC"}
@@ -0,0 +1,122 @@
1
+ import { isWorldMessageEvent, sendWorldEventToMoltnet } from "../moltnet/world-participant.js";
2
+ import { scanMarkers } from "../ledger/markers.js";
3
+ import { resolveClockState } from "../runtime/clock.js";
4
+ import { ingestWorldActs } from "../runtime/world-act.js";
5
+ import { createTraceEvent, stepSimfileTick } from "../runtime/step-tick.js";
6
+ import { evaluateExchangeCompletion } from "./exchangeWait.js";
7
+ import { listMoltnetRoomMessages } from "./moltnetRoomClient.js";
8
+ import { ingestNewRoomMessages, moltnetMessageToLedgerEvent } from "./worldTickIngest.js";
9
+ /**
10
+ * The live loop itself (memetics increment (a)): one iteration per
11
+ * wall-clock `clock.tick`. Ingest -> step -> scan -> deliver -> append, then
12
+ * decide stop/continue with the same pure `evaluateExchangeCompletion`
13
+ * decision the batch composed driver uses for its own exchange wait — never
14
+ * a coax/resend, per this package's derail-guard rule
15
+ * (`composedOfficeSimDriver.ts`'s own doc comment; unchanged here).
16
+ *
17
+ * - Ingest: `ingestNewRoomMessages` GETs the room and returns only messages
18
+ * not yet folded into a tick (the cursor IS the determinism contract).
19
+ * - Step: `stepSimfileTick` advances the world exactly one tick (clock
20
+ * resolve -> world-acts -> generators -> derived -> rules).
21
+ * - Scan: `scanMarkers` runs LIVE over this tick's own newly ingested
22
+ * messages (never the whole transcript, never the world's own minted
23
+ * events) so a `marker.seen` hit is always evidence of a REAL agent
24
+ * utterance. Its `cause_event_ids` is this tick's own `clock.sync` only —
25
+ * the moltnet message id is carried as `source_event_id` in payload, a
26
+ * display/measurement join, never a synthesized causal link — so stele
27
+ * reconciliation over the world stream stays complete.
28
+ * - Deliver: any rule-emitted `world.message`/`world.dm`/`wake.recommended`
29
+ * from this tick goes out through the existing moltnet world-participant
30
+ * bridge, unchanged.
31
+ * - Append: the tick's events (plus any minted `marker.seen`), its variable
32
+ * sample, and the list of ingested message ids all land in
33
+ * `raw/world/causal.jsonl` / `world/telemetry.json` /
34
+ * `world/ingested-messages.jsonl` via `ledgerWriter`.
35
+ */
36
+ export const runLiveTickLoop = async (ctx) => {
37
+ const worldActIngestion = ingestWorldActs([], {
38
+ ticks: ctx.maxTicks,
39
+ ranges: ctx.runtime.ranges,
40
+ variableFedBy: ctx.runtime.variableFedBy
41
+ });
42
+ const cursor = new Set();
43
+ const agentTurns = [];
44
+ let seq = 1;
45
+ let markerFired = false;
46
+ const startedAt = Date.now();
47
+ let lastAgentMessageAt = startedAt;
48
+ for (let tick = 0; tick < ctx.maxTicks; tick += 1) {
49
+ const { simTime } = resolveClockState(ctx.clock, tick);
50
+ const { newMessages, ingestedIds } = await ingestNewRoomMessages(ctx.baseUrl, ctx.roomId, cursor, listMoltnetRoomMessages);
51
+ const step = stepSimfileTick(tick, {
52
+ runId: ctx.runId,
53
+ seed: ctx.seed,
54
+ precision: ctx.precision,
55
+ clock: ctx.clock,
56
+ runtime: ctx.runtime,
57
+ state: ctx.state,
58
+ seq,
59
+ worldActsForTick: worldActIngestion.queueByTick.get(tick),
60
+ worldActResultsByActId: worldActIngestion.resultsByActId
61
+ });
62
+ seq = step.nextSeq;
63
+ const clockSyncEventId = step.events[0]?.event_id;
64
+ if (!clockSyncEventId) {
65
+ throw new Error(`tick ${tick} produced no clock.sync event`);
66
+ }
67
+ const ledgerCandidates = newMessages.map((message) => moltnetMessageToLedgerEvent(message, ctx.roomScope, simTime));
68
+ const hits = scanMarkers(ledgerCandidates, ctx.simfile.markers);
69
+ const markerEvents = [];
70
+ for (const [markerId, markerHits] of Object.entries(hits)) {
71
+ for (const hit of markerHits) {
72
+ markerFired = true;
73
+ markerEvents.push(createTraceEvent(ctx.runId, seq, "marker.seen", simTime, markerId, hit.scope, hit.scope, {
74
+ alias: hit.alias,
75
+ marker: markerId,
76
+ // Display/measurement join to the Moltnet message that carried
77
+ // the token — NOT a synthesized causal link. The real
78
+ // `cause_event_ids` below is this tick's own `clock.sync` only,
79
+ // so stele reconciliation over the world stream stays complete.
80
+ source_event_id: hit.eventId,
81
+ source_event_kind: hit.eventKind
82
+ }, [clockSyncEventId]));
83
+ seq += 1;
84
+ }
85
+ }
86
+ for (const event of step.events) {
87
+ if (isWorldMessageEvent(event)) {
88
+ await sendWorldEventToMoltnet(event, { baseUrl: ctx.baseUrl, networkId: ctx.networkId });
89
+ }
90
+ }
91
+ await ctx.ledgerWriter.appendTick({
92
+ tick,
93
+ tickEvents: [...step.events, ...markerEvents],
94
+ sample: step.sample,
95
+ ingestedMessageIds: ingestedIds
96
+ });
97
+ for (const message of newMessages) {
98
+ if (ctx.agentIds.includes(message.from.id)) {
99
+ agentTurns.push({ id: message.from.id });
100
+ lastAgentMessageAt = Date.now();
101
+ }
102
+ }
103
+ const evaluation = evaluateExchangeCompletion({
104
+ agentTurnCount: agentTurns.length,
105
+ elapsedSinceLastAgentMessageMs: Date.now() - lastAgentMessageAt,
106
+ elapsedSinceStartMs: Date.now() - startedAt,
107
+ quietGraceMs: ctx.quietGraceMs,
108
+ targetTurns: ctx.targetTurns,
109
+ timeoutMs: ctx.timeoutMs
110
+ });
111
+ if (evaluation.done) {
112
+ return {
113
+ endedReason: evaluation.reason,
114
+ ticksRun: tick + 1,
115
+ markerFired,
116
+ agentTurnCount: agentTurns.length
117
+ };
118
+ }
119
+ await ctx.pollOptions.sleep(ctx.pollOptions.intervalMs);
120
+ }
121
+ throw new Error(`world tick fuse tripped at ${ctx.maxTicks} ticks without the exchange concluding`);
122
+ };