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
@@ -56,77 +56,39 @@ function readTextIfPresentEffect(path, operation) {
56
56
  function readDirectoryIfPresentEffect(path, operation) {
57
57
  return nodeIo(operation, (signal) => (0, promises_1.readdir)(path)).pipe(effect_1.Effect.catchAll((cause) => isAbsent(cause) ? effect_1.Effect.succeed(undefined) : effect_1.Effect.fail(cause)));
58
58
  }
59
- function recordOf(value) {
60
- return typeof value === "object" && value !== null && !Array.isArray(value)
61
- ? value
62
- : undefined;
63
- }
64
- function isNonNegativeInteger(value) {
65
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
66
- }
67
- function isPositiveInteger(value) {
68
- return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
69
- }
70
- function isTimestamp(value) {
71
- return typeof value === "string" && Number.isFinite(Date.parse(value));
72
- }
73
- function isVerdict(value) {
74
- return value === "passed" || value === "failed" || value === "errored" || value === "skipped";
75
- }
76
- function isKeptSandboxState(value) {
77
- return value === "alive" || value === "dormant" || value === "expired" || value === "unknown";
78
- }
59
+ const NonNegativeSafeIntegerSchema = effect_1.Schema.Number.pipe(effect_1.Schema.filter((value) => Number.isSafeInteger(value) && value >= 0, { identifier: "NonNegativeSafeInteger" }));
60
+ const PositiveSafeIntegerSchema = effect_1.Schema.Number.pipe(effect_1.Schema.filter((value) => Number.isSafeInteger(value) && value > 0, { identifier: "PositiveSafeInteger" }));
61
+ const TimestampSchema = effect_1.Schema.String.pipe(effect_1.Schema.filter((value) => Number.isFinite(Date.parse(value)), { identifier: "Timestamp" }));
62
+ const KeptSandboxEntrySchema = effect_1.Schema.Struct({
63
+ sandboxId: effect_1.Schema.String,
64
+ provider: effect_1.Schema.String,
65
+ evalId: effect_1.Schema.String,
66
+ attempt: NonNegativeSafeIntegerSchema,
67
+ experimentId: effect_1.Schema.optional(effect_1.Schema.String),
68
+ locator: effect_1.Schema.String,
69
+ verdict: effect_1.Schema.Literal("passed", "failed", "errored", "skipped"),
70
+ keptAt: TimestampSchema,
71
+ workdir: effect_1.Schema.String,
72
+ enter: effect_1.Schema.optional(effect_1.Schema.String),
73
+ expiresAt: effect_1.Schema.optional(TimestampSchema),
74
+ state: effect_1.Schema.Literal("alive", "dormant", "expired", "unknown"),
75
+ });
76
+ const PersistedKeptSandboxLeaseSchema = effect_1.Schema.Struct({
77
+ holder: effect_1.Schema.String,
78
+ op: effect_1.Schema.String,
79
+ acquiredAt: TimestampSchema,
80
+ ttlMs: PositiveSafeIntegerSchema,
81
+ token: effect_1.Schema.optional(effect_1.Schema.String),
82
+ });
79
83
  /** 留存条目的全部字段均在磁盘边界验证,特别是两个字符串联合与可选时间字段。 */
80
84
  function decodeKeptSandboxEntry(value) {
81
- const record = recordOf(value);
82
- if (record === undefined ||
83
- typeof record.sandboxId !== "string" ||
84
- typeof record.provider !== "string" ||
85
- typeof record.evalId !== "string" ||
86
- !isNonNegativeInteger(record.attempt) ||
87
- (record.experimentId !== undefined && typeof record.experimentId !== "string") ||
88
- typeof record.locator !== "string" ||
89
- !isVerdict(record.verdict) ||
90
- !isTimestamp(record.keptAt) ||
91
- typeof record.workdir !== "string" ||
92
- (record.enter !== undefined && typeof record.enter !== "string") ||
93
- (record.expiresAt !== undefined && !isTimestamp(record.expiresAt)) ||
94
- !isKeptSandboxState(record.state)) {
95
- return undefined;
96
- }
97
- return {
98
- sandboxId: record.sandboxId,
99
- provider: record.provider,
100
- evalId: record.evalId,
101
- attempt: record.attempt,
102
- ...(record.experimentId === undefined ? {} : { experimentId: record.experimentId }),
103
- locator: record.locator,
104
- verdict: record.verdict,
105
- keptAt: record.keptAt,
106
- workdir: record.workdir,
107
- ...(record.enter === undefined ? {} : { enter: record.enter }),
108
- ...(record.expiresAt === undefined ? {} : { expiresAt: record.expiresAt }),
109
- state: record.state,
110
- };
85
+ const decoded = effect_1.Schema.decodeUnknownEither(KeptSandboxEntrySchema)(value);
86
+ return effect_1.Either.isRight(decoded) ? decoded.right : undefined;
111
87
  }
112
88
  /** lease 同样来自持久 JSON;token 是内部互斥凭据,可选但类型必须正确。 */
113
89
  function decodeKeptSandboxLease(value) {
114
- const record = recordOf(value);
115
- if (record === undefined ||
116
- typeof record.holder !== "string" ||
117
- typeof record.op !== "string" ||
118
- !isTimestamp(record.acquiredAt) ||
119
- !isPositiveInteger(record.ttlMs) ||
120
- (record.token !== undefined && typeof record.token !== "string")) {
121
- return undefined;
122
- }
123
- return {
124
- holder: record.holder,
125
- op: record.op,
126
- acquiredAt: record.acquiredAt,
127
- ttlMs: record.ttlMs,
128
- ...(record.token === undefined ? {} : { token: record.token }),
129
- };
90
+ const decoded = effect_1.Schema.decodeUnknownEither(PersistedKeptSandboxLeaseSchema)(value);
91
+ return effect_1.Either.isRight(decoded) ? decoded.right : undefined;
130
92
  }
131
93
  /** entry id:provider + sandboxId 的稳定散列(条目文件名)。 */
132
94
  function keptEntryId(provider, sandboxId) {
@@ -1 +1 @@
1
- {"version":3,"file":"keep-registry.js","sourceRoot":"","sources":["keep-registry.ts"],"names":[],"mappings":"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","sourcesContent":["// 留存沙箱的持久注册表:`.niceeval/sandboxes/` 下的逐条目文件(不是多个 attempt 竞争改写的\n// 一份 JSON)。entry id 由 provider + sandboxId 做稳定散列;每条走 shared/entry-file-store.ts\n// 的原子写纪律(临时文件 → fsync 文件 → rename → fsync 目录)——不同 attempt 与不同 niceeval\n// 进程不会覆盖彼此。\n// 契约见 docs/feature/sandbox/architecture.md「留存(keep)与注册表」。\n//\n// 条目旁独立的 `.lease` 文件是另一套机制(短命的操作互斥,见 acquireKeptLease 一节),不走\n// entry-file-store 的原子写纪律——lease 的持有点是 `wx` 独占创建本身,不需要 tmp+rename。\nimport { mkdir, open, readdir, readFile, rm, type FileHandle } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Data, Effect } from \"effect\";\nimport { fsyncDirEffect, hashEntryId, readEntryFileEffect, writeEntryFileEffect, } from \"../shared/entry-file-store.ts\";\nimport type { Verdict } from \"../types.ts\";\n/** 一条留存登记项(逐条目文件的 JSON 形状)。 */\nexport interface KeptSandboxEntry {\n sandboxId: string;\n provider: string;\n evalId: string;\n attempt: number;\n experimentId?: string;\n locator: string;\n verdict: Verdict;\n keptAt: string;\n workdir: string;\n /** provider 原生的进入命令(直连与审计用);日常入口是 `niceeval sandbox enter`。 */\n enter?: string;\n /** 现场可找回的截止时刻——provider 声明了保留期限才写(vercel 写,e2b pause 无限期保留则不写)。 */\n expiresAt?: string;\n /** alive = 实例在跑;dormant = 可唤醒;expired = 确认不存在;unknown = 探测失败。 */\n state: \"alive\" | \"dormant\" | \"expired\" | \"unknown\";\n}\n/** 条目旁独立 lease 文件的内容。注册表条目本体不承载短暂互斥状态。 */\nexport interface KeptSandboxLease {\n holder: string;\n op: string;\n acquiredAt: string;\n ttlMs: number;\n}\ninterface PersistedKeptSandboxLease extends KeptSandboxLease {\n token?: string;\n}\n/** 注册表自己的可恢复 I/O 失败;损坏或缺失的逐条目数据仍是成功 ADT。 */\nexport class KeptSandboxRegistryError extends Data.TaggedError(\"KeptSandboxRegistryError\")<{\n readonly operation: string;\n readonly cause: unknown;\n}> {\n}\nexport type KeptSandboxRegistryEffect<A> = Effect.Effect<A, KeptSandboxRegistryError>;\ntype RegistryEffect<A> = KeptSandboxRegistryEffect<A>;\nfunction registryError(operation: string, cause: unknown): KeptSandboxRegistryError {\n return new KeptSandboxRegistryError({ operation, cause });\n}\n/** Node Promise 边界:传入 Effect 的 signal,因而 fiber interruption 能传给支持 AbortSignal 的调用。 */\nfunction nodeIo<A>(operation: string, run: (signal: AbortSignal) => Promise<A>): RegistryEffect<A> {\n return Effect.tryPromise({\n try: run,\n catch: (cause) => registryError(operation, cause),\n });\n}\nfunction errnoCode(cause: unknown): string | undefined {\n const original = cause instanceof KeptSandboxRegistryError ? cause.cause : cause;\n return typeof original === \"object\" && original !== null && \"code\" in original && typeof original.code === \"string\"\n ? original.code\n : undefined;\n}\nfunction isAbsent(cause: unknown): boolean {\n const code = errnoCode(cause);\n return code === \"ENOENT\" || code === \"ENOTDIR\";\n}\nfunction withOpenFile<A>(operation: string, path: string, flags: string, use: (handle: FileHandle) => RegistryEffect<A>): RegistryEffect<A> {\n return Effect.scoped(Effect.acquireRelease(nodeIo(`${operation}: open`, (signal) => open(path, flags)), (handle) => nodeIo(`${operation}: close`, () => handle.close()).pipe(Effect.orDie)).pipe(Effect.flatMap(use)));\n}\nfunction readTextIfPresentEffect(path: string, operation: string): RegistryEffect<string | undefined> {\n return nodeIo(operation, (signal) => readFile(path, { encoding: \"utf-8\", signal })).pipe(Effect.catchAll((cause) => isAbsent(cause) ? Effect.succeed(undefined) : Effect.fail(cause)));\n}\nfunction readDirectoryIfPresentEffect(path: string, operation: string): RegistryEffect<string[] | undefined> {\n return nodeIo(operation, (signal) => readdir(path)).pipe(Effect.catchAll((cause) => isAbsent(cause) ? Effect.succeed(undefined) : Effect.fail(cause)));\n}\nfunction recordOf(value: unknown): globalThis.Record<string, unknown> | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? value as globalThis.Record<string, unknown>\n : undefined;\n}\nfunction isNonNegativeInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\nfunction isPositiveInteger(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value > 0;\n}\nfunction isTimestamp(value: unknown): value is string {\n return typeof value === \"string\" && Number.isFinite(Date.parse(value));\n}\nfunction isVerdict(value: unknown): value is Verdict {\n return value === \"passed\" || value === \"failed\" || value === \"errored\" || value === \"skipped\";\n}\nfunction isKeptSandboxState(value: unknown): value is KeptSandboxEntry[\"state\"] {\n return value === \"alive\" || value === \"dormant\" || value === \"expired\" || value === \"unknown\";\n}\n/** 留存条目的全部字段均在磁盘边界验证,特别是两个字符串联合与可选时间字段。 */\nfunction decodeKeptSandboxEntry(value: unknown): KeptSandboxEntry | undefined {\n const record = recordOf(value);\n if (record === undefined ||\n typeof record.sandboxId !== \"string\" ||\n typeof record.provider !== \"string\" ||\n typeof record.evalId !== \"string\" ||\n !isNonNegativeInteger(record.attempt) ||\n (record.experimentId !== undefined && typeof record.experimentId !== \"string\") ||\n typeof record.locator !== \"string\" ||\n !isVerdict(record.verdict) ||\n !isTimestamp(record.keptAt) ||\n typeof record.workdir !== \"string\" ||\n (record.enter !== undefined && typeof record.enter !== \"string\") ||\n (record.expiresAt !== undefined && !isTimestamp(record.expiresAt)) ||\n !isKeptSandboxState(record.state)) {\n return undefined;\n }\n return {\n sandboxId: record.sandboxId,\n provider: record.provider,\n evalId: record.evalId,\n attempt: record.attempt,\n ...(record.experimentId === undefined ? {} : { experimentId: record.experimentId }),\n locator: record.locator,\n verdict: record.verdict,\n keptAt: record.keptAt,\n workdir: record.workdir,\n ...(record.enter === undefined ? {} : { enter: record.enter }),\n ...(record.expiresAt === undefined ? {} : { expiresAt: record.expiresAt }),\n state: record.state,\n };\n}\n/** lease 同样来自持久 JSON;token 是内部互斥凭据,可选但类型必须正确。 */\nfunction decodeKeptSandboxLease(value: unknown): PersistedKeptSandboxLease | undefined {\n const record = recordOf(value);\n if (record === undefined ||\n typeof record.holder !== \"string\" ||\n typeof record.op !== \"string\" ||\n !isTimestamp(record.acquiredAt) ||\n !isPositiveInteger(record.ttlMs) ||\n (record.token !== undefined && typeof record.token !== \"string\")) {\n return undefined;\n }\n return {\n holder: record.holder,\n op: record.op,\n acquiredAt: record.acquiredAt,\n ttlMs: record.ttlMs,\n ...(record.token === undefined ? {} : { token: record.token }),\n };\n}\n/** entry id:provider + sandboxId 的稳定散列(条目文件名)。 */\nexport function keptEntryId(provider: string, sandboxId: string): string {\n return hashEntryId([provider, sandboxId]);\n}\nexport function sandboxesDirOf(niceevalRoot: string): string {\n return join(niceevalRoot, \"sandboxes\");\n}\nfunction leasePath(niceevalRoot: string, id: string): string {\n return join(sandboxesDirOf(niceevalRoot), `${id}.lease`);\n}\n/** 读取当前 lease;坏文件也视为占坑,避免在不明状态下并发操作现场。 */\nfunction readPersistedKeptLeaseEffect(niceevalRoot: string, id: string): RegistryEffect<PersistedKeptSandboxLease | undefined> {\n return readTextIfPresentEffect(leasePath(niceevalRoot, id), \"read kept sandbox lease\").pipe(Effect.map((raw) => {\n if (raw === undefined)\n return undefined;\n try {\n return decodeKeptSandboxLease(JSON.parse(raw));\n }\n catch {\n return undefined;\n }\n }));\n}\nexport function readKeptLeaseEffect(niceevalRoot: string, id: string): RegistryEffect<KeptSandboxLease | undefined> {\n return readPersistedKeptLeaseEffect(niceevalRoot, id).pipe(Effect.map((lease) => lease === undefined\n ? undefined\n : {\n holder: lease.holder,\n op: lease.op,\n acquiredAt: lease.acquiredAt,\n ttlMs: lease.ttlMs,\n }));\n}\nfunction createKeptLeaseExclusiveEffect(dir: string, id: string, payload: PersistedKeptSandboxLease): RegistryEffect<boolean> {\n const path = leasePathFromDir(dir, id);\n return nodeIo(\"create kept sandbox lease directory\", () => mkdir(dir, { recursive: true })).pipe(Effect.zipRight(withOpenFile(\"create kept sandbox lease\", path, \"wx\", (handle) => nodeIo(\"write kept sandbox lease\", (signal) => handle.writeFile(JSON.stringify(payload), { encoding: \"utf-8\", signal })).pipe(Effect.zipRight(nodeIo(\"sync kept sandbox lease\", () => handle.sync()))))), Effect.as(true), Effect.catchAll((cause) => errnoCode(cause) === \"EEXIST\" ? Effect.succeed(false) : Effect.fail(cause)));\n}\nfunction leasePathFromDir(dir: string, id: string): string {\n return join(dir, `${id}.lease`);\n}\n/**\n * 原子占坑:`wx` 是唯一持有点。TTL 到期时先移除旧文件、再重新竞争;因此旧持有者 finally\n * 只会尝试删除带自己 token 的文件,绝不剥离后来者。\n */\nexport function acquireKeptLeaseEffect(niceevalRoot: string, id: string, lease: KeptSandboxLease): RegistryEffect<{\n acquired: true;\n token: string;\n} | {\n acquired: false;\n lease: KeptSandboxLease;\n}> {\n const dir = sandboxesDirOf(niceevalRoot);\n const path = leasePathFromDir(dir, id);\n return Effect.gen(function* () {\n const token = `${lease.holder}:${lease.acquiredAt}:${Math.random().toString(36).slice(2)}`;\n const payload = { ...lease, token };\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (yield* createKeptLeaseExclusiveEffect(dir, id, payload))\n return { acquired: true, token };\n const current = yield* readKeptLeaseEffect(niceevalRoot, id);\n if (current && Date.now() - Date.parse(current.acquiredAt) < current.ttlMs) {\n return { acquired: false, lease: current };\n }\n // 过期或损坏的 lease 可以被接管;unlink 后所有竞争者重新 wx,不能覆盖彼此。\n yield* nodeIo(\"remove expired kept sandbox lease\", () => rm(path, { force: true }));\n }\n const current = yield* readKeptLeaseEffect(niceevalRoot, id);\n return { acquired: false, lease: current ?? lease };\n });\n}\nexport function releaseKeptLeaseEffect(niceevalRoot: string, id: string, token: string): RegistryEffect<void> {\n const path = leasePath(niceevalRoot, id);\n return readPersistedKeptLeaseEffect(niceevalRoot, id).pipe(Effect.flatMap((current) => current?.token === token\n ? nodeIo(\"release kept sandbox lease\", () => rm(path, { force: true }))\n : Effect.void));\n}\n/**\n * 注册表发现:从 cwd 向上找最近的 `.niceeval/`(与结果根发现同一规则)。\n * 找不到返回 undefined,调用方报错并提示 `--run <结果根>`。\n */\nexport function findNiceevalRootEffect(cwd: string): RegistryEffect<string | undefined> {\n return Effect.gen(function* () {\n let current = resolve(cwd);\n for (;;) {\n const candidate = join(current, \".niceeval\");\n const entries = yield* readDirectoryIfPresentEffect(candidate, \"find .niceeval directory\");\n if (entries !== undefined)\n return candidate;\n const parent = dirname(current);\n if (parent === current)\n return undefined;\n current = parent;\n }\n });\n}\n/** 原子写入一条登记项(委托给共享层的 write-tmp-then-rename 纪律)。 */\nexport function writeKeptEntryEffect(niceevalRoot: string, entry: KeptSandboxEntry): RegistryEffect<void> {\n const id = keptEntryId(entry.provider, entry.sandboxId);\n return writeEntryFileEffect(sandboxesDirOf(niceevalRoot), id, entry).pipe(Effect.mapError((cause) => registryError(\"write kept sandbox entry\", cause)));\n}\n/**\n * 读全部登记项(坏条目跳过并记名,不整体失败)。逐条目解析走共享层的 `readEntryFile`(损坏\n * 返回 undefined、不抛错);目录扫描与 malformed 文件名收集是留存注册表自己的诊断需求\n * (`readAllEntryFiles` 只做静默跳过,不回传坏文件名),因此这里保留自己的扫描循环。\n */\nexport function readKeptEntriesEffect(niceevalRoot: string): RegistryEffect<{\n entries: {\n id: string;\n entry: KeptSandboxEntry;\n }[];\n malformed: string[];\n}> {\n const dir = sandboxesDirOf(niceevalRoot);\n return Effect.gen(function* () {\n const files = (yield* readDirectoryIfPresentEffect(dir, \"read kept sandbox registry directory\")) ?? [];\n const decoded = yield* Effect.forEach(files, (file) => {\n if (!file.endsWith(\".json\") || file.startsWith(\".\"))\n return Effect.succeed(undefined);\n const id = file.slice(0, -\".json\".length);\n return readEntryFileEffect(dir, id, decodeKeptSandboxEntry).pipe(Effect.mapError((cause) => registryError(\"read kept sandbox entry\", cause)), Effect.map((entry) => ({ file, id, entry })));\n });\n const entries: {\n id: string;\n entry: KeptSandboxEntry;\n }[] = [];\n const malformed: string[] = [];\n for (const decodedEntry of decoded) {\n if (decodedEntry === undefined)\n continue;\n if (decodedEntry.entry === undefined)\n malformed.push(decodedEntry.file);\n else\n entries.push({ id: decodedEntry.id, entry: decodedEntry.entry });\n }\n entries.sort((a, b) => a.entry.keptAt.localeCompare(b.entry.keptAt));\n return { entries, malformed };\n });\n}\n/** 更新一条登记项(读-改-原子写;字段浅合并)。条目不存在时静默返回 false。 */\nexport function updateKeptEntryEffect(niceevalRoot: string, id: string, patch: Partial<KeptSandboxEntry> | ((entry: KeptSandboxEntry) => KeptSandboxEntry)): RegistryEffect<boolean> {\n const dir = sandboxesDirOf(niceevalRoot);\n return readEntryFileEffect(dir, id, decodeKeptSandboxEntry).pipe(Effect.mapError((cause) => registryError(\"read kept sandbox entry for update\", cause)), Effect.flatMap((entry) => {\n if (entry === undefined)\n return Effect.succeed(false);\n const next = typeof patch === \"function\" ? patch(entry) : { ...entry, ...patch };\n return writeKeptEntryEffect(niceevalRoot, next).pipe(Effect.as(true));\n }));\n}\n/** 删除一条登记项并同步目录(只在实例成功销毁或确认已不存在后调用)。 */\nexport function removeKeptEntryEffect(niceevalRoot: string, id: string): RegistryEffect<void> {\n const dir = sandboxesDirOf(niceevalRoot);\n return nodeIo(\"remove kept sandbox entry\", () => rm(join(dir, `${id}.json`), { force: true })).pipe(Effect.zipRight(fsyncDirEffect(dir).pipe(Effect.mapError((cause) => registryError(\"sync kept sandbox registry directory\", cause)))));\n}\n"]}
1
+ {"version":3,"file":"keep-registry.js","sourceRoot":"","sources":["keep-registry.ts"],"names":[],"mappings":"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","sourcesContent":["// 留存沙箱的持久注册表:`.niceeval/sandboxes/` 下的逐条目文件(不是多个 attempt 竞争改写的\n// 一份 JSON)。entry id 由 provider + sandboxId 做稳定散列;每条走 shared/entry-file-store.ts\n// 的原子写纪律(临时文件 → fsync 文件 → rename → fsync 目录)——不同 attempt 与不同 niceeval\n// 进程不会覆盖彼此。\n// 契约见 docs/feature/sandbox/architecture.md「留存(keep)与注册表」。\n//\n// 条目旁独立的 `.lease` 文件是另一套机制(短命的操作互斥,见 acquireKeptLease 一节),不走\n// entry-file-store 的原子写纪律——lease 的持有点是 `wx` 独占创建本身,不需要 tmp+rename。\nimport { mkdir, open, readdir, readFile, rm, type FileHandle } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { Data, Effect, Either, Schema } from \"effect\";\nimport { fsyncDirEffect, hashEntryId, readEntryFileEffect, writeEntryFileEffect, } from \"../shared/entry-file-store.ts\";\nimport type { Verdict } from \"../types.ts\";\n/** 一条留存登记项(逐条目文件的 JSON 形状)。 */\nexport interface KeptSandboxEntry {\n sandboxId: string;\n provider: string;\n evalId: string;\n attempt: number;\n experimentId?: string;\n locator: string;\n verdict: Verdict;\n keptAt: string;\n workdir: string;\n /** provider 原生的进入命令(直连与审计用);日常入口是 `niceeval sandbox enter`。 */\n enter?: string;\n /** 现场可找回的截止时刻——provider 声明了保留期限才写(vercel 写,e2b pause 无限期保留则不写)。 */\n expiresAt?: string;\n /** alive = 实例在跑;dormant = 可唤醒;expired = 确认不存在;unknown = 探测失败。 */\n state: \"alive\" | \"dormant\" | \"expired\" | \"unknown\";\n}\n/** 条目旁独立 lease 文件的内容。注册表条目本体不承载短暂互斥状态。 */\nexport interface KeptSandboxLease {\n holder: string;\n op: string;\n acquiredAt: string;\n ttlMs: number;\n}\ninterface PersistedKeptSandboxLease extends KeptSandboxLease {\n token?: string;\n}\n/** 注册表自己的可恢复 I/O 失败;损坏或缺失的逐条目数据仍是成功 ADT。 */\nexport class KeptSandboxRegistryError extends Data.TaggedError(\"KeptSandboxRegistryError\")<{\n readonly operation: string;\n readonly cause: unknown;\n}> {\n}\nexport type KeptSandboxRegistryEffect<A> = Effect.Effect<A, KeptSandboxRegistryError>;\ntype RegistryEffect<A> = KeptSandboxRegistryEffect<A>;\nfunction registryError(operation: string, cause: unknown): KeptSandboxRegistryError {\n return new KeptSandboxRegistryError({ operation, cause });\n}\n/** Node Promise 边界:传入 Effect 的 signal,因而 fiber interruption 能传给支持 AbortSignal 的调用。 */\nfunction nodeIo<A>(operation: string, run: (signal: AbortSignal) => Promise<A>): RegistryEffect<A> {\n return Effect.tryPromise({\n try: run,\n catch: (cause) => registryError(operation, cause),\n });\n}\nfunction errnoCode(cause: unknown): string | undefined {\n const original = cause instanceof KeptSandboxRegistryError ? cause.cause : cause;\n return typeof original === \"object\" && original !== null && \"code\" in original && typeof original.code === \"string\"\n ? original.code\n : undefined;\n}\nfunction isAbsent(cause: unknown): boolean {\n const code = errnoCode(cause);\n return code === \"ENOENT\" || code === \"ENOTDIR\";\n}\nfunction withOpenFile<A>(operation: string, path: string, flags: string, use: (handle: FileHandle) => RegistryEffect<A>): RegistryEffect<A> {\n return Effect.scoped(Effect.acquireRelease(nodeIo(`${operation}: open`, (signal) => open(path, flags)), (handle) => nodeIo(`${operation}: close`, () => handle.close()).pipe(Effect.orDie)).pipe(Effect.flatMap(use)));\n}\nfunction readTextIfPresentEffect(path: string, operation: string): RegistryEffect<string | undefined> {\n return nodeIo(operation, (signal) => readFile(path, { encoding: \"utf-8\", signal })).pipe(Effect.catchAll((cause) => isAbsent(cause) ? Effect.succeed(undefined) : Effect.fail(cause)));\n}\nfunction readDirectoryIfPresentEffect(path: string, operation: string): RegistryEffect<string[] | undefined> {\n return nodeIo(operation, (signal) => readdir(path)).pipe(Effect.catchAll((cause) => isAbsent(cause) ? Effect.succeed(undefined) : Effect.fail(cause)));\n}\nconst NonNegativeSafeIntegerSchema = Schema.Number.pipe(Schema.filter((value) => Number.isSafeInteger(value) && value >= 0, { identifier: \"NonNegativeSafeInteger\" }));\nconst PositiveSafeIntegerSchema = Schema.Number.pipe(Schema.filter((value) => Number.isSafeInteger(value) && value > 0, { identifier: \"PositiveSafeInteger\" }));\nconst TimestampSchema = Schema.String.pipe(Schema.filter((value) => Number.isFinite(Date.parse(value)), { identifier: \"Timestamp\" }));\nconst KeptSandboxEntrySchema = Schema.Struct({\n sandboxId: Schema.String,\n provider: Schema.String,\n evalId: Schema.String,\n attempt: NonNegativeSafeIntegerSchema,\n experimentId: Schema.optional(Schema.String),\n locator: Schema.String,\n verdict: Schema.Literal(\"passed\", \"failed\", \"errored\", \"skipped\"),\n keptAt: TimestampSchema,\n workdir: Schema.String,\n enter: Schema.optional(Schema.String),\n expiresAt: Schema.optional(TimestampSchema),\n state: Schema.Literal(\"alive\", \"dormant\", \"expired\", \"unknown\"),\n});\nconst PersistedKeptSandboxLeaseSchema = Schema.Struct({\n holder: Schema.String,\n op: Schema.String,\n acquiredAt: TimestampSchema,\n ttlMs: PositiveSafeIntegerSchema,\n token: Schema.optional(Schema.String),\n});\n/** 留存条目的全部字段均在磁盘边界验证,特别是两个字符串联合与可选时间字段。 */\nfunction decodeKeptSandboxEntry(value: unknown): KeptSandboxEntry | undefined {\n const decoded = Schema.decodeUnknownEither(KeptSandboxEntrySchema)(value);\n return Either.isRight(decoded) ? decoded.right : undefined;\n}\n/** lease 同样来自持久 JSON;token 是内部互斥凭据,可选但类型必须正确。 */\nfunction decodeKeptSandboxLease(value: unknown): PersistedKeptSandboxLease | undefined {\n const decoded = Schema.decodeUnknownEither(PersistedKeptSandboxLeaseSchema)(value);\n return Either.isRight(decoded) ? decoded.right : undefined;\n}\n/** entry id:provider + sandboxId 的稳定散列(条目文件名)。 */\nexport function keptEntryId(provider: string, sandboxId: string): string {\n return hashEntryId([provider, sandboxId]);\n}\nexport function sandboxesDirOf(niceevalRoot: string): string {\n return join(niceevalRoot, \"sandboxes\");\n}\nfunction leasePath(niceevalRoot: string, id: string): string {\n return join(sandboxesDirOf(niceevalRoot), `${id}.lease`);\n}\n/** 读取当前 lease;坏文件也视为占坑,避免在不明状态下并发操作现场。 */\nfunction readPersistedKeptLeaseEffect(niceevalRoot: string, id: string): RegistryEffect<PersistedKeptSandboxLease | undefined> {\n return readTextIfPresentEffect(leasePath(niceevalRoot, id), \"read kept sandbox lease\").pipe(Effect.map((raw) => {\n if (raw === undefined)\n return undefined;\n try {\n return decodeKeptSandboxLease(JSON.parse(raw));\n }\n catch {\n return undefined;\n }\n }));\n}\nexport function readKeptLeaseEffect(niceevalRoot: string, id: string): RegistryEffect<KeptSandboxLease | undefined> {\n return readPersistedKeptLeaseEffect(niceevalRoot, id).pipe(Effect.map((lease) => lease === undefined\n ? undefined\n : {\n holder: lease.holder,\n op: lease.op,\n acquiredAt: lease.acquiredAt,\n ttlMs: lease.ttlMs,\n }));\n}\nfunction createKeptLeaseExclusiveEffect(dir: string, id: string, payload: PersistedKeptSandboxLease): RegistryEffect<boolean> {\n const path = leasePathFromDir(dir, id);\n return nodeIo(\"create kept sandbox lease directory\", () => mkdir(dir, { recursive: true })).pipe(Effect.zipRight(withOpenFile(\"create kept sandbox lease\", path, \"wx\", (handle) => nodeIo(\"write kept sandbox lease\", (signal) => handle.writeFile(JSON.stringify(payload), { encoding: \"utf-8\", signal })).pipe(Effect.zipRight(nodeIo(\"sync kept sandbox lease\", () => handle.sync()))))), Effect.as(true), Effect.catchAll((cause) => errnoCode(cause) === \"EEXIST\" ? Effect.succeed(false) : Effect.fail(cause)));\n}\nfunction leasePathFromDir(dir: string, id: string): string {\n return join(dir, `${id}.lease`);\n}\n/**\n * 原子占坑:`wx` 是唯一持有点。TTL 到期时先移除旧文件、再重新竞争;因此旧持有者 finally\n * 只会尝试删除带自己 token 的文件,绝不剥离后来者。\n */\nexport function acquireKeptLeaseEffect(niceevalRoot: string, id: string, lease: KeptSandboxLease): RegistryEffect<{\n acquired: true;\n token: string;\n} | {\n acquired: false;\n lease: KeptSandboxLease;\n}> {\n const dir = sandboxesDirOf(niceevalRoot);\n const path = leasePathFromDir(dir, id);\n return Effect.gen(function* () {\n const token = `${lease.holder}:${lease.acquiredAt}:${Math.random().toString(36).slice(2)}`;\n const payload = { ...lease, token };\n for (let attempt = 0; attempt < 2; attempt += 1) {\n if (yield* createKeptLeaseExclusiveEffect(dir, id, payload))\n return { acquired: true, token };\n const current = yield* readKeptLeaseEffect(niceevalRoot, id);\n if (current && Date.now() - Date.parse(current.acquiredAt) < current.ttlMs) {\n return { acquired: false, lease: current };\n }\n // 过期或损坏的 lease 可以被接管;unlink 后所有竞争者重新 wx,不能覆盖彼此。\n yield* nodeIo(\"remove expired kept sandbox lease\", () => rm(path, { force: true }));\n }\n const current = yield* readKeptLeaseEffect(niceevalRoot, id);\n return { acquired: false, lease: current ?? lease };\n });\n}\nexport function releaseKeptLeaseEffect(niceevalRoot: string, id: string, token: string): RegistryEffect<void> {\n const path = leasePath(niceevalRoot, id);\n return readPersistedKeptLeaseEffect(niceevalRoot, id).pipe(Effect.flatMap((current) => current?.token === token\n ? nodeIo(\"release kept sandbox lease\", () => rm(path, { force: true }))\n : Effect.void));\n}\n/**\n * 注册表发现:从 cwd 向上找最近的 `.niceeval/`(与结果根发现同一规则)。\n * 找不到返回 undefined,调用方报错并提示 `--run <结果根>`。\n */\nexport function findNiceevalRootEffect(cwd: string): RegistryEffect<string | undefined> {\n return Effect.gen(function* () {\n let current = resolve(cwd);\n for (;;) {\n const candidate = join(current, \".niceeval\");\n const entries = yield* readDirectoryIfPresentEffect(candidate, \"find .niceeval directory\");\n if (entries !== undefined)\n return candidate;\n const parent = dirname(current);\n if (parent === current)\n return undefined;\n current = parent;\n }\n });\n}\n/** 原子写入一条登记项(委托给共享层的 write-tmp-then-rename 纪律)。 */\nexport function writeKeptEntryEffect(niceevalRoot: string, entry: KeptSandboxEntry): RegistryEffect<void> {\n const id = keptEntryId(entry.provider, entry.sandboxId);\n return writeEntryFileEffect(sandboxesDirOf(niceevalRoot), id, entry).pipe(Effect.mapError((cause) => registryError(\"write kept sandbox entry\", cause)));\n}\n/**\n * 读全部登记项(坏条目跳过并记名,不整体失败)。逐条目解析走共享层的 `readEntryFile`(损坏\n * 返回 undefined、不抛错);目录扫描与 malformed 文件名收集是留存注册表自己的诊断需求\n * (`readAllEntryFiles` 只做静默跳过,不回传坏文件名),因此这里保留自己的扫描循环。\n */\nexport function readKeptEntriesEffect(niceevalRoot: string): RegistryEffect<{\n entries: {\n id: string;\n entry: KeptSandboxEntry;\n }[];\n malformed: string[];\n}> {\n const dir = sandboxesDirOf(niceevalRoot);\n return Effect.gen(function* () {\n const files = (yield* readDirectoryIfPresentEffect(dir, \"read kept sandbox registry directory\")) ?? [];\n const decoded = yield* Effect.forEach(files, (file) => {\n if (!file.endsWith(\".json\") || file.startsWith(\".\"))\n return Effect.succeed(undefined);\n const id = file.slice(0, -\".json\".length);\n return readEntryFileEffect(dir, id, decodeKeptSandboxEntry).pipe(Effect.mapError((cause) => registryError(\"read kept sandbox entry\", cause)), Effect.map((entry) => ({ file, id, entry })));\n });\n const entries: {\n id: string;\n entry: KeptSandboxEntry;\n }[] = [];\n const malformed: string[] = [];\n for (const decodedEntry of decoded) {\n if (decodedEntry === undefined)\n continue;\n if (decodedEntry.entry === undefined)\n malformed.push(decodedEntry.file);\n else\n entries.push({ id: decodedEntry.id, entry: decodedEntry.entry });\n }\n entries.sort((a, b) => a.entry.keptAt.localeCompare(b.entry.keptAt));\n return { entries, malformed };\n });\n}\n/** 更新一条登记项(读-改-原子写;字段浅合并)。条目不存在时静默返回 false。 */\nexport function updateKeptEntryEffect(niceevalRoot: string, id: string, patch: Partial<KeptSandboxEntry> | ((entry: KeptSandboxEntry) => KeptSandboxEntry)): RegistryEffect<boolean> {\n const dir = sandboxesDirOf(niceevalRoot);\n return readEntryFileEffect(dir, id, decodeKeptSandboxEntry).pipe(Effect.mapError((cause) => registryError(\"read kept sandbox entry for update\", cause)), Effect.flatMap((entry) => {\n if (entry === undefined)\n return Effect.succeed(false);\n const next = typeof patch === \"function\" ? patch(entry) : { ...entry, ...patch };\n return writeKeptEntryEffect(niceevalRoot, next).pipe(Effect.as(true));\n }));\n}\n/** 删除一条登记项并同步目录(只在实例成功销毁或确认已不存在后调用)。 */\nexport function removeKeptEntryEffect(niceevalRoot: string, id: string): RegistryEffect<void> {\n const dir = sandboxesDirOf(niceevalRoot);\n return nodeIo(\"remove kept sandbox entry\", () => rm(join(dir, `${id}.json`), { force: true })).pipe(Effect.zipRight(fsyncDirEffect(dir).pipe(Effect.mapError((cause) => registryError(\"sync kept sandbox registry directory\", cause)))));\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["// sandbox 域类型:运行中 Sandbox、生命周期 hook、命令与文件 IO 的形状。\n// 作者声明使用 layer.ts 的闭合 SandboxLayer;本文件不再保留旧 provider spec。\nimport type { ScopedFeedback } from \"../shared/types.ts\";\nexport interface CommandResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n /**\n * 这次执行的命令摘要(有界、已脱敏,与时间树 command 节点同一份文案)。由执行面在最外层\n * 公开调用处附加;直接从 provider 拿到的裸结果可能没有。断言失败时用作 evidence\n * (`commandSucceeded()` 的「命令行本身」),消费方按可选字段读。\n */\n command?: string;\n}\n/** 内置 provider 名;ProviderModule 的完成态计划使用普通 string 兼容自定义 provider。 */\nexport type SandboxProvider = \"docker\" | \"vercel\" | \"e2b\" | \"local\";\n/** 镜像/模板里的 Node 运行时版本。 */\nexport type SandboxRuntime = \"node20\" | \"node24\";\n/**\n * Sandbox hook 的窄上下文:只有 `experimentId`、`signal` 与作用域绑定的 `progress/diagnostic`,\n * 不借用包含 session / model / telemetry 的完整 `AgentContext`\n * (见 docs/feature/sandbox/library.md「环境层生命周期钩子」)。\n */\nexport interface SandboxHookContext extends ScopedFeedback {\n /** 运行计划中已解析的实验 id;生命周期 hook 不在脱离 Experiment 的上下文执行。 */\n readonly experimentId: string;\n /** 当前物理 Sandbox 归属的 Eval Group;未分组 Sandbox 省略。 */\n readonly evalGroup?: {\n readonly id: string;\n readonly definitionHash: string;\n };\n /** 当前 Invocation 的中止信号;物理 Sandbox 生命周期不从某条 Attempt 借用信号。 */\n readonly signal: AbortSignal;\n}\n/** 沙箱级生命周期钩子(`SandboxLayer.setup()` / `.teardown()` 链式挂载)。 */\nexport type SandboxHook = (sandbox: Sandbox, ctx: SandboxHookContext) => void | Promise<void>;\nexport interface CommandOptions {\n /**\n * 这条命令已知会处理的敏感明文(例如 API key、token、HTTP header value)。Runner 仍把\n * 原值交给 provider 执行,但在任何 timing / commands / execution / error 证据落盘前按\n * 这些值做精确替换;本数组本身不落盘、不进指纹。空字符串被忽略。\n *\n * 这是显式 provenance,不是 secret 扫描器:没有登记的自由文本无法被可靠识别;值若先被\n * 调用方编码或拆分,应把实际会出现在命令/输出里的编码形态一并登记。\n */\n readonly sensitiveValues?: readonly string[];\n /**\n * 追加/覆盖本命令的环境变量(与 Sandbox 默认环境叠加,不清空默认值)。`PATH` 是 Sandbox\n * 受管变量,各 provider 保留自己算出的 `PATH`,不保证能被这里覆盖;需要扩展 PATH 用\n * Sandbox factory 的 `pathPrepend`(见 docs/feature/sandbox/library.md「PATH:受管变量与\n * pathPrepend」)。\n */\n readonly env?: Readonly<globalThis.Record<string, string>>;\n /** 本命令的工作目录;省略时落到 `Sandbox.workdir`。相对路径按 workdir 解析,绝对路径原样使用。 */\n readonly cwd?: string;\n /**\n * 把本命令的输出也送进 Sandbox 的「原生日志流」(于是 `docker logs` / Docker UI 的 Logs\n * 标签页能实时看到它)。给 agent 命令(codex exec / bub run / claude)开它,就能在容器\n * 日志里看到 agent 的【原始输出】。provider 各自实现(docker:tee 到 PID1 tail 的文件;\n * 不支持的 provider 忽略)—— 日志怎么浮现是 provider 的事,adapter 只声明意图。\n */\n readonly stream?: boolean;\n /**\n * 命令 stdout 每到一块就调用一次。回调只用于运行中的短命反馈;完整 stdout 仍会原样\n * 出现在返回的 `CommandResult` 里。provider 不支持真流时,至少会在命令结束后按完整\n * stdout 调用一次,不能静默丢掉。\n */\n readonly onStdout?: (chunk: string) => void | Promise<void>;\n /** `onStdout` 的 stderr 对应物;完整 stderr 仍保留在 `CommandResult`。 */\n readonly onStderr?: (chunk: string) => void | Promise<void>;\n /**\n * 覆盖本条命令的执行身份;省略 = Sandbox 默认身份(沿用环境自己声明的身份——Docker 镜像 `USER`、\n * Compose service `user:`、E2B template 默认用户、宿主当前用户,见\n * docs/feature/sandbox/library.md「执行身份」)。\n *\n * 语义跨 provider 一致,各 provider 映射到自己的原生机制(docker:`exec --user`;E2B:\n * `{ user }`;Vercel:只认 `\"root\"`,映射 `{ sudo: true }`,其它值报错;local:任何值都报错)。\n * 本就全程 root 的 provider视作 no-op;完全无法换身份的 provider 可不支持(抛错)—— 但**省略与\n * 显式值的语义保持一致**,不因 provider 而变。\n */\n readonly user?: string;\n /**\n * 这条命令自己的上限(毫秒)。**省略才是常态**:省略时上限 = attempt deadline 的剩余量\n * (见 docs/feature/sandbox/architecture.md「时限归属」),provider 层没有独立默认。\n * 显式传一个更短的值是有意声明,照常生效;撞线时归属记成「命令显式 timeout」。\n */\n readonly timeoutMs?: number;\n /**\n * 取消本次受管命令树。Provider 必须在 Promise settle 前确认命令树已经终止;无法精确\n * 终止时应退休整个 Sandbox,不能只关闭 transport 后把进程留在后台。\n */\n readonly signal?: AbortSignal;\n}\nexport interface SuccessfulCommandResult extends CommandResult {\n readonly exitCode: 0;\n}\n/** 三个运行中 Sandbox 视图共用的操作词汇;同名成员在不同视图中不得改变语义。 */\nexport interface SandboxOperations {\n /** Sandbox 内项目/工作区根目录的绝对路径(agent 命令的默认 cwd,也是 git baseline 提交的位置)。各方法的相对路径都以此为基准解析,省略 `cwd`/`targetDir` 时也落到这里。 */\n readonly workdir: string;\n /**\n * 执行单个命令,`args` 作为独立 argv 传递、不经 shell 解释(无 `&&`、管道、通配符展开)。\n * 只想跑一个可执行文件、参数来自外部输入、担心注入时优先用它。\n */\n runCommand(cmd: string, args?: readonly string[], opts?: CommandOptions): Promise<CommandResult>;\n /**\n * 执行一整段脚本,经 shell(bash)解释,支持 `&&`、管道、`$()`、重定向等。\n * 需要拼多条命令或做条件判断时用它。\n */\n runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;\n /**\n * 像 `runCommand` 一样执行单个命令,但非零退出时抛出 `SandboxCommandExitError`。错误消息附带\n * 有界、已清理和脱敏的 stderr 尾部;stderr 为空时回退 stdout。完整输出保留在异常的\n * `result` 字段中。成功结果的 `exitCode` 在类型上固定为 `0`。\n */\n runCommandOrThrow(cmd: string, args?: readonly string[], opts?: CommandOptions): Promise<SuccessfulCommandResult>;\n /**\n * 像 `runShell` 一样执行 shell 脚本,但非零退出时抛出 `SandboxCommandExitError`。错误摘要、\n * 完整输出和成功结果的语义与 `runCommandOrThrow` 相同。\n */\n runShellOrThrow(script: string, opts?: CommandOptions): Promise<SuccessfulCommandResult>;\n /** 读取 Sandbox 内文件的文本内容(UTF-8)。文件不存在时抛错。 */\n readText(path: string): Promise<string>;\n /** 写入 Sandbox 内一个 UTF-8 文本文件;父目录不存在时自动创建。 */\n writeText(path: string, content: string): Promise<void>;\n /** 精确读取 Sandbox 内文件字节;公共契约不绑定 Node Buffer。 */\n readBytes(path: string): Promise<Uint8Array>;\n /** 精确写入 Sandbox 内文件字节;父目录不存在时自动创建。 */\n writeBytes(path: string, content: Uint8Array): Promise<void>;\n /** 检查 Sandbox 内文件或目录路径是否存在。 */\n pathExists(path: string): Promise<boolean>;\n}\n/** 只用于宿主机与 Sandbox 之间真实传输;内存内容读写使用 read/writeText/Bytes。 */\nexport interface SandboxTransferOperations {\n uploadFile(source: string | URL, targetPath: string): Promise<void>;\n uploadDirectory(sourceDir: string | URL, targetDir?: string, options?: {\n readonly ignore?: readonly string[];\n }): Promise<void>;\n downloadFile(sourcePath: string, target: string | URL): Promise<void>;\n downloadDirectory(sourceDir: string, targetDir: string | URL, options?: {\n readonly ignore?: readonly string[];\n }): Promise<void>;\n}\nexport interface Sandbox extends SandboxOperations, SandboxTransferOperations {\n /** 销毁 Sandbox 占用的计算资源(容器/microVM)。调用后 Sandbox 不可再用;是否可安全重复调用因 provider 而异,不要依赖这一点。 */\n stop(): Promise<void>;\n /** 本 Sandbox 的稳定标识(各 provider 原生 ID,如 Docker 容器 ID 前缀);用于跨调用关联同一 Sandbox 的会话状态,也用于日志展示。 */\n readonly sandboxId: string;\n /**\n * OTLP receiver 的放置能力。\n * - `string`:Sandbox 内可通过该 hostname 访问宿主 receiver。\n * - `null`:provider 不承诺宿主回连;runner 尝试在 Sandbox 内启动 attempt-scope receiver。\n * 这不保证 tracing 成功;镜像缺少 receiver 所需运行时时只记录 supplemental diagnostic。\n * `defineConfig({ telemetry: { host } })` 可在作者已经提供受控 tunnel 时显式覆盖。\n */\n readonly otlpHost: string | null;\n /**\n * 可选:把一行写进容器的「主日志」(PID1 在 tail 它)——于是 `docker logs` /\n * Docker UI 的 Logs 标签页能实时看到 agent 逐轮活动。docker provider 实现,其它可省略。\n */\n appendLog?(line: string): Promise<void>;\n}\n/** 复用调度需要的中立寿命确认能力。 */\nexport interface SandboxReuseCapability {\n ensureLifetime(minRemainingMs: number): Promise<{\n ready: true;\n expiresAt?: string;\n } | {\n ready: false;\n reason: string;\n }>;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"","sourcesContent":["// sandbox 域类型:运行中 Sandbox、生命周期 hook、命令与文件 IO 的形状。\n// 作者声明使用 layer.ts 的闭合 SandboxLayer;本文件不再保留旧 provider spec。\nimport type { ScopedFeedback } from \"../shared/types.ts\";\nexport interface CommandResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n /**\n * 这次执行的命令摘要(有界、已脱敏,与时间树 command 节点同一份文案)。由执行面在最外层\n * 公开调用处附加;直接从 provider 拿到的裸结果可能没有。断言失败时用作 evidence\n * (`commandSucceeded()` 的「命令行本身」),消费方按可选字段读。\n */\n command?: string;\n}\n/** 内置 provider 名;ProviderModule 的完成态计划使用普通 string 兼容自定义 provider。 */\nexport type SandboxProvider = \"docker\" | \"vercel\" | \"e2b\" | \"local\";\n/** 镜像/模板里的 Node 运行时版本。 */\nexport type SandboxRuntime = \"node20\" | \"node24\";\n/**\n * Sandbox hook 的窄上下文:只有 `experimentId`、`signal` 与作用域绑定的 `progress/diagnostic`,\n * 不借用包含 session / model / telemetry 的完整 `AgentContext`\n * (见 docs/feature/sandbox/library.md「环境层生命周期钩子」)。\n */\nexport interface SandboxHookContext extends ScopedFeedback {\n /** 运行计划中已解析的实验 id;生命周期 hook 不在脱离 Experiment 的上下文执行。 */\n readonly experimentId: string;\n /** 当前物理 Sandbox 归属的 Eval Group;未分组 Sandbox 省略。 */\n readonly evalGroup?: {\n readonly id: string;\n readonly definitionHash: string;\n };\n /** 当前 Invocation 的中止信号;物理 Sandbox 生命周期不从某条 Attempt 借用信号。 */\n readonly signal: AbortSignal;\n}\n/** 沙箱级生命周期钩子(`SandboxLayer.setup()` / `.teardown()` 链式挂载)。 */\nexport type SandboxHook = (sandbox: Sandbox, ctx: SandboxHookContext) => void | Promise<void>;\nexport interface CommandOptions {\n /**\n * 这条命令已知会处理的敏感明文(例如 API key、token、HTTP header value)。Runner 仍把\n * 原值交给 provider 执行,但在任何 timing / commands / execution / error 证据落盘前按\n * 这些值做精确替换;本数组本身不落盘、不进指纹。空字符串被忽略。\n *\n * 这是显式 provenance,不是 secret 扫描器:没有登记的自由文本无法被可靠识别;值若先被\n * 调用方编码或拆分,应把实际会出现在命令/输出里的编码形态一并登记。\n */\n readonly sensitiveValues?: readonly string[];\n /**\n * 追加/覆盖本命令的环境变量(与 Sandbox 默认环境叠加,不清空默认值)。`PATH` 是 Sandbox\n * 受管变量,各 provider 保留自己算出的 `PATH`,不保证能被这里覆盖;需要扩展 PATH 用\n * Sandbox factory 的 `pathPrepend`(见 docs/feature/sandbox/library.md「PATH:受管变量与\n * pathPrepend」)。\n */\n readonly env?: Readonly<globalThis.Record<string, string>>;\n /** 本命令的工作目录;省略时落到 `Sandbox.workdir`。相对路径按 workdir 解析,绝对路径原样使用。 */\n readonly cwd?: string;\n /**\n * 把本命令的输出也送进 Sandbox 的「原生日志流」(于是 `docker logs` / Docker UI 的 Logs\n * 标签页能实时看到它)。给 agent 命令(codex exec / bub run / claude)开它,就能在容器\n * 日志里看到 agent 的【原始输出】。provider 各自实现(docker:tee 到 PID1 tail 的文件;\n * 不支持的 provider 忽略)—— 日志怎么浮现是 provider 的事,adapter 只声明意图。\n */\n readonly stream?: boolean;\n /**\n * 命令 stdout 每到一块就调用一次。回调只用于运行中的短命反馈;完整 stdout 仍会原样\n * 出现在返回的 `CommandResult` 里。provider 不支持真流时,至少会在命令结束后按完整\n * stdout 调用一次,不能静默丢掉。\n */\n readonly onStdout?: (chunk: string) => void | Promise<void>;\n /** `onStdout` 的 stderr 对应物;完整 stderr 仍保留在 `CommandResult`。 */\n readonly onStderr?: (chunk: string) => void | Promise<void>;\n /**\n * 覆盖本条命令的执行身份;省略 = Sandbox 默认身份(沿用环境自己声明的身份——Docker 镜像 `USER`、\n * Compose service `user:`、E2B template 默认用户、宿主当前用户,见\n * docs/feature/sandbox/library.md「执行身份」)。\n *\n * 语义跨 provider 一致,各 provider 映射到自己的原生机制(docker:`exec --user`;E2B:\n * `{ user }`;Vercel:只认 `\"root\"`,映射 `{ sudo: true }`,其它值报错;local:任何值都报错)。\n * 本就全程 root 的 provider 视作 no-op;完全无法换身份的 provider 可不支持(抛错)——但**省略与\n * 显式值的语义保持一致**,不因 provider 而变。\n */\n readonly user?: string;\n /**\n * 这条命令自己的上限(毫秒)。**省略才是常态**:省略时上限 = attempt deadline 的剩余量\n * (见 docs/feature/sandbox/architecture.md「时限归属」),provider 层没有独立默认。\n * 显式传一个更短的值是有意声明,照常生效;撞线时归属记成「命令显式 timeout」。\n */\n readonly timeoutMs?: number;\n /**\n * 取消本次受管命令树。Provider 必须在 Promise settle 前确认命令树已经终止;无法精确\n * 终止时应退休整个 Sandbox,不能只关闭 transport 后把进程留在后台。\n */\n readonly signal?: AbortSignal;\n}\nexport interface SuccessfulCommandResult extends CommandResult {\n readonly exitCode: 0;\n}\n/** 三个运行中 Sandbox 视图共用的操作词汇;同名成员在不同视图中不得改变语义。 */\nexport interface SandboxOperations {\n /** Sandbox 内项目/工作区根目录的绝对路径(agent 命令的默认 cwd,也是 git baseline 提交的位置)。各方法的相对路径都以此为基准解析,省略 `cwd`/`targetDir` 时也落到这里。 */\n readonly workdir: string;\n /**\n * 执行单个命令,`args` 作为独立 argv 传递、不经 shell 解释(无 `&&`、管道、通配符展开)。\n * 只想跑一个可执行文件、参数来自外部输入、担心注入时优先用它。\n */\n runCommand(cmd: string, args?: readonly string[], opts?: CommandOptions): Promise<CommandResult>;\n /**\n * 执行一整段脚本,经 shell(bash)解释,支持 `&&`、管道、`$()`、重定向等。\n * 需要拼多条命令或做条件判断时用它。\n */\n runShell(script: string, opts?: CommandOptions): Promise<CommandResult>;\n /**\n * 像 `runCommand` 一样执行单个命令,但非零退出时抛出 `SandboxCommandExitError`。错误消息附带\n * 有界、已清理和脱敏的 stderr 尾部;stderr 为空时回退 stdout。完整输出保留在异常的\n * `result` 字段中。成功结果的 `exitCode` 在类型上固定为 `0`。\n */\n runCommandOrThrow(cmd: string, args?: readonly string[], opts?: CommandOptions): Promise<SuccessfulCommandResult>;\n /**\n * 像 `runShell` 一样执行 shell 脚本,但非零退出时抛出 `SandboxCommandExitError`。错误摘要、\n * 完整输出和成功结果的语义与 `runCommandOrThrow` 相同。\n */\n runShellOrThrow(script: string, opts?: CommandOptions): Promise<SuccessfulCommandResult>;\n /** 读取 Sandbox 内文件的文本内容(UTF-8)。文件不存在时抛错。 */\n readText(path: string): Promise<string>;\n /** 写入 Sandbox 内一个 UTF-8 文本文件;父目录不存在时自动创建。 */\n writeText(path: string, content: string): Promise<void>;\n /** 精确读取 Sandbox 内文件字节;公共契约不绑定 Node Buffer。 */\n readBytes(path: string): Promise<Uint8Array>;\n /** 精确写入 Sandbox 内文件字节;父目录不存在时自动创建。 */\n writeBytes(path: string, content: Uint8Array): Promise<void>;\n /** 检查 Sandbox 内文件或目录路径是否存在。 */\n pathExists(path: string): Promise<boolean>;\n}\n/** 只用于宿主机与 Sandbox 之间真实传输;内存内容读写使用 read/writeText/Bytes。 */\nexport interface SandboxTransferOperations {\n uploadFile(source: string | URL, targetPath: string): Promise<void>;\n uploadDirectory(sourceDir: string | URL, targetDir?: string, options?: {\n readonly ignore?: readonly string[];\n }): Promise<void>;\n downloadFile(sourcePath: string, target: string | URL): Promise<void>;\n downloadDirectory(sourceDir: string, targetDir: string | URL, options?: {\n readonly ignore?: readonly string[];\n }): Promise<void>;\n}\nexport interface Sandbox extends SandboxOperations, SandboxTransferOperations {\n /** 销毁 Sandbox 占用的计算资源(容器/microVM)。调用后 Sandbox 不可再用;是否可安全重复调用因 provider 而异,不要依赖这一点。 */\n stop(): Promise<void>;\n /** 本 Sandbox 的稳定标识(各 provider 原生 ID,如 Docker 容器 ID 前缀);用于跨调用关联同一 Sandbox 的会话状态,也用于日志展示。 */\n readonly sandboxId: string;\n /**\n * OTLP receiver 的放置能力。\n * - `string`:Sandbox 内可通过该 hostname 访问宿主 receiver。\n * - `null`:provider 不承诺宿主回连;runner 尝试在 Sandbox 内启动 attempt-scope receiver。\n * 这不保证 tracing 成功;镜像缺少 receiver 所需运行时时只记录 supplemental diagnostic。\n * `defineConfig({ telemetry: { host } })` 可在作者已经提供受控 tunnel 时显式覆盖。\n */\n readonly otlpHost: string | null;\n /**\n * 可选:把一行写进容器的「主日志」(PID1 在 tail 它)——于是 `docker logs` /\n * Docker UI 的 Logs 标签页能实时看到 agent 逐轮活动。docker provider 实现,其它可省略。\n */\n appendLog?(line: string): Promise<void>;\n}\n/** 复用调度需要的中立寿命确认能力。 */\nexport interface SandboxReuseCapability {\n ensureLifetime(minRemainingMs: number): Promise<{\n ready: true;\n expiresAt?: string;\n } | {\n ready: false;\n reason: string;\n }>;\n}\n"]}
@@ -73,7 +73,7 @@ export interface CommandOptions {
73
73
  *
74
74
  * 语义跨 provider 一致,各 provider 映射到自己的原生机制(docker:`exec --user`;E2B:
75
75
  * `{ user }`;Vercel:只认 `"root"`,映射 `{ sudo: true }`,其它值报错;local:任何值都报错)。
76
- * 本就全程 root 的 provider视作 no-op;完全无法换身份的 provider 可不支持(抛错)—— 但**省略与
76
+ * 本就全程 root 的 provider 视作 no-op;完全无法换身份的 provider 可不支持(抛错)——但**省略与
77
77
  * 显式值的语义保持一致**,不因 provider 而变。
78
78
  */
79
79
  readonly user?: string;
@@ -73,7 +73,7 @@ export interface CommandOptions {
73
73
  *
74
74
  * 语义跨 provider 一致,各 provider 映射到自己的原生机制(docker:`exec --user`;E2B:
75
75
  * `{ user }`;Vercel:只认 `"root"`,映射 `{ sudo: true }`,其它值报错;local:任何值都报错)。
76
- * 本就全程 root 的 provider视作 no-op;完全无法换身份的 provider 可不支持(抛错)—— 但**省略与
76
+ * 本就全程 root 的 provider 视作 no-op;完全无法换身份的 provider 可不支持(抛错)——但**省略与
77
77
  * 显式值的语义保持一致**,不因 provider 而变。
78
78
  */
79
79
  readonly user?: string;
@@ -73,7 +73,7 @@ export interface CommandOptions {
73
73
  *
74
74
  * 语义跨 provider 一致,各 provider 映射到自己的原生机制(docker:`exec --user`;E2B:
75
75
  * `{ user }`;Vercel:只认 `"root"`,映射 `{ sudo: true }`,其它值报错;local:任何值都报错)。
76
- * 本就全程 root 的 provider视作 no-op;完全无法换身份的 provider 可不支持(抛错)—— 但**省略与
76
+ * 本就全程 root 的 provider 视作 no-op;完全无法换身份的 provider 可不支持(抛错)——但**省略与
77
77
  * 显式值的语义保持一致**,不因 provider 而变。
78
78
  */
79
79
  readonly user?: string;
@@ -35,14 +35,24 @@ function displayExperimentName(id) {
35
35
  return id.split("/").filter(Boolean).at(-1) ?? id;
36
36
  }
37
37
  /**
38
- * 实验 id 的组推导:去掉末段的目录前缀("compare/bub-low" "compare");
39
- * "/" 的顶层实验不属于任何组,返回 undefined。view 实验列表分组与自定义报告
40
- * 的分组用同一份,两边的「组」永远指同一个东西。
38
+ * The sole Experiment Group derivation. A named group is always the first
39
+ * ExperimentId segment; a root Experiment is its own singleton group.
41
40
  */
42
41
  function experimentGroupOf(experimentId) {
43
- if (!experimentId.includes("/"))
44
- return undefined;
45
- return experimentId.split("/").slice(0, -1).join("/");
42
+ const slash = experimentId.indexOf("/");
43
+ if (slash === -1) {
44
+ return Object.freeze({
45
+ kind: "singleton",
46
+ experimentId,
47
+ key: `singleton/${experimentId}`,
48
+ });
49
+ }
50
+ const groupId = experimentId.slice(0, slash);
51
+ return Object.freeze({
52
+ kind: "named",
53
+ groupId,
54
+ key: `named/${groupId}`,
55
+ });
46
56
  }
47
57
  /**
48
58
  * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["aggregate.ts"],"names":[],"mappings":"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","sourcesContent":["// report 聚合(report/aggregate.ts)与 view 实验列表(view/app/lib/rows.ts)共用的聚合小工具。\n// 实验标签推导、token/成本求和、verdict 排序各只有一份 —— 否则同一个实验在终端和网页上\n// 会显示成两个名字 / 两组数。保持环境无关(纯函数,只 type import)。\nimport type { Usage } from \"../o11y/types.ts\";\nimport type { EvalResult, ExperimentRunInfo } from \"../runner/types.ts\";\nimport type { Verdict } from \"./types.ts\";\n/** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */\nexport const VERDICT_ORDER: globalThis.Record<Verdict, number> = {\n failed: 0,\n errored: 1,\n skipped: 2,\n passed: 3,\n};\nexport function totalTokens(items: Array<Usage | undefined>): number {\n return items.reduce((n, u) => n + (u?.inputTokens ?? 0) + (u?.outputTokens ?? 0), 0);\n}\n/** 求和,但「全都没报」时返回 undefined(区别于真 0 成本)。 */\nexport function sumMaybe(items: Array<number | undefined>): number | undefined {\n const known = items.filter((n): n is number => n !== undefined);\n return known.length ? known.reduce((sum, n) => sum + n, 0) : undefined;\n}\nexport function avg(items: number[]): number {\n return items.length ? items.reduce((sum, n) => sum + n, 0) / items.length : 0;\n}\n/** 实验 id 的展示名:取路径最后一段(exp 分组用目录表达)。 */\nexport function displayExperimentName(id: string | undefined): string | undefined {\n if (!id)\n return undefined;\n return id.split(\"/\").filter(Boolean).at(-1) ?? id;\n}\n/**\n * 实验 id 的组推导:去掉末段的目录前缀(\"compare/bub-low\" \"compare\");\n * \"/\" 的顶层实验不属于任何组,返回 undefined。view 实验列表分组与自定义报告\n * 的分组用同一份,两边的「组」永远指同一个东西。\n */\nexport function experimentGroupOf(experimentId: string): string | undefined {\n if (!experimentId.includes(\"/\"))\n return undefined;\n return experimentId.split(\"/\").slice(0, -1).join(\"/\");\n}\n/**\n * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):\n * eval 位置参数是收窄过滤,按**裸前缀宽松匹配**——\"algebra\" 命中 \"algebra\"、\"algebra/...\"\n * 也命中 \"algebra2\",多命中正是它的用途(与 `--exp` 的按路径段匹配有意不同)。\n */\nexport function evalPrefixPredicate(evals?: string | string[]): (id: string) => boolean {\n if (evals === undefined)\n return () => true;\n const prefixes = Array.isArray(evals) ? evals : [evals];\n return (id) => prefixes.some((prefix) => id.startsWith(prefix));\n}\n/**\n * 实验选择器解析:`niceeval exp` 位置参数与 `--exp` 共用同一条规则(docs/feature/experiments/cli.md\n * 「实验选择器怎样解析」)。按序:①精确 id 优先,即使它同时是同目录内其它文件名的前缀;\n * ②目录段精确前缀(含更深层目录),选中该目录下全部实验;③以上都不命中且选择器形如\n * `目录/文件名前缀` 时,目录段精确匹配后按文件名段前缀选中一族配置——目录段永远精确,不跨目录\n * 误配,文件名段裸前缀,与 `evalPrefixPredicate` 同一条\"共享前缀即家族\"的逻辑。\n */\nexport function matchExperimentSelector(ids: readonly string[], selector: string): string[] {\n const exact = ids.find((id) => id === selector);\n if (exact !== undefined)\n return [exact];\n const dirMatches = ids.filter((id) => id.startsWith(selector + \"/\"));\n if (dirMatches.length > 0)\n return dirMatches;\n const lastSlash = selector.lastIndexOf(\"/\");\n if (lastSlash === -1)\n return [];\n const dir = selector.slice(0, lastSlash);\n const namePrefix = selector.slice(lastSlash + 1);\n if (namePrefix === \"\")\n return [];\n return ids.filter((id) => id.startsWith(dir + \"/\") && id.slice(dir.length + 1).startsWith(namePrefix));\n}\n/**\n * 零命中时展示的可浏览路径清单(docs/feature/experiments/cli.md「零命中」):按 id 的顶层目录段\n * 去重、排序,带尾斜杠;没有目录段的顶层 experiment id 原样列出(它本身就是一个可选择的目标,\n * 不是一段路径)。这是运行选择的展示投影,不是报告分组。\n */\nexport function browsableExperimentPaths(ids: readonly string[]): string[] {\n return [...new Set(ids.map((id) => (id.includes(\"/\") ? `${id.split(\"/\")[0]}/` : id)))].sort();\n}\n/** 无 experimentId 时的兜底标签。 */\nexport function fallbackExperimentLabel(result: {\n experimentId?: string;\n experiment?: ExperimentRunInfo;\n agent: string;\n model?: string;\n}): string {\n if (result.experimentId)\n return displayExperimentName(result.experimentId) ?? result.experimentId;\n if (result.model)\n return `${result.agent}/${result.model}`;\n return result.agent || \"ad hoc run\";\n}\nexport type { EvalResult };\n"]}
1
+ {"version":3,"file":"aggregate.js","sourceRoot":"","sources":["aggregate.ts"],"names":[],"mappings":"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","sourcesContent":["// report 聚合(report/aggregate.ts)与 view 实验列表(view/app/lib/rows.ts)共用的聚合小工具。\n// 实验标签推导、token/成本求和、verdict 排序各只有一份 —— 否则同一个实验在终端和网页上\n// 会显示成两个名字 / 两组数。保持环境无关(纯函数,只 type import)。\nimport type { Usage } from \"../o11y/types.ts\";\nimport type { EvalResult, ExperimentRunInfo } from \"../runner/types.ts\";\nimport type { Verdict } from \"./types.ts\";\n/** 明细行排序:失败最靠前(failed > errored > skipped > passed 的紧急程度)。 */\nexport const VERDICT_ORDER: globalThis.Record<Verdict, number> = {\n failed: 0,\n errored: 1,\n skipped: 2,\n passed: 3,\n};\nexport function totalTokens(items: Array<Usage | undefined>): number {\n return items.reduce((n, u) => n + (u?.inputTokens ?? 0) + (u?.outputTokens ?? 0), 0);\n}\n/** 求和,但「全都没报」时返回 undefined(区别于真 0 成本)。 */\nexport function sumMaybe(items: Array<number | undefined>): number | undefined {\n const known = items.filter((n): n is number => n !== undefined);\n return known.length ? known.reduce((sum, n) => sum + n, 0) : undefined;\n}\nexport function avg(items: number[]): number {\n return items.length ? items.reduce((sum, n) => sum + n, 0) / items.length : 0;\n}\n/** 实验 id 的展示名:取路径最后一段(exp 分组用目录表达)。 */\nexport function displayExperimentName(id: string | undefined): string | undefined {\n if (!id)\n return undefined;\n return id.split(\"/\").filter(Boolean).at(-1) ?? id;\n}\nexport type ExperimentGroupIdentityValue = {\n readonly kind: \"named\";\n readonly groupId: string;\n readonly key: `named/${string}`;\n} | {\n readonly kind: \"singleton\";\n readonly experimentId: string;\n readonly key: `singleton/${string}`;\n};\n/**\n * The sole Experiment Group derivation. A named group is always the first\n * ExperimentId segment; a root Experiment is its own singleton group.\n */\nexport function experimentGroupOf(experimentId: string): ExperimentGroupIdentityValue {\n const slash = experimentId.indexOf(\"/\");\n if (slash === -1) {\n return Object.freeze({\n kind: \"singleton\" as const,\n experimentId,\n key: `singleton/${experimentId}` as const,\n });\n }\n const groupId = experimentId.slice(0, slash);\n return Object.freeze({\n kind: \"named\" as const,\n groupId,\n key: `named/${groupId}` as const,\n });\n}\n/**\n * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):\n * eval 位置参数是收窄过滤,按**裸前缀宽松匹配**——\"algebra\" 命中 \"algebra\"、\"algebra/...\"\n * 也命中 \"algebra2\",多命中正是它的用途(与 `--exp` 的按路径段匹配有意不同)。\n */\nexport function evalPrefixPredicate(evals?: string | string[]): (id: string) => boolean {\n if (evals === undefined)\n return () => true;\n const prefixes = Array.isArray(evals) ? evals : [evals];\n return (id) => prefixes.some((prefix) => id.startsWith(prefix));\n}\n/**\n * 实验选择器解析:`niceeval exp` 位置参数与 `--exp` 共用同一条规则(docs/feature/experiments/cli.md\n * 「实验选择器怎样解析」)。按序:①精确 id 优先,即使它同时是同目录内其它文件名的前缀;\n * ②目录段精确前缀(含更深层目录),选中该目录下全部实验;③以上都不命中且选择器形如\n * `目录/文件名前缀` 时,目录段精确匹配后按文件名段前缀选中一族配置——目录段永远精确,不跨目录\n * 误配,文件名段裸前缀,与 `evalPrefixPredicate` 同一条\"共享前缀即家族\"的逻辑。\n */\nexport function matchExperimentSelector(ids: readonly string[], selector: string): string[] {\n const exact = ids.find((id) => id === selector);\n if (exact !== undefined)\n return [exact];\n const dirMatches = ids.filter((id) => id.startsWith(selector + \"/\"));\n if (dirMatches.length > 0)\n return dirMatches;\n const lastSlash = selector.lastIndexOf(\"/\");\n if (lastSlash === -1)\n return [];\n const dir = selector.slice(0, lastSlash);\n const namePrefix = selector.slice(lastSlash + 1);\n if (namePrefix === \"\")\n return [];\n return ids.filter((id) => id.startsWith(dir + \"/\") && id.slice(dir.length + 1).startsWith(namePrefix));\n}\n/**\n * 零命中时展示的可浏览路径清单(docs/feature/experiments/cli.md「零命中」):按 id 的顶层目录段\n * 去重、排序,带尾斜杠;没有目录段的顶层 experiment id 原样列出(它本身就是一个可选择的目标,\n * 不是一段路径)。这是运行选择的展示投影,不是报告分组。\n */\nexport function browsableExperimentPaths(ids: readonly string[]): string[] {\n return [...new Set(ids.map((id) => (id.includes(\"/\") ? `${id.split(\"/\")[0]}/` : id)))].sort();\n}\n/** 无 experimentId 时的兜底标签。 */\nexport function fallbackExperimentLabel(result: {\n experimentId?: string;\n experiment?: ExperimentRunInfo;\n agent: string;\n model?: string;\n}): string {\n if (result.experimentId)\n return displayExperimentName(result.experimentId) ?? result.experimentId;\n if (result.model)\n return `${result.agent}/${result.model}`;\n return result.agent || \"ad hoc run\";\n}\nexport type { EvalResult };\n"]}
@@ -9,12 +9,20 @@ export declare function sumMaybe(items: Array<number | undefined>): number | und
9
9
  export declare function avg(items: number[]): number;
10
10
  /** 实验 id 的展示名:取路径最后一段(exp 分组用目录表达)。 */
11
11
  export declare function displayExperimentName(id: string | undefined): string | undefined;
12
+ export type ExperimentGroupIdentityValue = {
13
+ readonly kind: "named";
14
+ readonly groupId: string;
15
+ readonly key: `named/${string}`;
16
+ } | {
17
+ readonly kind: "singleton";
18
+ readonly experimentId: string;
19
+ readonly key: `singleton/${string}`;
20
+ };
12
21
  /**
13
- * 实验 id 的组推导:去掉末段的目录前缀("compare/bub-low" "compare");
14
- * "/" 的顶层实验不属于任何组,返回 undefined。view 实验列表分组与自定义报告
15
- * 的分组用同一份,两边的「组」永远指同一个东西。
22
+ * The sole Experiment Group derivation. A named group is always the first
23
+ * ExperimentId segment; a root Experiment is its own singleton group.
16
24
  */
17
- export declare function experimentGroupOf(experimentId: string): string | undefined;
25
+ export declare function experimentGroupOf(experimentId: string): ExperimentGroupIdentityValue;
18
26
  /**
19
27
  * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):
20
28
  * eval 位置参数是收窄过滤,按**裸前缀宽松匹配**——"algebra" 命中 "algebra"、"algebra/..."
@@ -9,12 +9,20 @@ export declare function sumMaybe(items: Array<number | undefined>): number | und
9
9
  export declare function avg(items: number[]): number;
10
10
  /** 实验 id 的展示名:取路径最后一段(exp 分组用目录表达)。 */
11
11
  export declare function displayExperimentName(id: string | undefined): string | undefined;
12
+ export type ExperimentGroupIdentityValue = {
13
+ readonly kind: "named";
14
+ readonly groupId: string;
15
+ readonly key: `named/${string}`;
16
+ } | {
17
+ readonly kind: "singleton";
18
+ readonly experimentId: string;
19
+ readonly key: `singleton/${string}`;
20
+ };
12
21
  /**
13
- * 实验 id 的组推导:去掉末段的目录前缀("compare/bub-low" "compare");
14
- * "/" 的顶层实验不属于任何组,返回 undefined。view 实验列表分组与自定义报告
15
- * 的分组用同一份,两边的「组」永远指同一个东西。
22
+ * The sole Experiment Group derivation. A named group is always the first
23
+ * ExperimentId segment; a root Experiment is its own singleton group.
16
24
  */
17
- export declare function experimentGroupOf(experimentId: string): string | undefined;
25
+ export declare function experimentGroupOf(experimentId: string): ExperimentGroupIdentityValue;
18
26
  /**
19
27
  * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):
20
28
  * eval 位置参数是收窄过滤,按**裸前缀宽松匹配**——"algebra" 命中 "algebra"、"algebra/..."
@@ -9,12 +9,20 @@ export declare function sumMaybe(items: Array<number | undefined>): number | und
9
9
  export declare function avg(items: number[]): number;
10
10
  /** 实验 id 的展示名:取路径最后一段(exp 分组用目录表达)。 */
11
11
  export declare function displayExperimentName(id: string | undefined): string | undefined;
12
+ export type ExperimentGroupIdentityValue = {
13
+ readonly kind: "named";
14
+ readonly groupId: string;
15
+ readonly key: `named/${string}`;
16
+ } | {
17
+ readonly kind: "singleton";
18
+ readonly experimentId: string;
19
+ readonly key: `singleton/${string}`;
20
+ };
12
21
  /**
13
- * 实验 id 的组推导:去掉末段的目录前缀("compare/bub-low" "compare");
14
- * "/" 的顶层实验不属于任何组,返回 undefined。view 实验列表分组与自定义报告
15
- * 的分组用同一份,两边的「组」永远指同一个东西。
22
+ * The sole Experiment Group derivation. A named group is always the first
23
+ * ExperimentId segment; a root Experiment is its own singleton group.
16
24
  */
17
- export declare function experimentGroupOf(experimentId: string): string | undefined;
25
+ export declare function experimentGroupOf(experimentId: string): ExperimentGroupIdentityValue;
18
26
  /**
19
27
  * eval id 前缀过滤,同 CLI 位置参数语义(docs/feature/reports/README.md「打开与收窄」):
20
28
  * eval 位置参数是收窄过滤,按**裸前缀宽松匹配**——"algebra" 命中 "algebra"、"algebra/..."
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatTurnLabel = formatTurnLabel;
4
+ exports.isCanonicalTurnLabel = isCanonicalTurnLabel;
4
5
  exports.normalizeTurnLabel = normalizeTurnLabel;
5
6
  /**
6
7
  * 生成 attempt 内跨 execution、timing、source 与 diff 的同一轮身份 token。
@@ -11,9 +12,26 @@ exports.normalizeTurnLabel = normalizeTurnLabel;
11
12
  function formatTurnLabel(sessionIndex, turnIndex) {
12
13
  return sessionIndex === 1 ? `turn${turnIndex}` : `session${sessionIndex}/turn${turnIndex}`;
13
14
  }
15
+ /** Current durable turn identity: main session is implicit and every index is a positive safe integer. */
16
+ function isCanonicalTurnLabel(label) {
17
+ const main = /^turn([1-9]\d*)$/.exec(label);
18
+ if (main !== null)
19
+ return Number.isSafeInteger(Number(main[1]));
20
+ const additional = /^session([1-9]\d*)\/turn([1-9]\d*)$/.exec(label);
21
+ if (additional === null)
22
+ return false;
23
+ const sessionIndex = Number(additional[1]);
24
+ const turnIndex = Number(additional[2]);
25
+ return sessionIndex >= 2 &&
26
+ Number.isSafeInteger(sessionIndex) &&
27
+ Number.isSafeInteger(turnIndex);
28
+ }
14
29
  /** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */
15
30
  function normalizeTurnLabel(label) {
16
- const match = /^s([1-9]\d*)\/t([1-9]\d*)$/.exec(label);
17
- return match === null ? label : formatTurnLabel(Number(match[1]), Number(match[2]));
31
+ const legacyMatch = /^s([1-9]\d*)\/t([1-9]\d*)$/.exec(label);
32
+ if (legacyMatch !== null) {
33
+ return formatTurnLabel(Number(legacyMatch[1]), Number(legacyMatch[2]));
34
+ }
35
+ return label;
18
36
  }
19
37
  //# sourceMappingURL=turn-label.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"turn-label.js","sourceRoot":"","sources":["turn-label.ts"],"names":[],"mappings":";;AAMA,0CAEC;AAED,gDAGC;AAbD;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,YAAoB,EAAE,SAAiB;IACnE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,SAAS,EAAE,CAAC,CAAC,CAAC,UAAU,YAAY,QAAQ,SAAS,EAAE,CAAC;AAC/F,CAAC;AACD,uDAAuD;AACvD,SAAgB,kBAAkB,CAAC,KAAa;IAC5C,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxF,CAAC","sourcesContent":["/**\n * 生成 attempt 内跨 execution、timing、source 与 diff 的同一轮身份 token。\n *\n * 新写入只使用本函数;主会话使用 `turn<N>`,额外会话使用\n * `session<K>/turn<N>`。旧 artifact 在展示/读取边界使用 `normalizeTurnLabel`,不回写落盘值。\n */\nexport function formatTurnLabel(sessionIndex: number, turnIndex: number): string {\n return sessionIndex === 1 ? `turn${turnIndex}` : `session${sessionIndex}/turn${turnIndex}`;\n}\n/** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */\nexport function normalizeTurnLabel(label: string): string {\n const match = /^s([1-9]\\d*)\\/t([1-9]\\d*)$/.exec(label);\n return match === null ? label : formatTurnLabel(Number(match[1]), Number(match[2]));\n}\n"]}
1
+ {"version":3,"file":"turn-label.js","sourceRoot":"","sources":["turn-label.ts"],"names":[],"mappings":";;AAMA,0CAEC;AAED,oDAYC;AAED,gDAMC;AA9BD;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,YAAoB,EAAE,SAAiB;IACnE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,SAAS,EAAE,CAAC,CAAC,CAAC,UAAU,YAAY,QAAQ,SAAS,EAAE,CAAC;AAC/F,CAAC;AACD,0GAA0G;AAC1G,SAAgB,oBAAoB,CAAC,KAAa;IAC9C,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,IAAI,KAAK,IAAI;QACb,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,qCAAqC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrE,IAAI,UAAU,KAAK,IAAI;QACnB,OAAO,KAAK,CAAC;IACjB,MAAM,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,YAAY,IAAI,CAAC;QACpB,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC;QAClC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;AACxC,CAAC;AACD,uDAAuD;AACvD,SAAgB,kBAAkB,CAAC,KAAa;IAC5C,MAAM,WAAW,GAAG,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7D,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO,eAAe,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC","sourcesContent":["/**\n * 生成 attempt 内跨 execution、timing、source 与 diff 的同一轮身份 token。\n *\n * 新写入只使用本函数;主会话使用 `turn<N>`,额外会话使用\n * `session<K>/turn<N>`。旧 artifact 在展示/读取边界使用 `normalizeTurnLabel`,不回写落盘值。\n */\nexport function formatTurnLabel(sessionIndex: number, turnIndex: number): string {\n return sessionIndex === 1 ? `turn${turnIndex}` : `session${sessionIndex}/turn${turnIndex}`;\n}\n/** Current durable turn identity: main session is implicit and every index is a positive safe integer. */\nexport function isCanonicalTurnLabel(label: string): boolean {\n const main = /^turn([1-9]\\d*)$/.exec(label);\n if (main !== null)\n return Number.isSafeInteger(Number(main[1]));\n const additional = /^session([1-9]\\d*)\\/turn([1-9]\\d*)$/.exec(label);\n if (additional === null)\n return false;\n const sessionIndex = Number(additional[1]);\n const turnIndex = Number(additional[2]);\n return sessionIndex >= 2 &&\n Number.isSafeInteger(sessionIndex) &&\n Number.isSafeInteger(turnIndex);\n}\n/** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */\nexport function normalizeTurnLabel(label: string): string {\n const legacyMatch = /^s([1-9]\\d*)\\/t([1-9]\\d*)$/.exec(label);\n if (legacyMatch !== null) {\n return formatTurnLabel(Number(legacyMatch[1]), Number(legacyMatch[2]));\n }\n return label;\n}\n"]}
@@ -5,5 +5,7 @@
5
5
  * `session<K>/turn<N>`。旧 artifact 在展示/读取边界使用 `normalizeTurnLabel`,不回写落盘值。
6
6
  */
7
7
  export declare function formatTurnLabel(sessionIndex: number, turnIndex: number): string;
8
+ /** Current durable turn identity: main session is implicit and every index is a positive safe integer. */
9
+ export declare function isCanonicalTurnLabel(label: string): boolean;
8
10
  /** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */
9
11
  export declare function normalizeTurnLabel(label: string): string;
@@ -5,5 +5,7 @@
5
5
  * `session<K>/turn<N>`。旧 artifact 在展示/读取边界使用 `normalizeTurnLabel`,不回写落盘值。
6
6
  */
7
7
  export declare function formatTurnLabel(sessionIndex: number, turnIndex: number): string;
8
+ /** Current durable turn identity: main session is implicit and every index is a positive safe integer. */
9
+ export declare function isCanonicalTurnLabel(label: string): boolean;
8
10
  /** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */
9
11
  export declare function normalizeTurnLabel(label: string): string;
@@ -5,5 +5,7 @@
5
5
  * `session<K>/turn<N>`。旧 artifact 在展示/读取边界使用 `normalizeTurnLabel`,不回写落盘值。
6
6
  */
7
7
  export declare function formatTurnLabel(sessionIndex: number, turnIndex: number): string;
8
+ /** Current durable turn identity: main session is implicit and every index is a positive safe integer. */
9
+ export declare function isCanonicalTurnLabel(label: string): boolean;
8
10
  /** 把旧 artifact 的坐标标签归一成当前展示格式;其它 opaque label 原样保留。 */
9
11
  export declare function normalizeTurnLabel(label: string): string;
@@ -3,6 +3,8 @@ const __niceevalRequire = __niceevalCreateRequire(import.meta.url);
3
3
  const __niceevalCanonical = __niceevalRequire("./turn-label.cjs");
4
4
  const __niceevalExport0 = __niceevalCanonical["formatTurnLabel"];
5
5
  export { __niceevalExport0 as formatTurnLabel };
6
- const __niceevalExport1 = __niceevalCanonical["normalizeTurnLabel"];
7
- export { __niceevalExport1 as normalizeTurnLabel };
6
+ const __niceevalExport1 = __niceevalCanonical["isCanonicalTurnLabel"];
7
+ export { __niceevalExport1 as isCanonicalTurnLabel };
8
+ const __niceevalExport2 = __niceevalCanonical["normalizeTurnLabel"];
9
+ export { __niceevalExport2 as normalizeTurnLabel };
8
10
  //# sourceMappingURL=turn-label.js.map