@smthrs/gateway 0.35.0 → 1.0.0-rc.3

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 (605) hide show
  1. package/CHANGELOG.md +198 -0
  2. package/LICENSE +1 -1
  3. package/README.md +92 -0
  4. package/dist/cjs/Diagnosis.d.ts +275 -0
  5. package/dist/cjs/Diagnosis.d.ts.map +1 -0
  6. package/dist/cjs/Diagnosis.js +476 -0
  7. package/dist/cjs/Diagnosis.js.map +7 -0
  8. package/dist/cjs/EngineTrace.d.ts +76 -0
  9. package/dist/cjs/EngineTrace.d.ts.map +1 -0
  10. package/dist/cjs/EngineTrace.js +501 -0
  11. package/dist/cjs/EngineTrace.js.map +7 -0
  12. package/dist/cjs/GatewayError.d.ts +54 -0
  13. package/dist/cjs/GatewayError.d.ts.map +1 -0
  14. package/dist/cjs/GatewayError.js +51 -0
  15. package/dist/cjs/GatewayError.js.map +7 -0
  16. package/dist/cjs/GatewayProjection.d.ts +480 -0
  17. package/dist/cjs/GatewayProjection.d.ts.map +1 -0
  18. package/dist/cjs/GatewayProjection.js +462 -0
  19. package/dist/cjs/GatewayProjection.js.map +7 -0
  20. package/dist/cjs/GatewayRpcs.d.ts +1886 -0
  21. package/dist/cjs/GatewayRpcs.d.ts.map +1 -0
  22. package/dist/cjs/GatewayRpcs.js +121 -0
  23. package/dist/cjs/GatewayRpcs.js.map +7 -0
  24. package/dist/cjs/GatewaySchema.d.ts +3274 -0
  25. package/dist/cjs/GatewaySchema.d.ts.map +1 -0
  26. package/dist/cjs/GatewaySchema.js +141 -0
  27. package/dist/cjs/GatewaySchema.js.map +7 -0
  28. package/dist/cjs/GatewayServer.d.ts +427 -0
  29. package/dist/cjs/GatewayServer.d.ts.map +1 -0
  30. package/dist/cjs/GatewayServer.js +428 -0
  31. package/dist/cjs/GatewayServer.js.map +7 -0
  32. package/dist/cjs/HostBranchPresence.d.ts +12 -0
  33. package/dist/cjs/HostBranchPresence.d.ts.map +1 -0
  34. package/dist/cjs/HostBranchPresence.js +82 -0
  35. package/dist/cjs/HostBranchPresence.js.map +7 -0
  36. package/dist/cjs/Projections.d.ts +209 -0
  37. package/dist/cjs/Projections.d.ts.map +1 -0
  38. package/dist/cjs/Projections.js +906 -0
  39. package/dist/cjs/Projections.js.map +7 -0
  40. package/dist/cjs/RunDevTools.d.ts +181 -0
  41. package/dist/cjs/RunDevTools.d.ts.map +1 -0
  42. package/dist/cjs/RunDevTools.js +195 -0
  43. package/dist/cjs/RunDevTools.js.map +7 -0
  44. package/dist/cjs/RunHistory.d.ts +185 -0
  45. package/dist/cjs/RunHistory.d.ts.map +1 -0
  46. package/dist/cjs/RunHistory.js +68 -0
  47. package/dist/cjs/RunHistory.js.map +7 -0
  48. package/dist/cjs/RunTrace.d.ts +753 -0
  49. package/dist/cjs/RunTrace.d.ts.map +1 -0
  50. package/dist/cjs/RunTrace.js +1593 -0
  51. package/dist/cjs/RunTrace.js.map +7 -0
  52. package/dist/cjs/RuntimeBridge.d.ts +1386 -0
  53. package/dist/cjs/RuntimeBridge.d.ts.map +1 -0
  54. package/dist/cjs/RuntimeBridge.js +598 -0
  55. package/dist/cjs/RuntimeBridge.js.map +7 -0
  56. package/dist/cjs/bun/BunGateway.d.ts +20 -0
  57. package/dist/cjs/bun/BunGateway.d.ts.map +1 -0
  58. package/dist/cjs/bun/BunGateway.js +62 -0
  59. package/dist/cjs/bun/BunGateway.js.map +7 -0
  60. package/dist/cjs/index.d.ts +65 -0
  61. package/dist/cjs/index.d.ts.map +1 -0
  62. package/dist/cjs/index.js +59 -0
  63. package/dist/cjs/index.js.map +7 -0
  64. package/dist/cjs/internal/NativeGateway.d.ts +176 -0
  65. package/dist/cjs/internal/NativeGateway.d.ts.map +1 -0
  66. package/dist/cjs/internal/NativeGateway.js +187 -0
  67. package/dist/cjs/internal/NativeGateway.js.map +7 -0
  68. package/dist/cjs/internal/actionRenderings.d.ts +2 -0
  69. package/dist/cjs/internal/actionRenderings.d.ts.map +1 -0
  70. package/dist/cjs/internal/actionRenderings.js +224 -0
  71. package/dist/cjs/internal/actionRenderings.js.map +7 -0
  72. package/dist/cjs/internal/callEvents.d.ts +97 -0
  73. package/dist/cjs/internal/callEvents.d.ts.map +1 -0
  74. package/dist/cjs/internal/callEvents.js +169 -0
  75. package/dist/cjs/internal/callEvents.js.map +7 -0
  76. package/dist/cjs/internal/digestIndex.d.ts +31 -0
  77. package/dist/cjs/internal/digestIndex.d.ts.map +1 -0
  78. package/dist/cjs/internal/digestIndex.js +105 -0
  79. package/dist/cjs/internal/digestIndex.js.map +7 -0
  80. package/dist/cjs/internal/digestMemory.d.ts +19 -0
  81. package/dist/cjs/internal/digestMemory.d.ts.map +1 -0
  82. package/dist/cjs/internal/digestMemory.js +33 -0
  83. package/dist/cjs/internal/digestMemory.js.map +7 -0
  84. package/dist/cjs/internal/inspectLabels.d.ts +8 -0
  85. package/dist/cjs/internal/inspectLabels.d.ts.map +1 -0
  86. package/dist/cjs/internal/inspectLabels.js +29 -0
  87. package/dist/cjs/internal/inspectLabels.js.map +7 -0
  88. package/dist/cjs/internal/nativeResolution.d.ts +41 -0
  89. package/dist/cjs/internal/nativeResolution.d.ts.map +1 -0
  90. package/dist/cjs/internal/nativeResolution.js +62 -0
  91. package/dist/cjs/internal/nativeResolution.js.map +7 -0
  92. package/dist/cjs/internal/nodeEvents.d.ts +47 -0
  93. package/dist/cjs/internal/nodeEvents.d.ts.map +1 -0
  94. package/dist/cjs/internal/nodeEvents.js +60 -0
  95. package/dist/cjs/internal/nodeEvents.js.map +7 -0
  96. package/dist/cjs/node/NodeGateway.d.ts +16 -0
  97. package/dist/cjs/node/NodeGateway.d.ts.map +1 -0
  98. package/dist/cjs/node/NodeGateway.js +48 -0
  99. package/dist/cjs/node/NodeGateway.js.map +7 -0
  100. package/dist/cjs/package.json +1 -0
  101. package/dist/esm/Diagnosis.d.ts +275 -0
  102. package/dist/esm/Diagnosis.d.ts.map +1 -0
  103. package/dist/esm/Diagnosis.js +697 -0
  104. package/dist/esm/Diagnosis.js.map +1 -0
  105. package/dist/esm/EngineTrace.d.ts +76 -0
  106. package/dist/esm/EngineTrace.d.ts.map +1 -0
  107. package/dist/esm/EngineTrace.js +548 -0
  108. package/dist/esm/EngineTrace.js.map +1 -0
  109. package/dist/esm/GatewayError.d.ts +54 -0
  110. package/dist/esm/GatewayError.d.ts.map +1 -0
  111. package/dist/esm/GatewayError.js +60 -0
  112. package/dist/esm/GatewayError.js.map +1 -0
  113. package/dist/esm/GatewayProjection.d.ts +480 -0
  114. package/dist/esm/GatewayProjection.d.ts.map +1 -0
  115. package/dist/esm/GatewayProjection.js +792 -0
  116. package/dist/esm/GatewayProjection.js.map +1 -0
  117. package/dist/esm/GatewayRpcs.d.ts +1886 -0
  118. package/dist/esm/GatewayRpcs.d.ts.map +1 -0
  119. package/dist/esm/GatewayRpcs.js +145 -0
  120. package/dist/esm/GatewayRpcs.js.map +1 -0
  121. package/dist/esm/GatewaySchema.d.ts +3274 -0
  122. package/dist/esm/GatewaySchema.d.ts.map +1 -0
  123. package/dist/esm/GatewaySchema.js +244 -0
  124. package/dist/esm/GatewaySchema.js.map +1 -0
  125. package/dist/esm/GatewayServer.d.ts +427 -0
  126. package/dist/esm/GatewayServer.d.ts.map +1 -0
  127. package/dist/esm/GatewayServer.js +725 -0
  128. package/dist/esm/GatewayServer.js.map +1 -0
  129. package/dist/esm/HostBranchPresence.d.ts +12 -0
  130. package/dist/esm/HostBranchPresence.d.ts.map +1 -0
  131. package/dist/esm/HostBranchPresence.js +54 -0
  132. package/dist/esm/HostBranchPresence.js.map +1 -0
  133. package/dist/esm/Projections.d.ts +209 -0
  134. package/dist/esm/Projections.d.ts.map +1 -0
  135. package/dist/esm/Projections.js +1254 -0
  136. package/dist/esm/Projections.js.map +1 -0
  137. package/dist/esm/RunDevTools.d.ts +181 -0
  138. package/dist/esm/RunDevTools.d.ts.map +1 -0
  139. package/dist/esm/RunDevTools.js +281 -0
  140. package/dist/esm/RunDevTools.js.map +1 -0
  141. package/dist/esm/RunHistory.d.ts +185 -0
  142. package/dist/esm/RunHistory.d.ts.map +1 -0
  143. package/dist/esm/RunHistory.js +109 -0
  144. package/dist/esm/RunHistory.js.map +1 -0
  145. package/dist/esm/RunTrace.d.ts +753 -0
  146. package/dist/esm/RunTrace.d.ts.map +1 -0
  147. package/dist/esm/RunTrace.js +2105 -0
  148. package/dist/esm/RunTrace.js.map +1 -0
  149. package/dist/esm/RuntimeBridge.d.ts +1386 -0
  150. package/dist/esm/RuntimeBridge.d.ts.map +1 -0
  151. package/dist/esm/RuntimeBridge.js +659 -0
  152. package/dist/esm/RuntimeBridge.js.map +1 -0
  153. package/dist/esm/bun/BunGateway.d.ts +20 -0
  154. package/dist/esm/bun/BunGateway.d.ts.map +1 -0
  155. package/dist/esm/bun/BunGateway.js +32 -0
  156. package/dist/esm/bun/BunGateway.js.map +1 -0
  157. package/dist/esm/index.d.ts +65 -0
  158. package/dist/esm/index.d.ts.map +1 -0
  159. package/dist/esm/index.js +65 -0
  160. package/dist/esm/index.js.map +1 -0
  161. package/dist/esm/internal/NativeGateway.d.ts +176 -0
  162. package/dist/esm/internal/NativeGateway.d.ts.map +1 -0
  163. package/dist/esm/internal/NativeGateway.js +268 -0
  164. package/dist/esm/internal/NativeGateway.js.map +1 -0
  165. package/dist/esm/internal/actionRenderings.d.ts +2 -0
  166. package/dist/esm/internal/actionRenderings.d.ts.map +1 -0
  167. package/dist/esm/internal/actionRenderings.js +202 -0
  168. package/dist/esm/internal/actionRenderings.js.map +1 -0
  169. package/dist/esm/internal/callEvents.d.ts +97 -0
  170. package/dist/esm/internal/callEvents.d.ts.map +1 -0
  171. package/dist/esm/internal/callEvents.js +206 -0
  172. package/dist/esm/internal/callEvents.js.map +1 -0
  173. package/dist/esm/internal/digestIndex.d.ts +31 -0
  174. package/dist/esm/internal/digestIndex.d.ts.map +1 -0
  175. package/dist/esm/internal/digestIndex.js +87 -0
  176. package/dist/esm/internal/digestIndex.js.map +1 -0
  177. package/dist/esm/internal/digestMemory.d.ts +19 -0
  178. package/dist/esm/internal/digestMemory.d.ts.map +1 -0
  179. package/dist/esm/internal/digestMemory.js +23 -0
  180. package/dist/esm/internal/digestMemory.js.map +1 -0
  181. package/dist/esm/internal/inspectLabels.d.ts +8 -0
  182. package/dist/esm/internal/inspectLabels.d.ts.map +1 -0
  183. package/dist/esm/internal/inspectLabels.js +12 -0
  184. package/dist/esm/internal/inspectLabels.js.map +1 -0
  185. package/dist/esm/internal/nativeResolution.d.ts +41 -0
  186. package/dist/esm/internal/nativeResolution.d.ts.map +1 -0
  187. package/dist/esm/internal/nativeResolution.js +78 -0
  188. package/dist/esm/internal/nativeResolution.js.map +1 -0
  189. package/dist/esm/internal/nodeEvents.d.ts +47 -0
  190. package/dist/esm/internal/nodeEvents.d.ts.map +1 -0
  191. package/dist/esm/internal/nodeEvents.js +60 -0
  192. package/dist/esm/internal/nodeEvents.js.map +1 -0
  193. package/dist/esm/node/NodeGateway.d.ts +16 -0
  194. package/dist/esm/node/NodeGateway.d.ts.map +1 -0
  195. package/dist/esm/node/NodeGateway.js +13 -0
  196. package/dist/esm/node/NodeGateway.js.map +1 -0
  197. package/dist/vendor/adapters.json +2707 -0
  198. package/dist/vendor/bun/BunChildProcessSpawner.d.ts +7 -0
  199. package/dist/vendor/bun/BunChildProcessSpawner.js +14 -0
  200. package/dist/vendor/bun/BunClusterHttp.d.ts +83 -0
  201. package/dist/vendor/bun/BunClusterHttp.js +39 -0
  202. package/dist/vendor/bun/BunClusterSocket.d.ts +65 -0
  203. package/dist/vendor/bun/BunClusterSocket.js +22 -0
  204. package/dist/vendor/bun/BunCrypto.d.ts +10 -0
  205. package/dist/vendor/bun/BunCrypto.js +8 -0
  206. package/dist/vendor/bun/BunFileSystem.d.ts +10 -0
  207. package/dist/vendor/bun/BunFileSystem.js +9 -0
  208. package/dist/vendor/bun/BunHttpClient.d.ts +9 -0
  209. package/dist/vendor/bun/BunHttpClient.js +1 -0
  210. package/dist/vendor/bun/BunHttpPlatform.d.ts +10 -0
  211. package/dist/vendor/bun/BunHttpPlatform.js +11 -0
  212. package/dist/vendor/bun/BunHttpServer.d.ts +142 -0
  213. package/dist/vendor/bun/BunHttpServer.js +38 -0
  214. package/dist/vendor/bun/BunHttpServerRequest.d.ts +18 -0
  215. package/dist/vendor/bun/BunHttpServerRequest.js +7 -0
  216. package/dist/vendor/bun/BunMultipart.d.ts +31 -0
  217. package/dist/vendor/bun/BunMultipart.js +12 -0
  218. package/dist/vendor/bun/BunPath.d.ts +24 -0
  219. package/dist/vendor/bun/BunPath.js +12 -0
  220. package/dist/vendor/bun/BunRedis.d.ts +49 -0
  221. package/dist/vendor/bun/BunRedis.js +11 -0
  222. package/dist/vendor/bun/BunRuntime.d.ts +85 -0
  223. package/dist/vendor/bun/BunRuntime.js +8 -0
  224. package/dist/vendor/bun/BunServices.d.ts +34 -0
  225. package/dist/vendor/bun/BunServices.js +23 -0
  226. package/dist/vendor/bun/BunSink.d.ts +8 -0
  227. package/dist/vendor/bun/BunSink.js +12 -0
  228. package/dist/vendor/bun/BunSocket.d.ts +40 -0
  229. package/dist/vendor/bun/BunSocket.js +29 -0
  230. package/dist/vendor/bun/BunSocketServer.d.ts +8 -0
  231. package/dist/vendor/bun/BunSocketServer.js +21 -0
  232. package/dist/vendor/bun/BunStdio.d.ts +11 -0
  233. package/dist/vendor/bun/BunStdio.js +10 -0
  234. package/dist/vendor/bun/BunStream.d.ts +19 -0
  235. package/dist/vendor/bun/BunStream.js +30 -0
  236. package/dist/vendor/bun/BunTerminal.d.ts +21 -0
  237. package/dist/vendor/bun/BunTerminal.js +10 -0
  238. package/dist/vendor/bun/BunWorker.d.ts +20 -0
  239. package/dist/vendor/bun/BunWorker.js +9 -0
  240. package/dist/vendor/bun/BunWorkerRunner.d.ts +12 -0
  241. package/dist/vendor/bun/BunWorkerRunner.js +7 -0
  242. package/dist/vendor/bun/LICENSE +21 -0
  243. package/dist/vendor/bun/index.d.ts +92 -0
  244. package/dist/vendor/bun/index.js +107 -0
  245. package/dist/vendor/chunk-2GCOZTTW.js +48 -0
  246. package/dist/vendor/chunk-37CUR3PO.js +81 -0
  247. package/dist/vendor/chunk-3ARKKENZ.js +45 -0
  248. package/dist/vendor/chunk-3NV2UDBI.js +7 -0
  249. package/dist/vendor/chunk-3OSW7CT7.js +143 -0
  250. package/dist/vendor/chunk-62R63L74.js +62 -0
  251. package/dist/vendor/chunk-6AQLBC2I.js +118 -0
  252. package/dist/vendor/chunk-6C5H7S6B.js +18 -0
  253. package/dist/vendor/chunk-6DY5RBZD.js +85 -0
  254. package/dist/vendor/chunk-6UPBD47P.js +53 -0
  255. package/dist/vendor/chunk-AVVVN75Z.js +96 -0
  256. package/dist/vendor/chunk-B4DHEMBB.js +14 -0
  257. package/dist/vendor/chunk-BCK3NJOX.js +15 -0
  258. package/dist/vendor/chunk-BFBIHJ6X.js +0 -0
  259. package/dist/vendor/chunk-CES4OUM4.js +63 -0
  260. package/dist/vendor/chunk-D64L6YND.js +59 -0
  261. package/dist/vendor/chunk-DS2R6WOM.js +18 -0
  262. package/dist/vendor/chunk-DX4Z23DM.js +65 -0
  263. package/dist/vendor/chunk-DZ2VOUDP.js +564 -0
  264. package/dist/vendor/chunk-FOXN6JPJ.js +61 -0
  265. package/dist/vendor/chunk-FPQF7CR5.js +55 -0
  266. package/dist/vendor/chunk-FUTCTCEG.js +20 -0
  267. package/dist/vendor/chunk-GUPAV2YZ.js +18 -0
  268. package/dist/vendor/chunk-H2Z2Y4QH.js +70 -0
  269. package/dist/vendor/chunk-HAEDVSSM.js +129 -0
  270. package/dist/vendor/chunk-HY7BBL3K.js +93 -0
  271. package/dist/vendor/chunk-IFTTT6D5.js +31 -0
  272. package/dist/vendor/chunk-KQDSNB7M.js +26 -0
  273. package/dist/vendor/chunk-LIHLXXGQ.js +26 -0
  274. package/dist/vendor/chunk-LKCGQEE5.js +28 -0
  275. package/dist/vendor/chunk-MH2J3BYB.js +0 -0
  276. package/dist/vendor/chunk-ML2NAFH2.js +131 -0
  277. package/dist/vendor/chunk-MLFIZ5O2.js +0 -0
  278. package/dist/vendor/chunk-MLKGABMK.js +9 -0
  279. package/dist/vendor/chunk-MSCLEMYX.js +71 -0
  280. package/dist/vendor/chunk-OBWXQKVP.js +57 -0
  281. package/dist/vendor/chunk-QR557K66.js +20 -0
  282. package/dist/vendor/chunk-R4QUY7DH.js +18 -0
  283. package/dist/vendor/chunk-RGECXXJS.js +18 -0
  284. package/dist/vendor/chunk-TFY7W6D4.js +36 -0
  285. package/dist/vendor/chunk-TGPIVY4A.js +104 -0
  286. package/dist/vendor/chunk-TGXCX6W3.js +398 -0
  287. package/dist/vendor/chunk-TOHSFXMV.js +437 -0
  288. package/dist/vendor/chunk-UHIDPUFF.js +34 -0
  289. package/dist/vendor/chunk-ULT6CWUI.js +109 -0
  290. package/dist/vendor/chunk-UXBGYQR3.js +18 -0
  291. package/dist/vendor/chunk-V6LUGQ7O.js +21 -0
  292. package/dist/vendor/chunk-W4OASVX6.js +92 -0
  293. package/dist/vendor/chunk-WAII54GA.js +18 -0
  294. package/dist/vendor/chunk-WECQDT36.js +426 -0
  295. package/dist/vendor/chunk-WHKMOC7Z.js +28 -0
  296. package/dist/vendor/chunk-WVFOZ2UL.js +40 -0
  297. package/dist/vendor/chunk-X73E2UPI.js +18 -0
  298. package/dist/vendor/chunk-XCYARUII.js +63 -0
  299. package/dist/vendor/chunk-XFEVTDVD.js +34 -0
  300. package/dist/vendor/chunk-XFZZYSJ4.js +22 -0
  301. package/dist/vendor/chunk-XJWJXLXJ.js +18 -0
  302. package/dist/vendor/chunk-XY3Q62EO.js +18 -0
  303. package/dist/vendor/chunk-YDASSC75.js +394 -0
  304. package/dist/vendor/chunk-Z6C7SO5Z.js +507 -0
  305. package/dist/vendor/chunk-Z7QNPJEB.js +229 -0
  306. package/dist/vendor/chunk-Z7QPP4YH.js +249 -0
  307. package/dist/vendor/chunk-ZCHYLHJH.js +86 -0
  308. package/dist/vendor/chunk-ZIQVYVMA.js +22 -0
  309. package/dist/vendor/chunk-ZPIG2CUA.js +34 -0
  310. package/dist/vendor/cjs/bun/BunChildProcessSpawner.d.ts +7 -0
  311. package/dist/vendor/cjs/bun/BunChildProcessSpawner.js +1 -0
  312. package/dist/vendor/cjs/bun/BunClusterHttp.d.ts +83 -0
  313. package/dist/vendor/cjs/bun/BunClusterHttp.js +1 -0
  314. package/dist/vendor/cjs/bun/BunClusterSocket.d.ts +65 -0
  315. package/dist/vendor/cjs/bun/BunClusterSocket.js +1 -0
  316. package/dist/vendor/cjs/bun/BunCrypto.d.ts +10 -0
  317. package/dist/vendor/cjs/bun/BunCrypto.js +1 -0
  318. package/dist/vendor/cjs/bun/BunFileSystem.d.ts +10 -0
  319. package/dist/vendor/cjs/bun/BunFileSystem.js +1 -0
  320. package/dist/vendor/cjs/bun/BunHttpClient.d.ts +9 -0
  321. package/dist/vendor/cjs/bun/BunHttpClient.js +1 -0
  322. package/dist/vendor/cjs/bun/BunHttpPlatform.d.ts +10 -0
  323. package/dist/vendor/cjs/bun/BunHttpPlatform.js +1 -0
  324. package/dist/vendor/cjs/bun/BunHttpServer.d.ts +142 -0
  325. package/dist/vendor/cjs/bun/BunHttpServer.js +1 -0
  326. package/dist/vendor/cjs/bun/BunHttpServerRequest.d.ts +18 -0
  327. package/dist/vendor/cjs/bun/BunHttpServerRequest.js +1 -0
  328. package/dist/vendor/cjs/bun/BunMultipart.d.ts +31 -0
  329. package/dist/vendor/cjs/bun/BunMultipart.js +1 -0
  330. package/dist/vendor/cjs/bun/BunPath.d.ts +24 -0
  331. package/dist/vendor/cjs/bun/BunPath.js +1 -0
  332. package/dist/vendor/cjs/bun/BunRedis.d.ts +49 -0
  333. package/dist/vendor/cjs/bun/BunRedis.js +1 -0
  334. package/dist/vendor/cjs/bun/BunRuntime.d.ts +85 -0
  335. package/dist/vendor/cjs/bun/BunRuntime.js +1 -0
  336. package/dist/vendor/cjs/bun/BunServices.d.ts +34 -0
  337. package/dist/vendor/cjs/bun/BunServices.js +1 -0
  338. package/dist/vendor/cjs/bun/BunSink.d.ts +8 -0
  339. package/dist/vendor/cjs/bun/BunSink.js +1 -0
  340. package/dist/vendor/cjs/bun/BunSocket.d.ts +40 -0
  341. package/dist/vendor/cjs/bun/BunSocket.js +1 -0
  342. package/dist/vendor/cjs/bun/BunSocketServer.d.ts +8 -0
  343. package/dist/vendor/cjs/bun/BunSocketServer.js +1 -0
  344. package/dist/vendor/cjs/bun/BunStdio.d.ts +11 -0
  345. package/dist/vendor/cjs/bun/BunStdio.js +1 -0
  346. package/dist/vendor/cjs/bun/BunStream.d.ts +19 -0
  347. package/dist/vendor/cjs/bun/BunStream.js +1 -0
  348. package/dist/vendor/cjs/bun/BunTerminal.d.ts +21 -0
  349. package/dist/vendor/cjs/bun/BunTerminal.js +1 -0
  350. package/dist/vendor/cjs/bun/BunWorker.d.ts +20 -0
  351. package/dist/vendor/cjs/bun/BunWorker.js +1 -0
  352. package/dist/vendor/cjs/bun/BunWorkerRunner.d.ts +12 -0
  353. package/dist/vendor/cjs/bun/BunWorkerRunner.js +1 -0
  354. package/dist/vendor/cjs/bun/index.d.ts +92 -0
  355. package/dist/vendor/cjs/bun/index.js +1 -0
  356. package/dist/vendor/cjs/node/NodeChildProcessSpawner.d.ts +7 -0
  357. package/dist/vendor/cjs/node/NodeChildProcessSpawner.js +1 -0
  358. package/dist/vendor/cjs/node/NodeClusterHttp.d.ts +63 -0
  359. package/dist/vendor/cjs/node/NodeClusterHttp.js +1 -0
  360. package/dist/vendor/cjs/node/NodeClusterSocket.d.ts +79 -0
  361. package/dist/vendor/cjs/node/NodeClusterSocket.js +1 -0
  362. package/dist/vendor/cjs/node/NodeCrypto.d.ts +10 -0
  363. package/dist/vendor/cjs/node/NodeCrypto.js +1 -0
  364. package/dist/vendor/cjs/node/NodeFileSystem.d.ts +10 -0
  365. package/dist/vendor/cjs/node/NodeFileSystem.js +1 -0
  366. package/dist/vendor/cjs/node/NodeHttpClient.d.ts +179 -0
  367. package/dist/vendor/cjs/node/NodeHttpClient.js +1 -0
  368. package/dist/vendor/cjs/node/NodeHttpIncomingMessage.d.ts +62 -0
  369. package/dist/vendor/cjs/node/NodeHttpIncomingMessage.js +1 -0
  370. package/dist/vendor/cjs/node/NodeHttpPlatform.d.ts +36 -0
  371. package/dist/vendor/cjs/node/NodeHttpPlatform.js +1 -0
  372. package/dist/vendor/cjs/node/NodeHttpServer.d.ts +120 -0
  373. package/dist/vendor/cjs/node/NodeHttpServer.js +1 -0
  374. package/dist/vendor/cjs/node/NodeHttpServerRequest.d.ts +29 -0
  375. package/dist/vendor/cjs/node/NodeHttpServerRequest.js +1 -0
  376. package/dist/vendor/cjs/node/NodeMultipart.d.ts +46 -0
  377. package/dist/vendor/cjs/node/NodeMultipart.js +1 -0
  378. package/dist/vendor/cjs/node/NodeMultipartParser.d.ts +97 -0
  379. package/dist/vendor/cjs/node/NodeMultipartParser.js +1 -0
  380. package/dist/vendor/cjs/node/NodePath.d.ts +27 -0
  381. package/dist/vendor/cjs/node/NodePath.js +1 -0
  382. package/dist/vendor/cjs/node/NodeRedis.d.ts +74 -0
  383. package/dist/vendor/cjs/node/NodeRedis.js +1 -0
  384. package/dist/vendor/cjs/node/NodeRuntime.d.ts +85 -0
  385. package/dist/vendor/cjs/node/NodeRuntime.js +1 -0
  386. package/dist/vendor/cjs/node/NodeServices.d.ts +34 -0
  387. package/dist/vendor/cjs/node/NodeServices.js +1 -0
  388. package/dist/vendor/cjs/node/NodeSink.d.ts +8 -0
  389. package/dist/vendor/cjs/node/NodeSink.js +1 -0
  390. package/dist/vendor/cjs/node/NodeSocket.d.ts +38 -0
  391. package/dist/vendor/cjs/node/NodeSocket.js +1 -0
  392. package/dist/vendor/cjs/node/NodeSocketServer.d.ts +8 -0
  393. package/dist/vendor/cjs/node/NodeSocketServer.js +1 -0
  394. package/dist/vendor/cjs/node/NodeStdio.d.ts +11 -0
  395. package/dist/vendor/cjs/node/NodeStdio.js +1 -0
  396. package/dist/vendor/cjs/node/NodeStream.d.ts +8 -0
  397. package/dist/vendor/cjs/node/NodeStream.js +1 -0
  398. package/dist/vendor/cjs/node/NodeTerminal.d.ts +21 -0
  399. package/dist/vendor/cjs/node/NodeTerminal.js +1 -0
  400. package/dist/vendor/cjs/node/NodeWorker.d.ts +22 -0
  401. package/dist/vendor/cjs/node/NodeWorker.js +1 -0
  402. package/dist/vendor/cjs/node/NodeWorkerRunner.d.ts +12 -0
  403. package/dist/vendor/cjs/node/NodeWorkerRunner.js +1 -0
  404. package/dist/vendor/cjs/node/Undici.d.ts +28 -0
  405. package/dist/vendor/cjs/node/Undici.js +1 -0
  406. package/dist/vendor/cjs/node/index.d.ts +100 -0
  407. package/dist/vendor/cjs/node/index.js +1 -0
  408. package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.d.ts +57 -0
  409. package/dist/vendor/cjs/node-shared/NodeChildProcessSpawner.js +1 -0
  410. package/dist/vendor/cjs/node-shared/NodeClusterSocket.d.ts +22 -0
  411. package/dist/vendor/cjs/node-shared/NodeClusterSocket.js +1 -0
  412. package/dist/vendor/cjs/node-shared/NodeCrypto.d.ts +27 -0
  413. package/dist/vendor/cjs/node-shared/NodeCrypto.js +1 -0
  414. package/dist/vendor/cjs/node-shared/NodeFileSystem.d.ts +11 -0
  415. package/dist/vendor/cjs/node-shared/NodeFileSystem.js +1 -0
  416. package/dist/vendor/cjs/node-shared/NodeHttpCompression.d.ts +36 -0
  417. package/dist/vendor/cjs/node-shared/NodeHttpCompression.js +1 -0
  418. package/dist/vendor/cjs/node-shared/NodePath.d.ts +27 -0
  419. package/dist/vendor/cjs/node-shared/NodePath.js +1 -0
  420. package/dist/vendor/cjs/node-shared/NodeRuntime.d.ts +47 -0
  421. package/dist/vendor/cjs/node-shared/NodeRuntime.js +1 -0
  422. package/dist/vendor/cjs/node-shared/NodeSink.d.ts +69 -0
  423. package/dist/vendor/cjs/node-shared/NodeSink.js +1 -0
  424. package/dist/vendor/cjs/node-shared/NodeSocket.d.ts +134 -0
  425. package/dist/vendor/cjs/node-shared/NodeSocket.js +1 -0
  426. package/dist/vendor/cjs/node-shared/NodeSocketServer.d.ts +80 -0
  427. package/dist/vendor/cjs/node-shared/NodeSocketServer.js +1 -0
  428. package/dist/vendor/cjs/node-shared/NodeStdio.d.ts +12 -0
  429. package/dist/vendor/cjs/node-shared/NodeStdio.js +1 -0
  430. package/dist/vendor/cjs/node-shared/NodeStream.d.ts +179 -0
  431. package/dist/vendor/cjs/node-shared/NodeStream.js +1 -0
  432. package/dist/vendor/cjs/node-shared/NodeTerminal.d.ts +22 -0
  433. package/dist/vendor/cjs/node-shared/NodeTerminal.js +1 -0
  434. package/dist/vendor/cjs/node-shared/index.d.ts +56 -0
  435. package/dist/vendor/cjs/node-shared/index.js +1 -0
  436. package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
  437. package/dist/vendor/cjs/node-shared/internal/nodeChildProcessSpawner.js +1 -0
  438. package/dist/vendor/cjs/node-shared/internal/utils.d.ts +2 -0
  439. package/dist/vendor/cjs/node-shared/internal/utils.js +1 -0
  440. package/dist/vendor/cjs/package.json +1 -0
  441. package/dist/vendor/cjs/platform-bun.d.ts +92 -0
  442. package/dist/vendor/cjs/platform-bun.js +1 -0
  443. package/dist/vendor/cjs/platform-node-shared.d.ts +56 -0
  444. package/dist/vendor/cjs/platform-node-shared.js +1 -0
  445. package/dist/vendor/cjs/platform-node.d.ts +100 -0
  446. package/dist/vendor/cjs/platform-node.js +1 -0
  447. package/dist/vendor/node/LICENSE +21 -0
  448. package/dist/vendor/node/NodeChildProcessSpawner.d.ts +7 -0
  449. package/dist/vendor/node/NodeChildProcessSpawner.js +14 -0
  450. package/dist/vendor/node/NodeClusterHttp.d.ts +63 -0
  451. package/dist/vendor/node/NodeClusterHttp.js +43 -0
  452. package/dist/vendor/node/NodeClusterSocket.d.ts +79 -0
  453. package/dist/vendor/node/NodeClusterSocket.js +30 -0
  454. package/dist/vendor/node/NodeCrypto.d.ts +10 -0
  455. package/dist/vendor/node/NodeCrypto.js +8 -0
  456. package/dist/vendor/node/NodeFileSystem.d.ts +10 -0
  457. package/dist/vendor/node/NodeFileSystem.js +9 -0
  458. package/dist/vendor/node/NodeHttpClient.d.ts +179 -0
  459. package/dist/vendor/node/NodeHttpClient.js +46 -0
  460. package/dist/vendor/node/NodeHttpIncomingMessage.d.ts +62 -0
  461. package/dist/vendor/node/NodeHttpIncomingMessage.js +10 -0
  462. package/dist/vendor/node/NodeHttpPlatform.d.ts +36 -0
  463. package/dist/vendor/node/NodeHttpPlatform.js +16 -0
  464. package/dist/vendor/node/NodeHttpServer.d.ts +120 -0
  465. package/dist/vendor/node/NodeHttpServer.js +46 -0
  466. package/dist/vendor/node/NodeHttpServerRequest.d.ts +29 -0
  467. package/dist/vendor/node/NodeHttpServerRequest.js +9 -0
  468. package/dist/vendor/node/NodeMultipart.d.ts +46 -0
  469. package/dist/vendor/node/NodeMultipart.js +15 -0
  470. package/dist/vendor/node/NodeMultipartParser.d.ts +97 -0
  471. package/dist/vendor/node/NodeMultipartParser.js +11 -0
  472. package/dist/vendor/node/NodePath.d.ts +27 -0
  473. package/dist/vendor/node/NodePath.js +12 -0
  474. package/dist/vendor/node/NodeRedis.d.ts +74 -0
  475. package/dist/vendor/node/NodeRedis.js +11 -0
  476. package/dist/vendor/node/NodeRuntime.d.ts +85 -0
  477. package/dist/vendor/node/NodeRuntime.js +8 -0
  478. package/dist/vendor/node/NodeServices.d.ts +34 -0
  479. package/dist/vendor/node/NodeServices.js +23 -0
  480. package/dist/vendor/node/NodeSink.d.ts +8 -0
  481. package/dist/vendor/node/NodeSink.js +12 -0
  482. package/dist/vendor/node/NodeSocket.d.ts +38 -0
  483. package/dist/vendor/node/NodeSocket.js +31 -0
  484. package/dist/vendor/node/NodeSocketServer.d.ts +8 -0
  485. package/dist/vendor/node/NodeSocketServer.js +21 -0
  486. package/dist/vendor/node/NodeStdio.d.ts +11 -0
  487. package/dist/vendor/node/NodeStdio.js +10 -0
  488. package/dist/vendor/node/NodeStream.d.ts +8 -0
  489. package/dist/vendor/node/NodeStream.js +27 -0
  490. package/dist/vendor/node/NodeTerminal.d.ts +21 -0
  491. package/dist/vendor/node/NodeTerminal.js +10 -0
  492. package/dist/vendor/node/NodeWorker.d.ts +22 -0
  493. package/dist/vendor/node/NodeWorker.js +9 -0
  494. package/dist/vendor/node/NodeWorkerRunner.d.ts +12 -0
  495. package/dist/vendor/node/NodeWorkerRunner.js +7 -0
  496. package/dist/vendor/node/Undici.d.ts +28 -0
  497. package/dist/vendor/node/Undici.js +9 -0
  498. package/dist/vendor/node/index.d.ts +100 -0
  499. package/dist/vendor/node/index.js +115 -0
  500. package/dist/vendor/node-shared/LICENSE +21 -0
  501. package/dist/vendor/node-shared/NodeChildProcessSpawner.d.ts +57 -0
  502. package/dist/vendor/node-shared/NodeChildProcessSpawner.js +13 -0
  503. package/dist/vendor/node-shared/NodeClusterSocket.d.ts +22 -0
  504. package/dist/vendor/node-shared/NodeClusterSocket.js +11 -0
  505. package/dist/vendor/node-shared/NodeCrypto.d.ts +27 -0
  506. package/dist/vendor/node-shared/NodeCrypto.js +9 -0
  507. package/dist/vendor/node-shared/NodeFileSystem.d.ts +11 -0
  508. package/dist/vendor/node-shared/NodeFileSystem.js +8 -0
  509. package/dist/vendor/node-shared/NodeHttpCompression.d.ts +36 -0
  510. package/dist/vendor/node-shared/NodeHttpCompression.js +13 -0
  511. package/dist/vendor/node-shared/NodePath.d.ts +27 -0
  512. package/dist/vendor/node-shared/NodePath.js +11 -0
  513. package/dist/vendor/node-shared/NodeRuntime.d.ts +47 -0
  514. package/dist/vendor/node-shared/NodeRuntime.js +7 -0
  515. package/dist/vendor/node-shared/NodeSink.d.ts +69 -0
  516. package/dist/vendor/node-shared/NodeSink.js +11 -0
  517. package/dist/vendor/node-shared/NodeSocket.d.ts +134 -0
  518. package/dist/vendor/node-shared/NodeSocket.js +23 -0
  519. package/dist/vendor/node-shared/NodeSocketServer.d.ts +80 -0
  520. package/dist/vendor/node-shared/NodeSocketServer.js +20 -0
  521. package/dist/vendor/node-shared/NodeStdio.d.ts +12 -0
  522. package/dist/vendor/node-shared/NodeStdio.js +9 -0
  523. package/dist/vendor/node-shared/NodeStream.d.ts +179 -0
  524. package/dist/vendor/node-shared/NodeStream.js +26 -0
  525. package/dist/vendor/node-shared/NodeTerminal.d.ts +22 -0
  526. package/dist/vendor/node-shared/NodeTerminal.js +9 -0
  527. package/dist/vendor/node-shared/index.d.ts +56 -0
  528. package/dist/vendor/node-shared/index.js +58 -0
  529. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.d.ts +4 -0
  530. package/dist/vendor/node-shared/internal/nodeChildProcessSpawner.js +7 -0
  531. package/dist/vendor/node-shared/internal/utils.d.ts +2 -0
  532. package/dist/vendor/node-shared/internal/utils.js +7 -0
  533. package/dist/vendor/platform-bun.d.ts +92 -0
  534. package/dist/vendor/platform-bun.js +107 -0
  535. package/dist/vendor/platform-node-shared.d.ts +56 -0
  536. package/dist/vendor/platform-node-shared.js +58 -0
  537. package/dist/vendor/platform-node.d.ts +100 -0
  538. package/dist/vendor/platform-node.js +115 -0
  539. package/docs/README.md +158 -0
  540. package/docs/api.md +601 -0
  541. package/docs/concepts/projections.md +250 -0
  542. package/docs/concepts/subscriptions.md +204 -0
  543. package/docs/concepts/trust-boundary.md +256 -0
  544. package/docs/guides/diagnose-a-run.md +115 -0
  545. package/docs/guides/follow-a-run.md +150 -0
  546. package/docs/guides/host-the-gateway.md +153 -0
  547. package/docs/guides/read-a-projection.md +138 -0
  548. package/docs/guides/serve-beyond-loopback.md +166 -0
  549. package/docs/guides/submit-an-approval.md +187 -0
  550. package/docs/guides/testing.md +148 -0
  551. package/docs/installation.md +89 -0
  552. package/docs/quickstart.md +200 -0
  553. package/docs/troubleshooting.md +355 -0
  554. package/package.json +264 -32
  555. package/src/Diagnosis.ts +892 -0
  556. package/src/EngineTrace.ts +605 -0
  557. package/src/GatewayError.ts +71 -0
  558. package/src/GatewayProjection.ts +948 -0
  559. package/src/GatewayRpcs.ts +179 -0
  560. package/src/GatewaySchema.ts +516 -0
  561. package/src/GatewayServer.ts +932 -0
  562. package/src/HostBranchPresence.ts +72 -0
  563. package/src/Projections.ts +1735 -0
  564. package/src/RunDevTools.ts +394 -0
  565. package/src/RunHistory.ts +174 -0
  566. package/src/RunTrace.ts +2793 -0
  567. package/src/RuntimeBridge.ts +835 -0
  568. package/src/bun/BunGateway.ts +52 -0
  569. package/src/index.ts +77 -0
  570. package/src/internal/NativeGateway.ts +360 -0
  571. package/src/internal/actionRenderings.ts +201 -0
  572. package/src/internal/callEvents.ts +257 -0
  573. package/src/internal/digestIndex.ts +134 -0
  574. package/src/internal/digestMemory.ts +27 -0
  575. package/src/internal/inspectLabels.ts +13 -0
  576. package/src/internal/nativeResolution.ts +103 -0
  577. package/src/internal/nodeEvents.ts +100 -0
  578. package/src/node/NodeGateway.ts +30 -0
  579. package/openapi.yaml +0 -29945
  580. package/src/README.md +0 -39
  581. package/src/api/README.md +0 -23
  582. package/src/api/apiCollectionNames.js +0 -10
  583. package/src/api/index.d.ts +0 -126
  584. package/src/api/index.js +0 -14
  585. package/src/api/normalizeApiRow.js +0 -36
  586. package/src/api/serializeAccountRow.js +0 -10
  587. package/src/api/serializeApprovalRow.js +0 -53
  588. package/src/api/serializeComparisonScoreRow.js +0 -18
  589. package/src/api/serializeCronRow.js +0 -30
  590. package/src/api/serializeDocRow.js +0 -20
  591. package/src/api/serializeMemoryFactRow.js +0 -21
  592. package/src/api/serializePromptRow.js +0 -10
  593. package/src/api/serializeRunEventRow.js +0 -28
  594. package/src/api/serializeRunRow.js +0 -93
  595. package/src/api/serializeScoreDetailRow.js +0 -25
  596. package/src/api/serializeScoreRow.js +0 -26
  597. package/src/api/serializeTicketRow.js +0 -20
  598. package/src/api/serializeWorkflowRow.js +0 -10
  599. package/src/auth/scopes.d.ts +0 -19
  600. package/src/auth/scopes.js +0 -164
  601. package/src/index.d.ts +0 -316
  602. package/src/index.js +0 -108
  603. package/src/rpc/gatewayRpcTypes.ts +0 -186
  604. package/src/rpc/index.d.ts +0 -223
  605. package/src/rpc/index.js +0 -2527
@@ -0,0 +1,2105 @@
1
+ /**
2
+ * The run trace model (factory spec 06, "the run trace": one card shows every
3
+ * run).
4
+ *
5
+ * A run is a recursive program: the agent writes REPL cells in a code
6
+ * container, every `ctx.call` is a durable span, and each turn is a frame. The
7
+ * control journal already records exactly that (`@smthrs/agent` AgentSession
8
+ * journals turn-opened, model-settled, cell-produced, cell-call-started,
9
+ * cell-call-settled, cell-printed, cell-settled, resolved), and the gateway
10
+ * serves it as the `run-events` projection the run card already reads. This
11
+ * module folds those records into a span tree, run → frame → cell → call, and
12
+ * the geometry of one bar per span on a shared time axis. No second data
13
+ * source: what the journal does not carry (realm variables, child runs' own
14
+ * frames) the model does not invent.
15
+ *
16
+ * Pure: the card renders the model, the tests read it from a fixture.
17
+ * @since 1.0.0
18
+ */
19
+ import { CallFact } from "@smthrs/journal";
20
+ import { CallPresentation, FlowActivity } from "@smthrs/registry/Descriptor";
21
+ import { Schema } from "effect";
22
+ import { callScope, openCallIndex } from "./Diagnosis.js";
23
+ import { engineExecutionEvidence, engineTraceFromJournal } from "./EngineTrace.js";
24
+ import { ACTION_RENDERINGS } from "./internal/actionRenderings.js";
25
+ import { callEventFilter, nativeStepEvent, uniqueCallEvents } from "./internal/callEvents.js";
26
+ import { inspectLabel } from "./internal/inspectLabels.js";
27
+ export { inspectLabel } from "./internal/inspectLabels.js";
28
+ const TERMINAL_RUN = new Set(["completed", "failed", "cancelled", "no-capacity"]);
29
+ const asRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
30
+ const asString = (value) => typeof value === "string" ? value : undefined;
31
+ const asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
32
+ /** The record's time: the agent's own `at` stamp when it carries one, the journal's otherwise (the gateway's rule). */
33
+ const timeOf = (record, payload) => asNumber(payload.at) ?? asNumber(record.occurredAt) ?? 0;
34
+ /** One line of text for a value the journal traced, or nothing. */
35
+ const textOf = (value) => {
36
+ if (value === undefined || value === null)
37
+ return undefined;
38
+ if (typeof value === "string")
39
+ return value;
40
+ try {
41
+ return JSON.stringify(value);
42
+ }
43
+ catch {
44
+ return String(value);
45
+ }
46
+ };
47
+ /** The payload fields the pane does not already show by name. */
48
+ const restOf = (payload, shown) => {
49
+ const rest = {};
50
+ for (const [key, value] of Object.entries(payload)) {
51
+ if (shown.includes(key) || key === "at" || key === "journalVersion")
52
+ continue;
53
+ rest[key] = value;
54
+ }
55
+ return Object.keys(rest).length === 0 ? undefined : rest;
56
+ };
57
+ const builder = (id, kind, label, status, startedAt, detail) => ({ id, kind, label, status, startedAt, children: [], detail });
58
+ /** Compatibility for journals whose descriptors predate presentation metadata. */
59
+ const legacy = (activity, pending, success, failure, subject, result) => ({
60
+ ...(activity === undefined ? {} : { activity }),
61
+ presentation: { verb: { pending, success, failure }, subject, result }
62
+ });
63
+ const LEGACY_PRESENTATION = new Map([
64
+ // `bash` has no compatibility activity for the same reason `@smthrs/std`
65
+ // declares none: what a shell call does is in the command, which the rule
66
+ // below reads off the record.
67
+ ["bash", legacy(undefined, "running", "ran", "failed to run", "command", "command")],
68
+ ["test", legacy("tests", "running", "ran", "failed to run", "selection", "tests")],
69
+ ["edit", legacy("writes", "editing", "edited", "failed to edit", "path", "edit")],
70
+ ["write", legacy("writes", "writing", "wrote", "failed to write", "path", "write")],
71
+ ["apply_patch", legacy("writes", "patching", "patched", "failed to patch", "patch", "patch")],
72
+ ["read", legacy("reads", "reading", "read", "failed to read", "path", "read")],
73
+ ["grep", legacy("reads", "searching", "searched", "failed to search", "pattern", "matches")],
74
+ ["glob", legacy("reads", "listing", "listed", "failed to list", "pattern", "paths")],
75
+ ["ls", legacy("reads", "listing", "listed", "failed to list", "path", "entries")]
76
+ ]);
77
+ /** Read only the descriptor's validated display fields; a call input is never metadata. */
78
+ const recordedMetadata = (value, flowName) => {
79
+ const descriptor = asRecord(value);
80
+ if (descriptor.name !== flowName)
81
+ return undefined;
82
+ if (descriptor.activity === undefined && descriptor.presentation === undefined)
83
+ return undefined;
84
+ const activity = Schema.is(FlowActivity)(descriptor.activity) ? descriptor.activity : undefined;
85
+ const presentation = Schema.is(CallPresentation)(descriptor.presentation) ? descriptor.presentation : undefined;
86
+ return { activity, presentation };
87
+ };
88
+ /**
89
+ * The name a queued checkpoint mint carries (`QuickJSSandbox` `checkpointFlow`).
90
+ * A mint journals `control.agent.checkpoint-minted` and never a call, so no
91
+ * call in a journal wears this name today; the dominant-call rule skips it
92
+ * anyway, so a host that binds a flow by that name cannot make a frame's
93
+ * headline read as bookkeeping.
94
+ */
95
+ const CHECKPOINT_FLOW = "checkpoint";
96
+ /** The command line a call ran, for the flows that run one. */
97
+ const commandOf = (input) => {
98
+ const fields = asRecord(input);
99
+ return asString(fields.command) ?? asString(fields.script);
100
+ };
101
+ /** The last segment of a path: the name a person calls the file. */
102
+ const basename = (path) => path.split("/").filter((part) => part !== "").pop() ?? path;
103
+ /**
104
+ * The files a V4A patch names (`@smthrs/std` ApplyPatch's `*** Add File: `,
105
+ * `*** Delete File: ` and `*** Update File: ` markers).
106
+ *
107
+ * `apply_patch` carries its entire patch in one `input` string, and the patch
108
+ * is not the subject: what it patches is. A string that is not a patch names no
109
+ * files and yields none, so a flow that happens to call its field `input` is
110
+ * not mistaken for one.
111
+ */
112
+ const patchedPaths = (patch) => {
113
+ const paths = [];
114
+ for (const line of patch.split("\n")) {
115
+ const named = /^\*\*\* (?:Add|Delete|Update) File:\s*(\S.*)$/.exec(line.trim())?.[1];
116
+ if (named !== undefined)
117
+ paths.push(named.trim());
118
+ }
119
+ return paths;
120
+ };
121
+ /**
122
+ * Several files in the room one name has: the first named, the rest counted.
123
+ *
124
+ * A multi-file patch's line and a frame's write pin both say it through here,
125
+ * so `views.py +1` means one thing wherever the card prints it. The count is a
126
+ * fact the journal carried, never an estimate. No file, no words.
127
+ */
128
+ const filesLabel = (paths) => {
129
+ const first = paths[0];
130
+ if (first === undefined)
131
+ return "";
132
+ return paths.length === 1 ? basename(first) : `${basename(first)} +${paths.length - 1}`;
133
+ };
134
+ /** The files an edit-like call names: its `path`, or every file its patch names. */
135
+ const writtenPaths = (input) => {
136
+ const fields = asRecord(input);
137
+ const path = asString(fields.path);
138
+ if (path !== undefined)
139
+ return [path];
140
+ const patch = asString(fields.input);
141
+ return patch === undefined ? [] : patchedPaths(patch);
142
+ };
143
+ /**
144
+ * What one recorded call was about, in the shape its declaration chose.
145
+ *
146
+ * Exported because the run header names the same subject as the row beneath
147
+ * it: two readings of one input is two answers to one question.
148
+ *
149
+ * @param input the call input, as journaled
150
+ * @param format the declared subject field, when the declaration named one
151
+ *
152
+ * @category utilities
153
+ * @since 1.0.0
154
+ */
155
+ export const callSubject = (input, format) => {
156
+ const fields = asRecord(input);
157
+ if (format === "none")
158
+ return "";
159
+ if (format === "path")
160
+ return asString(fields.path) === undefined ? "" : basename(fields.path);
161
+ if (format === "command")
162
+ return commandOf(input) ?? "";
163
+ if (format === "patch")
164
+ return filesLabel(patchedPaths(asString(fields.input) ?? ""));
165
+ if (format === "pattern")
166
+ return asString(fields.pattern) ?? "";
167
+ if (format === "selection")
168
+ return stringList(fields.selection)?.join(" ") ?? "";
169
+ const path = asString(fields.path);
170
+ if (path !== undefined)
171
+ return basename(path);
172
+ const command = commandOf(input);
173
+ if (command !== undefined)
174
+ return command;
175
+ const patch = asString(fields.input);
176
+ if (patch !== undefined) {
177
+ // The patch is not the subject; the files it patches are.
178
+ const files = filesLabel(patchedPaths(patch));
179
+ if (files !== "")
180
+ return files;
181
+ }
182
+ // `test` names what it selected (`@smthrs/std` TestRun `Input.selection`).
183
+ // An omitted selection means the record named nothing; that the flow would
184
+ // then run everything is the flow's default, not a fact this record carries.
185
+ const selection = Array.isArray(fields.selection)
186
+ ? fields.selection.filter((one) => typeof one === "string")
187
+ : undefined;
188
+ if (selection !== undefined && selection.length > 0)
189
+ return selection.join(" ");
190
+ return asString(fields.pattern) ?? "";
191
+ };
192
+ /**
193
+ * Legacy shell journals have no structured activity. Recognize only direct
194
+ * invocations of known runners. Shell composition, expansion and quoted
195
+ * programs remain unknown. Arguments can narrow a check without changing
196
+ * its activity; no command here establishes a plan's required coverage.
197
+ */
198
+ const shellActivity = (command) => {
199
+ if (/[;&|<>`$\n\r]/.test(command))
200
+ return "other";
201
+ const words = command.trim().split(/\s+/);
202
+ const first = words[0];
203
+ if (["pytest", "vitest", "jest"].includes(first))
204
+ return "tests";
205
+ if (/^python[23]?$/.test(first) && words[1] === "-m" && ["pytest", "unittest"].includes(words[2] ?? "")) {
206
+ return "tests";
207
+ }
208
+ if (first === "bun" && words[1] === "test")
209
+ return "tests";
210
+ if (["pnpm", "npm", "bun"].includes(first)) {
211
+ if (words[1] === "test" || (words[1] === "run" && words[2] === "test"))
212
+ return "tests";
213
+ if (words[1] === "exec" && ["vitest", "jest"].includes(words[2] ?? ""))
214
+ return "tests";
215
+ if (words[1] === "run" && ["check", "typecheck", "lint"].includes(words[2] ?? ""))
216
+ return "checks";
217
+ }
218
+ if (first === "go" && words[1] === "test")
219
+ return "tests";
220
+ if (first === "cargo" && words[1] === "test")
221
+ return "tests";
222
+ if (first === "tsc")
223
+ return "checks";
224
+ return "other";
225
+ };
226
+ /**
227
+ * The one reading of a recorded call's semantics, shared by the rows, the
228
+ * phase bands and the run header.
229
+ *
230
+ * Precedence, and why: the record's own `descriptor` wins, because it is what
231
+ * the declaration said when the call was made, and only a record that carries
232
+ * none falls back to {@link LEGACY_PRESENTATION}, which is a guess about the
233
+ * flows that were journaled before a declaration could say anything. A flow the
234
+ * table does not know stays unknown, and unknown is said as unknown.
235
+ *
236
+ * `bash` is the one activity read off the call rather than the declaration.
237
+ * `@smthrs/std` declares none for it on purpose — the same flow runs a test
238
+ * suite, a type check and `cat` — so a recognised runner in the recorded
239
+ * command is the only thing that names the activity, and an explicit
240
+ * declaration still outranks it.
241
+ *
242
+ * @param flowName the flow the record names
243
+ * @param payload the `cell-call-started` payload, as journaled
244
+ *
245
+ * @category utilities
246
+ * @since 1.0.0
247
+ */
248
+ export const callSemantics = (flowName, payload) => {
249
+ const recorded = recordedMetadata(payload.descriptor, flowName);
250
+ const metadata = recorded ?? LEGACY_PRESENTATION.get(flowName);
251
+ const activity = metadata?.activity
252
+ ?? (flowName === "bash" ? shellActivity(commandOf(payload.input) ?? "") : undefined);
253
+ return {
254
+ ...(activity === undefined ? {} : { activity }),
255
+ ...(metadata?.presentation === undefined ? {} : { presentation: metadata.presentation })
256
+ };
257
+ };
258
+ const stringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : undefined;
259
+ const countOf = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined;
260
+ const counted = (value, noun, plural = `${noun}s`) => `${value} ${value === 1 ? noun : plural}`;
261
+ /** Known output fields only. Unknown objects remain in the call's raw output. */
262
+ const resultOf = (value, format) => {
263
+ if (format === "none")
264
+ return "";
265
+ if (typeof value === "string")
266
+ return clip(value) ?? "";
267
+ const fields = asRecord(value);
268
+ if (fields.truncated === true && typeof fields.digest === "string")
269
+ return "";
270
+ switch (format) {
271
+ case "read": {
272
+ const start = countOf(fields.startLine);
273
+ const end = countOf(fields.endLine);
274
+ return start !== undefined && start > 0 && end !== undefined && end >= start - 1
275
+ ? counted(end - start + 1, "line") :
276
+ "";
277
+ }
278
+ case "write":
279
+ return countOf(fields.bytesWritten) === undefined ? "" : counted(fields.bytesWritten, "byte");
280
+ case "edit":
281
+ return countOf(fields.replacements) === undefined ? "" : counted(fields.replacements, "replacement");
282
+ case "patch": {
283
+ const groups = [stringList(fields.added), stringList(fields.modified), stringList(fields.deleted)];
284
+ return groups.every((group) => group !== undefined) ? counted(new Set(groups.flat()).size, "file") : "";
285
+ }
286
+ case "tests": {
287
+ const passed = countOf(fields.passed);
288
+ const failed = stringList(fields.failed);
289
+ if (fields.parsed === true && passed !== undefined && failed !== undefined && fields.invalidProbe === undefined) {
290
+ const exit = countOf(fields.exitCode);
291
+ return `${passed} passed${failed.length > 0 ? ` · ${failed.length} failed` : exit !== undefined && exit !== 0 ? ` · exit ${exit}` : ""}`;
292
+ }
293
+ return countOf(fields.exitCode) === undefined ? "" : `exit ${fields.exitCode}`;
294
+ }
295
+ case "command":
296
+ return countOf(fields.exitCode) === undefined ? "" : `exit ${fields.exitCode}`;
297
+ case "matches":
298
+ return Array.isArray(fields.matches) ? counted(fields.matches.length, "match", "matches") : "";
299
+ case "paths":
300
+ return stringList(fields.paths) === undefined
301
+ ? ""
302
+ : counted(fields.paths.length, "file");
303
+ case "entries":
304
+ return Array.isArray(fields.entries) ? counted(fields.entries.length, "entry", "entries") : "";
305
+ default:
306
+ return "";
307
+ }
308
+ };
309
+ /** A note body from the sentences the payload carried; a field the record omitted contributes nothing. */
310
+ const bodyOf = (...sentences) => sentences.filter((sentence) => sentence !== undefined).join(" ");
311
+ /** One quoted line of evidence, bounded the way the turn list bounds prose. */
312
+ const clip = (value) => {
313
+ const line = asString(value);
314
+ if (line === undefined || line === "")
315
+ return undefined;
316
+ return line.length <= 160 ? line : `${line.slice(0, 159).trimEnd()}…`;
317
+ };
318
+ /** Whether the frame made an edit-like call the journal recorded settling successfully. */
319
+ const madeAWrite = (entry) => entry.calls.some((call) => call.activity === "writes" && call.settled?.failed === false && !call.settled.denied);
320
+ /**
321
+ * The call a frame's line is about: a change it made, else the check it ran,
322
+ * else the first thing it asked for.
323
+ *
324
+ * The phase rules and the lines read the same answer, so the two can never
325
+ * disagree about whether the journal said anything about a frame at all.
326
+ */
327
+ const dominantCall = (entry) => entry.calls.find((one) => one.activity === "writes")
328
+ ?? entry.calls.find((one) => one.activity === "checks" || one.activity === "tests")
329
+ ?? entry.calls.find((one) => one.flowName !== CHECKPOINT_FLOW);
330
+ const disciplineInit = (runId) => ({
331
+ runId,
332
+ frames: [],
333
+ notes: [],
334
+ milestones: [],
335
+ owners: [],
336
+ open: [],
337
+ frame: undefined,
338
+ lastAt: 0,
339
+ lineOf: new WeakMap()
340
+ });
341
+ /** One record of the discipline fold; the state is advanced in place. */
342
+ const disciplineStep = (state, record) => {
343
+ const { runId, frames, notes, milestones, owners, open } = state;
344
+ let frame = state.frame;
345
+ /** Where a discipline record attaches: the open frame, else the run itself. */
346
+ const here = () => frame?.id ?? `run:${runId}`;
347
+ const note = (seq, tone, title, body, evidence) => {
348
+ const quoted = evidence.filter((line) => line !== undefined);
349
+ notes.push({ seq, spanId: here(), tone, title, body, ...(quoted.length === 0 ? {} : { evidence: quoted }) });
350
+ };
351
+ /** A moment, owned by the frame that recorded it, exactly the way a note is. */
352
+ const pin = (seq, at, label, tone) => {
353
+ milestones.push({ seq, at, label, tone, spanId: here() });
354
+ };
355
+ {
356
+ const kind = record.kind ?? "";
357
+ const payload = asRecord(record.payload);
358
+ const at = timeOf(record, payload);
359
+ const seq = record.sequence ?? 0;
360
+ state.lastAt = Math.max(state.lastAt, at);
361
+ if (frame !== undefined)
362
+ frame.endedAt = Math.max(frame.endedAt, at);
363
+ switch (kind) {
364
+ case "control.agent.turn-opened": {
365
+ frame = {
366
+ id: `frame-${frames.length + 1}`,
367
+ frame: frames.length + 1,
368
+ seq,
369
+ startedAt: at,
370
+ endedAt: at,
371
+ blocked: false,
372
+ calls: []
373
+ };
374
+ frames.push(frame);
375
+ break;
376
+ }
377
+ case "control.agent.cell-call-started": {
378
+ const flowName = asString(payload.flowName) ?? "";
379
+ const metadata = callSemantics(flowName, payload);
380
+ const call = {
381
+ flowName,
382
+ callId: asString(payload.callId),
383
+ input: payload.input,
384
+ activity: metadata.activity,
385
+ presentation: metadata.presentation,
386
+ signature: `${flowName} ${textOf(payload.input) ?? ""}`
387
+ };
388
+ open.push(call);
389
+ // A call journaled outside a frame belongs to no frame, and the tree
390
+ // above puts it on the run. It is still open work, so it still pairs.
391
+ frame?.calls.push(call);
392
+ break;
393
+ }
394
+ case "control.agent.cell-call-settled": {
395
+ if (payload.outcome !== "success" && payload.outcome !== "failure")
396
+ break;
397
+ const index = openCallIndex(open, asString(payload.callId), asString(payload.flowName));
398
+ const call = index < 0 ? undefined : open.splice(index, 1)[0];
399
+ if (call === undefined)
400
+ break;
401
+ const failed = asString(payload.outcome) === "failure";
402
+ // `ask` settles SUCCESSFULLY with the person's answer (`AgentSession`
403
+ // answers `{ answer, approved }`), so a refusal is a denial and not a
404
+ // failure. That is the call that settles with a denial.
405
+ const denied = !failed && asRecord(payload.value).approved === false;
406
+ call.settled = {
407
+ failed,
408
+ result: (failed ? textOf(payload.message) : textOf(payload.value)) ?? "",
409
+ denied,
410
+ value: payload.value
411
+ };
412
+ if (denied && frame !== undefined)
413
+ frame.blocked = true;
414
+ // A write is the fastest thing a run does, so its band is always too
415
+ // narrow to carry a label, and its own marker is the only way to reach
416
+ // the moment that matters most.
417
+ if (!failed && !denied && call.activity === "writes") {
418
+ // A pin is its label, so a pin with none is a marker nobody can read
419
+ // and nobody can tell from the next one. The journal gave this call
420
+ // no file; no file, no pin.
421
+ const paths = [...new Set(writtenPaths(call.input))];
422
+ const minted = frame?.wrote;
423
+ if (minted !== undefined) {
424
+ // A frame's writes are ONE moment. A frame that edits fifteen files
425
+ // does it inside a second, so a pin per file is fifteen names
426
+ // stacked over a point on the axis nobody can aim between. The pin
427
+ // stays where the frame started writing, on the file it names, and
428
+ // counts the rest; a file written twice is still one file.
429
+ for (const path of paths)
430
+ if (!minted.paths.includes(path))
431
+ minted.paths.push(path);
432
+ milestones[minted.milestone] = { ...milestones[minted.milestone], label: filesLabel(minted.paths) };
433
+ }
434
+ else if (paths.length > 0) {
435
+ // A write journaled outside a frame has no frame to be counted
436
+ // into, so it stays a moment of its own.
437
+ if (frame !== undefined)
438
+ frame.wrote = { milestone: milestones.length, paths };
439
+ pin(seq, at, filesLabel(paths), "brand");
440
+ }
441
+ }
442
+ break;
443
+ }
444
+ case "control.agent.mutation-observed": {
445
+ const mutated = payload.mutated === true;
446
+ if (frame !== undefined)
447
+ frame.mutated = mutated;
448
+ if (frame !== undefined && payload.basis === "observed" && typeof payload.mutated === "boolean") {
449
+ frame.observedMutation = payload.mutated;
450
+ }
451
+ // Written for every frame, so only a frame that changed something is
452
+ // worth a note. `basis` is the body because a `declared` answer is
453
+ // paperwork and an `observed` one is a fact about the tree, and a
454
+ // reader must not have to guess which one they are holding. `paths` is
455
+ // a COUNT of what the measurement covered, not the paths themselves,
456
+ // so it is not quotable evidence and is not quoted.
457
+ if (mutated) {
458
+ note(seq, asString(payload.basis) === "observed" ? "good" : "warn", "changed", asString(payload.basis) ?? "", []);
459
+ }
460
+ break;
461
+ }
462
+ case "control.agent.permission-required": {
463
+ if (frame !== undefined)
464
+ frame.blocked = true;
465
+ pin(seq, at, "permission", "warn");
466
+ break;
467
+ }
468
+ case "control.agent.suspended": {
469
+ if (frame !== undefined)
470
+ frame.blocked = true;
471
+ break;
472
+ }
473
+ case "control.agent.checkpoint-minted": {
474
+ note(seq, "good", "checkpoint", asString(payload.ref) ?? "", []);
475
+ break;
476
+ }
477
+ case "control.agent.read-only-demanded": {
478
+ // The caps are this run's own armed numbers, read off the payload: a
479
+ // host overrides any of them, so a constant here would print a number
480
+ // the run never used.
481
+ const streak = asNumber(payload.streak);
482
+ const cap = asNumber(payload.cap);
483
+ const nextFrame = asNumber(payload.nextFrame);
484
+ const nextAction = asString(payload.nextAction);
485
+ note(seq, "warn", "read-only", bodyOf(streak === undefined || cap === undefined ? undefined : `${streak} of ${cap} frames changed nothing.`, nextFrame === undefined || nextAction === undefined ? undefined : `Frame ${nextFrame}: ${nextAction}.`), []);
486
+ pin(seq, at, "read-only", "warn");
487
+ break;
488
+ }
489
+ case "control.agent.repeat-demanded": {
490
+ const spent = asNumber(payload.frames);
491
+ const cap = asNumber(payload.cap);
492
+ const nextFrame = asNumber(payload.nextFrame);
493
+ note(seq, "warn", "repeat", bodyOf(spent === undefined || cap === undefined ? undefined : `${spent} of ${cap} frames repeated calls.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`), []);
494
+ pin(seq, at, "repeat", "warn");
495
+ break;
496
+ }
497
+ case "control.agent.narrowed-demanded": {
498
+ const flow = asString(payload.flow);
499
+ const nextFrame = asNumber(payload.nextFrame);
500
+ note(seq, "bad", "narrowed", bodyOf(flow === undefined ? undefined : `${flow} ran narrower than the reading it stands in for.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`), [clip(payload.broader), clip(payload.narrower)]);
501
+ pin(seq, at, "narrowed", "bad");
502
+ break;
503
+ }
504
+ case "control.agent.unmoved-demanded": {
505
+ const nextFrame = asNumber(payload.nextFrame);
506
+ // Both digests or neither: one alone cannot tell an unmoved tree from a
507
+ // measurement that never happened, and either may be the empty string.
508
+ const opened = clip(payload.openedDigest);
509
+ const current = clip(payload.currentDigest);
510
+ note(seq, "bad", "unmoved", bodyOf("The tree the run opened on is the tree it closed on.", nextFrame === undefined ? undefined : `Frame ${nextFrame}.`), opened === undefined || current === undefined ? [] : [opened, current]);
511
+ pin(seq, at, "unmoved", "bad");
512
+ break;
513
+ }
514
+ case "control.agent.unresolved-demanded": {
515
+ const flow = asString(payload.flow);
516
+ const nextFrame = asNumber(payload.nextFrame);
517
+ note(seq, "bad", "unresolved", bodyOf(flow === undefined ? undefined : `${flow} failed and was not answered.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`), [clip(payload.failed), clip(payload.instead)]);
518
+ pin(seq, at, "unresolved", "bad");
519
+ break;
520
+ }
521
+ case "control.agent.claim-demanded": {
522
+ // Written on EVERY evaluation, and a reading that neither handed the
523
+ // completion back nor refused it cost the run nothing and changed
524
+ // nothing, so only those two are moments anyone would scrub to. A
525
+ // refusal is the larger of them: it ends the run and takes its answer.
526
+ if (payload.demanded !== true && payload.refused !== true)
527
+ break;
528
+ const complete = asNumber(payload.complete);
529
+ const overclaims = asNumber(payload.overclaims);
530
+ const nextFrame = asNumber(payload.nextFrame);
531
+ note(seq, "bad", payload.refused === true ? "claim refused" : "claim", bodyOf(complete === undefined || overclaims === undefined
532
+ ? undefined
533
+ : `complete ${complete}, overclaims ${overclaims}.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`), []);
534
+ pin(seq, at, payload.refused === true ? "claim refused" : "claim", "bad");
535
+ break;
536
+ }
537
+ /*
538
+ * The five kinds `AgentSession`'s `default` arm once journaled with
539
+ * `payload: {}`, however rich their `AgentEvent` schema was:
540
+ * cell-rejected-in-frame, read-only-demand-issued,
541
+ * narrow-only-demanded, steering-drained and sufficiency-observed.
542
+ * Their whole payload is late (`AgentSession` `lateFields`), so one
543
+ * record carries every field or none, and each arm below reads what its
544
+ * record has: a legacy record still folds, and still says nothing the
545
+ * record does not carry. A note with no body and no evidence is a title
546
+ * on its own, so a fieldless record writes none.
547
+ */
548
+ case "control.agent.cell-rejected-in-frame": {
549
+ // A refused reply is a model call the frame paid for. The code is why
550
+ // the parse refused it; the message is what the frame said back.
551
+ const attempt = asNumber(payload.attempt);
552
+ const code = asString(payload.code);
553
+ const body = bodyOf(code === undefined ? undefined : `${code}.`, attempt === undefined ? undefined : `Attempt ${attempt}.`);
554
+ if (body !== "")
555
+ note(seq, "warn", "rejected", body, [clip(payload.message)]);
556
+ break;
557
+ }
558
+ case "control.agent.read-only-demand-issued": {
559
+ // The issuance's own numbers. `read-only-demanded` above carries the
560
+ // same demand's later answer, and a run that crashes between the two
561
+ // boundaries still has this one.
562
+ const streak = asNumber(payload.streak);
563
+ const cap = asNumber(payload.cap);
564
+ const nextFrame = asNumber(payload.nextFrame);
565
+ const body = bodyOf(streak === undefined || cap === undefined ? undefined : `${streak} of ${cap} frames changed nothing.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`);
566
+ if (body !== "")
567
+ note(seq, "warn", "read-only", body, []);
568
+ pin(seq, at, "read-only", "warn");
569
+ break;
570
+ }
571
+ case "control.agent.narrow-only-demanded": {
572
+ // The sibling of `narrowed-demanded`: that one fires when a broader
573
+ // reading exists and was not re-run, this one when none was ever
574
+ // taken. There is no broader input to quote, so the subjects the
575
+ // demand is about stand in its place.
576
+ const flow = asString(payload.flow);
577
+ const targets = Array.isArray(payload.targets)
578
+ ? payload.targets.filter((one) => typeof one === "string")
579
+ : [];
580
+ const nextFrame = asNumber(payload.nextFrame);
581
+ const body = bodyOf(flow === undefined || targets.length === 0
582
+ ? undefined
583
+ : `${flow} ran on ${targets.join(", ")} and nothing broader.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`);
584
+ if (body !== "")
585
+ note(seq, "warn", "narrow-only", body, [clip(payload.check)]);
586
+ pin(seq, at, "narrow-only", "warn");
587
+ break;
588
+ }
589
+ case "control.agent.steering-drained": {
590
+ // A drain is written at every frame boundary, and the queue is empty
591
+ // at almost all of them. Only messages ON the record establish that a
592
+ // person reached the run, so an empty drain is not a moment and a
593
+ // legacy record — which carries no messages either way — cannot be
594
+ // read as one. A steer too large to trace was still delivered; the
595
+ // record says so even where its words are gone.
596
+ const messages = Array.isArray(payload.messages) ? payload.messages : [];
597
+ if (messages.length === 0)
598
+ break;
599
+ note(seq, "warn", "steering", messages.length === 1 ? "1 steer." : `${messages.length} steers.`, messages.map((message) => clip(asRecord(message).text)));
600
+ pin(seq, at, "steering", "warn");
601
+ break;
602
+ }
603
+ case "control.agent.sufficiency-observed": {
604
+ // The one observation that rewards a run rather than braking it: a
605
+ // failing check answered by a passing one. The two inputs are the
606
+ // whole of it, so they are quoted.
607
+ const flow = asString(payload.flow);
608
+ const nextFrame = asNumber(payload.nextFrame);
609
+ const body = bodyOf(flow === undefined ? undefined : `${flow} failed before the change and passed after it.`, nextFrame === undefined ? undefined : `Frame ${nextFrame}.`);
610
+ if (body !== "")
611
+ note(seq, "good", "sufficiency", body, [clip(payload.failed), clip(payload.passed)]);
612
+ pin(seq, at, "sufficiency", "good");
613
+ break;
614
+ }
615
+ case "control.run.completed":
616
+ case "control.run.failed":
617
+ case "control.run.cancelled": {
618
+ const verdict = kind.slice("control.run.".length);
619
+ // A person's stop reads as the product word every surface uses, and
620
+ // is never drawn as a failure.
621
+ if (verdict === "cancelled")
622
+ pin(seq, at, "stopped", "muted");
623
+ else
624
+ pin(seq, at, verdict, verdict === "completed" ? "good" : "bad");
625
+ break;
626
+ }
627
+ default:
628
+ break;
629
+ }
630
+ // After the record's own arm, so a turn-opened belongs to the frame it
631
+ // opened: the rule `here()` already gives a note and a pin.
632
+ owners.push({ seq, spanId: here() });
633
+ }
634
+ state.frame = frame;
635
+ };
636
+ /**
637
+ * What {@link disciplineStep} has read so far, as the model's arrays.
638
+ *
639
+ * The derived rows are rebuilt from the frames; the recorded ones are copied,
640
+ * so a model taken now never sees a record stepped after it.
641
+ */
642
+ const disciplineModel = (state) => {
643
+ const { frames, lastAt } = state;
644
+ const notes = state.notes.slice();
645
+ const milestones = state.milestones.slice();
646
+ const owners = state.owners.slice();
647
+ // A measured unchanged tree is authoritative even when a write succeeded.
648
+ const changed = frames.map((entry) => entry.observedMutation ?? (entry.mutated === true || madeAWrite(entry)));
649
+ // A repeated call is only evidence of a stall inside a STREAK, and only
650
+ // across a span the workspace never moved in.
651
+ //
652
+ // Re-running one check after an edit is the OPPOSITE of a stall: it is the
653
+ // answer `control.agent.sufficiency-observed` rewards. A rule that flagged
654
+ // any second occurrence of a call would mark that good frame and a looping
655
+ // one identically, and a rule that only checked THIS frame for a change
656
+ // would still flag it, because the edit it answers happened in the frame
657
+ // before. So a change resets what a first occurrence is: a call is judged
658
+ // against the last time it ran on THIS tree, never against a run from before
659
+ // an edit landed. On top of that a repeat counts only where the frames beside
660
+ // it repeat too — each of them recording no change of its own — and
661
+ // `repeatOf` is set only inside a streak of two or more such frames. Every
662
+ // substantive call must repeat a settled reading; a fresh call, new result,
663
+ // or pending result means this frame has not been shown to be redundant.
664
+ const firstSeen = new Map();
665
+ const repeatedFrom = new Map();
666
+ const redundant = [];
667
+ frames.forEach((entry, index) => {
668
+ const calls = entry.calls.filter((call) => call.flowName !== CHECKPOINT_FLOW);
669
+ for (const call of calls) {
670
+ if (call.settled === undefined)
671
+ continue;
672
+ // A call settles once, so its reading is built once, not per model taken.
673
+ call.reading ??= `${call.signature} ${JSON.stringify(call.settled)}`;
674
+ const reading = call.reading;
675
+ const seen = firstSeen.get(reading);
676
+ if (seen === undefined)
677
+ firstSeen.set(reading, entry.frame);
678
+ else if (seen < entry.frame)
679
+ repeatedFrom.set(call, seen);
680
+ }
681
+ redundant.push(calls.length > 0 && calls.every((call) => repeatedFrom.has(call)));
682
+ // The tree moved in this frame, so every call recorded before it ran
683
+ // against a tree that no longer exists and none of them can be repeated.
684
+ if (changed[index] === true)
685
+ firstSeen.clear();
686
+ });
687
+ const stalling = frames.map((_frame, index) => redundant[index] === true && changed[index] !== true);
688
+ const stuck = stalling.map((flag, index) => flag && (stalling[index - 1] === true || stalling[index + 1] === true));
689
+ // Phase, most specific rule first. The last rule is the honesty one: every
690
+ // other word is a claim about what the frame was doing, and `researching` is
691
+ // as much of a claim as the rest. A frame this fold cannot name one call for
692
+ // is a frame it declines to write a line about, so it declines to name a
693
+ // phase for it too.
694
+ const phases = frames.map((entry, index) => entry.blocked
695
+ ? "blocked"
696
+ : stuck[index] === true
697
+ ? "stuck"
698
+ : changed[index] === true || entry.calls.some((call) => call.activity === "writes")
699
+ ? "implementing"
700
+ : entry.calls.some((call) => call.activity === "checks" || call.activity === "tests")
701
+ ? "testing"
702
+ : entry.calls.some((call) => call.activity === "reads")
703
+ ? "researching"
704
+ : "unrecorded");
705
+ const bands = [];
706
+ frames.forEach((entry, index) => {
707
+ const phase = phases[index];
708
+ // A frame's band ends where the next frame opens, and the last one ends
709
+ // where the journal does: the rule the tree's own frames close by.
710
+ const endedAt = frames[index + 1]?.startedAt ?? Math.max(entry.endedAt, lastAt);
711
+ const last = bands[bands.length - 1];
712
+ if (last !== undefined && last.phase === phase) {
713
+ bands[bands.length - 1] = { ...last, endedAt, frames: [...last.frames, entry.id] };
714
+ return;
715
+ }
716
+ bands.push({ phase, startedAt: entry.startedAt, endedAt, frames: [entry.id], seq: entry.seq });
717
+ });
718
+ const lines = frames.flatMap((entry, index) => {
719
+ const call = dominantCall(entry);
720
+ // A frame that called nothing has no line. Absence is absence.
721
+ if (call === undefined)
722
+ return [];
723
+ const repeatOf = stuck[index] === true ? repeatedFrom.get(call) : undefined;
724
+ const held = state.lineOf.get(entry);
725
+ if (held !== undefined && held.call === call && held.settled === call.settled && held.repeatOf === repeatOf) {
726
+ return [held.line];
727
+ }
728
+ const outcome = call.settled === undefined
729
+ ? "pending"
730
+ : call.settled.failed || call.settled.denied
731
+ ? "failure"
732
+ : "success";
733
+ const line = {
734
+ spanId: entry.id,
735
+ frame: entry.frame,
736
+ verb: call.presentation?.verb[outcome] ??
737
+ `${call.flowName}${outcome === "pending" ? " pending" : outcome === "failure" ? " failed" : ""}`,
738
+ subject: callSubject(call.input, call.presentation?.subject),
739
+ // Read supported output fields through the descriptor's presentation.
740
+ // The selected call retains the raw value, including unsupported shapes.
741
+ result: call.settled?.failed === true
742
+ ? clip(call.settled.result) ?? ""
743
+ : resultOf(call.settled?.value, call.presentation?.result),
744
+ failed: call.settled?.failed === true,
745
+ wrote: call.activity === "writes" && call.settled?.failed === false && !call.settled.denied,
746
+ ...(repeatOf === undefined ? {} : { repeatOf })
747
+ };
748
+ state.lineOf.set(entry, { call, settled: call.settled, repeatOf, line });
749
+ return [line];
750
+ });
751
+ return { bands, milestones, lines, notes, owners };
752
+ };
753
+ const isEngineRow = (record) => record.kind === "control.engine.event" || record.kind === "control.engine.projection-gap";
754
+ const foldInit = (run) => ({
755
+ run,
756
+ root: builder(`run:${run.runId}`, "run", `run ${run.runId} · ${run.flowId}`, "running", 0, {
757
+ ...(run.kind === undefined ? {} : { fields: { kind: run.kind } })
758
+ }),
759
+ frame: undefined,
760
+ cell: undefined,
761
+ frames: 0,
762
+ calls: 0,
763
+ seat: undefined,
764
+ lastAt: 0,
765
+ count: 0,
766
+ verdict: undefined,
767
+ openCalls: [],
768
+ approvals: new Map(),
769
+ discipline: disciplineInit(run.runId),
770
+ engine: [],
771
+ engineTrace: undefined,
772
+ frozen: new WeakMap(),
773
+ dirty: new Set(),
774
+ topOf: new WeakMap()
775
+ });
776
+ /**
777
+ * Folds one record, in sequence order, into a dispatch's trace.
778
+ *
779
+ * Frames open on `turn-opened` and close when the next opens or the run ends;
780
+ * cells open on `cell-produced` and close on `cell-settled`; calls open on
781
+ * `cell-call-started` under the open cell (or the frame, when a call is
782
+ * journaled outside a cell) and settle by stable call identity the way the gateway's own
783
+ * `run-tree` pairs them, so a call's `call-N` id is the node id the
784
+ * `node-output` projection knows it by. A journal with no records yields the
785
+ * run root alone, wearing the run's status: the honest empty trace.
786
+ *
787
+ * The same records fold a second time, into what a person would say the run
788
+ * was doing: see {@link disciplineStep}.
789
+ */
790
+ const foldStep = (state, record) => {
791
+ if (isEngineRow(record)) {
792
+ state.engine.push(record);
793
+ state.engineTrace = undefined;
794
+ }
795
+ // route receives only the records kept by the shared call-event filter.
796
+ const { root, openCalls, approvals, dirty, topOf } = state;
797
+ let { frame, cell, frames, calls, seat } = state;
798
+ const top = frame ?? cell;
799
+ if (top !== undefined)
800
+ dirty.add(top);
801
+ /** Marks the run's child that `span` sits under as changed. */
802
+ const touch = (span) => {
803
+ // Both callers retrieve spans registered in topOf when they were opened.
804
+ dirty.add(topOf.get(span));
805
+ };
806
+ /** Where a new span attaches: the open cell, else the open frame, else the run. */
807
+ const parent = () => cell ?? frame ?? root;
808
+ const closeCell = (at, status) => {
809
+ if (cell === undefined)
810
+ return;
811
+ cell.endedAt = at;
812
+ cell.status = status;
813
+ cell = undefined;
814
+ };
815
+ const closeFrame = (at) => {
816
+ if (frame === undefined)
817
+ return;
818
+ closeCell(at, cell?.children.some((child) => child.status === "failed") === true ? "failed" : "completed");
819
+ frame.endedAt = at;
820
+ frame.status = frame.children.some((child) => child.status === "failed") ? "failed" : "completed";
821
+ frame = undefined;
822
+ };
823
+ {
824
+ const kind = record.kind ?? "";
825
+ const payload = asRecord(record.payload);
826
+ const at = timeOf(record, payload);
827
+ if (state.count === 0) {
828
+ root.startedAt = at;
829
+ state.lastAt = at;
830
+ }
831
+ else
832
+ state.lastAt = Math.max(state.lastAt, at);
833
+ state.count += 1;
834
+ const opened = { sequence: record.sequence, event: kind };
835
+ switch (kind) {
836
+ case "control.engine.event":
837
+ case "control.engine.projection-gap":
838
+ case "control.engine.projection-started":
839
+ case "control.engine.projection-settled":
840
+ // Native records have their own identities and lifecycle. Never put
841
+ // them under whichever agent frame happened to be open at ingestion.
842
+ break;
843
+ case "control.agent.turn-opened": {
844
+ closeFrame(at);
845
+ frames += 1;
846
+ seat = asString(payload.seat) ?? seat;
847
+ frame = builder(`frame-${frames}`, "frame", `frame ${frames}${seat === undefined ? "" : ` · ${seat}`}`, "running", at, {
848
+ ...opened,
849
+ ...(seat === undefined ? {} : { seat }),
850
+ fields: restOf(payload, ["seat"])
851
+ });
852
+ root.children.push(frame);
853
+ break;
854
+ }
855
+ case "control.agent.model-settled": {
856
+ const usage = asRecord(payload.usage);
857
+ const duration = asNumber(payload.durationMillis);
858
+ const span = builder(`model-${record.sequence ?? at}`, "model", "model", "completed", duration === undefined ? at : at - duration, {
859
+ ...opened,
860
+ ...(seat === undefined ? {} : { seat }),
861
+ output: textOf(payload.text),
862
+ usage: { inputTokens: asNumber(usage.inputTokens), outputTokens: asNumber(usage.outputTokens) },
863
+ fields: restOf(payload, ["text", "usage", "durationMillis"])
864
+ });
865
+ span.endedAt = at;
866
+ (frame ?? root).children.push(span);
867
+ break;
868
+ }
869
+ case "control.agent.cell-produced": {
870
+ closeCell(at, "completed");
871
+ const language = asString(payload.language);
872
+ cell = builder(`cell-${record.sequence ?? at}`, "cell", `cell${language === undefined ? "" : ` · ${language}`}`, "running", at, {
873
+ ...opened,
874
+ source: asString(payload.text),
875
+ fields: restOf(payload, ["text", "language"])
876
+ });
877
+ (frame ?? root).children.push(cell);
878
+ break;
879
+ }
880
+ case "control.agent.cell-call-started": {
881
+ calls += 1;
882
+ const recordedName = asString(payload.flowName);
883
+ const flowName = recordedName ?? `call-${calls}`;
884
+ const span = builder(`call-${calls}`, "call", inspectLabel(flowName), "running", at, {
885
+ ...opened,
886
+ ...(recordedName === undefined ? {} : { flowName: recordedName }),
887
+ input: payload.input,
888
+ fields: restOf(payload, ["flowName", "input"])
889
+ });
890
+ openCalls.push({ flowName, callId: asString(payload.callId), span });
891
+ topOf.set(span, frame ?? cell ?? span);
892
+ parent().children.push(span);
893
+ break;
894
+ }
895
+ case "control.agent.cell-call-settled": {
896
+ const index = openCallIndex(openCalls, asString(payload.callId), asString(payload.flowName));
897
+ const settled = index < 0 ? undefined : openCalls.splice(index, 1)[0];
898
+ if (settled === undefined)
899
+ break;
900
+ const failed = asString(payload.outcome) === "failure";
901
+ touch(settled.span);
902
+ settled.span.endedAt = at;
903
+ settled.span.status = failed ? "failed" : "completed";
904
+ settled.span.detail = {
905
+ ...settled.span.detail,
906
+ ...(failed ? { message: textOf(payload.message) } : { output: textOf(payload.value) }),
907
+ ...(!failed && settled.flowName === "agent/spawn" && asString(asRecord(payload.value).child) !== undefined
908
+ ? { childRunId: asString(asRecord(payload.value).child) }
909
+ : {})
910
+ };
911
+ break;
912
+ }
913
+ case "control.agent.cell-printed": {
914
+ if (cell === undefined) {
915
+ parent().children.push(builder(`printed-${record.sequence ?? at}`, "event", "printed", "completed", at, {
916
+ ...opened,
917
+ printed: textOf(payload.text)
918
+ }));
919
+ break;
920
+ }
921
+ cell.detail = {
922
+ ...cell.detail,
923
+ printed: [cell.detail.printed, textOf(payload.text)].filter((text) => text !== undefined).join("\n")
924
+ };
925
+ break;
926
+ }
927
+ case "control.agent.cell-settled": {
928
+ closeCell(at, asString(payload.outcome) === "failure" ? "failed" : "completed");
929
+ break;
930
+ }
931
+ case "control.agent.resolved": {
932
+ const span = builder(`resolved-${record.sequence ?? at}`, "resolved", "resolved", "completed", at, {
933
+ ...opened,
934
+ output: textOf(payload.text)
935
+ });
936
+ span.endedAt = at;
937
+ (frame ?? root).children.push(span);
938
+ break;
939
+ }
940
+ case "control.approval.requested": {
941
+ const requestId = asString(payload.requestId) ?? `approval-${record.sequence ?? at}`;
942
+ const question = asString(payload.question);
943
+ const span = builder(`approval-${requestId}`, "approval", `approval${question === undefined ? "" : ` · ${question}`}`, "waiting", at, {
944
+ ...opened,
945
+ fields: restOf(payload, ["question", "requestId", "payload", "runId"])
946
+ });
947
+ approvals.set(requestId, span);
948
+ topOf.set(span, frame ?? span);
949
+ (frame ?? root).children.push(span);
950
+ break;
951
+ }
952
+ case "control.approval.approved":
953
+ case "control.approval.denied": {
954
+ const decided = kind === "control.approval.approved" ? "approved" : "denied";
955
+ const key = asString(payload.tokenId) ?? asString(payload.requestId);
956
+ const span = key === undefined
957
+ ? [...approvals.values()].find((entry) => entry.status === "waiting")
958
+ : approvals.get(key);
959
+ if (span === undefined)
960
+ break;
961
+ touch(span);
962
+ span.status = decided;
963
+ span.endedAt = at;
964
+ break;
965
+ }
966
+ case "control.run.completed":
967
+ case "control.run.failed":
968
+ case "control.run.cancelled": {
969
+ closeFrame(at);
970
+ root.endedAt = at;
971
+ // The journal's own verdict outranks the card's phase word: a scrub to this record shows the run settled.
972
+ state.verdict = kind.slice("control.run.".length);
973
+ break;
974
+ }
975
+ default: {
976
+ if (!kind.startsWith("control."))
977
+ break;
978
+ const span = builder(`event-${record.sequence ?? at}`, "event", kind.slice("control.".length), "completed", at, {
979
+ ...opened,
980
+ fields: restOf(payload, [])
981
+ });
982
+ span.endedAt = at;
983
+ parent().children.push(span);
984
+ if (kind === "control.agent.turn-closed" && payload.step !== undefined) {
985
+ const closed = frame;
986
+ closeFrame(at);
987
+ if (closed !== undefined && payload.outcome === "suspended")
988
+ closed.status = "waiting";
989
+ if (closed !== undefined && payload.outcome === "aborted")
990
+ closed.status = "cancelled";
991
+ }
992
+ }
993
+ }
994
+ }
995
+ Object.assign(state, { frame, cell, frames, calls, seat });
996
+ const after = frame ?? cell;
997
+ if (after !== undefined)
998
+ dirty.add(after);
999
+ disciplineStep(state.discipline, record);
1000
+ };
1001
+ /**
1002
+ * The trace {@link foldStep} has read so far, as the run with `status` would
1003
+ * show it.
1004
+ *
1005
+ * Nothing live is mutated. A settled run closes its open frame where the
1006
+ * journal ends, so that close is applied to the frozen copy only.
1007
+ */
1008
+ const foldModel = (state, status, journal, engine) => {
1009
+ const { root, frame, cell, lastAt } = state;
1010
+ const closed = new Map();
1011
+ const statusOf = (node) => closed.get(node)?.status ?? node.status;
1012
+ // A settled run leaves no frame open: the last frame ends where the journal does.
1013
+ const terminal = TERMINAL_RUN.has(status);
1014
+ if (terminal && frame !== undefined) {
1015
+ if (cell !== undefined) {
1016
+ closed.set(cell, {
1017
+ status: cell.children.some((child) => child.status === "failed") ? "failed" : "completed",
1018
+ endedAt: lastAt
1019
+ });
1020
+ }
1021
+ closed.set(frame, {
1022
+ status: frame.children.some((child) => statusOf(child) === "failed") ? "failed" : "completed",
1023
+ endedAt: lastAt
1024
+ });
1025
+ }
1026
+ if (engine === undefined && state.engineTrace === undefined)
1027
+ state.engineTrace = engineTraceFromJournal(state.engine);
1028
+ const children = [...root.children, ...(engine ?? state.engineTrace)];
1029
+ closed.set(root, {
1030
+ status: state.verdict ?? status,
1031
+ endedAt: root.endedAt ?? (terminal && state.count > 0 ? lastAt : Number.NaN)
1032
+ });
1033
+ const freeze = (node, depth, members) => freezeWith(node, depth, members.map((child) => freeze(child, depth + 1, child.children)));
1034
+ const freezeWith = (node, depth, frozenChildren) => {
1035
+ const override = closed.get(node);
1036
+ const nodeStatus = override?.status ?? node.status;
1037
+ const endedAt = override === undefined || Number.isNaN(override.endedAt) ? node.endedAt : override.endedAt;
1038
+ const held = state.frozen.get(node);
1039
+ if (held !== undefined && held.depth === depth && held.label === node.label && held.status === nodeStatus &&
1040
+ held.startedAt === node.startedAt && held.endedAt === endedAt && held.detail === node.detail &&
1041
+ held.children.length === frozenChildren.length &&
1042
+ held.children.every((child, index) => child === frozenChildren[index]))
1043
+ return held;
1044
+ const span = {
1045
+ id: node.id,
1046
+ kind: node.kind,
1047
+ label: node.label,
1048
+ status: nodeStatus,
1049
+ startedAt: node.startedAt,
1050
+ ...(endedAt === undefined ? {} : { endedAt }),
1051
+ depth,
1052
+ children: frozenChildren,
1053
+ detail: node.detail
1054
+ };
1055
+ state.frozen.set(node, span);
1056
+ return span;
1057
+ };
1058
+ // A child no step touched since the last model is a closed subtree: its
1059
+ // frozen span stands, and nothing under it is walked again.
1060
+ const settledChild = (child) => {
1061
+ if (state.dirty.has(child) || closed.has(child))
1062
+ return undefined;
1063
+ const held = state.frozen.get(child);
1064
+ return held?.depth === 1 ? held : undefined;
1065
+ };
1066
+ const frozenChildren = children.map((child) => settledChild(child) ?? freeze(child, 1, child.children));
1067
+ state.dirty.clear();
1068
+ const frozenRoot = freezeWith(root, 0, frozenChildren);
1069
+ const rows = [];
1070
+ const walk = (span) => {
1071
+ rows.push(span);
1072
+ for (const child of span.children)
1073
+ walk(child);
1074
+ };
1075
+ walk(frozenRoot);
1076
+ let start = Number.POSITIVE_INFINITY;
1077
+ let end = 0;
1078
+ let running = 0;
1079
+ let failed = 0;
1080
+ for (const span of rows) {
1081
+ start = Math.min(start, span.startedAt);
1082
+ end = Math.max(end, span.endedAt ?? span.startedAt);
1083
+ if (span.kind !== "run" && span.status === "running")
1084
+ running += 1;
1085
+ if (span.kind !== "run" && span.status === "failed")
1086
+ failed += 1;
1087
+ }
1088
+ const extent = state.count === 0 || !Number.isFinite(start)
1089
+ ? { start: 0, end: 0 }
1090
+ : { start, end: Math.max(end, start) };
1091
+ return {
1092
+ journal,
1093
+ root: frozenRoot,
1094
+ rows,
1095
+ extent,
1096
+ counts: { spans: rows.length - 1, running, failed },
1097
+ ...disciplineModel(state.discipline)
1098
+ };
1099
+ };
1100
+ const sequenceOf = (record) => record.sequence ?? 0;
1101
+ const TERMINAL_KINDS = new Set([
1102
+ "control.run.completed",
1103
+ "control.run.failed",
1104
+ "control.run.cancelled"
1105
+ ]);
1106
+ /** One deduplicated record to the fold of the dispatch it was recorded under. */
1107
+ const route = (state, record) => {
1108
+ // A run verdict stamped with another run's id (a child's, riding in this
1109
+ // journal) must not settle this run's trace or pin its milestone.
1110
+ if (record.kind?.startsWith("control.run.") === true && record.runId !== undefined && record.runId !== state.run.runId) {
1111
+ return;
1112
+ }
1113
+ state.firstAt = Math.min(state.firstAt, timeOf(record, asRecord(record.payload)));
1114
+ if (TERMINAL_KINDS.has(record.kind ?? ""))
1115
+ state.terminal = record;
1116
+ const scope = callScope(record);
1117
+ if (scope === undefined)
1118
+ return foldStep(state.base, record);
1119
+ let group = state.groups.get(scope);
1120
+ if (group === undefined) {
1121
+ group = foldInit(state.run);
1122
+ state.groups.set(scope, group);
1123
+ }
1124
+ foldStep(group, record);
1125
+ };
1126
+ /** Refolds `arrived` from nothing: the batch rules, exactly. */
1127
+ const reseed = (state, arrived) => {
1128
+ const journal = [...arrived].sort((left, right) => sequenceOf(left) - sequenceOf(right));
1129
+ const filter = callEventFilter();
1130
+ for (const record of journal)
1131
+ filter.push(record);
1132
+ Object.assign(state, {
1133
+ arrived,
1134
+ journal,
1135
+ filter,
1136
+ base: foldInit(state.run),
1137
+ groups: new Map(),
1138
+ engine: journal.filter(isEngineRow),
1139
+ engineTrace: undefined,
1140
+ firstAt: Number.POSITIVE_INFINITY,
1141
+ terminal: undefined,
1142
+ memo: undefined
1143
+ });
1144
+ for (const record of uniqueCallEvents(journal))
1145
+ route(state, record);
1146
+ };
1147
+ /**
1148
+ * Folds `records` into a fold that later records can be stepped onto.
1149
+ *
1150
+ * @param run the run the journal belongs to
1151
+ * @param records its journal so far, in any order
1152
+ *
1153
+ * @category constructors
1154
+ * @since 1.0.0
1155
+ */
1156
+ export const traceFold = (run, records = []) => {
1157
+ const state = {
1158
+ run: { runId: run.runId, flowId: run.flowId, ...(run.kind === undefined ? {} : { kind: run.kind }) }
1159
+ };
1160
+ state.source = undefined;
1161
+ reseed(state, [...records]);
1162
+ return state;
1163
+ };
1164
+ /**
1165
+ * Folds one more record onto `fold`, which it returns.
1166
+ *
1167
+ * A record in sequence order costs its own work. One that arrives before a
1168
+ * sequence already stepped, or a native fact that supersedes telemetry the
1169
+ * fold already placed, refolds the journal, because the batch rules move an
1170
+ * earlier record for both.
1171
+ *
1172
+ * @param fold the fold; consumed
1173
+ * @param record the next journal record
1174
+ *
1175
+ * @category combinators
1176
+ * @since 1.0.0
1177
+ */
1178
+ export const traceFoldStep = (fold, record) => {
1179
+ const state = fold;
1180
+ state.memo = undefined;
1181
+ state.arrived.push(record);
1182
+ const last = state.journal.at(-1);
1183
+ if (last !== undefined && !(sequenceOf(record) >= sequenceOf(last))) {
1184
+ reseed(state, state.arrived);
1185
+ return fold;
1186
+ }
1187
+ state.journal.push(record);
1188
+ if (isEngineRow(record)) {
1189
+ state.engine.push(record);
1190
+ state.engineTrace = undefined;
1191
+ }
1192
+ const verdict = state.filter.push(record);
1193
+ if (verdict._tag === "rewrite")
1194
+ reseed(state, state.arrived);
1195
+ else if (verdict._tag === "keep")
1196
+ route(state, verdict.event);
1197
+ return fold;
1198
+ };
1199
+ /**
1200
+ * Brings `fold` up to `records`, stepping only what it has not read.
1201
+ *
1202
+ * `records` extends the array the fold last read when every earlier element is
1203
+ * the same object; anything else, or a different run, starts a new fold. The
1204
+ * comparison is against the records the fold consumed, not the caller's array,
1205
+ * so growing or rewriting that same array in place is seen. A reader that
1206
+ * re-renders one growing journal therefore pays per new record.
1207
+ *
1208
+ * @param fold the fold the reader holds, if any; consumed
1209
+ * @param run the run the journal belongs to
1210
+ * @param records the whole journal as the reader now holds it
1211
+ *
1212
+ * @category combinators
1213
+ * @since 1.0.0
1214
+ */
1215
+ export const traceFoldSync = (fold, run, records) => {
1216
+ const held = fold;
1217
+ const source = held?.source;
1218
+ const extends_ = held !== undefined && source !== undefined && held.run.runId === run.runId &&
1219
+ held.run.flowId === run.flowId && held.run.kind === run.kind && records.length >= source.length &&
1220
+ source.every((record, index) => records[index] === record);
1221
+ const next = extends_ ? held : traceFold(run);
1222
+ for (let index = extends_ ? source.length : 0; index < records.length; index++)
1223
+ traceFoldStep(next, records[index]);
1224
+ next.source = records.slice();
1225
+ return next;
1226
+ };
1227
+ /**
1228
+ * The trace a fold has read, as the run with `status` shows it.
1229
+ *
1230
+ * Taken again without a step between, it is the same object.
1231
+ *
1232
+ * @param fold the fold
1233
+ * @param status the run card's phase word
1234
+ *
1235
+ * @category utilities
1236
+ * @since 1.0.0
1237
+ */
1238
+ export const traceFoldModel = (fold, status) => {
1239
+ const state = fold;
1240
+ if (state.memo?.status === status)
1241
+ return state.memo.model;
1242
+ const model = mergeModel(state, status);
1243
+ state.memo = { status, model };
1244
+ return model;
1245
+ };
1246
+ const mergeModel = (state, status) => {
1247
+ const journal = state.journal.slice();
1248
+ if (state.groups.size === 0) {
1249
+ if (state.engineTrace === undefined)
1250
+ state.engineTrace = engineTraceFromJournal(state.engine);
1251
+ return foldModel(state.base, status, journal, state.engineTrace);
1252
+ }
1253
+ const base = foldModel(state.base, status, journal);
1254
+ const children = [...base.root.children];
1255
+ const bands = [...base.bands];
1256
+ const milestones = [...base.milestones];
1257
+ const lines = [...base.lines];
1258
+ const notes = [...base.notes];
1259
+ const owners = [...base.owners];
1260
+ const terminal = state.terminal;
1261
+ const terminalAt = terminal === undefined ? undefined : timeOf(terminal, asRecord(terminal.payload));
1262
+ for (const [scope, group] of state.groups) {
1263
+ const prefix = `step:${encodeURIComponent(scope)}/`;
1264
+ const scoped = foldModel(group, "running", journal);
1265
+ const rename = (span) => ({
1266
+ ...span,
1267
+ id: `${prefix}${span.id}`,
1268
+ children: span.children.map(rename),
1269
+ ...(span.status === "running" && TERMINAL_RUN.has(base.root.status) ?
1270
+ {
1271
+ // A terminal run proves work stopped. It does not prove which open
1272
+ // step succeeded or caused the run to fail.
1273
+ status: "stopped",
1274
+ ...(terminalAt === undefined ? {} : { endedAt: terminalAt })
1275
+ } :
1276
+ {})
1277
+ });
1278
+ children.push(...scoped.root.children.map(rename));
1279
+ bands.push(...scoped.bands.map((band) => ({ ...band, frames: band.frames.map((id) => `${prefix}${id}`) })));
1280
+ // A moment, a line, a note and a position all name the frame they were
1281
+ // recorded under, so the merge renames each of them the same way: the
1282
+ // step's own frame, or this run's root for what the step recorded outside
1283
+ // any frame.
1284
+ const owned = (spanId) => spanId === scoped.root.id ? base.root.id : `${prefix}${spanId}`;
1285
+ milestones.push(...scoped.milestones.map((one) => ({ ...one, spanId: owned(one.spanId) })));
1286
+ lines.push(...scoped.lines.map((line) => ({ ...line, spanId: `${prefix}${line.spanId}` })));
1287
+ notes.push(...scoped.notes.map((note) => ({ ...note, spanId: owned(note.spanId) })));
1288
+ owners.push(...scoped.owners.map((one) => ({ ...one, spanId: owned(one.spanId) })));
1289
+ }
1290
+ children.sort((left, right) => left.startedAt - right.startedAt || (left.detail.sequence ?? 0) - (right.detail.sequence ?? 0));
1291
+ const root = { ...base.root, startedAt: state.firstAt, children };
1292
+ const rows = [];
1293
+ const walk = (span) => {
1294
+ rows.push(span);
1295
+ span.children.forEach(walk);
1296
+ };
1297
+ walk(root);
1298
+ const positions = new Map(rows.map((span, index) => [span.id, index]));
1299
+ // Each step folds its own frames from 1, so a merged run would print two
1300
+ // rows numbered 1 and a `same as 1` that names either. The merged list
1301
+ // numbers the rows a person reads, and a repeat names the renumbered row of
1302
+ // the step that recorded it.
1303
+ const merged = lines.sort((left, right) => positions.get(left.spanId) - positions.get(right.spanId));
1304
+ const stepOf = (spanId) => spanId.slice(0, spanId.lastIndexOf("/") + 1);
1305
+ const renumbered = new Map(merged.map((line, index) => [`${stepOf(line.spanId)}${line.frame}`, index + 1]));
1306
+ let start = Number.POSITIVE_INFINITY;
1307
+ let end = Number.NEGATIVE_INFINITY;
1308
+ for (const span of rows) {
1309
+ start = Math.min(start, span.startedAt);
1310
+ end = Math.max(end, span.endedAt ?? span.startedAt);
1311
+ }
1312
+ return {
1313
+ journal,
1314
+ root,
1315
+ rows,
1316
+ extent: { start, end },
1317
+ counts: {
1318
+ spans: rows.length - 1,
1319
+ running: rows.filter((span) => span.kind !== "run" && span.status === "running").length,
1320
+ failed: rows.filter((span) => span.kind !== "run" && span.status === "failed").length
1321
+ },
1322
+ bands: bands.sort((left, right) => left.startedAt - right.startedAt || left.seq - right.seq),
1323
+ milestones: milestones.sort((left, right) => left.seq - right.seq),
1324
+ lines: merged.map((line, index) => ({
1325
+ ...line,
1326
+ frame: index + 1,
1327
+ ...(line.repeatOf === undefined
1328
+ ? {}
1329
+ : { repeatOf: renumbered.get(`${stepOf(line.spanId)}${line.repeatOf}`) })
1330
+ })),
1331
+ notes: notes.sort((left, right) => left.seq - right.seq),
1332
+ owners: owners.sort((left, right) => left.seq - right.seq)
1333
+ };
1334
+ };
1335
+ /**
1336
+ * Fold each recorded dispatch independently, keeping prompt-run addresses unchanged.
1337
+ *
1338
+ * The same fold a live reader advances with {@link traceFoldStep}, taken over
1339
+ * the whole journal at once.
1340
+ *
1341
+ * The options the card carries are accepted and read by nothing: a plan's
1342
+ * check targets are a coverage requirement, and a requirement is not evidence
1343
+ * that any call ran it. Every word this fold says is read off a record.
1344
+ *
1345
+ * @category utilities
1346
+ * @since 1.0.0
1347
+ */
1348
+ export const traceFromJournal = (run, records, _options = {}) => traceFoldModel(traceFold(run, records), run.status);
1349
+ /**
1350
+ * One span's bar on the shared axis, as percentages. An open span runs to the
1351
+ * axis end; an instant span is zero width and renders as a marker.
1352
+ *
1353
+ * @param span the span
1354
+ * @param extent the axis
1355
+ *
1356
+ * @category utilities
1357
+ * @since 1.0.0
1358
+ */
1359
+ export const waterfallGeometry = (span, extent) => {
1360
+ const width = Math.max(extent.end - extent.start, 1);
1361
+ const from = span.startedAt;
1362
+ const to = span.endedAt ?? extent.end;
1363
+ const left = ((from - extent.start) / width) * 100;
1364
+ const bar = Math.max(((to - from) / width) * 100, 0);
1365
+ return { left: Math.round(left * 100) / 100, width: Math.round(bar * 100) / 100 };
1366
+ };
1367
+ /**
1368
+ * The axis the phase strip measures on: the bands' own span when the journal
1369
+ * opened frames, the run's whole extent when it opened none.
1370
+ *
1371
+ * A milestone is a journal record, and the records that write one do not need a
1372
+ * frame: a journal of `read-only-demanded` and `sufficiency-observed` with no
1373
+ * `turn-opened` carries two moments and opens no band. Milestones therefore
1374
+ * survive without bands, and the axis that places them cannot be read off the
1375
+ * bands alone. The other choice — dropping a moment because no band was drawn
1376
+ * beside it — would be this fold deciding a record the journal made did not
1377
+ * happen, which is the one thing it may never do.
1378
+ *
1379
+ * @param model the trace
1380
+ *
1381
+ * @category utilities
1382
+ * @since 1.0.0
1383
+ */
1384
+ export const phaseExtent = (model) => {
1385
+ return model.bands.length === 0 ? model.extent : {
1386
+ start: Math.min(...model.bands.map((band) => band.startedAt)),
1387
+ end: Math.max(...model.bands.map((band) => band.endedAt))
1388
+ };
1389
+ };
1390
+ /**
1391
+ * One band's share of that axis, as percentages, the way the waterfall measures
1392
+ * a span.
1393
+ *
1394
+ * A run whose frames all land in one millisecond recorded no elapsed time. Read
1395
+ * against a floored axis every such band is zero wide at left 0, so the whole
1396
+ * strip collapses onto the left edge and says nothing at all. The bands' ORDER
1397
+ * is a journal fact even where their durations are not, so with no duration to
1398
+ * measure the ordinal IS the axis: equal slices, left to right.
1399
+ *
1400
+ * @param band the band
1401
+ * @param extent the axis, from {@link phaseExtent}
1402
+ * @param index the band's place in the strip
1403
+ * @param count how many bands the strip holds
1404
+ *
1405
+ * @category utilities
1406
+ * @since 1.0.0
1407
+ */
1408
+ export const phaseBandGeometry = (band, extent, index, count) => {
1409
+ const round = (value) => Math.round(value * 100) / 100;
1410
+ const axis = extent.end - extent.start;
1411
+ if (axis <= 0) {
1412
+ const share = 100 / Math.max(count, 1);
1413
+ return { left: round(index * share), width: round(share) };
1414
+ }
1415
+ return {
1416
+ left: round(((band.startedAt - extent.start) / axis) * 100),
1417
+ width: round((Math.max(band.endedAt - band.startedAt, 0) / axis) * 100)
1418
+ };
1419
+ };
1420
+ /** The flows whose spans are messages between a coordinator and its workers (spec 06 §3). */
1421
+ const MESSAGE_FLOWS = new Set(["agent/send", "agent/await"]);
1422
+ /** Whether a span, or any span under it, passes the filter.
1423
+ *
1424
+ * @category utilities
1425
+ * @since 1.0.0
1426
+ */
1427
+ export const spanMatches = (span, filter) => {
1428
+ const own = filter === "all"
1429
+ ? true
1430
+ : filter === "running"
1431
+ ? span.status === "running" || span.status === "waiting"
1432
+ : filter === "failed"
1433
+ ? span.status === "failed"
1434
+ : filter === "model"
1435
+ ? span.kind === "model"
1436
+ : filter === "flow"
1437
+ ? span.kind === "call" || span.kind === "execution" || span.kind === "attempt"
1438
+ : span.kind === "call" && MESSAGE_FLOWS.has(span.label);
1439
+ return own || span.children.some((child) => spanMatches(child, filter));
1440
+ };
1441
+ /**
1442
+ * Stable filter identifiers accepted by run trace commands.
1443
+ *
1444
+ * @category utilities
1445
+ * @since 1.0.0
1446
+ */
1447
+ export const TRACE_FILTER_IDS = [
1448
+ "all",
1449
+ "running",
1450
+ "failed",
1451
+ "model",
1452
+ "flow",
1453
+ "messages"
1454
+ ];
1455
+ const FILTER_LABELS = {
1456
+ all: "all",
1457
+ running: "running",
1458
+ failed: "failed",
1459
+ model: "model calls",
1460
+ flow: "flow calls",
1461
+ messages: "messages"
1462
+ };
1463
+ /**
1464
+ * The filter chips a run of this kind shows (spec 06 §2 and §3): a prototype
1465
+ * has `all | messages | failed` and nothing else; every other run has the
1466
+ * shared set. `messages` is a prototype's conversation across workers, so it
1467
+ * is not offered elsewhere.
1468
+ *
1469
+ * @param kind the run's kind
1470
+ *
1471
+ * @category utilities
1472
+ * @since 1.0.0
1473
+ */
1474
+ export const traceFiltersFor = (kind) => (kind === "prototype"
1475
+ ? ["all", "messages", "failed"]
1476
+ : ["all", "running", "failed", "model", "flow"]).map((id) => [id, FILTER_LABELS[id]]);
1477
+ /** Whether a filter word is one the trace knows.
1478
+ *
1479
+ * @category utilities
1480
+ * @since 1.0.0
1481
+ */
1482
+ export const isTraceFilter = (value) => TRACE_FILTER_IDS.includes(value);
1483
+ /** The first prose line outside fenced code, bounded for the cheap turn list. Full text stays in the model span. */
1484
+ const proseLine = (text) => {
1485
+ if (text === undefined)
1486
+ return undefined;
1487
+ let fence;
1488
+ for (const line of text.split("\n")) {
1489
+ const trimmed = line.trim();
1490
+ const marker = /^(?:`{3,}|~{3,})/.exec(trimmed)?.[0];
1491
+ if (marker !== undefined) {
1492
+ if (fence === undefined)
1493
+ fence = marker;
1494
+ else if (marker[0] === fence[0] && marker.length >= fence.length)
1495
+ fence = undefined;
1496
+ continue;
1497
+ }
1498
+ if (fence !== undefined || trimmed === "")
1499
+ continue;
1500
+ // A code-only response or the journal's truncated-field object is not explanatory prose.
1501
+ if (/^(?:[[{]|(?:const|let|var|await|import|export|function|return)\b)/.test(trimmed))
1502
+ return undefined;
1503
+ const words = trimmed.replace(/^#{1,6}\s+|^[-*+]\s+|^>\s+/, "").replace(/\s+/g, " ");
1504
+ const sentence = words.match(/^.*?[.!?](?=\s|$)/)?.[0] ?? `${words}.`;
1505
+ return sentence.length <= 180 ? sentence : `${sentence.slice(0, 179).trimEnd()}…`;
1506
+ }
1507
+ return undefined;
1508
+ };
1509
+ /** One cheap line per turn; falls back to actual call names when the model recorded only code.
1510
+ *
1511
+ * @category utilities
1512
+ * @since 1.0.0
1513
+ */
1514
+ export const turnNarratives = (model) => model.root.children.filter((span) => span.kind === "frame").map((frame, index) => {
1515
+ const text = frame.children.filter((span) => span.kind === "model").map((span) => proseLine(span.detail.output))
1516
+ .find((value) => value !== undefined);
1517
+ if (text !== undefined)
1518
+ return { frame, number: index + 1, text, source: "model" };
1519
+ const names = [];
1520
+ const walk = (span) => {
1521
+ if (span.kind === "call" && !names.includes(span.label))
1522
+ names.push(span.label);
1523
+ for (const child of span.children)
1524
+ walk(child);
1525
+ };
1526
+ walk(frame);
1527
+ return names.length > 0
1528
+ ? {
1529
+ frame,
1530
+ number: index + 1,
1531
+ text: `The agent called ${names.slice(0, 3).join(", ")}${names.length > 3 ? ` and ${names.length - 3} other flows` : ""}.`,
1532
+ source: "calls"
1533
+ }
1534
+ : {
1535
+ frame,
1536
+ number: index + 1,
1537
+ text: frame.children.some((span) => span.kind === "cell")
1538
+ ? "The agent produced a script; no flow calls were recorded in this turn."
1539
+ : frame.children.some((span) => span.kind === "model")
1540
+ ? "The model responded; no script or flow calls were recorded in this turn."
1541
+ : "The turn started; no model response or flow calls have been recorded.",
1542
+ source: "journal"
1543
+ };
1544
+ });
1545
+ /** The recorded ancestry of a selection, for breadcrumbs and the selected turn's scoped call tree.
1546
+ *
1547
+ * @category utilities
1548
+ * @since 1.0.0
1549
+ */
1550
+ export const spanPath = (model, id) => {
1551
+ const visit = (span) => {
1552
+ if (span.id === id)
1553
+ return [span];
1554
+ for (const child of span.children) {
1555
+ const path = visit(child);
1556
+ if (path !== undefined)
1557
+ return [span, ...path];
1558
+ }
1559
+ return undefined;
1560
+ };
1561
+ return visit(model.root) ?? [model.root];
1562
+ };
1563
+ /** A duration in the trace's units: milliseconds under a second, seconds under a minute, minutes and seconds under an hour, hours and minutes after.
1564
+ *
1565
+ * @category utilities
1566
+ * @since 1.0.0
1567
+ */
1568
+ export const durationWords = (ms) => {
1569
+ if (ms < 1000)
1570
+ return `${Math.round(ms)}ms`;
1571
+ // Round before splitting, so 59.96s reads 1m00s rather than 60.0s, and 51m59.6s reads 52m00s rather than 51m60s.
1572
+ if (ms < 59_950)
1573
+ return `${(ms / 1000).toFixed(1)}s`;
1574
+ const total = Math.round(ms / 1000);
1575
+ if (total < 3600) {
1576
+ const minutes = Math.floor(total / 60);
1577
+ return `${minutes}m${String(total - minutes * 60).padStart(2, "0")}s`;
1578
+ }
1579
+ const minutes = Math.round(ms / 60_000);
1580
+ const hours = Math.floor(minutes / 60);
1581
+ return `${hours}h${String(minutes - hours * 60).padStart(2, "0")}m`;
1582
+ };
1583
+ const memoryItems = (value) => Array.isArray(value) ?
1584
+ value.flatMap((raw) => {
1585
+ const item = typeof raw === "object" && raw !== null ? raw : undefined;
1586
+ return item?.kind === "memory" && typeof item.id === "string" && typeof item.p === "number"
1587
+ ? [{ id: item.id, kind: "memory", relevance: 1 - item.p }]
1588
+ : [];
1589
+ }) :
1590
+ [];
1591
+ /**
1592
+ * What memory a run was brought, from the `control.agent.relevance-settled`
1593
+ * rows of its `run-events` projection: ids and probabilities, never text. A
1594
+ * reading also weighs flows, skills and instructions; only memory counts here.
1595
+ * An item any reading kept is in; one only ever withheld is withheld; both
1596
+ * most relevant first. A row whose verdicts were not retained is no reading.
1597
+ * Undefined when Jev never read for the run.
1598
+ *
1599
+ * @category folds
1600
+ * @since 1.0.0
1601
+ */
1602
+ export const runMemoryOf = (records) => {
1603
+ const kept = new Map();
1604
+ const withheld = new Map();
1605
+ let read = false;
1606
+ for (const record of records) {
1607
+ if (record.kind !== "control.agent.relevance-settled")
1608
+ continue;
1609
+ const payload = typeof record.payload === "object" && record.payload !== null
1610
+ ? record.payload
1611
+ : {};
1612
+ if (!Array.isArray(payload.kept) && !Array.isArray(payload.withheld))
1613
+ continue;
1614
+ read = true;
1615
+ for (const item of memoryItems(payload.kept))
1616
+ kept.set(item.id, item);
1617
+ for (const item of memoryItems(payload.withheld))
1618
+ withheld.set(item.id, item);
1619
+ }
1620
+ if (!read)
1621
+ return undefined;
1622
+ const byRelevance = (left, right) => right.relevance - left.relevance || left.id.localeCompare(right.id);
1623
+ return {
1624
+ kept: [...kept.values()].sort(byRelevance),
1625
+ withheld: [...withheld.values()].filter((item) => !kept.has(item.id)).sort(byRelevance)
1626
+ };
1627
+ };
1628
+ /** The system part an agent step's task travels in (`Agent.ts`). */
1629
+ const TASK_PREFIX = "The task for this run:\n\n";
1630
+ /** The agent's task from a traced request: its own `task` field, or the task
1631
+ * part of its system text when that fit the journal. Parsed when it is JSON. */
1632
+ const taskOf = (request) => {
1633
+ const parts = Array.isArray(request.system) ? request.system : [];
1634
+ const part = parts.find((text) => typeof text === "string" && text.startsWith(TASK_PREFIX));
1635
+ const task = typeof request.task === "string" ? request.task : part?.slice(TASK_PREFIX.length);
1636
+ if (task === undefined)
1637
+ return undefined;
1638
+ try {
1639
+ return JSON.parse(task);
1640
+ }
1641
+ catch {
1642
+ return task;
1643
+ }
1644
+ };
1645
+ const hexDigest = /^[0-9a-f]{64}$/;
1646
+ /** A transcript cell shows an excerpt; the journal keeps the full record. */
1647
+ const cellText = (value) => {
1648
+ const text = textOf(value);
1649
+ return text === undefined || text.length <= 2000 ? text : `${text.slice(0, 2000)}…`;
1650
+ };
1651
+ /** The recorded presentation of a tool call: "Read t5.md", "Failed to write t5.md". */
1652
+ const callCellLabel = (started, failed) => {
1653
+ const descriptor = asRecord(started.descriptor);
1654
+ const presentation = asRecord(descriptor.presentation);
1655
+ const verbs = asRecord(presentation.verb);
1656
+ const name = started.flowName;
1657
+ const verb = asString(failed ? verbs.failure : verbs.success) ?? (failed ? `failed ${name}` : name);
1658
+ const subjectKey = asString(presentation.subject);
1659
+ const subject = subjectKey === undefined ? undefined : asString(asRecord(started.input)[subjectKey]);
1660
+ const text = subject === undefined ? verb : `${verb} ${subject}`;
1661
+ return text.charAt(0).toUpperCase() + text.slice(1);
1662
+ };
1663
+ const callCellKind = (started) => {
1664
+ const activity = asString(asRecord(started.descriptor).activity);
1665
+ const name = started.flowName;
1666
+ return name === "ask" ? "ask" : activity === "reads" ? "read" : activity === "writes" ? "edit" : "run";
1667
+ };
1668
+ const decodeCallFact = Schema.decodeUnknownOption(CallFact.Fact);
1669
+ /** A native tool-call fact and the dispatch digest its scope names. */
1670
+ const callFactOf = (record) => {
1671
+ if (record.kind !== "control.engine.event")
1672
+ return undefined;
1673
+ const envelope = asRecord(record.payload);
1674
+ if (envelope.version !== 1 || envelope.eventType !== CallFact.eventType || envelope.sourceSequence !== 0) {
1675
+ return undefined;
1676
+ }
1677
+ const fact = decodeCallFact(envelope.payload);
1678
+ if (fact._tag === "None" || envelope.sourceId !== `call-fact-v1:${fact.value.callId}:${fact.value.phase}`) {
1679
+ return undefined;
1680
+ }
1681
+ const stepId = /@([0-9a-f]{64}):\d+#\d+$/.exec(fact.value.identity.runId)?.[1];
1682
+ return stepId === undefined ? undefined : { stepId, fact: fact.value };
1683
+ };
1684
+ /**
1685
+ * The agent transcript of every native dispatch in `journal`: model replies,
1686
+ * cells with what they printed, and tool calls, in journal order. Keyed by the
1687
+ * dispatch's step key digest, which the engine's node settlement names in
1688
+ * `stepKeyDigests`. Pure over the records; no model call.
1689
+ */
1690
+ const stepTranscripts = (journal) => {
1691
+ const transcripts = new Map();
1692
+ const entry = (key) => {
1693
+ let found = transcripts.get(key);
1694
+ if (found === undefined)
1695
+ transcripts.set(key, found = { cells: [], tokens: 0, models: 0 });
1696
+ return found;
1697
+ };
1698
+ const lastCell = new Map();
1699
+ const calls = new Map();
1700
+ for (const record of journal) {
1701
+ const sequence = record.sequence ?? 0;
1702
+ const step = nativeStepEvent(record);
1703
+ if (step !== undefined) {
1704
+ const payload = asRecord(step.payload);
1705
+ // nativeStepEvent has decoded StepFact.Scope, whose stepId is a 64-digit hex string.
1706
+ const stepId = asString(asRecord(payload.step).stepId);
1707
+ const transcript = entry(stepId);
1708
+ if (step.kind === "control.agent.model-requested" && transcript.input === undefined) {
1709
+ const task = taskOf(payload);
1710
+ if (task !== undefined)
1711
+ transcript.input = task;
1712
+ }
1713
+ else if (step.kind === "control.agent.model-settled") {
1714
+ const usage = asRecord(payload.usage);
1715
+ const tokens = (asNumber(usage.inputTokens) ?? 0) + (asNumber(usage.outputTokens) ?? 0);
1716
+ const duration = asNumber(payload.durationMillis);
1717
+ const text = cellText(payload.text);
1718
+ transcript.tokens += tokens;
1719
+ transcript.models += 1;
1720
+ transcript.cells.push({
1721
+ id: `cell:${sequence}`,
1722
+ kind: "think",
1723
+ label: "Model reply",
1724
+ ...(text === undefined ? {} : { quote: text }),
1725
+ tokens,
1726
+ ...(duration === undefined ? {} : { took_s: Math.max(0, duration) / 1000 })
1727
+ });
1728
+ }
1729
+ else if (step.kind === "control.agent.cell-produced") {
1730
+ const code = cellText(payload.text);
1731
+ const cell = {
1732
+ id: `cell:${sequence}`,
1733
+ kind: "run",
1734
+ label: "Ran a cell",
1735
+ ...(code === undefined ? {} : { code })
1736
+ };
1737
+ transcript.cells.push(cell);
1738
+ lastCell.set(stepId, cell);
1739
+ }
1740
+ else if (step.kind === "control.agent.cell-printed") {
1741
+ const cell = lastCell.get(stepId);
1742
+ const printed = cellText(payload.text);
1743
+ if (cell !== undefined && printed !== undefined && printed !== "") {
1744
+ cell.output = cell.output === undefined ? printed : `${cell.output}\n${printed}`;
1745
+ }
1746
+ }
1747
+ else if (step.kind === "control.agent.cell-settled") {
1748
+ const cell = lastCell.get(stepId);
1749
+ if (cell !== undefined)
1750
+ cell.tone = asString(payload.outcome) === "failure" ? "fail" : "ok";
1751
+ }
1752
+ continue;
1753
+ }
1754
+ const call = callFactOf(record);
1755
+ if (call === undefined)
1756
+ continue;
1757
+ const handle = `${call.stepId}:${call.fact.callId}`;
1758
+ if (call.fact.phase === "invoked") {
1759
+ const input = cellText(call.fact.input);
1760
+ const cell = {
1761
+ id: `cell:${sequence}`,
1762
+ kind: callCellKind(call.fact),
1763
+ label: callCellLabel(call.fact, false),
1764
+ ...(input === undefined ? {} : { code: input }),
1765
+ tone: "live"
1766
+ };
1767
+ entry(call.stepId).cells.push(cell);
1768
+ calls.set(handle, { cell, started: call.fact });
1769
+ continue;
1770
+ }
1771
+ const open = calls.get(handle);
1772
+ if (open === undefined)
1773
+ continue;
1774
+ const failed = call.fact.outcome === "failure";
1775
+ open.cell.label = callCellLabel(open.started, failed);
1776
+ open.cell.tone = failed ? "fail" : "ok";
1777
+ const result = failed ? cellText(call.fact.message) : cellText(call.fact.value);
1778
+ if (result !== undefined)
1779
+ open.cell.output = result;
1780
+ }
1781
+ return transcripts;
1782
+ };
1783
+ /** The dispatches a settled engine node drove, as `<execution id>:<step key digest>`. */
1784
+ const nodeDispatches = (row) => {
1785
+ if (!row.id.startsWith("engine-node:"))
1786
+ return [];
1787
+ const fields = asRecord(row.detail.fields);
1788
+ const execution = asString(fields.executionId);
1789
+ const digests = asRecord(fields.payload).stepKeyDigests;
1790
+ if (execution === undefined || !Array.isArray(digests))
1791
+ return [];
1792
+ return [
1793
+ ...new Set(digests.filter((digest) => typeof digest === "string" && hexDigest.test(digest)))
1794
+ ]
1795
+ .map((digest) => `${execution}:${digest}`);
1796
+ };
1797
+ /** Plan node kinds that only shape the plan (@smthrs/plan); a child flow's own nodes are its steps. */
1798
+ const PLAN_STRUCTURE = new Set([
1799
+ "Succeed",
1800
+ "Fail",
1801
+ "All",
1802
+ "Race",
1803
+ "Map",
1804
+ "AndThen",
1805
+ "Branch",
1806
+ "Catch",
1807
+ "FlowCall",
1808
+ "FunctionIdentity",
1809
+ "PlannedReference"
1810
+ ]);
1811
+ /** A check receipt's leading scalar fields, read from a preview the engine
1812
+ * may have cut short (its evidence comes last): `{"checkId":…,"status":…`. */
1813
+ const RECEIPT_HEAD = /^\{"checkId":"[^"\\]*"(?:,"[A-Za-z]+":(?:"[^"\\]*"|-?\d+))*,"status":"(passed|failed|superseded)"/;
1814
+ /** How many checks a step's recorded output reports failed: a check receipt
1815
+ * (`{checkId, status, …}`, possibly a cut-short preview) or a list of them;
1816
+ * zero for any other output. */
1817
+ const checkFailures = (output) => {
1818
+ if (output === undefined)
1819
+ return 0;
1820
+ const head = RECEIPT_HEAD.exec(output);
1821
+ if (head !== null)
1822
+ return head[1] === "failed" ? 1 : 0;
1823
+ let value;
1824
+ try {
1825
+ value = JSON.parse(output);
1826
+ }
1827
+ catch {
1828
+ return 0;
1829
+ }
1830
+ return (Array.isArray(value) ? value : []).filter((receipt) => {
1831
+ const record = asRecord(receipt);
1832
+ return typeof record.checkId === "string" && record.status === "failed";
1833
+ }).length;
1834
+ };
1835
+ /** Each recorded engine node's kind and action, by its trace span id (without instance). */
1836
+ const engineNodeShapes = (journal) => {
1837
+ const shapes = new Map();
1838
+ for (const record of journal) {
1839
+ if (record.kind !== "control.engine.event")
1840
+ continue;
1841
+ const envelope = asRecord(record.payload);
1842
+ const executionId = asString(envelope.executionId);
1843
+ const generation = asNumber(envelope.generation);
1844
+ const payload = asRecord(envelope.payload);
1845
+ if (executionId === undefined || generation === undefined)
1846
+ continue;
1847
+ const prefix = `engine-node:${encodeURIComponent(`${encodeURIComponent(executionId)}:${generation}`)}:`;
1848
+ if (envelope.eventType === "flows.engine.plan-recorded" || envelope.eventType === "flows.engine.subgraph-appended") {
1849
+ const nodes = asRecord(payload.graph).nodes;
1850
+ for (const node of Array.isArray(nodes) ? nodes : []) {
1851
+ const shape = asRecord(node);
1852
+ const id = asString(shape.id);
1853
+ if (id !== undefined) {
1854
+ shapes.set(prefix + encodeURIComponent(id), { kind: asString(shape.kind), action: asString(shape.action) });
1855
+ }
1856
+ }
1857
+ }
1858
+ else if (envelope.eventType === "flows.engine.node-scheduled") {
1859
+ const id = asString(payload.nodeId);
1860
+ if (id === undefined)
1861
+ continue;
1862
+ const known = shapes.get(prefix + encodeURIComponent(id));
1863
+ shapes.set(prefix + encodeURIComponent(id), {
1864
+ kind: asString(payload.kind) ?? known?.kind,
1865
+ action: asString(payload.action) ?? known?.action
1866
+ });
1867
+ }
1868
+ }
1869
+ return shapes;
1870
+ };
1871
+ /** Adapt the canonical trace to the install monitor. IDs and timestamps come
1872
+ * from recorded spans; inspection never evaluates flow bodies or presentations.
1873
+ * @since 1.0.0
1874
+ * @category constructors
1875
+ */
1876
+ export const monitorFromJournal = (run, records, at) => {
1877
+ const last = records.reduce((last, row) => Math.max(last, row.sequence ?? 0), 0);
1878
+ const journal = at === undefined ? records : records.filter((row) => (row.sequence ?? 0) <= at);
1879
+ // A historical frame must not inherit the current run's terminal status.
1880
+ const lifecycleRow = [...journal].reverse().find((row) => /^control\.run\.(running|waiting-approval|paused|suspended|held|completed|failed|cancelled|interrupted)$/.test(row.kind ?? "") &&
1881
+ (row.runId === undefined || row.runId === run.runId));
1882
+ const status = at === undefined ? run.status : lifecycleRow?.kind?.split(".").at(-1) ?? "running";
1883
+ const state = (value) => value === "completed" || value === "done" ? "done" : value === "failed" ?
1884
+ "failed"
1885
+ : value === "cancelled" || value === "interrupted" ?
1886
+ "interrupted" :
1887
+ value === "held" ?
1888
+ "held"
1889
+ : value === "waiting" || value === "waiting-approval" || value === "paused" || value === "suspended"
1890
+ ? "waiting"
1891
+ : "running";
1892
+ const trace = traceFromJournal({ ...run, status }, journal);
1893
+ const engineLabel = (label) => ACTION_RENDERINGS[label] === null || /^<(?:boundary:|quota-park>|structured-output-count>)/.test(label);
1894
+ const bookkeeping = (row) => engineLabel(row.label);
1895
+ const transcripts = stepTranscripts(journal);
1896
+ // Every call below is a member of trace.rows, so both lookups are total.
1897
+ const dispatchesOf = new Map(trace.rows.map((row) => [row.id, nodeDispatches(row)]));
1898
+ // A step is a node that dispatched an action. Plan structure (sequences,
1899
+ // branches, maps) and child-flow calls, whose own nodes are the steps, are
1900
+ // never steps of their own.
1901
+ const structural = (kind) => kind !== undefined && PLAN_STRUCTURE.has(kind);
1902
+ const nodeShapes = engineNodeShapes(journal);
1903
+ const calls = trace.rows.filter((row) => {
1904
+ // A tool call an agent made inside a step is a cell of that step's
1905
+ // transcript (step:<scope>/…), never a step of its own.
1906
+ if (row.kind !== "call" || bookkeeping(row) || row.id.startsWith("step:"))
1907
+ return false;
1908
+ const shape = nodeShapes.get(row.id.replace(/#\d+$/, ""));
1909
+ return shape === undefined || !structural(shape.kind);
1910
+ });
1911
+ const transcriptOf = (row) => {
1912
+ const parts = dispatchesOf.get(row.id).flatMap((dispatch) => {
1913
+ const found = transcripts.get(dispatch.slice(dispatch.lastIndexOf(":") + 1));
1914
+ return found === undefined ? [] : [found];
1915
+ });
1916
+ return {
1917
+ cells: parts.flatMap((part) => part.cells).sort((left, right) => Number(left.id.slice(5)) - Number(right.id.slice(5))),
1918
+ tokens: parts.reduce((total, part) => total + part.tokens, 0),
1919
+ models: parts.reduce((total, part) => total + part.models, 0),
1920
+ input: parts.find((part) => part.input !== undefined)?.input
1921
+ };
1922
+ };
1923
+ const steps = calls.map((row) => {
1924
+ const meter = dispatchesOf.get(row.id);
1925
+ const transcript = transcriptOf(row);
1926
+ return {
1927
+ ...(() => {
1928
+ const matched = /^(engine-node:.*)#(\d+)$/.exec(row.id);
1929
+ const id = matched?.[1] ?? row.id;
1930
+ const k = Number(matched?.[2] ?? 1);
1931
+ return { key: `${id}#${k}`, id, k };
1932
+ })(),
1933
+ label: row.label,
1934
+ state: row.status,
1935
+ started_at: new Date(row.startedAt).toISOString(),
1936
+ ...(row.endedAt === undefined
1937
+ ? {}
1938
+ : { ended_at: new Date(row.endedAt).toISOString(), took_s: Math.max(0, row.endedAt - row.startedAt) / 1000 }),
1939
+ ...(row.detail.input !== undefined
1940
+ ? { input: row.detail.input }
1941
+ : transcript.input !== undefined
1942
+ ? { input: transcript.input }
1943
+ : {}),
1944
+ ...(row.detail.output === undefined ? {} : { output: row.detail.output }),
1945
+ // The install prices these dispatches from its metered proxy rows and
1946
+ // replaces them with `usage`; the journal's own token count rides along.
1947
+ ...(meter.length === 0 ? {} : { meter }),
1948
+ ...(transcript.models === 0 ? {} : { model_calls: transcript.models, tokens: transcript.tokens })
1949
+ };
1950
+ });
1951
+ const tone = (value) => value === "failed" ?
1952
+ "fail"
1953
+ : value === "completed"
1954
+ ? "ok"
1955
+ : value === "waiting"
1956
+ ? "wait"
1957
+ : "live";
1958
+ const phases = calls.map((row, index) => {
1959
+ const transcript = transcriptOf(row).cells;
1960
+ const failedChecks = checkFailures(row.detail.output);
1961
+ const cells = transcript.length > 0
1962
+ ? transcript
1963
+ : [{
1964
+ id: `cell:${row.id}`,
1965
+ kind: /Edited/.test(row.label) ? "edit" : /Read/.test(row.label) ? "read" : "run",
1966
+ label: row.label,
1967
+ ...(row.detail.output === undefined ? {} : { output: row.detail.output }),
1968
+ tone: tone(row.status)
1969
+ }];
1970
+ return {
1971
+ id: `phase:${row.id}`,
1972
+ step: steps[index].key,
1973
+ // The title is the Appendix C label plus the outcome its recorded
1974
+ // output reports: "Ran checks · 1 failed".
1975
+ title: failedChecks === 0 ? row.label : `${row.label} · ${failedChecks} failed`,
1976
+ took_s: Math.max(0, (row.endedAt ?? row.startedAt) - row.startedAt) / 1000,
1977
+ tone: failedChecks === 0 ? tone(row.status) : "fail",
1978
+ cells
1979
+ };
1980
+ });
1981
+ const declaredGraph = engineExecutionEvidence(journal).filter((execution) => execution.coherent).flatMap((execution) => {
1982
+ const prefix = `engine-node:${encodeURIComponent(`${encodeURIComponent(execution.executionId)}:${execution.generation}`)}:`;
1983
+ const nodes = execution.graph?.nodes ?? [];
1984
+ const hidden = new Map(nodes.filter((node) => engineLabel(node.action ?? node.id) || structural(node.kind))
1985
+ .map((node) => [node.id, node]));
1986
+ // Engine bookkeeping stays in the Engine row. Its dependency edges still
1987
+ // order the visible steps, including across several hidden boundaries.
1988
+ const visibleDeps = (id, seen) => {
1989
+ const node = hidden.get(id);
1990
+ if (node === undefined)
1991
+ return [prefix + encodeURIComponent(id)];
1992
+ if (seen.has(id))
1993
+ return [];
1994
+ return node.dependsOn.flatMap((dependency) => visibleDeps(dependency, new Set([...seen, id])));
1995
+ };
1996
+ return nodes.filter((node) => !hidden.has(node.id)).map((node) => {
1997
+ const id = prefix + encodeURIComponent(node.id);
1998
+ const instances = calls.filter((row) => row.id.startsWith(`${id}#`));
1999
+ const current = instances.at(-1);
2000
+ return {
2001
+ id,
2002
+ label: inspectLabel(node.action ?? node.id),
2003
+ // A branch not taken never ran: it stays next, even in a finished run.
2004
+ state: current === undefined || current.status === "skipped" ?
2005
+ "next" :
2006
+ current.status === "completed" ?
2007
+ "done"
2008
+ : current.status === "failed"
2009
+ ? "failed"
2010
+ : current.status === "waiting"
2011
+ ? "waiting"
2012
+ : "current",
2013
+ deps: [...new Set(node.dependsOn.flatMap((dependency) => visibleDeps(dependency, new Set([node.id]))))]
2014
+ };
2015
+ });
2016
+ });
2017
+ const usageOf = (row) => (row.detail.usage?.inputTokens ?? 0) + (row.detail.usage?.outputTokens ?? 0);
2018
+ const tokens = trace.rows.reduce((total, row) => total + usageOf(row), 0);
2019
+ // Model calls no native dispatch recorded (a legacy agent journal) can
2020
+ // never be joined to metered rows; the install refuses to price them.
2021
+ const unmetered = trace.rows.filter((row) => row.kind === "model" && asRecord(row.detail.fields).step === undefined)
2022
+ .reduce((total, row) => total + usageOf(row), 0);
2023
+ const approvalWaits = trace.rows.filter((row) => row.kind === "approval").map((row) => ({
2024
+ id: row.id,
2025
+ kind: "approval",
2026
+ label: row.label,
2027
+ since: new Date(row.startedAt).toISOString(),
2028
+ ...(row.endedAt === undefined
2029
+ ? {}
2030
+ : {
2031
+ settled: { by: { kind: "system", color_index: 7 }, at: new Date(row.endedAt).toISOString() }
2032
+ })
2033
+ }));
2034
+ // The machine records its declared custom view as data. Only the shipped
2035
+ // text renderer is supported; module paths, HTML and executable data never
2036
+ // cross this seam. Replay selects the last declaration in its own prefix.
2037
+ const custom = [...journal].reverse().find(row => (row.runId === undefined || row.runId === run.runId) &&
2038
+ row.kind === "control.engine.event" && asRecord(row.payload).eventType === "flows.run.presentation");
2039
+ const presentation = asRecord(asRecord(custom?.payload).payload);
2040
+ const text = asString(presentation.text);
2041
+ const customView = presentation.kind === "text" && text !== undefined && text.length <= 65536 &&
2042
+ Object.keys(presentation).every(key => key === "kind" || key === "text")
2043
+ ? { presentation: { kind: "text", text } } : {};
2044
+ return {
2045
+ ...customView,
2046
+ id: run.runId,
2047
+ flow: run.flowId,
2048
+ version: "",
2049
+ title: run.flowId,
2050
+ state: state(status),
2051
+ attempts: [{
2052
+ n: 1,
2053
+ run_id: run.runId,
2054
+ state: state(status),
2055
+ steps,
2056
+ phases,
2057
+ graph: declaredGraph.length > 0 ?
2058
+ declaredGraph :
2059
+ calls.map((row) => ({
2060
+ id: row.id,
2061
+ label: row.label,
2062
+ state: row.status === "skipped" ? "next" : row.status === "completed" ?
2063
+ "done"
2064
+ : row.status === "failed"
2065
+ ? "failed"
2066
+ : row.status === "waiting"
2067
+ ? "waiting"
2068
+ : "current",
2069
+ deps: []
2070
+ }))
2071
+ }],
2072
+ waits: [
2073
+ ...approvalWaits,
2074
+ ...engineExecutionEvidence(journal).filter((execution) => execution.coherent).flatMap((execution) => execution.waits.map((wait) => ({
2075
+ id: wait.id,
2076
+ kind: wait.kind,
2077
+ label: inspectLabel("wait"),
2078
+ since: new Date(wait.since).toISOString(),
2079
+ ...(wait.settledAt === undefined
2080
+ ? {}
2081
+ : {
2082
+ settled: {
2083
+ by: { kind: "system", color_index: 7 },
2084
+ at: new Date(wait.settledAt).toISOString()
2085
+ }
2086
+ })
2087
+ })))
2088
+ ],
2089
+ tokens,
2090
+ // Spend is priced where model calls are metered. The host never publishes
2091
+ // an unknown total as zero: a run with model tokens leaves cost to the install.
2092
+ time_s: Math.max(0, trace.extent.end - trace.extent.start) / 1000,
2093
+ ...(tokens === 0 ? { cost_usd: 0 } : {}),
2094
+ ...(unmetered === 0 ? {} : { unmetered_tokens: unmetered }),
2095
+ engine: trace.rows.filter((row) => row.kind === "event" || row.kind === "execution" || row.kind === "attempt" || bookkeeping(row)).map((row) => ({ label: row.label, detail: row.detail.output ?? row.detail.message ?? "" })),
2096
+ journal: journal.map((row) => ({
2097
+ seq: row.sequence ?? 0,
2098
+ at: new Date(row.occurredAt ?? 0).toISOString(),
2099
+ type: row.kind ?? "event",
2100
+ text: JSON.stringify(row.payload) ?? "null"
2101
+ })),
2102
+ ...(at === undefined ? {} : { replay: { at: Math.min(at, last), last } })
2103
+ };
2104
+ };
2105
+ //# sourceMappingURL=RunTrace.js.map