@openpond/evals 0.11.21 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (311) hide show
  1. package/README.md +12 -0
  2. package/dist/artifact-verification.js +2 -0
  3. package/dist/connected-evidence/codex-command-observations.js +81 -0
  4. package/dist/connected-evidence/contracts.js +13 -0
  5. package/dist/connected-evidence/git-file-contracts.js +16 -0
  6. package/dist/connected-evidence/git-file-proof.js +86 -0
  7. package/dist/connected-evidence/git-starting-state.js +81 -0
  8. package/dist/connected-evidence/git.js +2 -0
  9. package/dist/connected-evidence/imports.js +2 -1
  10. package/dist/connected-evidence/index.js +1 -0
  11. package/dist/connected-evidence/normalize.js +1 -0
  12. package/dist/connected-evidence/sources/claude-branches.js +34 -1
  13. package/dist/connected-evidence/sources/claude-code.js +1 -1
  14. package/dist/connected-evidence/sources/codex-metadata.js +33 -0
  15. package/dist/connected-evidence/sources/codex.js +16 -2
  16. package/dist/graders.js +10 -4
  17. package/dist/javascript-verifier.js +1 -0
  18. package/dist/native-conversations/codex-window-inventory.js +6 -7
  19. package/dist/native-conversations/collector-acquisition.js +17 -6
  20. package/dist/native-conversations/collector-source-cache.js +1 -1
  21. package/dist/native-conversations/collector-store.js +27 -2
  22. package/dist/native-conversations/collector.js +4 -3
  23. package/dist/native-conversations/git-assets.js +61 -0
  24. package/dist/native-conversations/history.js +7 -0
  25. package/dist/native-conversations/index.js +2 -0
  26. package/dist/native-conversations/selection.js +57 -0
  27. package/dist/native-conversations/supervisor.js +2 -0
  28. package/dist/prepared-process-isolation.js +131 -0
  29. package/dist/rewards.js +17 -5
  30. package/dist/tasksets.js +7 -0
  31. package/dist/types/artifact-verification.d.ts.map +1 -1
  32. package/dist/types/benchmarks.d.ts +22 -22
  33. package/dist/types/benchmarks.d.ts.map +1 -1
  34. package/dist/types/builtin-benchmarks/harness-refiner.d.ts +40 -35
  35. package/dist/types/builtin-benchmarks/harness-refiner.d.ts.map +1 -1
  36. package/dist/types/compatibility.d.ts.map +1 -1
  37. package/dist/types/conformance.d.ts +98 -88
  38. package/dist/types/conformance.d.ts.map +1 -1
  39. package/dist/types/connected-evidence/codex-command-observations.d.ts +15 -0
  40. package/dist/types/connected-evidence/codex-command-observations.d.ts.map +1 -0
  41. package/dist/types/connected-evidence/collector-coverage-contracts.d.ts +6 -6
  42. package/dist/types/connected-evidence/collector-coverage-contracts.d.ts.map +1 -1
  43. package/dist/types/connected-evidence/contracts.d.ts +80 -58
  44. package/dist/types/connected-evidence/contracts.d.ts.map +1 -1
  45. package/dist/types/connected-evidence/git-file-contracts.d.ts +18 -0
  46. package/dist/types/connected-evidence/git-file-contracts.d.ts.map +1 -0
  47. package/dist/types/connected-evidence/git-file-proof.d.ts +13 -0
  48. package/dist/types/connected-evidence/git-file-proof.d.ts.map +1 -0
  49. package/dist/types/connected-evidence/git-starting-state.d.ts +28 -0
  50. package/dist/types/connected-evidence/git-starting-state.d.ts.map +1 -0
  51. package/dist/types/connected-evidence/git.d.ts +3 -0
  52. package/dist/types/connected-evidence/git.d.ts.map +1 -0
  53. package/dist/types/connected-evidence/imports.d.ts.map +1 -1
  54. package/dist/types/connected-evidence/index.d.ts +1 -0
  55. package/dist/types/connected-evidence/index.d.ts.map +1 -1
  56. package/dist/types/connected-evidence/normalize.d.ts +1 -0
  57. package/dist/types/connected-evidence/normalize.d.ts.map +1 -1
  58. package/dist/types/connected-evidence/python-correctness.d.ts +38 -38
  59. package/dist/types/connected-evidence/python-correctness.d.ts.map +1 -1
  60. package/dist/types/connected-evidence/sources/claude-branches.d.ts +1 -0
  61. package/dist/types/connected-evidence/sources/claude-branches.d.ts.map +1 -1
  62. package/dist/types/connected-evidence/sources/claude-code.d.ts +25 -15
  63. package/dist/types/connected-evidence/sources/claude-code.d.ts.map +1 -1
  64. package/dist/types/connected-evidence/sources/codex-authorization.d.ts.map +1 -1
  65. package/dist/types/connected-evidence/sources/codex-metadata.d.ts +10 -0
  66. package/dist/types/connected-evidence/sources/codex-metadata.d.ts.map +1 -0
  67. package/dist/types/connected-evidence/sources/codex.d.ts +25 -15
  68. package/dist/types/connected-evidence/sources/codex.d.ts.map +1 -1
  69. package/dist/types/connected-evidence/sources/grok-build.d.ts +25 -15
  70. package/dist/types/connected-evidence/sources/grok-build.d.ts.map +1 -1
  71. package/dist/types/connected-evidence/sources/history.d.ts +50 -30
  72. package/dist/types/connected-evidence/sources/history.d.ts.map +1 -1
  73. package/dist/types/connected-evidence/sources/opencode.d.ts +25 -15
  74. package/dist/types/connected-evidence/sources/opencode.d.ts.map +1 -1
  75. package/dist/types/connected-evidence/sources/pi.d.ts +25 -15
  76. package/dist/types/connected-evidence/sources/pi.d.ts.map +1 -1
  77. package/dist/types/connected-evidence/sources/usage.d.ts.map +1 -1
  78. package/dist/types/connected-evidence/usage-summary.d.ts.map +1 -1
  79. package/dist/types/deterministic-graders.d.ts.map +1 -1
  80. package/dist/types/environment-execution-services-contract.d.ts +19 -19
  81. package/dist/types/environment-execution-services-contract.d.ts.map +1 -1
  82. package/dist/types/environment-execution-services.d.ts.map +1 -1
  83. package/dist/types/evidence/authoring.d.ts.map +1 -1
  84. package/dist/types/evidence/conformance.d.ts +98 -98
  85. package/dist/types/evidence/conformance.d.ts.map +1 -1
  86. package/dist/types/evidence/contracts.d.ts +183 -183
  87. package/dist/types/evidence/contracts.d.ts.map +1 -1
  88. package/dist/types/evidence/eligibility.d.ts +160 -160
  89. package/dist/types/evidence/eligibility.d.ts.map +1 -1
  90. package/dist/types/evidence/index.d.ts.map +1 -1
  91. package/dist/types/execution-contracts.d.ts +118 -110
  92. package/dist/types/execution-contracts.d.ts.map +1 -1
  93. package/dist/types/execution-receipts.d.ts.map +1 -1
  94. package/dist/types/experiments.d.ts +96 -96
  95. package/dist/types/experiments.d.ts.map +1 -1
  96. package/dist/types/graders.d.ts +38 -35
  97. package/dist/types/graders.d.ts.map +1 -1
  98. package/dist/types/harness-experiment-source.d.ts.map +1 -1
  99. package/dist/types/harness.d.ts.map +1 -1
  100. package/dist/types/human-review/comparison.d.ts +189 -189
  101. package/dist/types/human-review/comparison.d.ts.map +1 -1
  102. package/dist/types/human-review/contracts.d.ts +178 -178
  103. package/dist/types/human-review/contracts.d.ts.map +1 -1
  104. package/dist/types/human-review/execution.d.ts +53 -53
  105. package/dist/types/human-review/execution.d.ts.map +1 -1
  106. package/dist/types/human-review/handoff.d.ts +43 -43
  107. package/dist/types/human-review/handoff.d.ts.map +1 -1
  108. package/dist/types/human-review/index.d.ts.map +1 -1
  109. package/dist/types/human-review/local-publication.d.ts +4 -4
  110. package/dist/types/human-review/local-publication.d.ts.map +1 -1
  111. package/dist/types/human-review/repository.d.ts.map +1 -1
  112. package/dist/types/human-review/results.d.ts +8 -8
  113. package/dist/types/human-review/results.d.ts.map +1 -1
  114. package/dist/types/human-review/service.d.ts +292 -292
  115. package/dist/types/human-review/service.d.ts.map +1 -1
  116. package/dist/types/human-review/task-proposals.d.ts +2 -2
  117. package/dist/types/human-review/task-proposals.d.ts.map +1 -1
  118. package/dist/types/index.d.ts.map +1 -1
  119. package/dist/types/javascript-environment-attempt.d.ts.map +1 -1
  120. package/dist/types/javascript-environment-contract.d.ts +39 -39
  121. package/dist/types/javascript-environment-contract.d.ts.map +1 -1
  122. package/dist/types/javascript-environment-node.d.ts.map +1 -1
  123. package/dist/types/javascript-environment.d.ts +14 -14
  124. package/dist/types/javascript-environment.d.ts.map +1 -1
  125. package/dist/types/javascript-isolate-node.d.ts.map +1 -1
  126. package/dist/types/javascript-isolate-process-entry.d.ts.map +1 -1
  127. package/dist/types/javascript-isolate-process.d.ts.map +1 -1
  128. package/dist/types/javascript-isolate.d.ts.map +1 -1
  129. package/dist/types/javascript-verifier-contract.d.ts.map +1 -1
  130. package/dist/types/javascript-verifier-node.d.ts.map +1 -1
  131. package/dist/types/javascript-verifier-worker.d.ts.map +1 -1
  132. package/dist/types/javascript-verifier.d.ts +1 -0
  133. package/dist/types/javascript-verifier.d.ts.map +1 -1
  134. package/dist/types/learned-preference.d.ts +10 -10
  135. package/dist/types/learned-preference.d.ts.map +1 -1
  136. package/dist/types/learning/acceptance-group.d.ts +44 -44
  137. package/dist/types/learning/acceptance-group.d.ts.map +1 -1
  138. package/dist/types/learning/acceptance-plan.d.ts +8 -8
  139. package/dist/types/learning/acceptance-plan.d.ts.map +1 -1
  140. package/dist/types/learning/admission.d.ts +38 -36
  141. package/dist/types/learning/admission.d.ts.map +1 -1
  142. package/dist/types/learning/assets.d.ts +2 -2
  143. package/dist/types/learning/assets.d.ts.map +1 -1
  144. package/dist/types/learning/authoring-service.d.ts +11 -9
  145. package/dist/types/learning/authoring-service.d.ts.map +1 -1
  146. package/dist/types/learning/authoring.d.ts +46 -38
  147. package/dist/types/learning/authoring.d.ts.map +1 -1
  148. package/dist/types/learning/batch-service.d.ts +75 -68
  149. package/dist/types/learning/batch-service.d.ts.map +1 -1
  150. package/dist/types/learning/budgeted-reward-fixtures.d.ts.map +1 -1
  151. package/dist/types/learning/contracts.d.ts +314 -290
  152. package/dist/types/learning/contracts.d.ts.map +1 -1
  153. package/dist/types/learning/errors.d.ts.map +1 -1
  154. package/dist/types/learning/grade-judge-budget.d.ts.map +1 -1
  155. package/dist/types/learning/grade-worker.d.ts.map +1 -1
  156. package/dist/types/learning/index.d.ts.map +1 -1
  157. package/dist/types/learning/intake-contracts.d.ts +10 -10
  158. package/dist/types/learning/intake-contracts.d.ts.map +1 -1
  159. package/dist/types/learning/intake-csv.d.ts.map +1 -1
  160. package/dist/types/learning/intake-history.d.ts.map +1 -1
  161. package/dist/types/learning/intake-service.d.ts.map +1 -1
  162. package/dist/types/learning/intake-text.d.ts.map +1 -1
  163. package/dist/types/learning/intake.d.ts.map +1 -1
  164. package/dist/types/learning/iteration-budget.d.ts.map +1 -1
  165. package/dist/types/learning/iteration-candidate-decision.d.ts +16 -16
  166. package/dist/types/learning/iteration-candidate-decision.d.ts.map +1 -1
  167. package/dist/types/learning/iteration-dispatch-commands.d.ts.map +1 -1
  168. package/dist/types/learning/iteration-dispatch-contracts.d.ts +4 -4
  169. package/dist/types/learning/iteration-dispatch-contracts.d.ts.map +1 -1
  170. package/dist/types/learning/iteration-dispatch-worker.d.ts.map +1 -1
  171. package/dist/types/learning/iteration-eligibility.d.ts +1 -1
  172. package/dist/types/learning/iteration-eligibility.d.ts.map +1 -1
  173. package/dist/types/learning/iteration-reservation-cancellation.d.ts.map +1 -1
  174. package/dist/types/learning/iteration-reservation-contracts.d.ts.map +1 -1
  175. package/dist/types/learning/iteration-reservation-service.d.ts.map +1 -1
  176. package/dist/types/learning/iteration-training-parent.d.ts.map +1 -1
  177. package/dist/types/learning/judge-budget.d.ts +1 -1
  178. package/dist/types/learning/judge-budget.d.ts.map +1 -1
  179. package/dist/types/learning/model-judge.d.ts.map +1 -1
  180. package/dist/types/learning/nightly-schedule.d.ts.map +1 -1
  181. package/dist/types/learning/operations.d.ts +312 -298
  182. package/dist/types/learning/operations.d.ts.map +1 -1
  183. package/dist/types/learning/policy-inspection.d.ts.map +1 -1
  184. package/dist/types/learning/rating-reward-fixture.d.ts.map +1 -1
  185. package/dist/types/learning/repository.d.ts +197 -180
  186. package/dist/types/learning/repository.d.ts.map +1 -1
  187. package/dist/types/learning/reward-authoring.d.ts.map +1 -1
  188. package/dist/types/learning/reward-calibration-closure.d.ts +252 -244
  189. package/dist/types/learning/reward-calibration-closure.d.ts.map +1 -1
  190. package/dist/types/learning/reward-calibration.d.ts +62 -62
  191. package/dist/types/learning/reward-calibration.d.ts.map +1 -1
  192. package/dist/types/learning/reward-check-budget.d.ts.map +1 -1
  193. package/dist/types/learning/reward-check-service.d.ts.map +1 -1
  194. package/dist/types/learning/reward-check-worker.d.ts.map +1 -1
  195. package/dist/types/learning/reward-checks.d.ts +30 -28
  196. package/dist/types/learning/reward-checks.d.ts.map +1 -1
  197. package/dist/types/learning/reward-fixture-executor.d.ts.map +1 -1
  198. package/dist/types/learning/schedule-contracts.d.ts +3 -3
  199. package/dist/types/learning/schedule-contracts.d.ts.map +1 -1
  200. package/dist/types/learning/schedule-service.d.ts +2 -2
  201. package/dist/types/learning/schedule-service.d.ts.map +1 -1
  202. package/dist/types/learning/schedule-worker.d.ts.map +1 -1
  203. package/dist/types/learning/service.d.ts +3 -3
  204. package/dist/types/learning/service.d.ts.map +1 -1
  205. package/dist/types/learning/task-queue-inspection.d.ts.map +1 -1
  206. package/dist/types/learning/training-handoff.d.ts +9 -9
  207. package/dist/types/learning/training-handoff.d.ts.map +1 -1
  208. package/dist/types/learning/transport.d.ts +2203 -2179
  209. package/dist/types/learning/transport.d.ts.map +1 -1
  210. package/dist/types/metric-policy.d.ts +6 -6
  211. package/dist/types/metric-policy.d.ts.map +1 -1
  212. package/dist/types/metrics-node.d.ts.map +1 -1
  213. package/dist/types/metrics.d.ts.map +1 -1
  214. package/dist/types/model-improvement-qualification.d.ts +17 -17
  215. package/dist/types/model-improvement-qualification.d.ts.map +1 -1
  216. package/dist/types/native-conversations/appimage-runtime.d.ts.map +1 -1
  217. package/dist/types/native-conversations/codex-window-inventory.d.ts.map +1 -1
  218. package/dist/types/native-conversations/collector-acquisition.d.ts.map +1 -1
  219. package/dist/types/native-conversations/collector-branches.d.ts.map +1 -1
  220. package/dist/types/native-conversations/collector-calendar-gate.d.ts.map +1 -1
  221. package/dist/types/native-conversations/collector-contracts.d.ts +3 -0
  222. package/dist/types/native-conversations/collector-contracts.d.ts.map +1 -1
  223. package/dist/types/native-conversations/collector-coverage.d.ts +2 -2
  224. package/dist/types/native-conversations/collector-coverage.d.ts.map +1 -1
  225. package/dist/types/native-conversations/collector-cron.d.ts.map +1 -1
  226. package/dist/types/native-conversations/collector-destinations.d.ts.map +1 -1
  227. package/dist/types/native-conversations/collector-errors.d.ts.map +1 -1
  228. package/dist/types/native-conversations/collector-progress.d.ts.map +1 -1
  229. package/dist/types/native-conversations/collector-schedule.d.ts.map +1 -1
  230. package/dist/types/native-conversations/collector-service-files.d.ts.map +1 -1
  231. package/dist/types/native-conversations/collector-snapshots.d.ts.map +1 -1
  232. package/dist/types/native-conversations/collector-source-cache.d.ts +2 -2
  233. package/dist/types/native-conversations/collector-source-cache.d.ts.map +1 -1
  234. package/dist/types/native-conversations/collector-store.d.ts.map +1 -1
  235. package/dist/types/native-conversations/collector-sync-requests.d.ts.map +1 -1
  236. package/dist/types/native-conversations/collector.d.ts.map +1 -1
  237. package/dist/types/native-conversations/contracts.d.ts.map +1 -1
  238. package/dist/types/native-conversations/database.d.ts.map +1 -1
  239. package/dist/types/native-conversations/discovery.d.ts.map +1 -1
  240. package/dist/types/native-conversations/git-assets.d.ts +32 -0
  241. package/dist/types/native-conversations/git-assets.d.ts.map +1 -0
  242. package/dist/types/native-conversations/history.d.ts +1 -0
  243. package/dist/types/native-conversations/history.d.ts.map +1 -1
  244. package/dist/types/native-conversations/index.d.ts +2 -0
  245. package/dist/types/native-conversations/index.d.ts.map +1 -1
  246. package/dist/types/native-conversations/location.d.ts.map +1 -1
  247. package/dist/types/native-conversations/openclaw-cold-archive.d.ts.map +1 -1
  248. package/dist/types/native-conversations/openclaw-database.d.ts.map +1 -1
  249. package/dist/types/native-conversations/readiness.d.ts.map +1 -1
  250. package/dist/types/native-conversations/selection.d.ts +32 -0
  251. package/dist/types/native-conversations/selection.d.ts.map +1 -0
  252. package/dist/types/native-conversations/source-locations.d.ts.map +1 -1
  253. package/dist/types/native-conversations/supervisor.d.ts.map +1 -1
  254. package/dist/types/native-conversations/titles.d.ts.map +1 -1
  255. package/dist/types/native-harness-experiment-attempt.d.ts.map +1 -1
  256. package/dist/types/preferences.d.ts +22 -22
  257. package/dist/types/preferences.d.ts.map +1 -1
  258. package/dist/types/prepared-process-isolation.d.ts +5 -0
  259. package/dist/types/prepared-process-isolation.d.ts.map +1 -0
  260. package/dist/types/profile-evaluation-comparisons.d.ts +18 -18
  261. package/dist/types/profile-evaluation-comparisons.d.ts.map +1 -1
  262. package/dist/types/profile-evaluation-execution.d.ts.map +1 -1
  263. package/dist/types/profile-evaluation-reports.d.ts +2 -2
  264. package/dist/types/profile-evaluation-reports.d.ts.map +1 -1
  265. package/dist/types/profile-evaluation-suites.d.ts.map +1 -1
  266. package/dist/types/profile-evaluation-target.d.ts.map +1 -1
  267. package/dist/types/profile-evaluations.d.ts +15 -15
  268. package/dist/types/profile-evaluations.d.ts.map +1 -1
  269. package/dist/types/profile-external-evaluation.d.ts +84 -84
  270. package/dist/types/profile-external-evaluation.d.ts.map +1 -1
  271. package/dist/types/profile-source-candidate.d.ts.map +1 -1
  272. package/dist/types/review-conformance.d.ts +58 -58
  273. package/dist/types/review-conformance.d.ts.map +1 -1
  274. package/dist/types/rewards.d.ts +110 -101
  275. package/dist/types/rewards.d.ts.map +1 -1
  276. package/dist/types/rollouts.d.ts +16 -16
  277. package/dist/types/rollouts.d.ts.map +1 -1
  278. package/dist/types/runs.d.ts +22 -22
  279. package/dist/types/runs.d.ts.map +1 -1
  280. package/dist/types/sql-execution-contract.d.ts +3 -3
  281. package/dist/types/sql-execution-contract.d.ts.map +1 -1
  282. package/dist/types/sql-execution-engine.d.ts.map +1 -1
  283. package/dist/types/sql-execution-node.d.ts.map +1 -1
  284. package/dist/types/sql-execution-process-entry.d.ts.map +1 -1
  285. package/dist/types/task-schema-validation.d.ts.map +1 -1
  286. package/dist/types/task-schema.d.ts.map +1 -1
  287. package/dist/types/task-value-validator.d.ts.map +1 -1
  288. package/dist/types/taskset-run-contract.d.ts +26 -26
  289. package/dist/types/taskset-run-contract.d.ts.map +1 -1
  290. package/dist/types/tasksets.d.ts +89 -69
  291. package/dist/types/tasksets.d.ts.map +1 -1
  292. package/dist/types/telemetry/index.d.ts.map +1 -1
  293. package/dist/types/telemetry-analysis.d.ts +6 -6
  294. package/dist/types/telemetry-analysis.d.ts.map +1 -1
  295. package/dist/types/telemetry-bundle.d.ts +42 -42
  296. package/dist/types/telemetry-bundle.d.ts.map +1 -1
  297. package/dist/types/telemetry-catalog.d.ts +67 -67
  298. package/dist/types/telemetry-catalog.d.ts.map +1 -1
  299. package/dist/types/telemetry.d.ts +44 -44
  300. package/dist/types/telemetry.d.ts.map +1 -1
  301. package/package.json +6 -1
  302. package/schemas/learning/v1/binding.schema.json +8 -0
  303. package/schemas/learning/v1/command-request.schema.json +12 -0
  304. package/schemas/learning/v1/decision.schema.json +5 -1
  305. package/schemas/learning/v1/definition.schema.json +4 -0
  306. package/schemas/learning/v1/draft.schema.json +8 -0
  307. package/schemas/learning/v1/evidence.schema.json +4 -0
  308. package/schemas/learning/v1/example-submission.schema.json +4 -0
  309. package/schemas/learning/v1/grade.schema.json +5 -1
  310. package/schemas/learning/v1/package.schema.json +117 -93
  311. package/schemas/learning/v1/reward_check.schema.json +2 -1
package/README.md CHANGED
@@ -163,6 +163,18 @@ overrides that state.
163
163
 
164
164
  ## Runtime adapters
165
165
 
166
+ Graders may declare `taskIds` to apply to a nonempty, unique population within
167
+ the Taskset. Grading binds the full grader-set hash while executing and
168
+ aggregating only the applicable checks. Reward binding sources carry the same
169
+ scope; scoped compositions retain `taskId` and an explicit `not_applicable`
170
+ result for each excluded source. Excluded checks cannot satisfy a required check
171
+ or change the score, and a composition without its scope is rejected.
172
+
173
+ `@openpond/evals/connected-evidence/git` verifies retained Git starting-file
174
+ receipts against exact object identities and bytes. The receipt is evidence,
175
+ not authorization to read a repository. Hosts remain responsible for owner
176
+ authorization, dependency closure, and runtime qualification.
177
+
166
178
  Implement `HarnessRuntime` for environment state and `HarnessExecutor` around
167
179
  the host's existing model/Agent loop. Do not reimplement prompting, tool dispatch,
168
180
  session persistence, cost accounting, cancellation, or cleanup in this package.
@@ -143,6 +143,8 @@ function structuralFailures(required, entry) {
143
143
  }
144
144
  if (entry.parseStatus === "failed")
145
145
  failures.push(`Required output ${required.path} could not be parsed.`);
146
+ if (required.submissionMode === "structured_json" && (entry.schemaStatus !== "passed" || entry.parseStatus !== "passed" || entry.metadata.structuredSchemaHash !== required.schemaRef?.contentHash || entry.metadata.structuredArtifactHash !== entry.artifact?.contentHash))
147
+ failures.push(`Required output ${required.path} lacks bound structured validation evidence.`);
146
148
  if (entry.schemaStatus === "failed")
147
149
  failures.push(`Required output ${required.path} failed schema validation.`);
148
150
  return failures;
@@ -0,0 +1,81 @@
1
+ import { parse } from "acorn";
2
+ const ast = (value) => {
3
+ if (!value || typeof value !== "object" || !("type" in value))
4
+ throw new Error("connected_command_syntax_unsupported");
5
+ return value;
6
+ };
7
+ function member(value, object, property) {
8
+ const node = ast(value);
9
+ return node.type === "MemberExpression" && !node.computed && !node.optional
10
+ && ast(node.object).type === "Identifier" && ast(node.object).name === object
11
+ && ast(node.property).type === "Identifier" && ast(node.property).name === property;
12
+ }
13
+ /** Interpret recorded syntax, never execute source code. Only sequential static
14
+ * exec_command calls have an unambiguous correspondence with retained results. */
15
+ export function recordedCodexCommands(source) {
16
+ if (source.length > 128_000)
17
+ throw new Error("connected_command_syntax_limit");
18
+ const program = parse(source, { ecmaVersion: "latest", sourceType: "module" });
19
+ const commands = [];
20
+ for (const statement of program.body) {
21
+ const outer = ast(ast(statement).expression);
22
+ const args = outer.arguments;
23
+ if (statement.type !== "ExpressionStatement" || outer.type !== "CallExpression" || outer.optional
24
+ || ast(outer.callee).type !== "Identifier" || ast(outer.callee).name !== "text" || args.length !== 1)
25
+ throw new Error("connected_command_syntax_unsupported");
26
+ const awaited = ast(args[0]);
27
+ const call = ast(awaited.argument);
28
+ const innerArgs = call.arguments;
29
+ if (awaited.type !== "AwaitExpression" || call.type !== "CallExpression" || call.optional
30
+ || !member(call.callee, "tools", "exec_command") || innerArgs.length !== 1)
31
+ throw new Error("connected_command_syntax_unsupported");
32
+ const object = ast(innerArgs[0]);
33
+ if (object.type !== "ObjectExpression")
34
+ throw new Error("connected_command_syntax_unsupported");
35
+ const values = new Map();
36
+ for (const raw of object.properties) {
37
+ const property = ast(raw), key = ast(property.key), value = ast(property.value);
38
+ const name = key.type === "Identifier" ? key.name : key.type === "Literal" ? key.value : null;
39
+ if (property.type !== "Property" || property.computed || property.method || property.shorthand || property.kind !== "init"
40
+ || typeof name !== "string" || values.has(name) || value.type !== "Literal"
41
+ || !["cmd", "workdir", "max_output_tokens", "yield_time_ms"].includes(name))
42
+ throw new Error("connected_command_syntax_unsupported");
43
+ values.set(name, value.value);
44
+ }
45
+ const cmd = values.get("cmd"), budget = values.get("max_output_tokens"), cwd = values.get("workdir");
46
+ if (typeof cmd !== "string" || !cmd.length || cmd.length > 16_000
47
+ || typeof budget !== "number" || !Number.isInteger(budget) || budget <= 0 || budget > 100_000
48
+ || cwd !== undefined && typeof cwd !== "string")
49
+ throw new Error("connected_command_syntax_unsupported");
50
+ commands.push({ cmd, maxOutputTokens: budget, cwd: typeof cwd === "string" ? cwd : null });
51
+ }
52
+ if (!commands.length || commands.length > 20)
53
+ throw new Error("connected_command_syntax_unsupported");
54
+ return commands;
55
+ }
56
+ /** This deliberately bounded read-only prefix establishes that no earlier
57
+ * recorded command modified the checkout before the status observation. */
58
+ export function isRecordedReadOnlyInventory(cmd) {
59
+ return cmd === "pwd" || /^pwd && rg --files(?: -g '[a-zA-Z0-9_.*\/-]+')*$/.test(cmd);
60
+ }
61
+ export function recordedCodexCommandResults(value, count) {
62
+ if (!Array.isArray(value) || value.length !== count + 1)
63
+ throw new Error("connected_command_results_incomplete");
64
+ const blocks = value.map(block => {
65
+ if (!block || typeof block !== "object" || !("type" in block) || block.type !== "input_text"
66
+ || !("text" in block) || typeof block.text !== "string")
67
+ throw new Error("connected_command_results_incomplete");
68
+ return block.text;
69
+ });
70
+ if (!/^Script completed\nWall time [0-9.]+ seconds\nOutput:\n$/.test(blocks[0]))
71
+ throw new Error("connected_command_results_incomplete");
72
+ return blocks.slice(1).map(text => {
73
+ const result = JSON.parse(text);
74
+ if (!result || typeof result !== "object" || !("exit_code" in result) || result.exit_code !== 0
75
+ || "session_id" in result || !("output" in result) || typeof result.output !== "string"
76
+ || !("original_token_count" in result) || typeof result.original_token_count !== "number"
77
+ || !Number.isSafeInteger(result.original_token_count) || result.original_token_count < 0)
78
+ throw new Error("connected_command_results_incomplete");
79
+ return { output: result.output, originalTokenCount: result.original_token_count };
80
+ });
81
+ }
@@ -29,11 +29,24 @@ export const ConnectedBoundarySchema = z.object({
29
29
  process: z.enum(["retained", "partial", "absent"]), artifacts: z.enum(["retained", "references_only", "absent"]),
30
30
  replay: z.literal("unavailable"), gaps: z.array(z.string().max(500)).max(100) }).strict(),
31
31
  }).strict();
32
+ /** Source-reported repository identity is reconstruction evidence, not an
33
+ * admitted checkout or proof that the working tree was clean. */
34
+ export const ConnectedRepositoryContextSchema = z.object({
35
+ schemaVersion: z.literal("openpond.connectedRepositoryContext.v1"),
36
+ kind: z.literal("git"),
37
+ commitHash: z.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/),
38
+ branch: z.string().min(1).max(500).nullable(),
39
+ recordedAt: Time,
40
+ sourceFileHash: Hash,
41
+ metadataHash: Hash,
42
+ workingDirectoryHash: Hash.optional(),
43
+ }).strict();
32
44
  export const ConnectedSessionSchema = z.object({
33
45
  schemaVersion: z.literal(CONNECTED_EVIDENCE_VERSION), normalizerVersion: z.literal(CONNECTED_NORMALIZER_VERSION),
34
46
  origin: ConnectedSourceKindSchema, sessionId: Id, branchId: Id.nullable(), parentSessionId: Id.nullable(),
35
47
  exporterVersion: z.string().max(200).nullable(),
36
48
  acquisition: z.object({ machineId: Id, sourceInstanceId: Id }).strict().optional(),
49
+ repositoryContext: ConnectedRepositoryContextSchema.optional(),
37
50
  sourceFiles: z.array(z.object({ path: Id, contentHash: Hash, sizeBytes: z.number().int().nonnegative() }).strict()).max(CONNECTED_EVIDENCE_LIMITS.files),
38
51
  events: z.array(ConnectedEventSchema).max(CONNECTED_EVIDENCE_LIMITS.events),
39
52
  unmappedEvents: z.array(ConnectedEventSchema).max(CONNECTED_EVIDENCE_LIMITS.events),
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ const ObjectHash = z.string().regex(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/);
3
+ const Base64 = z.string().max(3_000_000);
4
+ const ObjectBytes = z.object({ hash: ObjectHash, base64: Base64 }).strict();
5
+ const safePath = (path) => !/[\x00-\x1f\x7f]/.test(path) && !path.startsWith("/") && !path.includes("\\")
6
+ && path.split("/").length <= 32 && path.split("/").every(part => part && part !== "." && part !== ".."
7
+ && part !== ".git" && !part.startsWith(".env"));
8
+ /** A Git object proof pins bytes to recorded history. It does not establish
9
+ * that the historical working tree was clean or that these are all dependencies. */
10
+ export const ConnectedGitFileProofSchema = z.object({
11
+ schemaVersion: z.literal("openpond.connectedGitFileProof.v1"),
12
+ path: z.string().min(1).max(1_000).refine(safePath),
13
+ commit: ObjectBytes,
14
+ trees: z.array(ObjectBytes).min(1).max(32),
15
+ contentsBase64: Base64,
16
+ }).strict();
@@ -0,0 +1,86 @@
1
+ import { createHash } from "node:crypto";
2
+ import { ConnectedGitFileProofSchema } from "./git-file-contracts.js";
3
+ export * from "./git-file-contracts.js";
4
+ import { ConnectedRepositoryContextSchema } from "./contracts.js";
5
+ function bytes(base64, limit) {
6
+ const value = Buffer.from(base64, "base64");
7
+ if (value.toString("base64") !== base64 || value.byteLength > limit)
8
+ throw new Error("connected_git_object_bytes_invalid");
9
+ return value;
10
+ }
11
+ function objectHash(kind, value, width) {
12
+ return createHash(width === 40 ? "sha1" : "sha256")
13
+ .update(`${kind} ${value.byteLength}\0`).update(value).digest("hex");
14
+ }
15
+ function treeEntry(tree, name, width) {
16
+ let offset = 0;
17
+ const names = new Set();
18
+ let found = null;
19
+ const decoder = new TextDecoder("utf-8", { fatal: true });
20
+ while (offset < tree.byteLength) {
21
+ const space = tree.indexOf(32, offset), nul = tree.indexOf(0, space + 1);
22
+ const end = nul + 1 + width / 2;
23
+ if (space < offset || nul < space || end > tree.byteLength)
24
+ throw new Error("connected_git_tree_invalid");
25
+ const mode = tree.subarray(offset, space).toString("ascii");
26
+ const entryName = decoder.decode(tree.subarray(space + 1, nul));
27
+ if (!entryName || entryName.includes("/") || names.has(entryName))
28
+ throw new Error("connected_git_tree_invalid");
29
+ names.add(entryName);
30
+ if (entryName === name)
31
+ found = { mode, hash: tree.subarray(nul + 1, end).toString("hex") };
32
+ offset = end;
33
+ }
34
+ if (!found)
35
+ throw new Error("connected_git_path_missing");
36
+ return found;
37
+ }
38
+ /** The caller must supply repositoryContext from an authorized, hash-verified
39
+ * connected snapshot. No filesystem access or caller-selected Git command occurs. */
40
+ export function verifyConnectedGitFileProof(repositoryContext, raw) {
41
+ const context = ConnectedRepositoryContextSchema.parse(repositoryContext);
42
+ const proof = ConnectedGitFileProofSchema.parse(raw);
43
+ const width = context.commitHash.length;
44
+ const commit = bytes(proof.commit.base64, 256 * 1024);
45
+ if (proof.commit.hash !== context.commitHash || objectHash("commit", commit, width) !== context.commitHash)
46
+ throw new Error("connected_git_commit_mismatch");
47
+ const firstNewline = commit.indexOf(10);
48
+ if (firstNewline < 0)
49
+ throw new Error("connected_git_commit_invalid");
50
+ const firstLine = commit.subarray(0, firstNewline).toString("ascii");
51
+ if (!new RegExp(`^tree [a-f0-9]{${width}}$`).test(firstLine))
52
+ throw new Error("connected_git_commit_invalid");
53
+ const trees = new Map();
54
+ let totalBytes = commit.byteLength;
55
+ for (const object of proof.trees) {
56
+ const value = bytes(object.base64, 2 * 1024 * 1024);
57
+ totalBytes += value.byteLength;
58
+ if (totalBytes > 8 * 1024 * 1024 || trees.has(object.hash) || object.hash.length !== width
59
+ || objectHash("tree", value, width) !== object.hash)
60
+ throw new Error("connected_git_tree_mismatch");
61
+ trees.set(object.hash, value);
62
+ }
63
+ const parts = proof.path.split("/");
64
+ const visited = new Set();
65
+ let hash = firstLine.slice(5);
66
+ let mode = "";
67
+ for (const [index, part] of parts.entries()) {
68
+ const tree = trees.get(hash);
69
+ if (!tree || visited.has(hash))
70
+ throw new Error("connected_git_tree_missing");
71
+ visited.add(hash);
72
+ const entry = treeEntry(tree, part, width);
73
+ mode = entry.mode;
74
+ if (index < parts.length - 1 ? mode !== "40000" : !["100644", "100755"].includes(mode))
75
+ throw new Error("connected_git_file_mode_invalid");
76
+ hash = entry.hash;
77
+ }
78
+ if (visited.size !== trees.size)
79
+ throw new Error("connected_git_unreferenced_tree");
80
+ const content = bytes(proof.contentsBase64, 2 * 1024 * 1024);
81
+ if (objectHash("blob", content, width) !== hash)
82
+ throw new Error("connected_git_blob_mismatch");
83
+ return { path: proof.path, commitHash: context.commitHash, gitObjectHash: hash, mode,
84
+ sizeBytes: content.byteLength, sha256: createHash("sha256").update(content).digest("hex"),
85
+ contentsBase64: content.toString("base64") };
86
+ }
@@ -0,0 +1,81 @@
1
+ import { contentHash } from "@openpond/harness";
2
+ import { ConnectedSessionSchema, resolveConnectedBoundaryMetadata } from "./contracts.js";
3
+ import { recordedCodexCommands, recordedCodexCommandResults, isRecordedReadOnlyInventory } from "./codex-command-observations.js";
4
+ import { verifyConnectedGitFileProof } from "./git-file-proof.js";
5
+ function object(value) {
6
+ if (!value || typeof value !== "object" || Array.isArray(value))
7
+ throw new Error("connected_starting_state_unavailable");
8
+ return value;
9
+ }
10
+ function dirtyPaths(output) {
11
+ const paths = [];
12
+ for (const line of output.split("\n")) {
13
+ if (!line)
14
+ continue;
15
+ // Quoted/escaped paths and rename pairs need a distinct binary porcelain
16
+ // protocol. Treat them as unavailable instead of guessing path identities.
17
+ if (!/^[ MADRCU?!]{2} [^\x00-\x1f\x7f]+$/.test(line) || line.slice(3).includes('"')
18
+ || line.slice(3).includes(" -> ") || line.slice(3).includes("\\"))
19
+ throw new Error("connected_git_status_format_unsupported");
20
+ const path = line.slice(3);
21
+ if (path.startsWith("/") || path.split("/").some(part => part === "." || part === ".."))
22
+ throw new Error("connected_git_status_format_unsupported");
23
+ paths.push(path.replace(/\/$/, ""));
24
+ }
25
+ return paths;
26
+ }
27
+ /** Verify only the original first-tool boundary. Later requests need their own
28
+ * captured state. Both the repository context and observations come from the
29
+ * authorized immutable snapshot, never caller-supplied cleanliness claims. */
30
+ export function verifyConnectedGitStartingAssets(input) {
31
+ const session = ConnectedSessionSchema.parse(input.session);
32
+ const boundary = resolveConnectedBoundaryMetadata(session, input.boundaryId);
33
+ const context = session.repositoryContext;
34
+ if (session.origin !== "codex" || !context?.workingDirectoryHash || !context.recordedAt || boundary.projection !== "turn"
35
+ || !session.sourceFiles.some(file => file.contentHash === context.sourceFileHash)
36
+ || !input.proofs.length || input.proofs.length > 100)
37
+ throw new Error("connected_starting_state_unavailable");
38
+ const firstCallIndex = session.events.findIndex(event => event.kind === "tool_call");
39
+ if (firstCallIndex <= boundary.start || firstCallIndex >= boundary.end
40
+ || session.events.slice(0, firstCallIndex).some(event => event.kind === "compaction"))
41
+ throw new Error("connected_starting_state_boundary_unavailable");
42
+ const callEvent = session.events[firstCallIndex];
43
+ const requestEvent = session.events[boundary.start];
44
+ if (!callEvent.occurredAt || !requestEvent.occurredAt
45
+ || Date.parse(requestEvent.occurredAt) < Date.parse(context.recordedAt)
46
+ || Date.parse(callEvent.occurredAt) < Date.parse(requestEvent.occurredAt))
47
+ throw new Error("connected_starting_state_observation_time_unavailable");
48
+ const call = object(callEvent.content);
49
+ if (call.name !== "exec" || typeof call.input !== "string" || !callEvent.callId)
50
+ throw new Error("connected_starting_state_command_unsupported");
51
+ const commands = recordedCodexCommands(call.input);
52
+ const statusIndex = commands.findIndex(command => command.cmd === "git status --short");
53
+ if (statusIndex !== commands.length - 1 || statusIndex < 1
54
+ || commands.some(command => command.cwd !== null)
55
+ || !commands.slice(0, statusIndex).every(command => isRecordedReadOnlyInventory(command.cmd)))
56
+ throw new Error("connected_starting_state_command_unsupported");
57
+ const observations = session.events.slice(firstCallIndex + 1, boundary.end);
58
+ const resultIndex = observations.findIndex(event => event.kind === "tool_result" && event.callId === callEvent.callId);
59
+ if (resultIndex < 0 || observations.slice(0, resultIndex).some(event => ["tool_call", "tool_result", "compaction", "message"].includes(event.kind))
60
+ || observations.filter(event => event.kind === "tool_result" && event.callId === callEvent.callId).length !== 1)
61
+ throw new Error("connected_starting_state_observation_unavailable");
62
+ const resultEvent = observations[resultIndex];
63
+ const results = recordedCodexCommandResults(resultEvent.content, commands.length);
64
+ if (results.some((result, index) => result.originalTokenCount >= commands[index].maxOutputTokens
65
+ || /output truncated|truncated output|\[\.\.\./i.test(result.output)))
66
+ throw new Error("connected_starting_state_observation_truncated");
67
+ if (contentHash(results[0].output.split("\n")[0]) !== context.workingDirectoryHash)
68
+ throw new Error("connected_starting_state_directory_mismatch");
69
+ const dirty = dirtyPaths(results[statusIndex].output);
70
+ const assets = input.proofs.map(proof => verifyConnectedGitFileProof(context, proof));
71
+ if (new Set(assets.map(asset => asset.path)).size !== assets.length)
72
+ throw new Error("connected_starting_state_duplicate_path");
73
+ for (const asset of assets)
74
+ if (dirty.some(path => asset.path === path || asset.path.startsWith(path + "/")))
75
+ throw new Error("connected_starting_state_selected_file_modified");
76
+ return { schemaVersion: "openpond.connectedGitStartingAssets.v1",
77
+ sessionHash: session.contentHash, boundaryId: boundary.id, inputHash: boundary.inputHash,
78
+ revisionHash: boundary.revisionHash, commitHash: context.commitHash,
79
+ observationHash: contentHash({ call: callEvent, result: resultEvent }),
80
+ selectedPathsUnmodified: true, dependencyClosureQualified: false, assets };
81
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./git-file-proof.js";
2
+ export * from "./git-starting-state.js";
@@ -10,7 +10,7 @@ import { parseClaudeSession } from "./sources/claude-code.js";
10
10
  import { parseHermesEvidence, parseOpenClawEvidence, } from "./sources/history.js";
11
11
  /** Cache identity includes source projection policy, independent of file mtimes. */
12
12
  export function agentImportNormalizerRevision(source) {
13
- return source === "codex" ? `${CONNECTED_NORMALIZER_VERSION}/${CODEX_AUTHORIZATION_PROJECTION_POLICY}` : CONNECTED_NORMALIZER_VERSION;
13
+ return source === "codex" ? `${CONNECTED_NORMALIZER_VERSION}/${CODEX_AUTHORIZATION_PROJECTION_POLICY}/fork-repository-starting-state-v1` : CONNECTED_NORMALIZER_VERSION;
14
14
  }
15
15
  /** Never admits malformed files partly or executes referenced paths/commands. */
16
16
  export function previewAgentImport(input) {
@@ -49,6 +49,7 @@ export function previewAgentImport(input) {
49
49
  warnings: session.warnings,
50
50
  contextComplete: !session.boundaries.some((boundary) => boundary.coverage.context === "unknown"),
51
51
  acquisition: input.acquisition,
52
+ ...(session.repositoryContext ? { repositoryContext: session.repositoryContext } : {}),
52
53
  })
53
54
  : session));
54
55
  }
@@ -4,3 +4,4 @@ export * from "./imports.js";
4
4
  export * from "./usage-summary.js";
5
5
  export * from "./python-correctness.js";
6
6
  export * from "./collector-coverage-contracts.js";
7
+ export * from "./git-file-contracts.js";
@@ -67,6 +67,7 @@ export function normalizeConnectedSession(input) {
67
67
  origin: input.origin, sessionId: input.sessionId, branchId: input.branchId ?? null, parentSessionId: input.parentSessionId ?? null,
68
68
  exporterVersion: input.exporterVersion ?? null, sourceFiles: input.files.map(connectedFileIdentity).sort((a, b) => a.path.localeCompare(b.path)),
69
69
  ...(input.acquisition ? { acquisition: input.acquisition } : {}),
70
+ ...(input.repositoryContext ? { repositoryContext: input.repositoryContext } : {}),
70
71
  events, unmappedEvents: (input.unmappedEvents ?? []).map((event, sequence) => ({ ...event, sequence })), boundaries, warnings: input.warnings ?? [] };
71
72
  const session = ConnectedSessionSchema.parse({ ...body, contentHash: contentHash(body) });
72
73
  if (new TextEncoder().encode(JSON.stringify(session)).length > CONNECTED_EVIDENCE_LIMITS.decodedBytes)
@@ -48,5 +48,38 @@ export function claudeGraph(rows) {
48
48
  return { leafId: leaf.uuid, chainHash: selected.chainHash, title: title.slice(0, 180), updatedAt: connectedTimestamp(last?.timestamp), messages: messages.length };
49
49
  });
50
50
  }
51
- return { sessionId, leaves, select, branches };
51
+ function latestLeafId() {
52
+ // Match Claude's session browser: walk terminal bookkeeping back to a
53
+ // message, then prefer the last main-chain message in file order. Wall
54
+ // clock timestamps and later sidechain/meta records do not choose the chat.
55
+ const candidates = new Set();
56
+ for (const leaf of leaves) {
57
+ let current = leaf;
58
+ const seen = new Set();
59
+ while (current) {
60
+ const id = current.uuid;
61
+ if (seen.has(id))
62
+ throw new Error("Claude Code branch contains a parent cycle.");
63
+ seen.add(id);
64
+ if (current.type === "user" || current.type === "assistant") {
65
+ candidates.add(id);
66
+ break;
67
+ }
68
+ current = typeof current.parentUuid === "string" ? byId.get(current.parentUuid) : undefined;
69
+ }
70
+ }
71
+ let latest, fallback;
72
+ for (const row of nodes) {
73
+ const id = row.uuid;
74
+ if (!candidates.has(id))
75
+ continue;
76
+ fallback = id;
77
+ if (!row.isSidechain && !row.teamName && !row.isMeta)
78
+ latest = id;
79
+ }
80
+ if (!latest && !fallback)
81
+ throw new Error("Claude Code transcript has no conversation message.");
82
+ return (latest ?? fallback);
83
+ }
84
+ return { sessionId, leaves, select, branches, latestLeafId };
52
85
  }
@@ -30,7 +30,7 @@ export function parseClaudeSession(file, rows, leafId) {
30
30
  if (block.type === "tool_use")
31
31
  events.push(event({ ...base, id, kind: "tool_call", callId: typeof block.id === "string" ? block.id : null, content: json(block) }));
32
32
  else if (block.type === "tool_result")
33
- events.push(event({ ...base, id, kind: "tool_result", role: "tool", callId: typeof block.tool_use_id === "string" ? block.tool_use_id : null, content: json(block) }));
33
+ events.push(event({ ...base, id, kind: "tool_result", role: "tool", callId: typeof block.tool_use_id === "string" ? block.tool_use_id : null, content: json({ ...block, ...(row.tool_use_result !== undefined ? { nativeToolResult: row.tool_use_result } : {}) }) }));
34
34
  else
35
35
  events.push(event({ ...base, id, kind: "unknown", content: json(block) }));
36
36
  }
@@ -0,0 +1,33 @@
1
+ /** Forked Codex rollouts retain their own header followed by ancestor headers.
2
+ * Only the declared, contiguous ancestry is metadata for that source. */
3
+ export class CodexMetadataChain {
4
+ metadata = null;
5
+ parentId = null;
6
+ closed = false;
7
+ ids = new Set();
8
+ accept(row) {
9
+ if (row.type !== "session_meta") {
10
+ this.closed = true;
11
+ return true;
12
+ }
13
+ const payload = row.payload;
14
+ if (this.closed || !payload || typeof payload !== "object" || Array.isArray(payload))
15
+ return false;
16
+ const value = payload;
17
+ if (typeof value.id !== "string" || !value.id || value.id.length > 500
18
+ || this.ids.has(value.id) || this.ids.size >= 16)
19
+ return false;
20
+ if (this.metadata) {
21
+ if (value.id !== this.parentId)
22
+ return false;
23
+ for (const key of ["creator_user_id", "creator_account_id"])
24
+ if (this.metadata[key] != null && value[key] != null && this.metadata[key] !== value[key])
25
+ return false;
26
+ }
27
+ else
28
+ this.metadata = value;
29
+ this.ids.add(value.id);
30
+ this.parentId = typeof value.forked_from_id === "string" ? value.forked_from_id : null;
31
+ return true;
32
+ }
33
+ }
@@ -1,11 +1,24 @@
1
1
  import { contentHash } from "@openpond/harness";
2
- import { connectedTimestamp, event, json, normalizeConnectedSession, object } from "../normalize.js";
2
+ import { connectedFileIdentity, connectedTimestamp, event, json, normalizeConnectedSession, object } from "../normalize.js";
3
3
  import { projectCodexAuthorization } from "./codex-authorization.js";
4
4
  import { sourceUsage } from "./usage.js";
5
+ import { CodexMetadataChain } from "./codex-metadata.js";
5
6
  export function parseCodexSession(file, rows) {
6
- const metadata = object(rows.find(row => row.type === "session_meta")?.payload);
7
+ const chain = new CodexMetadataChain();
8
+ if (rows.some(row => !chain.accept(row)))
9
+ throw new Error("Codex rollout metadata does not match its declared fork ancestry.");
10
+ const metadata = chain.metadata ?? {};
7
11
  if (typeof metadata.id !== "string")
8
12
  throw new Error("This is not a supported Codex rollout: session_meta.id is missing.");
13
+ const git = object(metadata.git);
14
+ const repositoryContext = typeof git.commit_hash === "string" && /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(git.commit_hash)
15
+ ? { schemaVersion: "openpond.connectedRepositoryContext.v1", kind: "git",
16
+ commitHash: git.commit_hash, branch: typeof git.branch === "string" && git.branch.length > 0 && git.branch.length <= 500 ? git.branch : null,
17
+ recordedAt: connectedTimestamp(metadata.timestamp), sourceFileHash: connectedFileIdentity(file).contentHash,
18
+ metadataHash: contentHash(git) }
19
+ : undefined;
20
+ if (repositoryContext && typeof metadata.cwd === "string")
21
+ Object.assign(repositoryContext, { workingDirectoryHash: contentHash(metadata.cwd) });
9
22
  const events = [];
10
23
  let turnId = null;
11
24
  let authorizationOmissions = 0;
@@ -52,5 +65,6 @@ export function parseCodexSession(file, rows) {
52
65
  }
53
66
  return normalizeConnectedSession({ origin: "codex", sessionId: metadata.id, parentSessionId: typeof metadata.forked_from_id === "string" ? metadata.forked_from_id : null,
54
67
  exporterVersion: typeof metadata.cli_version === "string" ? metadata.cli_version : null, files: [file], events,
68
+ ...(repositoryContext ? { repositoryContext } : {}),
55
69
  warnings: ["Codex cumulative usage is retained as reported totals and is not summed as billed invocations.", "Referenced files are unavailable unless separately admitted.", ...(authorizationOmissions ? [`Codex native authorization state excluded by codex-native-authorization-v1 from ${authorizationOmissions} locations. Omission markers retain source-section hashes; sourceFiles retains original byte hashes. Other conversation evidence is unchanged and still requires privacy admission.`] : [])] });
56
70
  }
package/dist/graders.js CHANGED
@@ -39,9 +39,15 @@ export const GraderEvidenceContentSchema = z.object({
39
39
  modelJudgeReceipt: ModelJudgeReceiptSchema.optional(),
40
40
  }).strict();
41
41
  export const GraderEvidenceSchema = GraderEvidenceContentSchema.extend({ contentHash: ReleaseHashSchema }).strict();
42
+ /** Scope is part of the immutable grader. An absent scope applies to every
43
+ * task; an explicit scope is never inferred from answer content or metadata. */
44
+ export function gradersForTask(graders, task) {
45
+ return graders.filter(grader => !grader.taskIds || grader.taskIds.includes(task.id));
46
+ }
42
47
  export async function gradeEvidence(input) {
48
+ const graders = gradersForTask(input.graders, input.task);
43
49
  if (input.evidence.infrastructureError) {
44
- return input.graders.map((grader) => evidence(grader, {
50
+ return graders.map((grader) => evidence(grader, {
45
51
  score: null,
46
52
  passed: false,
47
53
  rewardEligible: false,
@@ -51,7 +57,7 @@ export async function gradeEvidence(input) {
51
57
  privilegedEvidenceRefs: [],
52
58
  }));
53
59
  }
54
- return Promise.all(input.graders.map(async (grader) => {
60
+ return Promise.all(graders.map(async (grader) => {
55
61
  if (grader.kind === "model_judge") {
56
62
  if (!input.modelJudge || (grader.calibrationStatus !== "passed" && input.purpose !== "fixture_calibration"))
57
63
  return evidence(grader, unavailable("Model judge is unavailable or uncalibrated."));
@@ -148,7 +154,7 @@ export async function gradeTaskEvidence(input) {
148
154
  const components = await gradeEvidence(input);
149
155
  const content = { schemaVersion: "openpond.taskGrade.v1",
150
156
  taskHash: contentHash(input.task), evidenceHash: contentHash(input.evidence), graderSetHash: contentHash(input.graders), components,
151
- ...aggregateGraderScores({ graders: input.graders, components, infrastructureError: input.evidence.infrastructureError }) };
157
+ ...aggregateGraderScores({ graders: gradersForTask(input.graders, input.task), components, infrastructureError: input.evidence.infrastructureError }) };
152
158
  return TaskGradeSchema.parse({ ...content, contentHash: contentHash(content) });
153
159
  }
154
160
  /** Verify retained results against the exact admitted inputs without rerunning
@@ -163,7 +169,7 @@ export function verifyTaskGrade(value, input) {
163
169
  if (componentHash !== contentHash(body))
164
170
  throw new Error("Grader evidence integrity failed.");
165
171
  }
166
- const summary = aggregateGraderScores({ graders: input.graders, components: result.components, infrastructureError: input.evidence.infrastructureError });
172
+ const summary = aggregateGraderScores({ graders: gradersForTask(input.graders, input.task), components: result.components, infrastructureError: input.evidence.infrastructureError });
167
173
  if (contentHash(GradeSummarySchema.parse({ score: result.score, passed: result.passed, rewardEligible: result.rewardEligible, failureClass: result.failureClass, gradingStatus: result.gradingStatus })) !== contentHash(summary))
168
174
  throw new Error("Task grade summary differs from its grader evidence.");
169
175
  return result;
@@ -1,6 +1,7 @@
1
1
  import { executeJavaScriptIsolate } from "./javascript-isolate.js";
2
2
  import { assertIsolatedVerifierRuntime, JavaScriptVerifierResultSchema } from "./javascript-verifier-contract.js";
3
3
  export { JavaScriptVerifierResultSchema } from "./javascript-verifier-contract.js";
4
+ export { PREPARED_PROCESS_ISOLATION_SOURCE } from "./prepared-process-isolation.js";
4
5
  /** A fresh interpreter with no filesystem, network, timers or module loader.
5
6
  * Hosts should use the worker entrypoint for cancellation and responsiveness. */
6
7
  export async function executeJavaScriptVerifier(input) {
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { open } from "node:fs/promises";
3
3
  import { CONNECTED_EVIDENCE_LIMITS } from "../connected-evidence/contracts.js";
4
+ import { CodexMetadataChain } from "../connected-evidence/sources/codex-metadata.js";
4
5
  /** Streaming inventory may prove an irrelevant source without retaining it.
5
6
  * It never truncates a selected conversation into an admissible snapshot. */
6
7
  export async function inspectCodexWindow(input) {
@@ -14,7 +15,8 @@ export async function inspectCodexWindow(input) {
14
15
  const decoder = new TextDecoder("utf-8", { fatal: true });
15
16
  const from = input.since === null ? -Infinity : Date.parse(input.since);
16
17
  const to = Date.parse(input.until);
17
- let metadata = false, records = 0, bytes = 0;
18
+ const metadata = new CodexMetadataChain();
19
+ let records = 0, bytes = 0;
18
20
  let pending = [], pendingBytes = 0;
19
21
  const inspect = (line) => {
20
22
  if (++records > 1_000_000 || line.byteLength > CONNECTED_EVIDENCE_LIMITS.lineBytes)
@@ -40,11 +42,8 @@ export async function inspectCodexWindow(input) {
40
42
  const value = row;
41
43
  const payload = value.payload && typeof value.payload === "object" && !Array.isArray(value.payload)
42
44
  ? value.payload : {};
43
- if (value.type === "session_meta") {
44
- if (metadata || payload.id !== input.sessionId)
45
- return false;
46
- metadata = true;
47
- }
45
+ if (!metadata.accept(value) || (metadata.metadata && metadata.metadata.id !== input.sessionId))
46
+ return false;
48
47
  // The Codex normalizer maps other native records to unknown events;
49
48
  // they cannot create request boundaries. Missing metadata is still refused.
50
49
  const request = value.type === "response_item" && payload.type === "message" && payload.role === "user"
@@ -89,7 +88,7 @@ export async function inspectCodexWindow(input) {
89
88
  if (initial.dev !== final.dev || initial.ino !== final.ino || initial.size !== final.size
90
89
  || initial.mtimeMs !== final.mtimeMs || initial.ctimeMs !== final.ctimeMs || bytes !== final.size)
91
90
  throw new Error("Native source changed during window inventory.");
92
- return metadata ? { sha256: digest.digest("hex") } : null;
91
+ return metadata.metadata ? { sha256: digest.digest("hex") } : null;
93
92
  }
94
93
  finally {
95
94
  await handle.close();
@@ -7,6 +7,7 @@ import { CollectorQueueFullError } from "./collector-snapshots.js";
7
7
  import { COLLECTOR_DEFAULTS } from "./collector-contracts.js";
8
8
  import { CollectorCoverage } from "./collector-coverage.js";
9
9
  import { CollectorSourceCache } from "./collector-source-cache.js";
10
+ import { nativeSessionSelectionKey } from "./selection.js";
10
11
  /** One pass over a frozen listing per page. Unchanged revisions are cheap; a
11
12
  * failed source is tried again only in a later explicitly launched job. */
12
13
  export async function acquireCollectorSource(input) {
@@ -14,23 +15,30 @@ export async function acquireCollectorSource(input) {
14
15
  const cache = new CollectorSourceCache(store);
15
16
  let cursor, processed = 0, discovered = 0;
16
17
  const failures = [];
18
+ const selection = connection.selectedSessionKeys ? new Set(connection.selectedSessionKeys) : null;
19
+ const found = new Set();
20
+ let enumerated = 0;
17
21
  do {
18
22
  await check();
19
23
  progress({ phase: "discovering" });
20
24
  // File mtimes and native activity summaries are not event-time authority.
21
25
  // Enumerate the source, then apply the consented window to native events.
22
26
  const page = await listSessions(connection.source, { cursor, limit: 50 });
23
- discovered += page.items.length;
24
- if (discovered > 10_000)
25
- throw new Error("Source inventory exceeds 10,000 sessions. Select a narrower import window.");
26
- for (const native of page.items) {
27
+ enumerated += page.items.length;
28
+ if (enumerated > 10_000)
29
+ throw new Error("Source inventory exceeds 10,000 sessions. Select a narrower source directory.");
30
+ const items = page.items.filter(native => !selection || selection.has(nativeSessionSelectionKey(native)));
31
+ for (const native of items)
32
+ found.add(nativeSessionSelectionKey(native));
33
+ discovered += items.length;
34
+ for (const native of items) {
27
35
  const scanKey = JSON.stringify([native.path, native.nativeSessionId]);
28
36
  store.coverage.record(connection.id, { sourceKeyHash: CollectorCoverage.sourceKey(scanKey),
29
37
  nativeRevisionHash: native.storageRevision ? contentHash([native.storageRevision, agentImportNormalizerRevision(connection.source.source)]) : null,
30
38
  state: "reading", reason: null, unknownTimeBoundaries: 0, normalized: [] });
31
39
  }
32
40
  progress({ discovered });
33
- for (const native of page.items) {
41
+ for (const native of items) {
34
42
  await check();
35
43
  // Do not parse another transcript while any retained admission is blocked.
36
44
  await drain();
@@ -139,7 +147,10 @@ export async function acquireCollectorSource(input) {
139
147
  cursor = page.nextCursor ?? undefined;
140
148
  } while (cursor);
141
149
  store.progress.discovered(connection.id);
142
- store.coverage.finish(connection.id, true);
150
+ // A chosen subset cannot certify coverage of the entire source window.
151
+ store.coverage.finish(connection.id, selection === null);
152
+ if (selection && [...selection].some(key => !found.has(key)))
153
+ throw new Error("A selected conversation is no longer available. Review the upload selection.");
143
154
  if (failures.length)
144
155
  throw new Error(`${failures.length} session(s) need attention. ${failures[0]}`);
145
156
  }