niceeval 0.13.1 → 0.13.2

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 (548) hide show
  1. package/INDEX.md +25 -2
  2. package/README.md +9 -10
  3. package/README.zh.md +9 -10
  4. package/dist/agents/ai-sdk.cjs.map +1 -1
  5. package/dist/agents/ai-sdk.d.cts +1 -1
  6. package/dist/agents/ai-sdk.d.mts +1 -1
  7. package/dist/agents/ai-sdk.d.ts +1 -1
  8. package/dist/agents/bub-install-spec.cjs +19 -5
  9. package/dist/agents/bub-install-spec.cjs.map +1 -1
  10. package/dist/agents/bub-install-spec.d.cts +9 -2
  11. package/dist/agents/bub-install-spec.d.mts +9 -2
  12. package/dist/agents/bub-install-spec.d.ts +9 -2
  13. package/dist/agents/bub-install-spec.js +14 -12
  14. package/dist/agents/bub.cjs +10 -6
  15. package/dist/agents/bub.cjs.map +1 -1
  16. package/dist/agents/bub.d.cts +2 -2
  17. package/dist/agents/bub.d.mts +2 -2
  18. package/dist/agents/bub.d.ts +2 -2
  19. package/dist/agents/claude-code.cjs.map +1 -1
  20. package/dist/agents/claude-code.d.cts +2 -2
  21. package/dist/agents/claude-code.d.mts +2 -2
  22. package/dist/agents/claude-code.d.ts +2 -2
  23. package/dist/agents/codex.cjs +4 -4
  24. package/dist/agents/codex.cjs.map +1 -1
  25. package/dist/agents/codex.d.cts +3 -3
  26. package/dist/agents/codex.d.mts +3 -3
  27. package/dist/agents/codex.d.ts +3 -3
  28. package/dist/agents/coding-cli-versions.cjs +10 -1
  29. package/dist/agents/coding-cli-versions.cjs.map +1 -1
  30. package/dist/agents/coding-cli-versions.d.cts +8 -3
  31. package/dist/agents/coding-cli-versions.d.mts +8 -3
  32. package/dist/agents/coding-cli-versions.d.ts +8 -3
  33. package/dist/agents/coding-cli-versions.js +18 -12
  34. package/dist/agents/deepseek-harness.cjs +151 -0
  35. package/dist/agents/deepseek-harness.cjs.map +1 -0
  36. package/dist/agents/deepseek-harness.d.cts +14 -0
  37. package/dist/agents/deepseek-harness.d.mts +14 -0
  38. package/dist/agents/deepseek-harness.d.ts +14 -0
  39. package/dist/agents/deepseek-harness.js +7 -0
  40. package/dist/agents/deepseek-harness.js.map +1 -0
  41. package/dist/agents/exact-npm-plugins.cjs +28 -0
  42. package/dist/agents/exact-npm-plugins.cjs.map +1 -0
  43. package/dist/agents/exact-npm-plugins.d.cts +10 -0
  44. package/dist/agents/exact-npm-plugins.d.mts +10 -0
  45. package/dist/agents/exact-npm-plugins.d.ts +10 -0
  46. package/dist/agents/exact-npm-plugins.js +8 -0
  47. package/dist/agents/exact-npm-plugins.js.map +1 -0
  48. package/dist/agents/index.cjs +5 -1
  49. package/dist/agents/index.cjs.map +1 -1
  50. package/dist/agents/index.d.cts +4 -0
  51. package/dist/agents/index.d.mts +4 -0
  52. package/dist/agents/index.d.ts +4 -0
  53. package/dist/agents/index.js +54 -50
  54. package/dist/agents/index.mjs +54 -50
  55. package/dist/agents/omp.cjs +170 -0
  56. package/dist/agents/omp.cjs.map +1 -0
  57. package/dist/agents/omp.d.cts +14 -0
  58. package/dist/agents/omp.d.mts +14 -0
  59. package/dist/agents/omp.d.ts +14 -0
  60. package/dist/agents/omp.js +7 -0
  61. package/dist/agents/omp.js.map +1 -0
  62. package/dist/agents/openclaw.cjs +51 -2
  63. package/dist/agents/openclaw.cjs.map +1 -1
  64. package/dist/agents/openclaw.d.cts +2 -0
  65. package/dist/agents/openclaw.d.mts +2 -0
  66. package/dist/agents/openclaw.d.ts +2 -0
  67. package/dist/agents/types.cjs.map +1 -1
  68. package/dist/agents/types.d.cts +4 -4
  69. package/dist/agents/types.d.mts +4 -4
  70. package/dist/agents/types.d.ts +4 -4
  71. package/dist/agents/ui-message-stream.cjs +24 -7
  72. package/dist/agents/ui-message-stream.cjs.map +1 -1
  73. package/dist/analysis/api.cjs +28 -2
  74. package/dist/analysis/api.cjs.map +1 -1
  75. package/dist/analysis/api.d.cts +22 -1
  76. package/dist/analysis/api.d.mts +22 -1
  77. package/dist/analysis/api.d.ts +22 -1
  78. package/dist/analysis/api.js +8 -6
  79. package/dist/analysis/bindings.cjs +11 -18
  80. package/dist/analysis/bindings.cjs.map +1 -1
  81. package/dist/analysis/bindings.d.cts +177 -49
  82. package/dist/analysis/bindings.d.mts +177 -49
  83. package/dist/analysis/bindings.d.ts +177 -49
  84. package/dist/analysis/contracts.cjs +1 -0
  85. package/dist/analysis/contracts.cjs.map +1 -1
  86. package/dist/analysis/contracts.d.cts +43 -3
  87. package/dist/analysis/contracts.d.mts +43 -3
  88. package/dist/analysis/contracts.d.ts +43 -3
  89. package/dist/analysis/cost-projection.cjs +2 -1
  90. package/dist/analysis/cost-projection.cjs.map +1 -1
  91. package/dist/analysis/cost-projection.d.cts +1 -1
  92. package/dist/analysis/cost-projection.d.mts +1 -1
  93. package/dist/analysis/cost-projection.d.ts +1 -1
  94. package/dist/analysis/cost.cjs +13 -3
  95. package/dist/analysis/cost.cjs.map +1 -1
  96. package/dist/analysis/cost.d.cts +10 -3
  97. package/dist/analysis/cost.d.mts +10 -3
  98. package/dist/analysis/cost.d.ts +10 -3
  99. package/dist/analysis/experiment-groups.cjs +96 -0
  100. package/dist/analysis/experiment-groups.cjs.map +1 -0
  101. package/dist/analysis/experiment-groups.d.cts +9 -0
  102. package/dist/analysis/experiment-groups.d.mts +9 -0
  103. package/dist/analysis/experiment-groups.d.ts +9 -0
  104. package/dist/analysis/experiment-groups.js +10 -0
  105. package/dist/analysis/experiment-groups.js.map +1 -0
  106. package/dist/analysis/index.cjs +4 -1
  107. package/dist/analysis/index.cjs.map +1 -1
  108. package/dist/analysis/index.d.cts +4 -2
  109. package/dist/analysis/index.d.mts +4 -2
  110. package/dist/analysis/index.d.ts +4 -2
  111. package/dist/analysis/index.js +40 -36
  112. package/dist/analysis/index.mjs +40 -36
  113. package/dist/assertions/limits.cjs +4 -0
  114. package/dist/assertions/limits.cjs.map +1 -1
  115. package/dist/assertions/limits.d.cts +4 -0
  116. package/dist/assertions/limits.d.mts +4 -0
  117. package/dist/assertions/limits.d.ts +4 -0
  118. package/dist/assertions/match.cjs +63 -25
  119. package/dist/assertions/match.cjs.map +1 -1
  120. package/dist/assertions/match.d.cts +7 -0
  121. package/dist/assertions/match.d.mts +7 -0
  122. package/dist/assertions/match.d.ts +7 -0
  123. package/dist/assertions/record/attachment.cjs +32 -4
  124. package/dist/assertions/record/attachment.cjs.map +1 -1
  125. package/dist/assertions/record/diff.cjs +10 -4
  126. package/dist/assertions/record/diff.cjs.map +1 -1
  127. package/dist/assertions/record/producer.cjs +101 -4
  128. package/dist/assertions/record/producer.cjs.map +1 -1
  129. package/dist/assertions/record/producer.d.cts +4 -1
  130. package/dist/assertions/record/producer.d.mts +4 -1
  131. package/dist/assertions/record/producer.d.ts +4 -1
  132. package/dist/assertions/runtime.cjs +45 -5
  133. package/dist/assertions/runtime.cjs.map +1 -1
  134. package/dist/cli/record.cjs +113 -8
  135. package/dist/cli/record.cjs.map +1 -1
  136. package/dist/cli/record.d.cts +20 -1
  137. package/dist/cli/record.d.mts +20 -1
  138. package/dist/cli/record.d.ts +20 -1
  139. package/dist/cli/record.js +4 -2
  140. package/dist/cli.cjs +386 -31
  141. package/dist/cli.cjs.map +1 -1
  142. package/dist/cli.d.cts +10 -2
  143. package/dist/cli.d.mts +10 -2
  144. package/dist/cli.d.ts +10 -2
  145. package/dist/context/send-retry.cjs +4 -4
  146. package/dist/context/send-retry.cjs.map +1 -1
  147. package/dist/coordination/host/runtime.cjs +8 -30
  148. package/dist/coordination/host/runtime.cjs.map +1 -1
  149. package/dist/coordination/host/runtime.d.cts +3 -3
  150. package/dist/coordination/host/runtime.d.mts +3 -3
  151. package/dist/coordination/host/runtime.d.ts +3 -3
  152. package/dist/coordination/host/types.cjs.map +1 -1
  153. package/dist/coordination/host/types.d.cts +0 -5
  154. package/dist/coordination/host/types.d.mts +0 -5
  155. package/dist/coordination/host/types.d.ts +0 -5
  156. package/dist/coordination/platform/node.cjs +90 -9
  157. package/dist/coordination/platform/node.cjs.map +1 -1
  158. package/dist/define.cjs +25 -1
  159. package/dist/define.cjs.map +1 -1
  160. package/dist/experiment/host/index.cjs +1 -0
  161. package/dist/experiment/host/index.cjs.map +1 -1
  162. package/dist/i18n/en.cjs +26 -8
  163. package/dist/i18n/en.cjs.map +1 -1
  164. package/dist/i18n/en.d.cts +19 -6
  165. package/dist/i18n/en.d.mts +19 -6
  166. package/dist/i18n/en.d.ts +19 -6
  167. package/dist/index.cjs.map +1 -1
  168. package/dist/index.d.cts +1 -1
  169. package/dist/index.d.mts +1 -1
  170. package/dist/index.d.ts +1 -1
  171. package/dist/o11y/parsers/hermes.cjs +1 -1
  172. package/dist/o11y/parsers/hermes.cjs.map +1 -1
  173. package/dist/o11y/prices.json +1 -1
  174. package/dist/o11y/record/families.cjs +12 -3
  175. package/dist/o11y/record/families.cjs.map +1 -1
  176. package/dist/o11y/record/families.d.cts +8 -8
  177. package/dist/o11y/record/families.d.mts +8 -8
  178. package/dist/o11y/record/families.d.ts +8 -8
  179. package/dist/o11y/record/family-projectors.d.cts +66 -18
  180. package/dist/o11y/record/family-projectors.d.mts +66 -18
  181. package/dist/o11y/record/family-projectors.d.ts +66 -18
  182. package/dist/o11y/record/family-writers.d.cts +11 -3
  183. package/dist/o11y/record/family-writers.d.mts +11 -3
  184. package/dist/o11y/record/family-writers.d.ts +11 -3
  185. package/dist/o11y/record/runner-producer.cjs +14 -4
  186. package/dist/o11y/record/runner-producer.cjs.map +1 -1
  187. package/dist/record/codec/core.d.cts +2 -2
  188. package/dist/record/codec/core.d.mts +2 -2
  189. package/dist/record/codec/core.d.ts +2 -2
  190. package/dist/record/family/catalog.d.cts +44 -12
  191. package/dist/record/family/catalog.d.mts +44 -12
  192. package/dist/record/family/catalog.d.ts +44 -12
  193. package/dist/record/family/index.js +128 -124
  194. package/dist/record/family/observability-v1.cjs +35 -0
  195. package/dist/record/family/observability-v1.cjs.map +1 -0
  196. package/dist/record/family/observability-v1.d.cts +3 -0
  197. package/dist/record/family/observability-v1.d.mts +3 -0
  198. package/dist/record/family/observability-v1.d.ts +3 -0
  199. package/dist/record/family/observability-v1.js +6 -0
  200. package/dist/record/family/observability-v1.js.map +1 -0
  201. package/dist/record/family/observability.cjs +58 -9
  202. package/dist/record/family/observability.cjs.map +1 -1
  203. package/dist/record/family/observability.d.cts +1014 -15
  204. package/dist/record/family/observability.d.mts +1014 -15
  205. package/dist/record/family/observability.d.ts +1014 -15
  206. package/dist/record/family/observability.js +22 -18
  207. package/dist/record/host/runtime.cjs +600 -31
  208. package/dist/record/host/runtime.cjs.map +1 -1
  209. package/dist/record/host/types.cjs.map +1 -1
  210. package/dist/record/host/types.d.cts +33 -9
  211. package/dist/record/host/types.d.mts +33 -9
  212. package/dist/record/host/types.d.ts +33 -9
  213. package/dist/record/maintenance/runtime.cjs +1 -2
  214. package/dist/record/maintenance/runtime.cjs.map +1 -1
  215. package/dist/record/model/core.cjs.map +1 -1
  216. package/dist/record/model/core.d.cts +1 -1
  217. package/dist/record/model/core.d.mts +1 -1
  218. package/dist/record/model/core.d.ts +1 -1
  219. package/dist/record/model/definition.d.cts +6 -6
  220. package/dist/record/model/definition.d.mts +6 -6
  221. package/dist/record/model/definition.d.ts +6 -6
  222. package/dist/record/model/identifiers.cjs +1 -1
  223. package/dist/record/model/identifiers.cjs.map +1 -1
  224. package/dist/record/model/identifiers.d.cts +1 -1
  225. package/dist/record/model/identifiers.d.mts +1 -1
  226. package/dist/record/model/identifiers.d.ts +1 -1
  227. package/dist/record/platform/node.cjs +159 -23
  228. package/dist/record/platform/node.cjs.map +1 -1
  229. package/dist/record/platform/services.cjs.map +1 -1
  230. package/dist/record/platform/services.d.cts +15 -2
  231. package/dist/record/platform/services.d.mts +15 -2
  232. package/dist/record/platform/services.d.ts +15 -2
  233. package/dist/record/reader/errors.cjs +37 -1
  234. package/dist/record/reader/errors.cjs.map +1 -1
  235. package/dist/record/reader/errors.d.cts +49 -1
  236. package/dist/record/reader/errors.d.mts +49 -1
  237. package/dist/record/reader/errors.d.ts +49 -1
  238. package/dist/record/reader/errors.js +22 -12
  239. package/dist/record/reader/format.cjs +76 -17
  240. package/dist/record/reader/format.cjs.map +1 -1
  241. package/dist/record/reader/format.d.cts +11 -4
  242. package/dist/record/reader/format.d.mts +11 -4
  243. package/dist/record/reader/format.d.ts +11 -4
  244. package/dist/record/reader/format.js +2 -0
  245. package/dist/record/reader/index.js +32 -18
  246. package/dist/record/reader/runtime.cjs +64 -0
  247. package/dist/record/reader/runtime.cjs.map +1 -1
  248. package/dist/record/reader/runtime.d.cts +15 -0
  249. package/dist/record/reader/runtime.d.mts +15 -0
  250. package/dist/record/reader/runtime.d.ts +15 -0
  251. package/dist/record/reader/runtime.js +2 -0
  252. package/dist/report/assets/styles.css +53 -29
  253. package/dist/report/built-in/analysis-values.cjs +21 -13
  254. package/dist/report/built-in/analysis-values.cjs.map +1 -1
  255. package/dist/report/built-in/analysis-values.d.cts +11 -22
  256. package/dist/report/built-in/analysis-values.d.mts +11 -22
  257. package/dist/report/built-in/analysis-values.d.ts +11 -22
  258. package/dist/report/built-in/analysis-values.js +4 -6
  259. package/dist/report/built-in/index.cjs +1 -3
  260. package/dist/report/built-in/index.cjs.map +1 -1
  261. package/dist/report/built-in/index.d.cts +1 -1
  262. package/dist/report/built-in/index.d.mts +1 -1
  263. package/dist/report/built-in/index.d.ts +1 -1
  264. package/dist/report/built-in/index.js +4 -8
  265. package/dist/report/built-in/index.mjs +4 -8
  266. package/dist/report/built-in/machine.cjs +0 -2
  267. package/dist/report/built-in/machine.cjs.map +1 -1
  268. package/dist/report/built-in/machine.d.cts +0 -2
  269. package/dist/report/built-in/machine.d.mts +0 -2
  270. package/dist/report/built-in/machine.d.ts +0 -2
  271. package/dist/report/built-in/result-components.cjs +49 -51
  272. package/dist/report/built-in/result-components.cjs.map +1 -1
  273. package/dist/report/built-in/result-components.d.cts +16 -17
  274. package/dist/report/built-in/result-components.d.mts +16 -17
  275. package/dist/report/built-in/result-components.d.ts +16 -17
  276. package/dist/report/built-in/result-components.js +0 -4
  277. package/dist/report/built-in/run-membership-overview.cjs +126 -17
  278. package/dist/report/built-in/run-membership-overview.cjs.map +1 -1
  279. package/dist/report/built-in/run-membership-overview.d.cts +14 -0
  280. package/dist/report/built-in/run-membership-overview.d.mts +14 -0
  281. package/dist/report/built-in/run-membership-overview.d.ts +14 -0
  282. package/dist/report/built-in/run-membership-overview.js +4 -2
  283. package/dist/report/built-in/standard.cjs +80 -45
  284. package/dist/report/built-in/standard.cjs.map +1 -1
  285. package/dist/report/built-in/standard.d.cts +19 -41
  286. package/dist/report/built-in/standard.d.mts +19 -41
  287. package/dist/report/built-in/standard.d.ts +19 -41
  288. package/dist/report/built-in/standard.js +6 -6
  289. package/dist/report/components/attempt-detail/compute.cjs +78 -2
  290. package/dist/report/components/attempt-detail/compute.cjs.map +1 -1
  291. package/dist/report/components/attempt-detail/compute.d.cts +1 -1
  292. package/dist/report/components/attempt-detail/compute.d.mts +1 -1
  293. package/dist/report/components/attempt-detail/compute.d.ts +1 -1
  294. package/dist/report/components/attempt-detail/content.cjs +1 -1
  295. package/dist/report/components/attempt-detail/content.cjs.map +1 -1
  296. package/dist/report/components/entity-lists/compute.cjs +42 -5
  297. package/dist/report/components/entity-lists/compute.cjs.map +1 -1
  298. package/dist/report/components/entity-lists/compute.d.cts +10 -8
  299. package/dist/report/components/entity-lists/compute.d.mts +10 -8
  300. package/dist/report/components/entity-lists/compute.d.ts +10 -8
  301. package/dist/report/components/entity-lists/content.cjs +31 -6
  302. package/dist/report/components/entity-lists/content.cjs.map +1 -1
  303. package/dist/report/components/entity-lists/index.cjs +9 -4
  304. package/dist/report/components/entity-lists/index.cjs.map +1 -1
  305. package/dist/report/components/entity-lists/index.d.cts +4 -7
  306. package/dist/report/components/entity-lists/index.d.mts +4 -7
  307. package/dist/report/components/entity-lists/index.d.ts +4 -7
  308. package/dist/report/components/summaries/compute.cjs +8 -1
  309. package/dist/report/components/summaries/compute.cjs.map +1 -1
  310. package/dist/report/components/summaries/compute.d.cts +9 -1
  311. package/dist/report/components/summaries/compute.d.mts +9 -1
  312. package/dist/report/components/summaries/compute.d.ts +9 -1
  313. package/dist/report/components/summaries/index.cjs +48 -4
  314. package/dist/report/components/summaries/index.cjs.map +1 -1
  315. package/dist/report/components/summaries/index.d.cts +7 -4
  316. package/dist/report/components/summaries/index.d.mts +7 -4
  317. package/dist/report/components/summaries/index.d.ts +7 -4
  318. package/dist/report/definition/cell.cjs +3 -0
  319. package/dist/report/definition/cell.cjs.map +1 -1
  320. package/dist/report/definition/cell.d.cts +2 -0
  321. package/dist/report/definition/cell.d.mts +2 -0
  322. package/dist/report/definition/cell.d.ts +2 -0
  323. package/dist/report/definition/primitives/chart.cjs +8 -5
  324. package/dist/report/definition/primitives/chart.cjs.map +1 -1
  325. package/dist/report/definition/primitives/points-dataset.cjs +1 -0
  326. package/dist/report/definition/primitives/points-dataset.cjs.map +1 -1
  327. package/dist/report/definition/primitives.cjs +15 -2
  328. package/dist/report/definition/primitives.cjs.map +1 -1
  329. package/dist/report/definition/report.cjs +27 -3
  330. package/dist/report/definition/report.cjs.map +1 -1
  331. package/dist/report/definition/report.d.cts +8 -0
  332. package/dist/report/definition/report.d.mts +8 -0
  333. package/dist/report/definition/report.d.ts +8 -0
  334. package/dist/report/execution/machine.cjs +30 -14
  335. package/dist/report/execution/machine.cjs.map +1 -1
  336. package/dist/report/execution/machine.d.cts +20 -10
  337. package/dist/report/execution/machine.d.mts +20 -10
  338. package/dist/report/execution/machine.d.ts +20 -10
  339. package/dist/report/execution/machine.js +6 -6
  340. package/dist/report/host/execute.cjs +25 -16
  341. package/dist/report/host/execute.cjs.map +1 -1
  342. package/dist/report/host/execute.d.cts +4 -0
  343. package/dist/report/host/execute.d.mts +4 -0
  344. package/dist/report/host/execute.d.ts +4 -0
  345. package/dist/report/host/from-record.cjs +22 -5
  346. package/dist/report/host/from-record.cjs.map +1 -1
  347. package/dist/report/host/machine.cjs +53 -11
  348. package/dist/report/host/machine.cjs.map +1 -1
  349. package/dist/report/host/site-runtime.cjs +9 -8
  350. package/dist/report/host/site-runtime.cjs.map +1 -1
  351. package/dist/report/host/site-runtime.d.cts +1 -1
  352. package/dist/report/host/site-runtime.d.mts +1 -1
  353. package/dist/report/host/site-runtime.d.ts +1 -1
  354. package/dist/report/host/static.cjs +56 -9
  355. package/dist/report/host/static.cjs.map +1 -1
  356. package/dist/report/index.cjs.map +1 -1
  357. package/dist/report/index.d.cts +1 -1
  358. package/dist/report/index.d.mts +1 -1
  359. package/dist/report/index.d.ts +1 -1
  360. package/dist/report/model/dataset.cjs +7 -2
  361. package/dist/report/model/dataset.cjs.map +1 -1
  362. package/dist/report/model/locale.cjs +2 -0
  363. package/dist/report/model/locale.cjs.map +1 -1
  364. package/dist/report/model/locale.d.cts +1 -0
  365. package/dist/report/model/locale.d.mts +1 -0
  366. package/dist/report/model/locale.d.ts +1 -0
  367. package/dist/report/model/metrics.cjs +3 -1
  368. package/dist/report/model/metrics.cjs.map +1 -1
  369. package/dist/report/model/pricing.cjs +16 -11
  370. package/dist/report/model/pricing.cjs.map +1 -1
  371. package/dist/report/react/styles.css +53 -29
  372. package/dist/runner/adoption.cjs +1 -0
  373. package/dist/runner/adoption.cjs.map +1 -1
  374. package/dist/runner/attempt.cjs +44 -5
  375. package/dist/runner/attempt.cjs.map +1 -1
  376. package/dist/runner/attempt.d.cts +13 -1
  377. package/dist/runner/attempt.d.mts +13 -1
  378. package/dist/runner/attempt.d.ts +13 -1
  379. package/dist/runner/config-identity.cjs +10 -2
  380. package/dist/runner/config-identity.cjs.map +1 -1
  381. package/dist/runner/config-identity.d.cts +4 -2
  382. package/dist/runner/config-identity.d.mts +4 -2
  383. package/dist/runner/config-identity.d.ts +4 -2
  384. package/dist/runner/feedback/human.cjs +298 -59
  385. package/dist/runner/feedback/human.cjs.map +1 -1
  386. package/dist/runner/feedback/json.cjs +14 -6
  387. package/dist/runner/feedback/json.cjs.map +1 -1
  388. package/dist/runner/feedback/json.d.cts +3 -2
  389. package/dist/runner/feedback/json.d.mts +3 -2
  390. package/dist/runner/feedback/json.d.ts +3 -2
  391. package/dist/runner/feedback/reducer.cjs +2 -0
  392. package/dist/runner/feedback/reducer.cjs.map +1 -1
  393. package/dist/runner/judge-config.cjs +1 -1
  394. package/dist/runner/judge-config.cjs.map +1 -1
  395. package/dist/runner/ledger.cjs +124 -12
  396. package/dist/runner/ledger.cjs.map +1 -1
  397. package/dist/runner/project-current.cjs +13 -5
  398. package/dist/runner/project-current.cjs.map +1 -1
  399. package/dist/runner/run.cjs +564 -256
  400. package/dist/runner/run.cjs.map +1 -1
  401. package/dist/runner/sandbox-pool.cjs +161 -30
  402. package/dist/runner/sandbox-pool.cjs.map +1 -1
  403. package/dist/runner/sandbox-pool.d.cts +34 -2
  404. package/dist/runner/sandbox-pool.d.mts +34 -2
  405. package/dist/runner/sandbox-pool.d.ts +34 -2
  406. package/dist/runner/shared-state-lease.cjs +803 -0
  407. package/dist/runner/shared-state-lease.cjs.map +1 -0
  408. package/dist/runner/shared-state-lease.d.cts +121 -0
  409. package/dist/runner/shared-state-lease.d.mts +121 -0
  410. package/dist/runner/shared-state-lease.d.ts +121 -0
  411. package/dist/runner/shared-state-lease.js +24 -0
  412. package/dist/runner/shared-state-lease.js.map +1 -0
  413. package/dist/runner/teardown-registry.cjs +97 -34
  414. package/dist/runner/teardown-registry.cjs.map +1 -1
  415. package/dist/runner/teardown-registry.d.cts +27 -1
  416. package/dist/runner/teardown-registry.d.mts +27 -1
  417. package/dist/runner/teardown-registry.d.ts +27 -1
  418. package/dist/runner/teardown-registry.js +20 -16
  419. package/dist/runner/types.cjs.map +1 -1
  420. package/dist/runner/types.d.cts +31 -5
  421. package/dist/runner/types.d.mts +31 -5
  422. package/dist/runner/types.d.ts +31 -5
  423. package/dist/sample/analysis.cjs +249 -459
  424. package/dist/sample/analysis.cjs.map +1 -1
  425. package/dist/sample/capability.cjs +138 -7
  426. package/dist/sample/capability.cjs.map +1 -1
  427. package/dist/sample/capability.d.cts +3 -1
  428. package/dist/sample/capability.d.mts +3 -1
  429. package/dist/sample/capability.d.ts +3 -1
  430. package/dist/sample/capability.js +8 -6
  431. package/dist/sandbox/docker-agent-image.cjs +5 -1
  432. package/dist/sandbox/docker-agent-image.cjs.map +1 -1
  433. package/dist/sandbox/docker-agent-image.d.cts +2 -0
  434. package/dist/sandbox/docker-agent-image.d.mts +2 -0
  435. package/dist/sandbox/docker-agent-image.d.ts +2 -0
  436. package/dist/sandbox/docker-agent-image.js +14 -10
  437. package/dist/sandbox/docker-profile/schema.cjs +283 -534
  438. package/dist/sandbox/docker-profile/schema.cjs.map +1 -1
  439. package/dist/sandbox/docker-profile/schema.d.cts +10 -27
  440. package/dist/sandbox/docker-profile/schema.d.mts +10 -27
  441. package/dist/sandbox/docker-profile/schema.d.ts +10 -27
  442. package/dist/sandbox/e2b-agent-template.cjs +5 -2
  443. package/dist/sandbox/e2b-agent-template.cjs.map +1 -1
  444. package/dist/sandbox/index.cjs +4 -2
  445. package/dist/sandbox/index.cjs.map +1 -1
  446. package/dist/sandbox/index.d.cts +1 -1
  447. package/dist/sandbox/index.d.mts +1 -1
  448. package/dist/sandbox/index.d.ts +1 -1
  449. package/dist/sandbox/index.js +120 -116
  450. package/dist/sandbox/index.mjs +120 -116
  451. package/dist/sandbox/keep-registry.cjs +28 -66
  452. package/dist/sandbox/keep-registry.cjs.map +1 -1
  453. package/dist/sandbox/types.cjs.map +1 -1
  454. package/dist/sandbox/types.d.cts +1 -1
  455. package/dist/sandbox/types.d.mts +1 -1
  456. package/dist/sandbox/types.d.ts +1 -1
  457. package/dist/shared/aggregate.cjs +16 -6
  458. package/dist/shared/aggregate.cjs.map +1 -1
  459. package/dist/shared/aggregate.d.cts +12 -4
  460. package/dist/shared/aggregate.d.mts +12 -4
  461. package/dist/shared/aggregate.d.ts +12 -4
  462. package/dist/shared/turn-label.cjs +20 -2
  463. package/dist/shared/turn-label.cjs.map +1 -1
  464. package/dist/shared/turn-label.d.cts +2 -0
  465. package/dist/shared/turn-label.d.mts +2 -0
  466. package/dist/shared/turn-label.d.ts +2 -0
  467. package/dist/shared/turn-label.js +4 -2
  468. package/dist/vendor/remark/remark-gfm.cjs +7 -7
  469. package/dist/vendor/remark/remark-gfm.cjs.map +1 -1
  470. package/dist/vendor/remark/remark-parse.cjs +12 -12
  471. package/dist/vendor/remark/remark-parse.cjs.map +1 -1
  472. package/dist/vendor/remark/remark.cjs +13 -13
  473. package/dist/vendor/remark/remark.cjs.map +1 -1
  474. package/dist/view/server.cjs +6 -0
  475. package/dist/view/server.cjs.map +1 -1
  476. package/docs-site/images/hitl-handshake-zh.svg +3 -3
  477. package/docs-site/zh/examples/ai-agent-application.mdx +1 -1
  478. package/docs-site/zh/examples/coding-agent-extensions.mdx +1 -1
  479. package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +4 -3
  480. package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
  481. package/docs-site/zh/examples/integrations/codex-sdk.mdx +5 -5
  482. package/docs-site/zh/examples/integrations/langgraph.mdx +4 -4
  483. package/docs-site/zh/examples/integrations/pi-sdk.mdx +5 -5
  484. package/docs-site/zh/explanation/adapter.mdx +5 -5
  485. package/docs-site/zh/explanation/assert.mdx +7 -7
  486. package/docs-site/zh/explanation/drive.mdx +4 -2
  487. package/docs-site/zh/explanation/evals.mdx +10 -8
  488. package/docs-site/zh/explanation/experiment.mdx +2 -2
  489. package/docs-site/zh/explanation/hitl.mdx +9 -9
  490. package/docs-site/zh/explanation/judge.mdx +5 -5
  491. package/docs-site/zh/explanation/overview.mdx +3 -3
  492. package/docs-site/zh/explanation/runner.mdx +1 -1
  493. package/docs-site/zh/index.mdx +3 -3
  494. package/docs-site/zh/reference/builtin-agents.mdx +13 -13
  495. package/docs-site/zh/reference/capabilities.mdx +1 -1
  496. package/docs-site/zh/reference/cli.mdx +18 -12
  497. package/docs-site/zh/reference/define-agent.mdx +130 -5
  498. package/docs-site/zh/reference/define-config.mdx +2 -2
  499. package/docs-site/zh/reference/define-eval.mdx +4 -4
  500. package/docs-site/zh/reference/events.mdx +11 -11
  501. package/docs-site/zh/reference/official-adapters.mdx +6 -6
  502. package/docs-site/zh/reference/results-data.mdx +6 -0
  503. package/docs-site/zh/troubleshooting/debugging.mdx +1 -1
  504. package/docs-site/zh/troubleshooting/recover-after-kill.mdx +37 -5
  505. package/docs-site/zh/tutorials/accept-results.mdx +72 -0
  506. package/docs-site/zh/tutorials/agent-onboarding.mdx +6 -6
  507. package/docs-site/zh/tutorials/authoring.mdx +9 -13
  508. package/docs-site/zh/tutorials/ci-integration.mdx +6 -6
  509. package/docs-site/zh/tutorials/compare-runs.mdx +61 -0
  510. package/docs-site/zh/tutorials/concurrency.mdx +13 -12
  511. package/docs-site/zh/tutorials/configuration.mdx +4 -6
  512. package/docs-site/zh/tutorials/connect-your-agent.mdx +1 -1
  513. package/docs-site/zh/tutorials/control-run-cost.mdx +58 -0
  514. package/docs-site/zh/tutorials/criteria-files.mdx +2 -2
  515. package/docs-site/zh/tutorials/custom-reports.mdx +2 -2
  516. package/docs-site/zh/tutorials/debug-lifecycle-plan.mdx +46 -0
  517. package/docs-site/zh/tutorials/docker-in-docker.mdx +52 -43
  518. package/docs-site/zh/tutorials/eval-groups.mdx +6 -6
  519. package/docs-site/zh/tutorials/evaluation-kinds.mdx +3 -3
  520. package/docs-site/zh/tutorials/experiments.mdx +2 -3
  521. package/docs-site/zh/tutorials/handle-execution-failures.mdx +18 -10
  522. package/docs-site/zh/tutorials/install-custom-sandbox-agent.mdx +16 -8
  523. package/docs-site/zh/tutorials/nixos-managed-dind.mdx +160 -0
  524. package/docs-site/zh/tutorials/plugins.mdx +14 -0
  525. package/docs-site/zh/tutorials/rerun-and-cache.mdx +4 -2
  526. package/docs-site/zh/tutorials/sandbox-agent.mdx +17 -11
  527. package/docs-site/zh/tutorials/sandbox-providers.mdx +17 -13
  528. package/docs-site/zh/tutorials/sandbox-reuse.mdx +12 -19
  529. package/docs-site/zh/tutorials/select-and-preview.mdx +65 -0
  530. package/docs-site/zh/tutorials/verify-judge.mdx +78 -0
  531. package/docs-site/zh/tutorials/viewing-results.mdx +38 -1
  532. package/docs-site/zh/tutorials/write-experiment.mdx +7 -5
  533. package/docs-site/zh/tutorials/write-send.mdx +9 -8
  534. package/package.json +9 -5
  535. package/dist/report/built-in/overview.cjs +0 -44
  536. package/dist/report/built-in/overview.cjs.map +0 -1
  537. package/dist/report/built-in/overview.d.cts +0 -6
  538. package/dist/report/built-in/overview.d.mts +0 -6
  539. package/dist/report/built-in/overview.d.ts +0 -6
  540. package/dist/report/built-in/overview.js +0 -9
  541. package/dist/report/built-in/overview.js.map +0 -1
  542. package/dist/runner/gate-lease.cjs +0 -283
  543. package/dist/runner/gate-lease.cjs.map +0 -1
  544. package/dist/runner/gate-lease.d.cts +0 -62
  545. package/dist/runner/gate-lease.d.mts +0 -62
  546. package/dist/runner/gate-lease.d.ts +0 -62
  547. package/dist/runner/gate-lease.js +0 -22
  548. package/dist/runner/gate-lease.js.map +0 -1
@@ -0,0 +1,803 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SharedStateLeaseError = exports.SHARED_STATE_LEASE_HEARTBEAT_INTERVAL_MS = void 0;
4
+ exports.sharedStateLeasesDirOf = sharedStateLeasesDirOf;
5
+ exports.readSharedStateLeaseEffect = readSharedStateLeaseEffect;
6
+ exports.readSharedStateLeaseRecoveryTargetEffect = readSharedStateLeaseRecoveryTargetEffect;
7
+ exports.processIdentityForPidEffect = processIdentityForPidEffect;
8
+ exports.currentProcessIdentityEffect = currentProcessIdentityEffect;
9
+ exports.acquireSharedStateLeaseEffect = acquireSharedStateLeaseEffect;
10
+ exports.beginExplicitSharedStateRecoveryEffect = beginExplicitSharedStateRecoveryEffect;
11
+ exports.completeExplicitSharedStateRecoveryEffect = completeExplicitSharedStateRecoveryEffect;
12
+ // Shared-state leases protect an author's external mutable state. They are
13
+ // deliberately *not* case locks: a lease never expires and is never taken
14
+ // over by a waiter. A crashed or failed cleanup path stays visible until an
15
+ // operator performs the explicit, owner-token-checked recovery flow.
16
+ //
17
+ // The durable authority is an append-only generation chain. A transition may
18
+ // only publish generation N + 1 after observing exact generation N, and uses
19
+ // a same-directory hard-link to occupy that fixed N + 1 name. Consequently a
20
+ // stale actor cannot delete, replace, or otherwise reach a newer generation.
21
+ const node_child_process_1 = require("node:child_process");
22
+ const node_crypto_1 = require("node:crypto");
23
+ const promises_1 = require("node:fs/promises");
24
+ const node_path_1 = require("node:path");
25
+ const effect_1 = require("effect");
26
+ const entry_file_store_ts_1 = require("../shared/entry-file-store.cjs");
27
+ const SHARED_STATE_LEASE_NAMESPACE = "niceeval/shared-state/v3";
28
+ const SHARED_STATE_GENERATION_FORMAT = "niceeval.shared-state-generation/v1";
29
+ const SHARED_STATE_HEARTBEAT_FORMAT = "niceeval.shared-state-heartbeat/v1";
30
+ const GENERATION_FILE_PREFIX = "generation-";
31
+ const GENERATION_FILE_SUFFIX = ".json";
32
+ const GENERATION_WIDTH = 20;
33
+ exports.SHARED_STATE_LEASE_HEARTBEAT_INTERVAL_MS = 10000;
34
+ class SharedStateLeaseError extends Error {
35
+ code;
36
+ constructor(code, message) {
37
+ super(message);
38
+ this.code = code;
39
+ this.name = "SharedStateLeaseError";
40
+ }
41
+ }
42
+ exports.SharedStateLeaseError = SharedStateLeaseError;
43
+ function errnoCode(cause) {
44
+ return typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string"
45
+ ? cause.code
46
+ : undefined;
47
+ }
48
+ function isPositiveInteger(value) {
49
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
50
+ }
51
+ function isNonNegativeInteger(value) {
52
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
53
+ }
54
+ function isTimestamp(value) {
55
+ return typeof value === "string" && Number.isFinite(Date.parse(value));
56
+ }
57
+ function isNonEmptyString(value) {
58
+ return typeof value === "string" && value.length > 0;
59
+ }
60
+ function recordOf(value) {
61
+ return typeof value === "object" && value !== null && !Array.isArray(value)
62
+ ? value
63
+ : undefined;
64
+ }
65
+ function nodeIo(operation) {
66
+ return effect_1.Effect.tryPromise({ try: operation, catch: (cause) => cause });
67
+ }
68
+ function nodeSync(operation) {
69
+ return effect_1.Effect.try({ try: operation, catch: (cause) => cause });
70
+ }
71
+ function withOpenFile(path, flags, use) {
72
+ return effect_1.Effect.scoped(effect_1.Effect.acquireRelease(nodeIo(() => (0, promises_1.open)(path, flags)), (handle) => nodeIo(() => handle.close()).pipe(effect_1.Effect.orDie)).pipe(effect_1.Effect.flatMap(use)));
73
+ }
74
+ function sharedStateLeasesDirOf(niceevalRoot) {
75
+ return (0, node_path_1.join)(niceevalRoot, "shared-state-leases");
76
+ }
77
+ function sharedStateLeaseEntryId(key) {
78
+ return (0, entry_file_store_ts_1.slugHashEntryId)(key, [SHARED_STATE_LEASE_NAMESPACE, key]);
79
+ }
80
+ function sharedStateLeaseGenerationDirOf(niceevalRoot, key) {
81
+ return (0, node_path_1.join)(sharedStateLeasesDirOf(niceevalRoot), `${sharedStateLeaseEntryId(key)}.generations`);
82
+ }
83
+ function sharedStateLeaseHeartbeatDirOf(generationDir) {
84
+ return (0, node_path_1.join)(generationDir, "heartbeats");
85
+ }
86
+ function sharedStateLeaseHeartbeatEntryId(record) {
87
+ return (0, entry_file_store_ts_1.slugHashEntryId)(`generation-${record.generation}`, ["niceeval/shared-state-heartbeat/v1", record.key, record.ownerToken, String(record.generation)]);
88
+ }
89
+ /** v2 used a mutable flat entry. We only read it to fail closed or explicitly recover it. */
90
+ function legacySharedStateLeasePath(niceevalRoot, key) {
91
+ return (0, node_path_1.join)(sharedStateLeasesDirOf(niceevalRoot), `${(0, entry_file_store_ts_1.slugHashEntryId)(key, ["niceeval/shared-state/v2", key])}.json`);
92
+ }
93
+ function generationFileName(generation) {
94
+ return `${GENERATION_FILE_PREFIX}${String(generation).padStart(GENERATION_WIDTH, "0")}${GENERATION_FILE_SUFFIX}`;
95
+ }
96
+ function generationFromFileName(name) {
97
+ const match = /^generation-(\d+)\.json$/u.exec(name);
98
+ if (match === null)
99
+ return undefined;
100
+ const generation = Number(match[1]);
101
+ return isPositiveInteger(generation) && name === generationFileName(generation) ? generation : undefined;
102
+ }
103
+ function decodeRecoveryActor(value) {
104
+ const actor = recordOf(value);
105
+ if (actor === undefined ||
106
+ !isPositiveInteger(actor.pid) ||
107
+ !isNonEmptyString(actor.host) ||
108
+ !isNonEmptyString(actor.processIdentity) ||
109
+ !isTimestamp(actor.startedAt)) {
110
+ return undefined;
111
+ }
112
+ return Object.freeze({
113
+ pid: actor.pid,
114
+ host: actor.host,
115
+ processIdentity: actor.processIdentity,
116
+ startedAt: actor.startedAt,
117
+ });
118
+ }
119
+ function decodeSharedStateLeaseRecord(key, value, opts = {}) {
120
+ const record = recordOf(value);
121
+ if (record === undefined)
122
+ return undefined;
123
+ const recoveryActor = record.recoveryActor === undefined ? undefined : decodeRecoveryActor(record.recoveryActor);
124
+ const recovering = record.status === "recovering";
125
+ const generation = opts.legacy ? 0 : record.generation;
126
+ const parentGeneration = opts.legacy ? 0 : record.parentGeneration;
127
+ if (record.key !== key ||
128
+ !isNonEmptyString(record.experimentId) ||
129
+ !isNonEmptyString(record.ownerToken) ||
130
+ !isPositiveInteger(record.pid) ||
131
+ !isNonEmptyString(record.host) ||
132
+ !isNonEmptyString(record.processIdentity) ||
133
+ !isTimestamp(record.acquiredAt) ||
134
+ !isTimestamp(record.heartbeatAt) ||
135
+ (record.status !== "active" && !recovering) ||
136
+ (!opts.legacy && (!isPositiveInteger(generation) || !isNonNegativeInteger(parentGeneration) || parentGeneration >= generation)) ||
137
+ (recovering && (!isNonEmptyString(record.recoveryId) || recoveryActor === undefined)) ||
138
+ (!recovering && (record.recoveryId !== undefined || record.recoveryActor !== undefined))) {
139
+ return undefined;
140
+ }
141
+ return Object.freeze({
142
+ key,
143
+ experimentId: record.experimentId,
144
+ ownerToken: record.ownerToken,
145
+ pid: record.pid,
146
+ host: record.host,
147
+ processIdentity: record.processIdentity,
148
+ acquiredAt: record.acquiredAt,
149
+ heartbeatAt: record.heartbeatAt,
150
+ status: record.status,
151
+ generation: generation,
152
+ parentGeneration: parentGeneration,
153
+ ...(recovering ? { recoveryId: record.recoveryId, recoveryActor: recoveryActor } : {}),
154
+ });
155
+ }
156
+ function decodeSharedStateLeaseHeartbeat(record, value) {
157
+ const heartbeat = recordOf(value);
158
+ if (heartbeat === undefined ||
159
+ heartbeat.format !== SHARED_STATE_HEARTBEAT_FORMAT ||
160
+ heartbeat.key !== record.key ||
161
+ heartbeat.ownerToken !== record.ownerToken ||
162
+ heartbeat.generation !== record.generation ||
163
+ heartbeat.parentGeneration !== record.parentGeneration ||
164
+ !isTimestamp(heartbeat.heartbeatAt) ||
165
+ Date.parse(heartbeat.heartbeatAt) < Date.parse(record.acquiredAt)) {
166
+ return undefined;
167
+ }
168
+ return Object.freeze({
169
+ format: SHARED_STATE_HEARTBEAT_FORMAT,
170
+ key: record.key,
171
+ ownerToken: record.ownerToken,
172
+ generation: record.generation,
173
+ parentGeneration: record.parentGeneration,
174
+ heartbeatAt: heartbeat.heartbeatAt,
175
+ });
176
+ }
177
+ /**
178
+ * A heartbeat is diagnostic only: unreadable, missing, stale, or malformed
179
+ * sidecars leave the immutable generation record unchanged. Matching all of
180
+ * key/token/generation/parent makes an old owner's sidecar inapplicable after
181
+ * any release, recovery, or later acquisition.
182
+ */
183
+ function readDiagnosticHeartbeatEffect(generationDir, record) {
184
+ return (0, entry_file_store_ts_1.readEntryFileEffect)(sharedStateLeaseHeartbeatDirOf(generationDir), sharedStateLeaseHeartbeatEntryId(record), (value) => decodeSharedStateLeaseHeartbeat(record, value)).pipe(effect_1.Effect.map((heartbeat) => heartbeat === undefined
185
+ ? record
186
+ : Object.freeze({ ...record, heartbeatAt: heartbeat.heartbeatAt })), effect_1.Effect.catchAll(() => effect_1.Effect.succeed(record)));
187
+ }
188
+ function writeDiagnosticHeartbeatEffect(generationDir, record, heartbeatAt) {
189
+ const heartbeat = Object.freeze({
190
+ format: SHARED_STATE_HEARTBEAT_FORMAT,
191
+ key: record.key,
192
+ ownerToken: record.ownerToken,
193
+ generation: record.generation,
194
+ parentGeneration: record.parentGeneration,
195
+ heartbeatAt,
196
+ });
197
+ return (0, entry_file_store_ts_1.writeEntryFileEffect)(sharedStateLeaseHeartbeatDirOf(generationDir), sharedStateLeaseHeartbeatEntryId(record), heartbeat);
198
+ }
199
+ function decodeGeneration(key, value) {
200
+ const generation = recordOf(value);
201
+ if (generation === undefined || generation.format !== SHARED_STATE_GENERATION_FORMAT)
202
+ return undefined;
203
+ if (generation.kind === "lease") {
204
+ const record = decodeSharedStateLeaseRecord(key, generation);
205
+ return record === undefined ? undefined : Object.freeze({ kind: "lease", record });
206
+ }
207
+ if (generation.kind !== "free")
208
+ return undefined;
209
+ const previous = decodeSharedStateLeaseRecord(key, generation.previous);
210
+ if (previous === undefined ||
211
+ generation.key !== key ||
212
+ !isPositiveInteger(generation.generation) ||
213
+ !isNonNegativeInteger(generation.parentGeneration) ||
214
+ generation.parentGeneration >= generation.generation ||
215
+ generation.parentGeneration !== previous.generation ||
216
+ !isTimestamp(generation.releasedAt)) {
217
+ return undefined;
218
+ }
219
+ return Object.freeze({
220
+ kind: "free",
221
+ key,
222
+ generation: generation.generation,
223
+ parentGeneration: generation.parentGeneration,
224
+ releasedAt: generation.releasedAt,
225
+ previous,
226
+ });
227
+ }
228
+ function durableLeaseGeneration(record) {
229
+ return { format: SHARED_STATE_GENERATION_FORMAT, kind: "lease", ...record };
230
+ }
231
+ function durableFreeGeneration(record) {
232
+ return {
233
+ format: SHARED_STATE_GENERATION_FORMAT,
234
+ kind: "free",
235
+ key: record.key,
236
+ generation: record.generation,
237
+ parentGeneration: record.parentGeneration,
238
+ releasedAt: record.releasedAt,
239
+ previous: record.previous,
240
+ };
241
+ }
242
+ function generationNumber(generation) {
243
+ if (generation === undefined)
244
+ return 0;
245
+ return generation.kind === "free" ? generation.generation : generation.record.generation;
246
+ }
247
+ function leaseRecordOf(generation) {
248
+ return generation?.kind === "lease" || generation?.kind === "legacy" ? generation.record : undefined;
249
+ }
250
+ function sameRecoveryActor(left, right) {
251
+ return left.pid === right.pid &&
252
+ left.host === right.host &&
253
+ left.processIdentity === right.processIdentity &&
254
+ left.startedAt === right.startedAt;
255
+ }
256
+ function sameLeaseRecord(left, right) {
257
+ return left.key === right.key &&
258
+ left.experimentId === right.experimentId &&
259
+ left.ownerToken === right.ownerToken &&
260
+ left.pid === right.pid &&
261
+ left.host === right.host &&
262
+ left.processIdentity === right.processIdentity &&
263
+ left.acquiredAt === right.acquiredAt &&
264
+ left.heartbeatAt === right.heartbeatAt &&
265
+ left.status === right.status &&
266
+ left.generation === right.generation &&
267
+ left.parentGeneration === right.parentGeneration &&
268
+ left.recoveryId === right.recoveryId &&
269
+ (left.recoveryActor === undefined
270
+ ? right.recoveryActor === undefined
271
+ : right.recoveryActor !== undefined && sameRecoveryActor(left.recoveryActor, right.recoveryActor));
272
+ }
273
+ /** The immutable holder evidence must survive a recovery-to-recovery transition. */
274
+ function sameLeaseOwnerEvidence(left, right) {
275
+ return left.key === right.key &&
276
+ left.experimentId === right.experimentId &&
277
+ left.ownerToken === right.ownerToken &&
278
+ left.pid === right.pid &&
279
+ left.host === right.host &&
280
+ left.processIdentity === right.processIdentity &&
281
+ left.acquiredAt === right.acquiredAt &&
282
+ left.heartbeatAt === right.heartbeatAt;
283
+ }
284
+ function legalFirstGeneration(first, legacy) {
285
+ if (first.kind !== "lease")
286
+ return false;
287
+ if (first.record.status === "active")
288
+ return legacy === undefined;
289
+ // The only allowed v2 migration is explicit recovery: legacy has no v3
290
+ // transition slot, so it becomes immutable recovering generation 1.
291
+ return legacy !== undefined && sameLeaseOwnerEvidence(first.record, legacy);
292
+ }
293
+ function legalGenerationTransition(previous, next) {
294
+ if (previous.kind === "free") {
295
+ // Only a normal acquisition may follow a released generation.
296
+ return next.kind === "lease" && next.record.status === "active";
297
+ }
298
+ const previousRecord = previous.record;
299
+ if (next.kind === "free") {
300
+ // A free record is a compare-owner release, not a newly invented history.
301
+ return sameLeaseRecord(next.previous, previousRecord);
302
+ }
303
+ // An active holder cannot be replaced by another active holder. The sole
304
+ // lease-to-lease transition is explicit recovery, retaining the original
305
+ // owner evidence while assigning a new recovery actor/id.
306
+ if (next.record.status !== "recovering" || !sameLeaseOwnerEvidence(next.record, previousRecord))
307
+ return false;
308
+ if (previousRecord.status !== "recovering")
309
+ return true;
310
+ // Recovering -> recovering is a new, separately identified recovery after
311
+ // the prior actor stopped. Reusing either identity would make a stale actor
312
+ // indistinguishable from the new operation.
313
+ return next.record.recoveryId !== previousRecord.recoveryId &&
314
+ next.record.recoveryActor !== undefined &&
315
+ previousRecord.recoveryActor !== undefined &&
316
+ !sameRecoveryActor(next.record.recoveryActor, previousRecord.recoveryActor);
317
+ }
318
+ function validateGenerationTransitions(key, entries, legacy) {
319
+ let previousGeneration = 0;
320
+ let previous;
321
+ for (const entry of entries) {
322
+ const stateGeneration = generationNumber(entry.state);
323
+ const parentGeneration = entry.state.kind === "free"
324
+ ? entry.state.parentGeneration
325
+ : entry.state.record.parentGeneration;
326
+ if (entry.generation !== previousGeneration + 1 || stateGeneration !== entry.generation || parentGeneration !== previousGeneration) {
327
+ return invalidLeaseError(key, "generations are not a contiguous exact-parent chain");
328
+ }
329
+ if (previous === undefined) {
330
+ if (!legalFirstGeneration(entry.state, legacy)) {
331
+ return invalidLeaseError(key, "generation 1 must be an active fresh lease or a recovery of the exact v2 legacy owner");
332
+ }
333
+ }
334
+ else if (!legalGenerationTransition(previous, entry.state)) {
335
+ return invalidLeaseError(key, `generation ${entry.generation} is not a legal transition from generation ${previousGeneration}`);
336
+ }
337
+ previous = entry.state;
338
+ previousGeneration = entry.generation;
339
+ }
340
+ return undefined;
341
+ }
342
+ function invalidLeaseError(key, message) {
343
+ return new SharedStateLeaseError("shared-state-lease-invalid", `sharedState generation chain for ${JSON.stringify(key)} is invalid: ${message}. Recovery fails closed.`);
344
+ }
345
+ function readLegacySharedStateLeaseEffect(niceevalRoot, key) {
346
+ return nodeIo(() => (0, promises_1.readFile)(legacySharedStateLeasePath(niceevalRoot, key), "utf8")).pipe(effect_1.Effect.flatMap((raw) => nodeSync(() => JSON.parse(raw))), effect_1.Effect.flatMap((value) => {
347
+ const record = decodeSharedStateLeaseRecord(key, value, { legacy: true });
348
+ return record === undefined
349
+ ? effect_1.Effect.fail(invalidLeaseError(key, "the legacy mutable record cannot be decoded"))
350
+ : effect_1.Effect.succeed(record);
351
+ }), effect_1.Effect.catchAll((cause) => errnoCode(cause) === "ENOENT" ? effect_1.Effect.succeed(undefined) : effect_1.Effect.fail(cause)));
352
+ }
353
+ /**
354
+ * Read a contiguous immutable generation chain. Proposal files are ignored:
355
+ * they are not authoritative until the hard-link has occupied a generation
356
+ * filename. A malformed committed filename or record is an explicit fail
357
+ * closed condition, never an opportunity to silently start a new holder.
358
+ */
359
+ function readSharedStateLedgerEffect(niceevalRoot, key) {
360
+ const dir = sharedStateLeaseGenerationDirOf(niceevalRoot, key);
361
+ return nodeIo(() => (0, promises_1.readdir)(dir)).pipe(effect_1.Effect.catchAll((cause) => errnoCode(cause) === "ENOENT" ? effect_1.Effect.succeed([]) : effect_1.Effect.fail(cause)), effect_1.Effect.flatMap((names) => {
362
+ const generationNames = names.filter((name) => name.startsWith(GENERATION_FILE_PREFIX));
363
+ const ordered = [];
364
+ for (const name of generationNames) {
365
+ const generation = generationFromFileName(name);
366
+ if (generation === undefined) {
367
+ return effect_1.Effect.fail(invalidLeaseError(key, `committed filename ${JSON.stringify(name)} is not canonical`));
368
+ }
369
+ ordered.push({ name, generation });
370
+ }
371
+ ordered.sort((left, right) => left.generation - right.generation);
372
+ if (ordered.length === 0) {
373
+ return readLegacySharedStateLeaseEffect(niceevalRoot, key).pipe(effect_1.Effect.map((legacy) => Object.freeze({
374
+ dir,
375
+ latest: legacy === undefined ? undefined : Object.freeze({ kind: "legacy", record: legacy }),
376
+ })));
377
+ }
378
+ return effect_1.Effect.forEach(ordered, ({ name, generation }) => nodeIo(() => (0, promises_1.readFile)((0, node_path_1.join)(dir, name), "utf8")).pipe(effect_1.Effect.flatMap((raw) => nodeSync(() => JSON.parse(raw))), effect_1.Effect.flatMap((value) => {
379
+ const decoded = decodeGeneration(key, value);
380
+ return decoded === undefined
381
+ ? effect_1.Effect.fail(invalidLeaseError(key, `generation ${generation} cannot be decoded`))
382
+ : effect_1.Effect.succeed({ generation, state: decoded });
383
+ }))).pipe(effect_1.Effect.flatMap((entries) => readLegacySharedStateLeaseEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((legacy) => {
384
+ const invalid = validateGenerationTransitions(key, entries, legacy);
385
+ return invalid === undefined
386
+ ? effect_1.Effect.succeed(Object.freeze({ dir, latest: entries.at(-1).state }))
387
+ : effect_1.Effect.fail(invalid);
388
+ }))));
389
+ }));
390
+ }
391
+ /**
392
+ * Publish one immutable generation. `link(proposal, generation-N)` is the
393
+ * linearization point: it succeeds only if no other transition has occupied
394
+ * the same next-generation slot. The proposal is unique and non-authoritative
395
+ * before the link, so a crash cannot leave a reusable fence or an ABA target.
396
+ */
397
+ function publishGenerationExclusiveEffect(dir, generation, state) {
398
+ const name = generationFileName(generation);
399
+ const proposalPath = (0, node_path_1.join)(dir, `.${name}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.proposal`);
400
+ const targetPath = (0, node_path_1.join)(dir, name);
401
+ const data = state.kind === "lease"
402
+ ? durableLeaseGeneration(state.record)
403
+ : state.kind === "free"
404
+ ? durableFreeGeneration(state)
405
+ : undefined;
406
+ if (data === undefined) {
407
+ return effect_1.Effect.fail(new SharedStateLeaseError("shared-state-lease-invalid", "A legacy sharedState record cannot be republished without explicit recovery."));
408
+ }
409
+ const writeProposal = withOpenFile(proposalPath, "wx", (handle) => nodeIo(() => handle.writeFile(JSON.stringify(data, null, 2), "utf8")).pipe(effect_1.Effect.zipRight(nodeIo(() => handle.sync()))));
410
+ return nodeIo(() => (0, promises_1.mkdir)(dir, { recursive: true })).pipe(effect_1.Effect.zipRight(writeProposal), effect_1.Effect.zipRight(nodeIo(() => (0, promises_1.link)(proposalPath, targetPath)).pipe(effect_1.Effect.as("published"), effect_1.Effect.catchAll((cause) => errnoCode(cause) === "EEXIST" ? effect_1.Effect.succeed("occupied") : effect_1.Effect.fail(cause)))), effect_1.Effect.flatMap((result) => result === "published"
411
+ ? (0, entry_file_store_ts_1.fsyncDirEffect)(dir).pipe(effect_1.Effect.as(result))
412
+ : effect_1.Effect.succeed(result)),
413
+ // Only this caller's random proposal is removed. Published generation
414
+ // files are intentionally never deleted, renamed, or reused.
415
+ effect_1.Effect.ensuring(nodeIo(() => (0, promises_1.rm)(proposalPath, { force: true })).pipe(effect_1.Effect.ignore)));
416
+ }
417
+ function readSharedStateLeaseEffect(niceevalRoot, key) {
418
+ return readSharedStateLedgerEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((ledger) => {
419
+ const record = leaseRecordOf(ledger.latest);
420
+ return record === undefined
421
+ ? effect_1.Effect.succeed(undefined)
422
+ // Sidecar I/O is deliberately non-authoritative. A bad/missing read
423
+ // cannot hide or invalidate the immutable lease generation.
424
+ : readDiagnosticHeartbeatEffect(ledger.dir, record).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(record)));
425
+ }));
426
+ }
427
+ /**
428
+ * The public recovery command may show the prior owner after a completed
429
+ * release. This lets it report `AlreadyReleased` without asking users to look
430
+ * at private files; a new active generation still takes precedence and makes
431
+ * an old token fail exact-owner validation.
432
+ */
433
+ function readSharedStateLeaseRecoveryTargetEffect(niceevalRoot, key) {
434
+ return readSharedStateLedgerEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((ledger) => {
435
+ const record = ledger.latest?.kind === "free" ? ledger.latest.previous : leaseRecordOf(ledger.latest);
436
+ return record === undefined
437
+ ? effect_1.Effect.succeed(undefined)
438
+ : readDiagnosticHeartbeatEffect(ledger.dir, record).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed(record)));
439
+ }));
440
+ }
441
+ function makeAbortError(signal) {
442
+ const reason = signal?.reason;
443
+ if (reason instanceof Error)
444
+ return reason;
445
+ const error = new Error("aborted while waiting for shared-state recovery");
446
+ error.name = "AbortError";
447
+ return error;
448
+ }
449
+ function awaitAbort(signal) {
450
+ if (signal === undefined)
451
+ return effect_1.Effect.never;
452
+ return effect_1.Effect.async((resume, effectSignal) => {
453
+ let completed = false;
454
+ const cleanup = () => {
455
+ signal.removeEventListener("abort", onAbort);
456
+ effectSignal.removeEventListener("abort", onEffectAbort);
457
+ };
458
+ const complete = () => {
459
+ if (completed)
460
+ return;
461
+ completed = true;
462
+ cleanup();
463
+ };
464
+ const onAbort = () => {
465
+ if (completed)
466
+ return;
467
+ complete();
468
+ resume(effect_1.Effect.fail(makeAbortError(signal)));
469
+ };
470
+ const onEffectAbort = () => complete();
471
+ signal.addEventListener("abort", onAbort, { once: true });
472
+ effectSignal.addEventListener("abort", onEffectAbort, { once: true });
473
+ // Both listeners must be live before inspecting either signal: otherwise
474
+ // an abort between inspection and registration could strand the waiter.
475
+ if (effectSignal.aborted)
476
+ onEffectAbort();
477
+ else if (signal.aborted)
478
+ onAbort();
479
+ return effect_1.Effect.sync(complete);
480
+ });
481
+ }
482
+ function delayOrAbortEffect(ms, signal) {
483
+ if (signal?.aborted)
484
+ return effect_1.Effect.fail(makeAbortError(signal));
485
+ return effect_1.Effect.raceFirst(effect_1.Effect.sleep(ms), awaitAbort(signal)).pipe(effect_1.Effect.asVoid);
486
+ }
487
+ /**
488
+ * Returns a host-local process identity that distinguishes a PID reuse. Linux
489
+ * uses the kernel start tick plus `/proc`'s terminal-state boundary; other
490
+ * POSIX hosts use `ps`'s process start time. An absent or terminal process
491
+ * returns undefined; unreadable or malformed identity evidence fails closed.
492
+ */
493
+ function processIdentityForPidEffect(pid) {
494
+ return effect_1.Effect.tryPromise({
495
+ try: async () => {
496
+ if (process.platform === "linux") {
497
+ try {
498
+ const raw = await (0, promises_1.readFile)(`/proc/${pid}/stat`, "utf8");
499
+ const closing = raw.lastIndexOf(")");
500
+ if (closing < 0 || raw[closing + 1] !== " ") {
501
+ throw new Error("Could not parse /proc/<pid>/stat process identity.");
502
+ }
503
+ const fields = raw.slice(closing + 2).trim().split(/\s+/u);
504
+ const state = fields[0];
505
+ // field 3 is the task state. A zombie or either Linux dead state
506
+ // cannot execute the owner cleanup, even though its start tick still
507
+ // matches the durable lease. Treat only these exact terminal states
508
+ // as stopped; a missing or malformed state remains unavailable.
509
+ if (state === "Z" || state === "X" || state === "x")
510
+ return undefined;
511
+ if (state === undefined || !/^[A-Za-z]$/u.test(state)) {
512
+ throw new Error("Could not parse /proc/<pid>/stat process state.");
513
+ }
514
+ // /proc/<pid>/stat field 22 is starttime; `fields[0]` is field 3.
515
+ const started = fields[19];
516
+ if (started !== undefined && /^\d+$/u.test(started))
517
+ return `linux-starttime:${started}`;
518
+ throw new Error("Could not parse /proc/<pid>/stat process identity.");
519
+ }
520
+ catch (cause) {
521
+ if (errnoCode(cause) === "ENOENT")
522
+ return undefined;
523
+ throw cause;
524
+ }
525
+ }
526
+ return await new Promise((resolve, reject) => {
527
+ (0, node_child_process_1.execFile)("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" }, (error, stdout) => {
528
+ if (error !== null) {
529
+ if (stdout.trim().length === 0)
530
+ resolve(undefined);
531
+ else
532
+ reject(error);
533
+ return;
534
+ }
535
+ const started = stdout.trim();
536
+ resolve(started.length === 0 ? undefined : `ps-lstart:${started}`);
537
+ });
538
+ });
539
+ },
540
+ catch: (cause) => cause,
541
+ });
542
+ }
543
+ function currentProcessIdentityEffect() {
544
+ return processIdentityForPidEffect(process.pid).pipe(effect_1.Effect.flatMap((identity) => identity === undefined
545
+ ? effect_1.Effect.fail(new SharedStateLeaseError("shared-state-process-identity-unavailable", "NiceEval could not establish this process identity; sharedState fails closed rather than coordinating from a PID alone."))
546
+ : effect_1.Effect.succeed(identity)));
547
+ }
548
+ function recoveryActor(host, processIdentity) {
549
+ return Object.freeze({
550
+ pid: process.pid,
551
+ host,
552
+ processIdentity,
553
+ startedAt: new Date().toISOString(),
554
+ });
555
+ }
556
+ /**
557
+ * Heartbeats intentionally make no authority transition. They verify that the
558
+ * exact immutable generation/token is still current, then atomically replace
559
+ * only that generation's diagnostic sidecar. A sidecar failure is ignored and
560
+ * cannot expire, take over, or resurrect any owner.
561
+ */
562
+ function heartbeatEffect(niceevalRoot, key, expected) {
563
+ return readSharedStateLedgerEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((ledger) => {
564
+ const current = leaseRecordOf(ledger.latest);
565
+ if (current === undefined || !sameLeaseRecord(current, expected))
566
+ return effect_1.Effect.succeed("lost");
567
+ return effect_1.Clock.currentTimeMillis.pipe(effect_1.Effect.flatMap((nowMs) => writeDiagnosticHeartbeatEffect(ledger.dir, expected, new Date(nowMs).toISOString()).pipe(
568
+ // This write is merely user-facing evidence. The immutable head is
569
+ // still authoritative even if the filesystem rejects the sidecar.
570
+ effect_1.Effect.catchAll(() => effect_1.Effect.void), effect_1.Effect.as("confirmed"))));
571
+ }));
572
+ }
573
+ function makeActiveLeaseRecord(input) {
574
+ return Object.freeze({
575
+ key: input.key,
576
+ experimentId: input.identity.experimentId,
577
+ ownerToken: input.ownerToken,
578
+ pid: input.identity.pid,
579
+ host: input.identity.host,
580
+ processIdentity: input.identity.processIdentity,
581
+ acquiredAt: input.now,
582
+ heartbeatAt: input.now,
583
+ status: "active",
584
+ generation: input.generation,
585
+ parentGeneration: input.parentGeneration,
586
+ });
587
+ }
588
+ function freeGenerationFrom(record, generation, releasedAt) {
589
+ return Object.freeze({
590
+ kind: "free",
591
+ key: record.key,
592
+ generation,
593
+ parentGeneration: record.generation,
594
+ releasedAt,
595
+ previous: record,
596
+ });
597
+ }
598
+ function releaseError(key, latest) {
599
+ return new SharedStateLeaseError(latest === undefined ? "shared-state-lease-missing" : "shared-state-lease-owner-mismatch", `sharedState lease ${JSON.stringify(key)} was not released because its exact owner generation no longer matches; explicit recovery is required.`);
600
+ }
601
+ /**
602
+ * Advance exactly this active generation to a free generation. A competing
603
+ * transition can only occupy the same next slot; after that happens we inspect
604
+ * the new immutable head and accept it only if it is this exact release.
605
+ */
606
+ function releaseExactLeaseEffect(niceevalRoot, key, expected) {
607
+ return effect_1.Effect.suspend(() => readSharedStateLedgerEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((ledger) => {
608
+ if (ledger.latest?.kind === "free" && sameLeaseRecord(ledger.latest.previous, expected))
609
+ return effect_1.Effect.void;
610
+ const current = leaseRecordOf(ledger.latest);
611
+ if (current === undefined || !sameLeaseRecord(current, expected))
612
+ return effect_1.Effect.fail(releaseError(key, ledger.latest));
613
+ const next = freeGenerationFrom(expected, expected.generation + 1, new Date().toISOString());
614
+ return publishGenerationExclusiveEffect(ledger.dir, next.generation, next).pipe(effect_1.Effect.flatMap((published) => published === "published" ? effect_1.Effect.void : releaseExactLeaseEffect(niceevalRoot, key, expected)));
615
+ })));
616
+ }
617
+ /**
618
+ * Acquires only a free/absent generation head. Existing active/recovering
619
+ * leases are waited on indefinitely; heartbeat age and PID liveness are
620
+ * diagnostic facts, never authority to take ownership.
621
+ */
622
+ function acquireSharedStateLeaseEffect(niceevalRoot, key, identity, opts = {}) {
623
+ const ownerToken = (0, node_crypto_1.randomUUID)();
624
+ const pollIntervalMs = opts.pollIntervalMs ?? exports.SHARED_STATE_LEASE_HEARTBEAT_INTERVAL_MS;
625
+ const heartbeatIntervalMs = opts.heartbeatIntervalMs ?? exports.SHARED_STATE_LEASE_HEARTBEAT_INTERVAL_MS;
626
+ let waitReported = false;
627
+ const acquire = () => effect_1.Effect.suspend(() => {
628
+ if (opts.signal?.aborted)
629
+ return effect_1.Effect.fail(makeAbortError(opts.signal));
630
+ return readSharedStateLedgerEffect(niceevalRoot, key).pipe(effect_1.Effect.flatMap((ledger) => {
631
+ const holder = leaseRecordOf(ledger.latest);
632
+ if (holder !== undefined) {
633
+ const reportWait = waitReported
634
+ ? effect_1.Effect.void
635
+ : readDiagnosticHeartbeatEffect(ledger.dir, holder).pipe(effect_1.Effect.flatMap((displayHolder) => effect_1.Effect.sync(() => {
636
+ waitReported = true;
637
+ opts.onWaitStart?.(displayHolder);
638
+ })));
639
+ return reportWait.pipe(effect_1.Effect.zipRight(delayOrAbortEffect(pollIntervalMs, opts.signal)), effect_1.Effect.zipRight(acquire()));
640
+ }
641
+ return effect_1.Clock.currentTimeMillis.pipe(effect_1.Effect.flatMap((nowMs) => {
642
+ const parentGeneration = generationNumber(ledger.latest);
643
+ const record = makeActiveLeaseRecord({
644
+ key,
645
+ identity,
646
+ ownerToken,
647
+ generation: parentGeneration + 1,
648
+ parentGeneration,
649
+ now: new Date(nowMs).toISOString(),
650
+ });
651
+ const next = Object.freeze({ kind: "lease", record });
652
+ return publishGenerationExclusiveEffect(ledger.dir, record.generation, next).pipe(effect_1.Effect.flatMap((published) => published === "published" ? effect_1.Effect.succeed(record) : acquire()));
653
+ }));
654
+ }));
655
+ });
656
+ return effect_1.Effect.uninterruptibleMask((restore) => restore(acquire()).pipe(effect_1.Effect.flatMap((record) => {
657
+ let heartbeatStopped = false;
658
+ let lostOwnership = false;
659
+ const heartbeat = effect_1.Effect.forever(effect_1.Effect.sleep(heartbeatIntervalMs).pipe(effect_1.Effect.zipRight(heartbeatEffect(niceevalRoot, key, record).pipe(effect_1.Effect.tap((outcome) => effect_1.Effect.sync(() => {
660
+ if (outcome === "lost")
661
+ lostOwnership = true;
662
+ })),
663
+ // A diagnostic probe failure does not silently abandon an
664
+ // otherwise-owned lease; release still performs the exact CAS.
665
+ effect_1.Effect.ignore))));
666
+ return effect_1.Effect.forkDaemon(restore(heartbeat)).pipe(effect_1.Effect.flatMap((fiber) => effect_1.Deferred.make().pipe(effect_1.Effect.map((releaseCompletion) => {
667
+ const stopHeartbeat = effect_1.Effect.uninterruptible(effect_1.Effect.suspend(() => {
668
+ if (heartbeatStopped)
669
+ return effect_1.Effect.void;
670
+ heartbeatStopped = true;
671
+ return effect_1.Fiber.interrupt(fiber).pipe(effect_1.Effect.asVoid);
672
+ }));
673
+ const releaseOperation = stopHeartbeat.pipe(effect_1.Effect.zipRight(releaseExactLeaseEffect(niceevalRoot, key, record)), effect_1.Effect.catchAll((cause) => {
674
+ // Keep the exact failure visible to the caller. `lostOwnership`
675
+ // only improves the diagnosis; it never changes authority.
676
+ if (cause instanceof SharedStateLeaseError)
677
+ return effect_1.Effect.fail(cause);
678
+ return effect_1.Effect.fail(lostOwnership
679
+ ? new SharedStateLeaseError("shared-state-lease-owner-mismatch", `sharedState lease ${JSON.stringify(key)} changed while this owner was active; explicit recovery is required.`)
680
+ : cause);
681
+ }));
682
+ const release = effect_1.Effect.uninterruptible(effect_1.Deferred.complete(releaseCompletion, releaseOperation).pipe(effect_1.Effect.zipRight(effect_1.Deferred.await(releaseCompletion))));
683
+ return Object.freeze({ claim: Object.freeze({ ownerToken, release, abandon: stopHeartbeat }) });
684
+ }))));
685
+ })));
686
+ }
687
+ function assertRecoveryConfirmed(input) {
688
+ return input.confirmOwnerTerminated && input.confirmRemoteQuiesced
689
+ ? effect_1.Effect.void
690
+ : effect_1.Effect.fail(new SharedStateLeaseError("shared-state-recovery-confirmation-required", "Shared-state recovery requires explicit confirmation that the owner terminated and remote state is quiesced."));
691
+ }
692
+ /** A local identity mismatch proves PID reuse; an unreadable identity fails closed. */
693
+ function assertActorStoppedEffect(actor, localHost, code, remoteQuiesced) {
694
+ if (actor.host !== localHost) {
695
+ return remoteQuiesced
696
+ ? effect_1.Effect.void
697
+ : effect_1.Effect.fail(new SharedStateLeaseError("shared-state-recovery-identity-unavailable", `NiceEval cannot verify remote owner ${actor.host} without an explicit remote-quiesced acknowledgement.`));
698
+ }
699
+ return processIdentityForPidEffect(actor.pid).pipe(effect_1.Effect.mapError(() => new SharedStateLeaseError("shared-state-recovery-identity-unavailable", `NiceEval could not verify local process identity for PID ${actor.pid}; recovery fails closed.`)), effect_1.Effect.flatMap((current) => {
700
+ if (current === undefined || current !== actor.processIdentity)
701
+ return effect_1.Effect.void;
702
+ return effect_1.Effect.fail(new SharedStateLeaseError(code, code === "shared-state-owner-still-live"
703
+ ? `sharedState owner is still live on ${actor.host} as PID ${actor.pid}; recovery is refused.`
704
+ : `A prior sharedState recovery is still live on ${actor.host} as PID ${actor.pid}; recovery is refused.`));
705
+ }));
706
+ }
707
+ function recoveryRecordFrom(source, recoveryId, actor) {
708
+ return Object.freeze({
709
+ ...source,
710
+ status: "recovering",
711
+ generation: source.generation + 1,
712
+ parentGeneration: source.generation,
713
+ recoveryId,
714
+ recoveryActor: actor,
715
+ });
716
+ }
717
+ function isExactRecoveringSource(record, recoveryId, actor) {
718
+ return record.status === "recovering" &&
719
+ record.recoveryId === recoveryId &&
720
+ record.recoveryActor !== undefined &&
721
+ // Do not reduce this to processIdentity: PID, host, and the immutable
722
+ // recovery id are all part of the replacement compare-owner predicate.
723
+ sameRecoveryActor(record.recoveryActor, actor);
724
+ }
725
+ /**
726
+ * Claims a failed holder for one recovery command without releasing it. The
727
+ * recovery record is an immutable next generation. If two operators recover
728
+ * concurrently, only one can publish that exact next slot; the loser never
729
+ * clears or reuses a coordination path and must re-inspect/retry explicitly.
730
+ */
731
+ function beginExplicitSharedStateRecoveryEffect(input) {
732
+ return effect_1.Effect.gen(function* () {
733
+ yield* assertRecoveryConfirmed(input);
734
+ const processIdentity = yield* currentProcessIdentityEffect();
735
+ const actor = recoveryActor(input.localHost, processIdentity);
736
+ const ledger = yield* readSharedStateLedgerEffect(input.niceevalRoot, input.key);
737
+ if (ledger.latest === undefined) {
738
+ return yield* effect_1.Effect.fail(new SharedStateLeaseError("shared-state-lease-missing", `No decodable sharedState lease exists for ${JSON.stringify(input.key)}.`));
739
+ }
740
+ if (ledger.latest.kind === "free") {
741
+ if (ledger.latest.previous.ownerToken === input.ownerToken) {
742
+ return Object.freeze({ _tag: "AlreadyReleased", record: ledger.latest.previous });
743
+ }
744
+ return yield* effect_1.Effect.fail(new SharedStateLeaseError("shared-state-lease-owner-mismatch", `The supplied owner token does not own sharedState key ${JSON.stringify(input.key)}.`));
745
+ }
746
+ const observed = ledger.latest.record;
747
+ if (observed.ownerToken !== input.ownerToken) {
748
+ return yield* effect_1.Effect.fail(new SharedStateLeaseError("shared-state-lease-owner-mismatch", `The supplied owner token does not own sharedState key ${JSON.stringify(input.key)}.`));
749
+ }
750
+ yield* assertActorStoppedEffect(observed, input.localHost, "shared-state-owner-still-live", input.confirmRemoteQuiesced);
751
+ if (observed.status === "recovering") {
752
+ const observedRecoveryId = observed.recoveryId;
753
+ const observedRecoveryActor = observed.recoveryActor;
754
+ // The observed immutable parent itself is the exact compare-owner input.
755
+ // Keep the full predicate explicit so a PID/processIdentity coincidence
756
+ // cannot replace a different recovery operation.
757
+ if (!isExactRecoveringSource(observed, observedRecoveryId, observedRecoveryActor)) {
758
+ return yield* effect_1.Effect.fail(invalidLeaseError(input.key, "recovering generation lacks an exact recovery actor"));
759
+ }
760
+ yield* assertActorStoppedEffect(observedRecoveryActor, input.localHost, "shared-state-recovery-in-progress", input.confirmRemoteQuiesced);
761
+ }
762
+ const recoveryId = (0, node_crypto_1.randomUUID)();
763
+ const recovered = recoveryRecordFrom(observed, recoveryId, actor);
764
+ const next = Object.freeze({ kind: "lease", record: recovered });
765
+ const published = yield* publishGenerationExclusiveEffect(ledger.dir, recovered.generation, next);
766
+ if (published === "published")
767
+ return Object.freeze({ _tag: "Claimed", record: recovered, recoveryId });
768
+ return yield* effect_1.Effect.fail(new SharedStateLeaseError("shared-state-recovery-state-mismatch", `sharedState lease ${JSON.stringify(input.key)} changed before explicit recovery could claim its exact generation.`));
769
+ });
770
+ }
771
+ function exactRecoveryActorMatchesCurrentProcess(actor, localHost, processIdentity) {
772
+ return actor.pid === process.pid && actor.host === localHost && actor.processIdentity === processIdentity;
773
+ }
774
+ /**
775
+ * Final recovery transition: exactly owner token + recovery id + recovery
776
+ * actor host/PID/process identity advances its observed generation to free.
777
+ * It does not unlink anything and therefore cannot erase a later holder.
778
+ */
779
+ function completeExplicitSharedStateRecoveryEffect(input) {
780
+ const complete = (expected) => effect_1.Effect.suspend(() => readSharedStateLedgerEffect(input.niceevalRoot, input.key).pipe(effect_1.Effect.flatMap((ledger) => {
781
+ if (ledger.latest?.kind === "free" && expected !== undefined && sameLeaseRecord(ledger.latest.previous, expected)) {
782
+ return effect_1.Effect.void;
783
+ }
784
+ const current = leaseRecordOf(ledger.latest);
785
+ if (current === undefined ||
786
+ (expected !== undefined && !sameLeaseRecord(current, expected)) ||
787
+ current.ownerToken !== input.ownerToken ||
788
+ current.status !== "recovering" ||
789
+ current.recoveryId !== input.recoveryId ||
790
+ current.recoveryActor === undefined) {
791
+ return effect_1.Effect.fail(new SharedStateLeaseError(ledger.latest === undefined ? "shared-state-lease-missing" : "shared-state-recovery-state-mismatch", `sharedState lease ${JSON.stringify(input.key)} no longer matches this explicit recovery operation.`));
792
+ }
793
+ return currentProcessIdentityEffect().pipe(effect_1.Effect.flatMap((processIdentity) => {
794
+ if (!exactRecoveryActorMatchesCurrentProcess(current.recoveryActor, input.localHost, processIdentity)) {
795
+ return effect_1.Effect.fail(new SharedStateLeaseError("shared-state-recovery-state-mismatch", `sharedState lease ${JSON.stringify(input.key)} is not owned by this exact recovery actor.`));
796
+ }
797
+ const next = freeGenerationFrom(current, current.generation + 1, new Date().toISOString());
798
+ return publishGenerationExclusiveEffect(ledger.dir, next.generation, next).pipe(effect_1.Effect.flatMap((published) => published === "published" ? effect_1.Effect.void : complete(current)));
799
+ }));
800
+ })));
801
+ return complete();
802
+ }
803
+ //# sourceMappingURL=shared-state-lease.cjs.map