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,97 @@
1
+ import type { RunTimeline, RunTimelineMembrane } from "./runTimelineTypes.js";
2
+ /**
3
+ * Adapts a run-replay `RunTimeline` into the same `viewer.trace.v1` shape
4
+ * `web/src/viewer/worldModel.ts`'s `buildViewerWorld` already consumes, so
5
+ * the existing `AsciiMap`/`worldModel`/`renderSettings` render this run's
6
+ * map unchanged (`docs/VIEW_DESIGN.md`'s two-layer rule). This type is a
7
+ * structural mirror of `web/src/viewer/types.ts`'s `ViewerContractTrace` —
8
+ * a deliberate duplicate, not a cross-package import, matching this
9
+ * package's existing src/web boundary: the contract is the JSON shape on
10
+ * the wire, not a shared TS type.
11
+ *
12
+ * These moltnet-room runs have no place-bearing world (Space Module has not
13
+ * landed): there is exactly one informational room anchor per room in the
14
+ * `rooms` list, no corridors, no presence stream. Agents render with
15
+ * `label_hint: "heuristic"`, which is the same "no presence stream yet"
16
+ * treatment `worldModel.ts` already gives any agent lacking presence events
17
+ * — nothing new to teach the renderer.
18
+ */
19
+ export interface RunWorldTraceRoom {
20
+ id: string;
21
+ label: string;
22
+ scope: string;
23
+ members: string[];
24
+ scene: [number, number, number];
25
+ access_hint?: string;
26
+ }
27
+ export interface RunWorldTraceAgent {
28
+ id: string;
29
+ label?: string;
30
+ scope: string;
31
+ label_hint?: string;
32
+ detail?: string;
33
+ }
34
+ export type RunWorldTraceLedgerFactType = "clock.sync" | "world.message" | "world.dm" | "wake.recommended" | "rule.fired" | "marker.seen" | "probe" | "other";
35
+ export interface RunWorldTraceLedgerFact {
36
+ type: RunWorldTraceLedgerFactType;
37
+ tick: number;
38
+ event_id: string;
39
+ kind: string;
40
+ sim_time: number;
41
+ provenance: "mechanical" | "agentic" | "external";
42
+ actor: string;
43
+ target: string;
44
+ detail: string;
45
+ scope?: string;
46
+ payload: unknown;
47
+ }
48
+ export interface RunWorldTrace {
49
+ version: "viewer.trace.v1";
50
+ run_id: string;
51
+ run_name: string;
52
+ rooms: RunWorldTraceRoom[];
53
+ corridors: never[];
54
+ agents: RunWorldTraceAgent[];
55
+ presence: never[];
56
+ ledger_facts: RunWorldTraceLedgerFact[];
57
+ signals: never[];
58
+ }
59
+ export declare const NO_PLACE_CAPTION = "no place-bearing world in this run \u2014 a chat-only room, rendered as an informational anchor";
60
+ /** One explicit room to render as an anchor — the generalized replacement for the old single-room `world` shape. */
61
+ export interface BuildRunWorldTraceRoomInput {
62
+ networkId: string;
63
+ roomId: string;
64
+ members?: string[];
65
+ }
66
+ export interface BuildRunWorldTraceParams {
67
+ runId: string;
68
+ runName: string;
69
+ /** Legacy single-room shape, kept for callers that only ever had one anchor. Ignored when `rooms` is given. */
70
+ world?: {
71
+ networkId?: string;
72
+ roomId?: string;
73
+ members?: string[];
74
+ };
75
+ /**
76
+ * Explicit room list — the parameterization the recursive membrane portal
77
+ * needs (`docs/VIEW_DESIGN.md` rule 5): the outer map's rooms (every
78
+ * `manifest.world` room that is NOT some membrane's interior room) and a
79
+ * membrane's own `interiorRooms` both go through this same param, laid out
80
+ * side by side (`ROOM_SPACING` apart) rather than stacked at one origin.
81
+ */
82
+ rooms?: BuildRunWorldTraceRoomInput[];
83
+ timeline: RunTimeline;
84
+ }
85
+ export declare const buildRunWorldTrace: ({ runId, runName, world, rooms: roomInputs, timeline }: BuildRunWorldTraceParams) => RunWorldTrace;
86
+ /**
87
+ * Builds a `viewer.trace.v1` mini-map for every membrane, scoped to exactly
88
+ * its own `interiorRooms` — the recursive membrane portal's interior map
89
+ * (`docs/VIEW_DESIGN.md` rule 5, "descend into a mind"). Called once by
90
+ * `server.ts` after the full `RunTimeline` (interior events included)
91
+ * exists; membranes with no parseable interior room ref pass through
92
+ * unchanged rather than throwing (defensive — every membrane
93
+ * `deriveMembranes` emits today always has at least one, but a future
94
+ * shape should degrade, not crash the server).
95
+ */
96
+ export declare const buildMembraneInteriorWorlds: (membranes: readonly RunTimelineMembrane[], timeline: RunTimeline) => RunTimelineMembrane[];
97
+ //# sourceMappingURL=runWorldTrace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runWorldTrace.d.ts","sourceRoot":"","sources":["../../src/view/runWorldTrace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG9E;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,2BAA2B,GACnC,YAAY,GACZ,eAAe,GACf,UAAU,GACV,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,2BAA2B,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,SAAS,EAAE,KAAK,EAAE,CAAC;IACnB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,YAAY,EAAE,uBAAuB,EAAE,CAAC;IACxC,OAAO,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,oGACiE,CAAC;AAW/F,oHAAoH;AACpH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,+GAA+G;IAC/G,KAAK,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACpE;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,2BAA2B,EAAE,CAAC;IACtC,QAAQ,EAAE,WAAW,CAAC;CACvB;AAKD,eAAO,MAAM,kBAAkB,GAAI,wDAAwD,wBAAwB,KAAG,aAyDrH,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GACtC,WAAW,SAAS,mBAAmB,EAAE,EACzC,UAAU,WAAW,KACpB,mBAAmB,EAalB,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { parseRoomRef, stripRefPrefix } from "./runTimelineRefs.js";
2
+ export const NO_PLACE_CAPTION = "no place-bearing world in this run — a chat-only room, rendered as an informational anchor";
3
+ const ledgerFactType = (viewClass) => {
4
+ if (viewClass === "message")
5
+ return "world.message";
6
+ if (viewClass === "wake")
7
+ return "wake.recommended";
8
+ return "other";
9
+ };
10
+ const provenanceFor = (authority) => authority === "moltnet" || authority === "daimon" ? "agentic" : "mechanical";
11
+ /** Deterministic x-offset between room anchors when more than one room is rendered — presentation only, never fed back into the schema (rule 4). */
12
+ const ROOM_SPACING = 3.2;
13
+ export const buildRunWorldTrace = ({ runId, runName, world, rooms: roomInputs, timeline }) => {
14
+ const inputs = roomInputs?.length
15
+ ? roomInputs
16
+ : [{ networkId: world?.networkId ?? "run", roomId: world?.roomId ?? "run-room", members: world?.members }];
17
+ const allAgentLabels = timeline.elements.filter((element) => element.kind === "agent").map((element) => element.label);
18
+ const rooms = inputs.map((input, index) => {
19
+ // The "fall back to every agent in the timeline" rule only makes sense
20
+ // for the single-anchor legacy shape; an explicit multi-room list always
21
+ // states its own membership (an empty interior/outer room legitimately
22
+ // has no members rather than borrowing every other room's agents).
23
+ const members = input.members?.length ? input.members : inputs.length === 1 ? allAgentLabels : [];
24
+ return {
25
+ id: input.roomId,
26
+ label: input.roomId,
27
+ scope: `room:${input.networkId}:${input.roomId}`,
28
+ members,
29
+ scene: [index * ROOM_SPACING, 0, 0],
30
+ access_hint: NO_PLACE_CAPTION,
31
+ };
32
+ });
33
+ const memberIds = new Set(rooms.flatMap((room) => room.members));
34
+ const agents = [...memberIds].map((id) => ({
35
+ id,
36
+ label: id,
37
+ scope: `agent:${id}`,
38
+ label_hint: "heuristic",
39
+ detail: "Placeless chat participant in this run — open its storyline portal for its own timeline.",
40
+ }));
41
+ const ledgerFacts = timeline.events.map((event) => ({
42
+ type: ledgerFactType(event.viewClass),
43
+ tick: event.t,
44
+ event_id: event.eventId,
45
+ kind: event.type,
46
+ sim_time: event.t,
47
+ provenance: provenanceFor(event.authority),
48
+ actor: event.actor ?? event.authority,
49
+ target: event.subjects[0] ?? "",
50
+ detail: event.text ?? event.type,
51
+ scope: event.subjects[0],
52
+ payload: event.payload,
53
+ }));
54
+ return {
55
+ version: "viewer.trace.v1",
56
+ run_id: runId,
57
+ run_name: runName,
58
+ rooms,
59
+ corridors: [],
60
+ agents,
61
+ presence: [],
62
+ ledger_facts: ledgerFacts,
63
+ signals: [],
64
+ };
65
+ };
66
+ /**
67
+ * Builds a `viewer.trace.v1` mini-map for every membrane, scoped to exactly
68
+ * its own `interiorRooms` — the recursive membrane portal's interior map
69
+ * (`docs/VIEW_DESIGN.md` rule 5, "descend into a mind"). Called once by
70
+ * `server.ts` after the full `RunTimeline` (interior events included)
71
+ * exists; membranes with no parseable interior room ref pass through
72
+ * unchanged rather than throwing (defensive — every membrane
73
+ * `deriveMembranes` emits today always has at least one, but a future
74
+ * shape should degrade, not crash the server).
75
+ */
76
+ export const buildMembraneInteriorWorlds = (membranes, timeline) => membranes.map((membrane) => {
77
+ const memberIds = membrane.members.map(stripRefPrefix);
78
+ const rooms = membrane.interiorRooms
79
+ .map(parseRoomRef)
80
+ .filter((room) => room !== undefined)
81
+ .map((room) => ({ ...room, members: memberIds }));
82
+ if (rooms.length === 0)
83
+ return membrane;
84
+ return {
85
+ ...membrane,
86
+ interiorWorld: buildRunWorldTrace({ runId: timeline.runId, runName: timeline.runId, rooms, timeline }),
87
+ };
88
+ });
@@ -0,0 +1,12 @@
1
+ export interface ViewerServerConfig {
2
+ port: number;
3
+ sourcePath: string;
4
+ statePath?: string;
5
+ mode: "live" | "replay";
6
+ }
7
+ export interface ViewerServerHandle {
8
+ close: () => Promise<void>;
9
+ url: string;
10
+ }
11
+ export declare const createViewerServer: (config: ViewerServerConfig) => Promise<ViewerServerHandle>;
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/view/server.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;CACb;AAqPD,eAAO,MAAM,kBAAkB,GAAU,QAAQ,kBAAkB,KAAG,OAAO,CAAC,kBAAkB,CA+F/F,CAAC"}
@@ -0,0 +1,306 @@
1
+ import { createServer } from "node:http";
2
+ import { createReadStream, existsSync } from "node:fs";
3
+ import { access, readFile, stat } from "node:fs/promises";
4
+ import { dirname, resolve, relative, extname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { isObserveRunDir } from "./runDetect.js";
7
+ import { buildRunViewModel } from "./runViewModel.js";
8
+ import { buildRunTimeline, readWorldRooms } from "./runTimeline.js";
9
+ import { buildMembraneInteriorWorlds, buildRunWorldTrace } from "./runWorldTrace.js";
10
+ const mimeTypes = {
11
+ ".html": "text/html; charset=utf-8",
12
+ ".css": "text/css; charset=utf-8",
13
+ ".js": "application/javascript; charset=utf-8",
14
+ ".json": "application/json; charset=utf-8",
15
+ ".svg": "image/svg+xml",
16
+ ".png": "image/png",
17
+ ".jpg": "image/jpeg",
18
+ ".jpeg": "image/jpeg",
19
+ ".gif": "image/gif",
20
+ };
21
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
22
+ const webDistRoot = resolve(packageRoot, "web", "dist");
23
+ const webSourceRoot = resolve(packageRoot, "web");
24
+ const webRoot = existsSync(resolve(webDistRoot, "index.html")) ? webDistRoot : webSourceRoot;
25
+ const replayRequiredArtifacts = ["manifest.yaml", "viewer-trace.json"];
26
+ const sendJson = (res, body, status = 200) => {
27
+ res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
28
+ res.end(JSON.stringify(body));
29
+ };
30
+ const safeJoin = (base, target) => {
31
+ const absolute = resolve(base, target);
32
+ const rel = relative(base, absolute);
33
+ if (rel.startsWith("..") || rel.includes("../")) {
34
+ throw new Error("Invalid path");
35
+ }
36
+ return absolute;
37
+ };
38
+ const streamFile = async (res, requestedPath) => {
39
+ try {
40
+ const path = safeJoin(webRoot, requestedPath);
41
+ await access(path);
42
+ const stats = await stat(path);
43
+ if (stats.isDirectory()) {
44
+ res.writeHead(403, { "Content-Type": "text/plain; charset=utf-8" });
45
+ res.end("Forbidden");
46
+ return;
47
+ }
48
+ const contentType = mimeTypes[extname(path)] || "application/octet-stream";
49
+ res.writeHead(200, { "Content-Type": contentType });
50
+ createReadStream(path).pipe(res);
51
+ }
52
+ catch {
53
+ res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
54
+ res.end("Not found");
55
+ }
56
+ };
57
+ const sendState = (req, res, config, effectiveMode) => {
58
+ if (req.url === undefined)
59
+ return;
60
+ sendJson(res, {
61
+ mode: effectiveMode,
62
+ sourcePath: config.sourcePath,
63
+ statePath: config.statePath,
64
+ now: new Date().toISOString(),
65
+ });
66
+ };
67
+ const sendSkins = (_req, res) => {
68
+ sendJson(res, {
69
+ selected: "default",
70
+ options: [
71
+ { id: "default", label: "Default (No Skin)" },
72
+ { id: "office", label: "Office Floor" },
73
+ { id: "night", label: "Night Shift" },
74
+ ],
75
+ });
76
+ };
77
+ const loadViewerTrace = async (path) => {
78
+ try {
79
+ return JSON.parse(await readFile(path, "utf8"));
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ };
85
+ const resolveReplayArtifacts = async (basePath) => {
86
+ const base = resolve(basePath);
87
+ const missing = await Promise.all(replayRequiredArtifacts.map(async (artifact) => {
88
+ try {
89
+ await access(join(base, artifact));
90
+ return undefined;
91
+ }
92
+ catch {
93
+ return artifact;
94
+ }
95
+ }));
96
+ return missing.filter((artifact) => artifact !== undefined);
97
+ };
98
+ const isViewerTrace = (value) => typeof value === "object" && value !== null;
99
+ const sendWorld = async (_req, res, config) => {
100
+ const sourcePath = resolve(config.statePath ?? config.sourcePath);
101
+ if (config.mode === "replay") {
102
+ const missingArtifacts = await resolveReplayArtifacts(sourcePath);
103
+ if (missingArtifacts.length > 0) {
104
+ sendJson(res, {
105
+ error: "viewer-trace.json requires a sealed replay directory",
106
+ mode: "replay",
107
+ source_path: sourcePath,
108
+ required_artifacts: [...replayRequiredArtifacts],
109
+ missing_artifacts: missingArtifacts,
110
+ }, 404);
111
+ return;
112
+ }
113
+ }
114
+ const trace = await loadViewerTrace(join(sourcePath, "viewer-trace.json"));
115
+ if (!isViewerTrace(trace)) {
116
+ sendJson(res, { error: "viewer-trace.json not found" }, 404);
117
+ return;
118
+ }
119
+ sendJson(res, {
120
+ now: new Date().toISOString(),
121
+ run_id: trace.run_id,
122
+ run_name: trace.run_name,
123
+ trace
124
+ });
125
+ };
126
+ const maxTraceTick = (trace) => {
127
+ const facts = Array.isArray(trace?.ledger_facts) ? trace.ledger_facts : [];
128
+ const presence = Array.isArray(trace?.presence) ? trace.presence : [];
129
+ return [...facts, ...presence].reduce((max, entry) => {
130
+ if (typeof entry === "object" && entry !== null && typeof entry.tick === "number") {
131
+ return Math.max(max, entry.tick);
132
+ }
133
+ return max;
134
+ }, 0);
135
+ };
136
+ const sendEvents = async (_req, res, config) => {
137
+ const tracePath = join(resolve(config.statePath ?? config.sourcePath), "viewer-trace.json");
138
+ const trace = await loadViewerTrace(tracePath);
139
+ const maxTick = Math.max(1, maxTraceTick(trace));
140
+ res.writeHead(200, {
141
+ "Content-Type": "text/event-stream; charset=utf-8",
142
+ "Cache-Control": "no-cache",
143
+ Connection: "keep-alive",
144
+ });
145
+ res.write(": viewer heartbeat stream\n\n");
146
+ let tick = 0;
147
+ const pushEvent = (payload) => {
148
+ res.write(`data: ${JSON.stringify(payload)}\n\n`);
149
+ };
150
+ pushEvent({ type: "connected", at: new Date().toISOString() });
151
+ const timer = setInterval(() => {
152
+ tick = tick >= maxTick ? 0 : tick + 1;
153
+ pushEvent({
154
+ type: "sim.tick",
155
+ tick,
156
+ at: new Date().toISOString(),
157
+ message: `tick ${tick}`,
158
+ });
159
+ }, 2500);
160
+ const cleanup = () => {
161
+ clearInterval(timer);
162
+ };
163
+ res.on("close", cleanup);
164
+ };
165
+ /**
166
+ * `simfile view <dir>` picks run-replay mode over the world/3D replay mode
167
+ * purely from the run directory's shape: a sealed compose-and-observe run
168
+ * directory (`manifest.json` @ `simfile.run-manifest.v1` +
169
+ * `raw/moltnet/transcript.json`, see `runDetect.ts`) never has `--state`
170
+ * set, so this only applies in replay mode. Built once at server start (the
171
+ * run directory is sealed, not tailed) rather than per-request. Run-replay
172
+ * mode serves the same React shell as world/live mode, fed by
173
+ * `/api/timeline`, the `runWorldTrace` adapter's `/api/world`, and
174
+ * `/api/run-meta` (verdict + provenance, reusing `computeVerdict`/
175
+ * `computeProvenance` from `runViewModelCompute.ts` unchanged; increment 3
176
+ * additionally carries `seedSpread`/`spreadSummary`/`variableSamples` when
177
+ * the run has them). The bespoke
178
+ * run-reader page (`runPage.ts`/`runPageScript.ts`/`runPageStyles.ts`) and
179
+ * its `/api/run-view-model.json` endpoint are retired as of increment 2: the
180
+ * React shell now renders verdict/provenance at parity, so `GET
181
+ * /api/run-view-model.json` 404s (falls through to the static-asset
182
+ * handler, same as any other unknown path).
183
+ */
184
+ const loadRunReplay = async (config) => {
185
+ if (config.mode !== "replay" || config.statePath)
186
+ return null;
187
+ const runDir = resolve(config.sourcePath);
188
+ if (!(await isObserveRunDir(runDir)))
189
+ return null;
190
+ const [model, timeline, worldRooms] = await Promise.all([
191
+ buildRunViewModel(runDir),
192
+ buildRunTimeline(runDir),
193
+ readWorldRooms(runDir),
194
+ ]);
195
+ // The outer map renders every declared room that is NOT some membrane's own
196
+ // interior council room — for a recursive-psyche run that is the parent
197
+ // floor (`commons`) alone; for a flat run (no membranes) it is every room
198
+ // the manifest declares, unchanged from before this parameterization
199
+ // (`runWorldTrace.ts`'s `rooms` param, `docs/VIEW_DESIGN.md` rule 5).
200
+ const interiorRoomRefs = new Set(timeline.membranes?.flatMap((membrane) => membrane.interiorRooms) ?? []);
201
+ const outerRooms = worldRooms.filter((room) => !interiorRoomRefs.has(room.ref));
202
+ const world = buildRunWorldTrace({
203
+ runId: model.runId,
204
+ runName: model.runId,
205
+ world: model.world,
206
+ rooms: outerRooms.map((room) => ({ networkId: room.networkId, roomId: room.roomId, members: room.members })),
207
+ timeline,
208
+ });
209
+ // Each membrane's own "descend into a mind" mini map — populated here,
210
+ // once the full timeline (interior events included) exists; `deriveMembranes`
211
+ // itself never sees a completed `RunTimeline` (increment's ordering: the
212
+ // report-derived membrane shape comes first, the interior world trace after).
213
+ const timelineWithInteriorWorlds = {
214
+ ...timeline,
215
+ membranes: buildMembraneInteriorWorlds(timeline.membranes ?? [], timeline),
216
+ };
217
+ return { model, timeline: timelineWithInteriorWorlds, world };
218
+ };
219
+ export const createViewerServer = async (config) => {
220
+ const runReplay = await loadRunReplay(config);
221
+ const server = createServer(async (req, res) => {
222
+ const path = req.url?.split("?")[0] ?? "/";
223
+ if (path === "/api/state") {
224
+ sendState(req, res, config, runReplay ? "run-replay" : config.mode);
225
+ return;
226
+ }
227
+ if (path === "/api/skins") {
228
+ sendSkins(req, res);
229
+ return;
230
+ }
231
+ if (runReplay) {
232
+ if (path === "/api/timeline") {
233
+ sendJson(res, runReplay.timeline);
234
+ return;
235
+ }
236
+ if (path === "/api/world") {
237
+ sendJson(res, {
238
+ now: new Date().toISOString(),
239
+ run_id: runReplay.world.run_id,
240
+ run_name: runReplay.world.run_name,
241
+ trace: runReplay.world,
242
+ });
243
+ return;
244
+ }
245
+ if (path === "/api/run-meta") {
246
+ sendJson(res, {
247
+ runId: runReplay.model.runId,
248
+ verdict: runReplay.model.verdict,
249
+ provenance: runReplay.model.provenance,
250
+ // Honesty-critical disclosure (never omitted): scripted vs
251
+ // real-engine vs mixed vs unknown, badged unmissably by the
252
+ // React shell's topbar (`EngineProvenanceBadge`).
253
+ engineProvenance: runReplay.model.engineProvenance,
254
+ // Passthrough context for the spread readout's "reach/total"
255
+ // denominator (never spread math itself — that stays in
256
+ // `spreadSummary`, computed once by `computeSeedSpread`).
257
+ participants: runReplay.model.participants,
258
+ // Increment 3: undefined on a run with no `manifest.seed_declaration`
259
+ // (JSON.stringify drops an undefined-valued key outright — the
260
+ // React shell's graceful-absence path, not an empty array/object).
261
+ seedSpread: runReplay.model.seedSpread,
262
+ spreadSummary: runReplay.model.spreadSummary,
263
+ variableSamples: runReplay.model.variableSamples,
264
+ });
265
+ return;
266
+ }
267
+ // No `/api/events`: run-replay's cursor is scrubbed by the client
268
+ // store, never a live/synthetic tick that would fight it.
269
+ // No `/api/run-view-model.json`: retired in increment 2 — the React
270
+ // shell's `RunMetaPanels` renders `/api/run-meta` (a subset of the
271
+ // same computed model) instead of the bespoke `runPage.ts` page.
272
+ }
273
+ else {
274
+ if (path === "/api/world") {
275
+ await sendWorld(req, res, config);
276
+ return;
277
+ }
278
+ if (path === "/api/events") {
279
+ await sendEvents(req, res, config);
280
+ return;
281
+ }
282
+ }
283
+ const assetPath = path === "/" || path === "/index.html" ? "index.html" : path.replace(/^\//, "");
284
+ await streamFile(res, assetPath);
285
+ });
286
+ await new Promise((resolve, reject) => {
287
+ server.listen(config.port, "127.0.0.1", () => resolve());
288
+ server.once("error", reject);
289
+ });
290
+ const address = server.address();
291
+ if (address === null || address === ":0" || typeof address === "string") {
292
+ throw new Error("Unable to bind viewer server");
293
+ }
294
+ return {
295
+ close: () => new Promise((resolveClose, rejectClose) => {
296
+ server.close((error) => {
297
+ if (error) {
298
+ rejectClose(error);
299
+ return;
300
+ }
301
+ resolveClose();
302
+ });
303
+ }),
304
+ url: `http://127.0.0.1:${address.port}`,
305
+ };
306
+ };
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "simfile",
3
+ "version": "0.0.1",
4
+ "description": "Declarative simulation world mechanics for agentic organizations.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bin": {
10
+ "simfile": "dist/cli/index.js"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./schema": {
18
+ "types": "./dist/schema/index.d.ts",
19
+ "import": "./dist/schema/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "web/dist",
25
+ "README.md"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+ssh://git@github.com/noopolis/simfile.git"
30
+ },
31
+ "scripts": {
32
+ "build": "rm -rf dist web/dist && tsc --project tsconfig.build.json && npm run build:web && chmod +x dist/cli/index.js",
33
+ "build:web": "vite build --config web/vite.config.ts",
34
+ "build:website": "npm --prefix website run build",
35
+ "clean": "rm -rf dist web/dist",
36
+ "prepack": "npm run build",
37
+ "prepublishOnly": "npm run typecheck && npm test",
38
+ "typecheck": "tsc --project tsconfig.json --noEmit && tsc --project tsconfig.web.json --noEmit",
39
+ "test": "node --import tsx --test \"src/**/*.test.ts\" \"web/src/**/*.test.ts\"",
40
+ "coverage:render": "node --import tsx scripts/render-coverage.ts",
41
+ "emit-causal-fixture": "tsx src/runtime/emit-causal-fixture.ts"
42
+ },
43
+ "engines": {
44
+ "node": ">=22.0.0"
45
+ },
46
+ "dependencies": {
47
+ "@noopolis/stele": "^0.0.1",
48
+ "yaml": "^2.8.2",
49
+ "zod": "^4.1.12"
50
+ },
51
+ "devDependencies": {
52
+ "@glyphcss/core": "^0.1.0",
53
+ "@glyphcss/react": "^0.1.0",
54
+ "@types/node": "^24.12.4",
55
+ "@types/react": "^19.2.14",
56
+ "@types/react-dom": "^19.2.3",
57
+ "@vitejs/plugin-react": "^5.1.4",
58
+ "glyphcss": "^0.1.0",
59
+ "react": "^19.2.6",
60
+ "react-dom": "^19.2.6",
61
+ "tsx": "^4.21.0",
62
+ "typescript": "^5.9.3",
63
+ "vite": "^6.0.0"
64
+ }
65
+ }