agent-hitch 0.2.0 → 0.2.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 (263) hide show
  1. package/README.md +16 -10
  2. package/README.zh-CN.md +13 -8
  3. package/dist/bin/hitch.js +1 -1
  4. package/dist/bin/hitch.js.map +1 -1
  5. package/dist/scripts/check-architecture.js +159 -0
  6. package/dist/scripts/check-architecture.js.map +1 -0
  7. package/dist/scripts/check-release.js +1 -1
  8. package/dist/scripts/check-release.js.map +1 -1
  9. package/dist/src/adapters/catalog.js +55 -0
  10. package/dist/src/adapters/catalog.js.map +1 -0
  11. package/dist/src/adapters/contract.js +2 -0
  12. package/dist/src/adapters/contract.js.map +1 -0
  13. package/dist/src/adapters/discovery.js +22 -0
  14. package/dist/src/adapters/discovery.js.map +1 -0
  15. package/dist/src/adapters/index.js +3 -0
  16. package/dist/src/adapters/index.js.map +1 -0
  17. package/dist/src/adapters/providers/claude.js +69 -0
  18. package/dist/src/adapters/providers/claude.js.map +1 -0
  19. package/dist/src/adapters/providers/codex.js +74 -0
  20. package/dist/src/adapters/providers/codex.js.map +1 -0
  21. package/dist/src/adapters/providers/deepseek.js +51 -0
  22. package/dist/src/adapters/providers/deepseek.js.map +1 -0
  23. package/dist/src/adapters/providers/opencode.js +73 -0
  24. package/dist/src/adapters/providers/opencode.js.map +1 -0
  25. package/dist/src/adapters/providers/pi.js +82 -0
  26. package/dist/src/adapters/providers/pi.js.map +1 -0
  27. package/dist/src/adapters/providers/shared.js +82 -0
  28. package/dist/src/adapters/providers/shared.js.map +1 -0
  29. package/dist/src/artifacts/index.js +3 -0
  30. package/dist/src/artifacts/index.js.map +1 -0
  31. package/dist/src/artifacts/integrity.js +188 -0
  32. package/dist/src/artifacts/integrity.js.map +1 -0
  33. package/dist/src/artifacts/preparer.js +379 -0
  34. package/dist/src/artifacts/preparer.js.map +1 -0
  35. package/dist/src/artifacts/store.js +87 -0
  36. package/dist/src/artifacts/store.js.map +1 -0
  37. package/dist/src/artifacts/types.js +2 -0
  38. package/dist/src/artifacts/types.js.map +1 -0
  39. package/dist/src/backends/contract.js +2 -0
  40. package/dist/src/backends/contract.js.map +1 -0
  41. package/dist/src/{harbor-backend.js → backends/harbor/backend.js} +39 -14
  42. package/dist/src/backends/harbor/backend.js.map +1 -0
  43. package/dist/src/backends/harbor/index.js +4 -0
  44. package/dist/src/backends/harbor/index.js.map +1 -0
  45. package/dist/src/backends/harbor/local-git-transport.js +447 -0
  46. package/dist/src/backends/harbor/local-git-transport.js.map +1 -0
  47. package/dist/src/{eval-tools.js → backends/harbor/tools.js} +2 -6
  48. package/dist/src/backends/harbor/tools.js.map +1 -0
  49. package/dist/src/backends/index.js +4 -0
  50. package/dist/src/backends/index.js.map +1 -0
  51. package/dist/src/cli/arguments.js +123 -0
  52. package/dist/src/cli/arguments.js.map +1 -0
  53. package/dist/src/cli/commands/compare.js +55 -0
  54. package/dist/src/cli/commands/compare.js.map +1 -0
  55. package/dist/src/cli/commands/daemon.js +106 -0
  56. package/dist/src/cli/commands/daemon.js.map +1 -0
  57. package/dist/src/cli/commands/eval.js +126 -0
  58. package/dist/src/cli/commands/eval.js.map +1 -0
  59. package/dist/src/cli/commands/feedback.js +84 -0
  60. package/dist/src/cli/commands/feedback.js.map +1 -0
  61. package/dist/src/cli/commands/inspect.js +20 -0
  62. package/dist/src/cli/commands/inspect.js.map +1 -0
  63. package/dist/src/cli/commands/list.js +17 -0
  64. package/dist/src/cli/commands/list.js.map +1 -0
  65. package/dist/src/cli/commands/prepare.js +21 -0
  66. package/dist/src/cli/commands/prepare.js.map +1 -0
  67. package/dist/src/cli/commands/resolve.js +17 -0
  68. package/dist/src/cli/commands/resolve.js.map +1 -0
  69. package/dist/src/cli/commands/run.js +83 -0
  70. package/dist/src/cli/commands/run.js.map +1 -0
  71. package/dist/src/cli/commands/runs.js +45 -0
  72. package/dist/src/cli/commands/runs.js.map +1 -0
  73. package/dist/src/cli/commands/trajectory.js +31 -0
  74. package/dist/src/cli/commands/trajectory.js.map +1 -0
  75. package/dist/src/cli/commands/workspace.js +44 -0
  76. package/dist/src/cli/commands/workspace.js.map +1 -0
  77. package/dist/src/cli/index.js +2 -0
  78. package/dist/src/cli/index.js.map +1 -0
  79. package/dist/src/cli/main.js +48 -0
  80. package/dist/src/cli/main.js.map +1 -0
  81. package/dist/src/cli/output.js +96 -0
  82. package/dist/src/cli/output.js.map +1 -0
  83. package/dist/src/controller-runtime/index.js +3 -0
  84. package/dist/src/controller-runtime/index.js.map +1 -0
  85. package/dist/src/controller-runtime/store.js +2 -7
  86. package/dist/src/controller-runtime/store.js.map +1 -1
  87. package/dist/src/daemon/auth.js +108 -0
  88. package/dist/src/daemon/auth.js.map +1 -0
  89. package/dist/src/daemon/client.js +76 -0
  90. package/dist/src/daemon/client.js.map +1 -0
  91. package/dist/src/daemon/index.js +5 -0
  92. package/dist/src/daemon/index.js.map +1 -0
  93. package/dist/src/daemon/launcher.js +24 -0
  94. package/dist/src/daemon/launcher.js.map +1 -0
  95. package/dist/src/{scheduler.js → daemon/scheduler.js} +6 -7
  96. package/dist/src/daemon/scheduler.js.map +1 -0
  97. package/dist/src/{daemon.js → daemon/server.js} +10 -155
  98. package/dist/src/daemon/server.js.map +1 -0
  99. package/dist/src/domain/artifacts.js +2 -0
  100. package/dist/src/domain/artifacts.js.map +1 -0
  101. package/dist/src/domain/controller-runtime.js +2 -0
  102. package/dist/src/domain/controller-runtime.js.map +1 -0
  103. package/dist/src/domain/eval-records.js +2 -0
  104. package/dist/src/domain/eval-records.js.map +1 -0
  105. package/dist/src/domain/evals.js +2 -0
  106. package/dist/src/domain/evals.js.map +1 -0
  107. package/dist/src/domain/feedback.js +2 -0
  108. package/dist/src/domain/feedback.js.map +1 -0
  109. package/dist/src/domain/{types.js → ids.js} +1 -1
  110. package/dist/src/domain/ids.js.map +1 -0
  111. package/dist/src/domain/index.js +11 -0
  112. package/dist/src/domain/index.js.map +1 -0
  113. package/dist/src/domain/revisions.js +2 -0
  114. package/dist/src/domain/revisions.js.map +1 -0
  115. package/dist/src/domain/runs.js +3 -0
  116. package/dist/src/domain/runs.js.map +1 -0
  117. package/dist/src/domain/trajectories.js +2 -0
  118. package/dist/src/domain/trajectories.js.map +1 -0
  119. package/dist/src/domain/{validate.js → validation.js} +194 -5
  120. package/dist/src/domain/validation.js.map +1 -0
  121. package/dist/src/domain/workspaces.js +2 -0
  122. package/dist/src/domain/workspaces.js.map +1 -0
  123. package/dist/src/evals/events.js +67 -0
  124. package/dist/src/evals/events.js.map +1 -0
  125. package/dist/src/evals/index.js +5 -0
  126. package/dist/src/evals/index.js.map +1 -0
  127. package/dist/src/evals/records.js +55 -0
  128. package/dist/src/evals/records.js.map +1 -0
  129. package/dist/src/evals/request.js +109 -0
  130. package/dist/src/evals/request.js.map +1 -0
  131. package/dist/src/evals/service.js +227 -0
  132. package/dist/src/evals/service.js.map +1 -0
  133. package/dist/src/evals/trial-import.js +394 -0
  134. package/dist/src/evals/trial-import.js.map +1 -0
  135. package/dist/src/feedback/index.js +2 -0
  136. package/dist/src/feedback/index.js.map +1 -0
  137. package/dist/src/feedback/service.js +3 -4
  138. package/dist/src/feedback/service.js.map +1 -1
  139. package/dist/src/{config.js → foundation/config.js} +1 -0
  140. package/dist/src/foundation/config.js.map +1 -0
  141. package/dist/src/foundation/errors.js.map +1 -0
  142. package/dist/src/foundation/executable.js +135 -0
  143. package/dist/src/foundation/executable.js.map +1 -0
  144. package/dist/src/foundation/fs.js.map +1 -0
  145. package/dist/src/foundation/hash.js +24 -0
  146. package/dist/src/foundation/hash.js.map +1 -0
  147. package/dist/src/foundation/index.js +11 -0
  148. package/dist/src/foundation/index.js.map +1 -0
  149. package/dist/src/foundation/line-stream.js.map +1 -0
  150. package/dist/src/foundation/locks.js +114 -0
  151. package/dist/src/foundation/locks.js.map +1 -0
  152. package/dist/src/{package-root.js → foundation/package-root.js} +3 -2
  153. package/dist/src/foundation/package-root.js.map +1 -0
  154. package/dist/src/foundation/process.js.map +1 -0
  155. package/dist/src/revisions/index.js +4 -0
  156. package/dist/src/revisions/index.js.map +1 -0
  157. package/dist/src/{harness-reference.js → revisions/reference.js} +16 -2
  158. package/dist/src/revisions/reference.js.map +1 -0
  159. package/dist/src/revisions/resolver.js +25 -0
  160. package/dist/src/revisions/resolver.js.map +1 -0
  161. package/dist/src/revisions/sources/git.js +180 -0
  162. package/dist/src/revisions/sources/git.js.map +1 -0
  163. package/dist/src/revisions/sources/installed.js +45 -0
  164. package/dist/src/revisions/sources/installed.js.map +1 -0
  165. package/dist/src/revisions/sources/npm.js +97 -0
  166. package/dist/src/revisions/sources/npm.js.map +1 -0
  167. package/dist/src/runs/compare.js +132 -0
  168. package/dist/src/runs/compare.js.map +1 -0
  169. package/dist/src/{events.js → runs/events.js} +1 -2
  170. package/dist/src/runs/events.js.map +1 -0
  171. package/dist/src/{engine.js → runs/executor.js} +166 -151
  172. package/dist/src/runs/executor.js.map +1 -0
  173. package/dist/src/runs/finalizer.js +22 -0
  174. package/dist/src/runs/finalizer.js.map +1 -0
  175. package/dist/src/runs/identity.js +35 -0
  176. package/dist/src/runs/identity.js.map +1 -0
  177. package/dist/src/runs/index.js +11 -0
  178. package/dist/src/runs/index.js.map +1 -0
  179. package/dist/src/runs/manifest.js +70 -0
  180. package/dist/src/runs/manifest.js.map +1 -0
  181. package/dist/src/runs/outcome.js +49 -0
  182. package/dist/src/runs/outcome.js.map +1 -0
  183. package/dist/src/runs/query.js +104 -0
  184. package/dist/src/runs/query.js.map +1 -0
  185. package/dist/src/runs/queued.js +54 -0
  186. package/dist/src/runs/queued.js.map +1 -0
  187. package/dist/src/runs/records.js +288 -0
  188. package/dist/src/runs/records.js.map +1 -0
  189. package/dist/src/runs/request.js +169 -0
  190. package/dist/src/runs/request.js.map +1 -0
  191. package/dist/src/trajectories/format.js +1 -1
  192. package/dist/src/trajectories/format.js.map +1 -1
  193. package/dist/src/trajectories/index.js +7 -0
  194. package/dist/src/trajectories/index.js.map +1 -0
  195. package/dist/src/trajectories/projector.js +1 -1
  196. package/dist/src/trajectories/projector.js.map +1 -1
  197. package/dist/src/trajectories/provider-capture.js +133 -0
  198. package/dist/src/trajectories/provider-capture.js.map +1 -0
  199. package/dist/src/trajectories/providers/deepseek.js +179 -0
  200. package/dist/src/trajectories/providers/deepseek.js.map +1 -0
  201. package/dist/src/trajectories/store.js +148 -8
  202. package/dist/src/trajectories/store.js.map +1 -1
  203. package/dist/src/workspaces/copy.js +142 -0
  204. package/dist/src/workspaces/copy.js.map +1 -0
  205. package/dist/src/workspaces/digest.js +47 -0
  206. package/dist/src/workspaces/digest.js.map +1 -0
  207. package/dist/src/workspaces/git.js +164 -0
  208. package/dist/src/workspaces/git.js.map +1 -0
  209. package/dist/src/workspaces/index.js +6 -0
  210. package/dist/src/workspaces/index.js.map +1 -0
  211. package/dist/src/workspaces/lifecycle.js +313 -0
  212. package/dist/src/workspaces/lifecycle.js.map +1 -0
  213. package/dist/src/workspaces/planner.js +80 -0
  214. package/dist/src/workspaces/planner.js.map +1 -0
  215. package/dist/src/workspaces/store.js +25 -0
  216. package/dist/src/workspaces/store.js.map +1 -0
  217. package/dist/src/workspaces/types.js +4 -0
  218. package/dist/src/workspaces/types.js.map +1 -0
  219. package/dist/src/workspaces/utils.js +56 -0
  220. package/dist/src/workspaces/utils.js.map +1 -0
  221. package/docs/schemas/eval-request.schema.json +2 -0
  222. package/docs/schemas/eval-result.schema.json +36 -1
  223. package/docs/schemas/local-git-transport-manifest.schema.json +34 -0
  224. package/docs/schemas/result.schema.json +9 -0
  225. package/docs/schemas/run-context.schema.json +39 -0
  226. package/docs/schemas/run-manifest.schema.json +81 -0
  227. package/docs/schemas/run-request.schema.json +16 -1
  228. package/docs/schemas/trajectory-ref.schema.json +66 -23
  229. package/integrations/harbor/hitch_harbor_agent.py +375 -1
  230. package/package.json +21 -3
  231. package/dist/src/adapters.js +0 -461
  232. package/dist/src/adapters.js.map +0 -1
  233. package/dist/src/artifacts.js +0 -1081
  234. package/dist/src/artifacts.js.map +0 -1
  235. package/dist/src/cli.js +0 -699
  236. package/dist/src/cli.js.map +0 -1
  237. package/dist/src/config.js.map +0 -1
  238. package/dist/src/daemon.js.map +0 -1
  239. package/dist/src/domain/types.js.map +0 -1
  240. package/dist/src/domain/validate.js.map +0 -1
  241. package/dist/src/engine.js.map +0 -1
  242. package/dist/src/errors.js.map +0 -1
  243. package/dist/src/eval-tools.js.map +0 -1
  244. package/dist/src/evals.js +0 -313
  245. package/dist/src/evals.js.map +0 -1
  246. package/dist/src/events.js.map +0 -1
  247. package/dist/src/fs.js.map +0 -1
  248. package/dist/src/harbor-backend.js.map +0 -1
  249. package/dist/src/harness-reference.js.map +0 -1
  250. package/dist/src/line-stream.js.map +0 -1
  251. package/dist/src/locks.js +0 -38
  252. package/dist/src/locks.js.map +0 -1
  253. package/dist/src/package-root.js.map +0 -1
  254. package/dist/src/process.js.map +0 -1
  255. package/dist/src/registry.js +0 -81
  256. package/dist/src/registry.js.map +0 -1
  257. package/dist/src/scheduler.js.map +0 -1
  258. package/dist/src/workspaces.js +0 -858
  259. package/dist/src/workspaces.js.map +0 -1
  260. /package/dist/src/{errors.js → foundation/errors.js} +0 -0
  261. /package/dist/src/{fs.js → foundation/fs.js} +0 -0
  262. /package/dist/src/{line-stream.js → foundation/line-stream.js} +0 -0
  263. /package/dist/src/{process.js → foundation/process.js} +0 -0
@@ -0,0 +1,227 @@
1
+ import path from "node:path";
2
+ import { resolveHarness } from "../artifacts/index.js";
3
+ import { HitchError, SCHEMA_VERSION, atomicWriteJSON, ensureDir, invalidInput, statePaths } from "../foundation/index.js";
4
+ import { parseHarnessReference } from "../revisions/index.js";
5
+ import { buildLocalGitTransport, lockedHarnessRef, runHarborBackend, verifyLocalGitTransport } from "../backends/index.js";
6
+ import { ensureControllerRuntime, writeRuntimeReference } from "../controller-runtime/index.js";
7
+ import { importEvalTrialRuns, validateEvalTrialReferences } from "./trial-import.js";
8
+ import { EvalEventSink } from "./events.js";
9
+ import { newEvalId, validateEvalRequest } from "./request.js";
10
+ export async function runEval({ evalId = newEvalId(), request, root, env = process.env, harborExecutable, signal, onEvent }) {
11
+ if (!root)
12
+ throw invalidInput("a Hitch state root is required for eval");
13
+ const normalized = await validateEvalRequest(request);
14
+ const evalDirectory = await ensureDir(path.join(statePaths(root).evals, evalId));
15
+ const startedAt = new Date();
16
+ const sink = new EvalEventSink(evalDirectory, evalId, onEvent);
17
+ await sink.open();
18
+ await atomicWriteJSON(path.join(evalDirectory, "request.json"), normalized);
19
+ let result;
20
+ let trialRefs = [];
21
+ try {
22
+ sink.emit({ type: "eval.started", backend: normalized.backend, dataset: normalized.dataset });
23
+ const requestedReference = parseHarnessReference(normalized.harness_ref);
24
+ const resolvedRevision = await resolveHarness(normalized.harness_ref, { root, env });
25
+ let localTransport;
26
+ if (resolvedRevision.source.type === "git" && resolvedRevision.source.registered !== true) {
27
+ const selector = requestedReference.selector;
28
+ if (selector.type !== "commit" || !selector.source?.explicit)
29
+ throw invalidInput("Harbor local Git eval requires an explicit git+file source");
30
+ if (resolvedRevision.revision.commit !== selector.value) {
31
+ throw new HitchError("local Git exact commit resolved to a different object", { code: "local_source_integrity_mismatch", exitCode: 12 });
32
+ }
33
+ localTransport = await buildLocalGitTransport({
34
+ evalDirectory,
35
+ resolvedRevision,
36
+ sourceDirectory: selector.source.local_path,
37
+ env,
38
+ ...(signal ? { signal } : {}),
39
+ });
40
+ sink.emit({
41
+ type: "eval.local-source.prepared",
42
+ resolution_identity: localTransport.manifest.resolution_identity,
43
+ commit: localTransport.manifest.commit,
44
+ payload_sha256: localTransport.manifest.payload_sha256,
45
+ payload_bytes: localTransport.manifest.payload_bytes,
46
+ status: "verified",
47
+ });
48
+ }
49
+ // Phase 2: the shared, read-only, SHA-256-addressed controller runtime
50
+ // cache replaces the per-eval Hitch runtime copy (spec §4).
51
+ const controllerRuntime = await ensureControllerRuntime({ root });
52
+ const runtimeRefFile = await writeRuntimeReference(evalDirectory, controllerRuntime);
53
+ sink.emit({
54
+ type: "eval.controller-runtime",
55
+ runtime_id: controllerRuntime.runtime_id,
56
+ cache_hit: controllerRuntime.cache_hit,
57
+ reference: runtimeRefFile,
58
+ });
59
+ const plan = {
60
+ schema_version: SCHEMA_VERSION,
61
+ eval_id: evalId,
62
+ backend: "harbor",
63
+ candidate: {
64
+ id: "candidate-1",
65
+ requested_harness_ref: normalized.harness_ref,
66
+ harness_ref: lockedHarnessRef(resolvedRevision),
67
+ harness_id: resolvedRevision.harness_id,
68
+ revision_identity: resolvedRevision.identity,
69
+ model: normalized.model || null,
70
+ },
71
+ dataset: normalized.dataset,
72
+ benchmark_id: normalized.benchmark_id,
73
+ benchmark_revision: normalized.benchmark_revision,
74
+ attempts: normalized.attempts,
75
+ max_concurrent: normalized.max_concurrent,
76
+ controller_runtime: {
77
+ runtime_id: controllerRuntime.runtime_id,
78
+ manifest_digest: controllerRuntime.manifest_digest,
79
+ },
80
+ ...(localTransport ? { local_source_transport: transportSummary(localTransport) } : {}),
81
+ created_at: new Date().toISOString(),
82
+ };
83
+ await atomicWriteJSON(path.join(evalDirectory, "resolution.json"), resolvedRevision);
84
+ await atomicWriteJSON(path.join(evalDirectory, "plan.json"), plan);
85
+ sink.emit({
86
+ type: "eval.planned",
87
+ harness: resolvedRevision.harness_id,
88
+ revision_identity: resolvedRevision.identity,
89
+ attempts: normalized.attempts,
90
+ });
91
+ if (localTransport) {
92
+ await verifyLocalGitTransport(localTransport, {
93
+ expected: {
94
+ harnessId: resolvedRevision.harness_id,
95
+ resolutionIdentity: resolvedRevision.identity,
96
+ commit: resolvedRevision.revision.commit,
97
+ },
98
+ env,
99
+ ...(signal ? { signal } : {}),
100
+ });
101
+ }
102
+ const backendRun = await runHarborBackend({
103
+ evalId,
104
+ evalDirectory,
105
+ request: normalized,
106
+ root,
107
+ resolvedRevision,
108
+ runtimeDirectory: controllerRuntime.directory,
109
+ runtimeId: controllerRuntime.runtime_id,
110
+ ...(localTransport ? { localTransport } : {}),
111
+ env,
112
+ ...(harborExecutable !== undefined ? { harborExecutable } : {}),
113
+ ...(signal ? { signal } : {}),
114
+ emit: (event) => sink.emit(event),
115
+ });
116
+ trialRefs = await importEvalTrialRuns({
117
+ root,
118
+ evalId,
119
+ evalDirectory,
120
+ request: normalized,
121
+ resolvedRevision,
122
+ benchmarkId: normalized.benchmark_id,
123
+ benchmarkRevision: normalized.benchmark_revision,
124
+ runtimeId: controllerRuntime.runtime_id,
125
+ rawResult: backendRun.rawResult,
126
+ });
127
+ await validateEvalTrialReferences(root, evalId, trialRefs, {
128
+ benchmarkId: normalized.benchmark_id,
129
+ benchmarkRevision: normalized.benchmark_revision,
130
+ });
131
+ const cancelled = signal?.aborted;
132
+ const localSourceFailure = localTransport ? localSourceBackendFailure(backendRun.rawResult) : false;
133
+ const succeeded = !cancelled && !localSourceFailure && backendRun.backend.process_exit_code === 0 && backendRun.rawResult !== null;
134
+ result = {
135
+ schema_version: SCHEMA_VERSION,
136
+ eval_id: evalId,
137
+ status: cancelled ? "cancelled" : succeeded ? "succeeded" : "failed",
138
+ exit_code: cancelled ? 9 : succeeded ? 0 : 13,
139
+ backend: backendRun.backend,
140
+ candidate: plan.candidate,
141
+ dataset: normalized.dataset,
142
+ benchmark_id: normalized.benchmark_id,
143
+ benchmark_revision: normalized.benchmark_revision,
144
+ trials: trialRefs,
145
+ summary: summarizeTrialRefs(trialRefs),
146
+ backend_summary: backendRun.summary,
147
+ ...(localTransport ? { local_source_transport: transportSummary(localTransport) } : {}),
148
+ ...(succeeded ? {} : {
149
+ error: {
150
+ code: cancelled ? "cancelled" : localSourceFailure ? "local_source_materialize_failed" : "harbor_failed",
151
+ message: cancelled
152
+ ? "eval was cancelled"
153
+ : localSourceFailure
154
+ ? "Harbor rejected the transported local Git source before candidate execution"
155
+ : backendRun.rawResult === null
156
+ ? `Harbor exited without a result (code ${backendRun.backend.process_exit_code ?? "null"})`
157
+ : `Harbor exited with code ${backendRun.backend.process_exit_code ?? "null"}`,
158
+ },
159
+ }),
160
+ started_at: startedAt.toISOString(),
161
+ completed_at: new Date().toISOString(),
162
+ };
163
+ }
164
+ catch (error) {
165
+ const typed = error instanceof HitchError;
166
+ result = {
167
+ schema_version: SCHEMA_VERSION,
168
+ eval_id: evalId,
169
+ status: signal?.aborted ? "cancelled" : "failed",
170
+ exit_code: signal?.aborted ? 9 : typed ? error.exitCode : 12,
171
+ error: {
172
+ code: signal?.aborted ? "cancelled" : typed ? error.code : "internal_error",
173
+ message: error?.message || String(error),
174
+ },
175
+ benchmark_id: normalized.benchmark_id,
176
+ benchmark_revision: normalized.benchmark_revision,
177
+ trials: trialRefs,
178
+ started_at: startedAt.toISOString(),
179
+ completed_at: new Date().toISOString(),
180
+ };
181
+ }
182
+ await atomicWriteJSON(path.join(evalDirectory, "result.json"), result);
183
+ sink.emit({ type: result.status === "succeeded" ? "eval.completed" : "eval.failed", status: result.status, exit_code: result.exit_code, error: result.error });
184
+ await sink.close();
185
+ return result;
186
+ }
187
+ function summarizeTrialRefs(trials) {
188
+ const valid = trials.filter((trial) => trial.observation_status === "valid" && typeof trial.reward === "number");
189
+ const rewards = valid.map((trial) => trial.reward);
190
+ const aggregate = rewards.length
191
+ ? {
192
+ count: rewards.length,
193
+ mean: rewards.reduce((sum, reward) => sum + reward, 0) / rewards.length,
194
+ min: Math.min(...rewards),
195
+ max: Math.max(...rewards),
196
+ }
197
+ : null;
198
+ return {
199
+ n_trials: trials.length,
200
+ n_completed: valid.length,
201
+ n_invalid: trials.length - valid.length,
202
+ primary_reward: aggregate?.mean ?? null,
203
+ rewards: aggregate ? { reward: aggregate } : {},
204
+ };
205
+ }
206
+ function localSourceBackendFailure(rawResult) {
207
+ if (!rawResult)
208
+ return false;
209
+ // The bridge uses this fixed, non-secret marker when setup cannot verify or
210
+ // materialize the uploaded source. Do not surface provider exception text in
211
+ // the durable Hitch error, because it may contain backend diagnostics.
212
+ return JSON.stringify(rawResult).includes("hitch-local-source-materialize:");
213
+ }
214
+ function transportSummary(transport) {
215
+ const manifest = transport.manifest;
216
+ return {
217
+ kind: manifest.kind,
218
+ resolution_identity: manifest.resolution_identity,
219
+ commit: manifest.commit,
220
+ tree: manifest.tree,
221
+ payload_sha256: manifest.payload_sha256,
222
+ payload_bytes: manifest.payload_bytes,
223
+ object_count: manifest.object_count,
224
+ file_count: manifest.file_count,
225
+ };
226
+ }
227
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/evals/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,394 @@
1
+ import { cp, lstat, mkdtemp, readFile, readdir, rename, rm, stat } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { atomicWriteJSON, ensureDir, readJSON, statePaths, writePrivateFile } from "../foundation/index.js";
4
+ import { validateRunContext } from "../domain/index.js";
5
+ import { newRunId, safeAgentArgsForPersistence } from "../runs/index.js";
6
+ import { benchmarkTaskDigest, benchmarkVerifierIdentity, defaultModelIdentity, loadRunRecord, projectRunRecord, sha256JSON, } from "../runs/index.js";
7
+ /** Import every Harbor trial into the authoritative runs/ store. */
8
+ export async function importEvalTrialRuns(options) {
9
+ const trials = Array.isArray(options.rawResult?.trial_results)
10
+ ? options.rawResult.trial_results
11
+ : [];
12
+ const refs = [];
13
+ for (const [index, trial] of trials.entries()) {
14
+ // Harbor's result task_name may be display-qualified (for example,
15
+ // terminal-bench/regex-log), while the persisted trial lock contains the
16
+ // canonical task id used by the in-container bridge and exported run.
17
+ // Keep task_name only as a compatibility fallback for Harbor runtimes
18
+ // that do not persist a lock.json.
19
+ const fallbackTaskId = nonEmpty(trial.task_name) || `trial-${index + 1}`;
20
+ const trialId = nonEmpty(trial.trial_name) || `${fallbackTaskId}__${index + 1}`;
21
+ const attempt = trialAttempt(trialId);
22
+ const trialDirectory = path.join(options.evalDirectory, "harbor", "job", trialId);
23
+ const bundle = await findRunBundle(trialDirectory);
24
+ let taskId = fallbackTaskId;
25
+ let published = false;
26
+ try {
27
+ taskId = await lockedTaskId(trialDirectory) || fallbackTaskId;
28
+ if (bundle) {
29
+ refs.push(await importRunBundle({ ...options, trial, taskId, trialId, attempt, bundle }));
30
+ published = true;
31
+ }
32
+ else {
33
+ refs.push(await createDiagnosticRun({ ...options, trial, taskId, trialId, attempt }));
34
+ }
35
+ }
36
+ catch (error) {
37
+ if (bundle) {
38
+ await atomicWriteJSON(path.join(path.dirname(bundle), "hitch-run-import-error.json"), {
39
+ schema_version: "1",
40
+ trial_id: trialId,
41
+ code: "run_bundle_import_failed",
42
+ message: error.message,
43
+ recorded_at: new Date().toISOString(),
44
+ }).catch(() => { });
45
+ }
46
+ refs.push(await createDiagnosticRun({
47
+ ...options,
48
+ trial: {
49
+ ...trial,
50
+ exception_info: "hitch-run-bundle-import-failed",
51
+ hitch_import_error: error.message,
52
+ },
53
+ taskId,
54
+ trialId,
55
+ attempt,
56
+ }));
57
+ }
58
+ finally {
59
+ // The exported bundle is staging, not a second trajectory authority.
60
+ // Delete it only after atomic publication. A failed import keeps the
61
+ // complete bundle beside its diagnostic so it can be inspected/retried.
62
+ if (bundle && published && isWithin(options.evalDirectory, bundle)) {
63
+ await rm(bundle, { recursive: true, force: true });
64
+ }
65
+ }
66
+ }
67
+ return refs;
68
+ }
69
+ async function lockedTaskId(trialDirectory) {
70
+ const lockPath = path.join(trialDirectory, "lock.json");
71
+ let lock;
72
+ try {
73
+ lock = await readJSON(lockPath);
74
+ }
75
+ catch (error) {
76
+ if (error.code === "ENOENT")
77
+ return null;
78
+ throw new Error(`Harbor trial lock is unreadable: ${lockPath}`, { cause: error });
79
+ }
80
+ if (!lock || typeof lock !== "object" || Array.isArray(lock)) {
81
+ throw new Error(`Harbor trial lock is invalid: ${lockPath}`);
82
+ }
83
+ const task = lock.task;
84
+ if (!task || typeof task !== "object" || Array.isArray(task)) {
85
+ throw new Error(`Harbor trial lock has no task.name: ${lockPath}`);
86
+ }
87
+ const taskId = nonEmpty(task.name);
88
+ if (!taskId)
89
+ throw new Error(`Harbor trial lock has no task.name: ${lockPath}`);
90
+ return taskId;
91
+ }
92
+ async function importRunBundle(input) {
93
+ await validateBundleTree(input.bundle);
94
+ const manifestValue = await readJSON(path.join(input.bundle, "manifest.json"));
95
+ const record = projectRunRecord(manifestValue);
96
+ if (!/^run_[a-f0-9]{32}$/.test(record.run_id))
97
+ throw new Error(`invalid exported run_id: ${record.run_id}`);
98
+ const context = validateRunContext(record.context);
99
+ if (context.kind !== "benchmark_task"
100
+ || context.benchmark_id !== input.benchmarkId
101
+ || context.benchmark_revision !== input.benchmarkRevision
102
+ || context.task_id !== input.taskId) {
103
+ throw new Error(`exported run ${record.run_id} has a mismatched benchmark context`);
104
+ }
105
+ const expectedVerifier = benchmarkVerifierIdentity(input.benchmarkId, input.benchmarkRevision);
106
+ if (context.verifier_identity !== expectedVerifier)
107
+ throw new Error(`exported run ${record.run_id} has a mismatched verifier identity`);
108
+ if (record.parent?.eval_id !== input.evalId
109
+ || record.parent.trial_id !== input.trialId
110
+ || record.parent.attempt !== input.attempt) {
111
+ throw new Error(`exported run ${record.run_id} has a mismatched eval parent`);
112
+ }
113
+ const stagingParent = await mkdtemp(path.join(await ensureDir(statePaths(input.root).temporary), "eval-run-import-"));
114
+ const staging = path.join(stagingParent, record.run_id);
115
+ try {
116
+ await cp(input.bundle, staging, { recursive: true, errorOnExist: true, force: false });
117
+ await readJSON(path.join(staging, "resolution.json"));
118
+ await validateJSONLines(path.join(staging, "events.jsonl"));
119
+ const verifier = verifierResult(input.trial);
120
+ const verifierRef = verifier ? "verifier/result.json" : undefined;
121
+ if (verifier)
122
+ await atomicWriteJSON(path.join(staging, verifierRef), verifier);
123
+ const beforeObservation = await loadRunRecord(staging, { verifyTrajectory: true });
124
+ const observation = trialObservation(input.trial, beforeObservation.record.status, beforeObservation.trajectory_status, beforeObservation.record_status, verifierRef);
125
+ const manifest = await readJSON(path.join(staging, "manifest.json"));
126
+ const portableManifest = withoutKeys(manifest, [
127
+ "workspace", "source_workspace", "execution_workspace",
128
+ "managed_workspace", "executable", "artifact_entrypoint",
129
+ ]);
130
+ const request = await readJSON(path.join(staging, "request.json"));
131
+ await atomicWriteJSON(path.join(staging, "request.json"), { ...request, cwd: "." });
132
+ const result = await readJSON(path.join(staging, "result.json"));
133
+ await atomicWriteJSON(path.join(staging, "result.json"), withoutKeys(result, ["workspace"]));
134
+ await atomicWriteJSON(path.join(staging, "manifest.json"), {
135
+ ...portableManifest,
136
+ observation,
137
+ result_ref: "result.json",
138
+ ...(manifest.trajectory_ref ? {} : { trajectory_ref: "trajectory.ref.json" }),
139
+ sealed: true,
140
+ });
141
+ const verified = await loadRunRecord(staging, { verifyTrajectory: true });
142
+ if (verified.record.observation?.status !== observation.status)
143
+ throw new Error(`failed to seal observation for ${record.run_id}`);
144
+ const destination = path.join(statePaths(input.root).runs, record.run_id);
145
+ await ensureDir(path.dirname(destination));
146
+ try {
147
+ await stat(destination);
148
+ throw new Error(`run destination already exists: ${record.run_id}`);
149
+ }
150
+ catch (error) {
151
+ if (error.code !== "ENOENT")
152
+ throw error;
153
+ }
154
+ await rename(staging, destination);
155
+ return evalTrialRef(input, record.run_id, observation);
156
+ }
157
+ finally {
158
+ await rm(stagingParent, { recursive: true, force: true });
159
+ }
160
+ }
161
+ async function createDiagnosticRun(input) {
162
+ const runId = newRunId();
163
+ const runDirectory = await ensureDir(path.join(statePaths(input.root).runs, runId));
164
+ const now = new Date().toISOString();
165
+ const verifier = verifierResult(input.trial);
166
+ const verifierRef = verifier ? "verifier/result.json" : undefined;
167
+ if (verifier)
168
+ await atomicWriteJSON(path.join(runDirectory, verifierRef), verifier);
169
+ const reason = input.trial.exception_info
170
+ ? "infrastructure_failure"
171
+ : verifier ? "trajectory_missing_or_corrupt" : "verifier_result_missing";
172
+ const observation = {
173
+ status: "invalid",
174
+ invalid_reason: reason,
175
+ ...(verifierRef ? { verifier_result_ref: verifierRef } : {}),
176
+ };
177
+ const context = {
178
+ kind: "benchmark_task",
179
+ benchmark_id: input.benchmarkId,
180
+ benchmark_revision: input.benchmarkRevision,
181
+ task_id: input.taskId,
182
+ task_digest: benchmarkTaskDigest(input.benchmarkId, input.benchmarkRevision, input.taskId),
183
+ verifier_identity: benchmarkVerifierIdentity(input.benchmarkId, input.benchmarkRevision),
184
+ };
185
+ const safeAgentArgs = safeAgentArgsForPersistence(input.request.agent_args);
186
+ const argsDigest = safeAgentArgs.length ? sha256JSON(safeAgentArgs) : undefined;
187
+ const artifactId = typeof input.trial.agent_result?.metadata === "object"
188
+ ? input.trial.agent_result.metadata?.hitch_artifact_id
189
+ : undefined;
190
+ await atomicWriteJSON(path.join(runDirectory, "request.json"), {
191
+ schema_version: "1",
192
+ context,
193
+ parent: { kind: "eval", eval_id: input.evalId, trial_id: input.trialId, attempt: input.attempt },
194
+ task_id: input.taskId,
195
+ prompt: null,
196
+ diagnostic: "trial bundle was not exported",
197
+ });
198
+ await atomicWriteJSON(path.join(runDirectory, "resolution.json"), input.resolvedRevision);
199
+ await writePrivateFile(path.join(runDirectory, "events.jsonl"), `${JSON.stringify({
200
+ schema_version: "1",
201
+ sequence: 1,
202
+ timestamp: now,
203
+ run_id: runId,
204
+ type: "eval.trial.import_failed",
205
+ reason,
206
+ })}\n`);
207
+ await atomicWriteJSON(path.join(runDirectory, "result.json"), {
208
+ schema_version: "1",
209
+ run_id: runId,
210
+ status: "failed",
211
+ exit_code: 12,
212
+ error: { code: reason, message: "Harbor trial completed without an importable Hitch run bundle" },
213
+ completed_at: now,
214
+ });
215
+ await atomicWriteJSON(path.join(runDirectory, "manifest.json"), {
216
+ schema_version: "1",
217
+ run_id: runId,
218
+ context,
219
+ parent: { kind: "eval", eval_id: input.evalId, trial_id: input.trialId, attempt: input.attempt },
220
+ status: "failed",
221
+ harness: {
222
+ harness_id: input.resolvedRevision.harness_id,
223
+ requested_ref: input.request.harness_ref,
224
+ revision_identity: input.resolvedRevision.identity,
225
+ ...(artifactId && /^sha256:[0-9a-f]{64}$/.test(artifactId) ? { artifact_id: artifactId } : {}),
226
+ ...(argsDigest ? { agent_args_sha256: argsDigest } : {}),
227
+ },
228
+ model: defaultModelIdentity(input.request.model, input.resolvedRevision.harness_id),
229
+ protocol: {
230
+ timeout_ms: input.request.timeout_ms,
231
+ workspace_mode: "shared",
232
+ ...(input.runtimeId && /^sha256:[0-9a-f]{64}$/.test(input.runtimeId)
233
+ ? { environment_identity: input.runtimeId }
234
+ : {}),
235
+ },
236
+ observation,
237
+ request_ref: "request.json",
238
+ resolution_ref: "resolution.json",
239
+ result_ref: "result.json",
240
+ created_at: now,
241
+ completed_at: now,
242
+ sealed: true,
243
+ });
244
+ return evalTrialRef(input, runId, observation);
245
+ }
246
+ function evalTrialRef(input, runId, observation) {
247
+ return {
248
+ trial_id: input.trialId,
249
+ run_id: runId,
250
+ task_id: input.taskId,
251
+ attempt: input.attempt,
252
+ observation_status: observation.status,
253
+ ...(observation.reward !== undefined ? { reward: observation.reward } : {}),
254
+ ...(observation.verifier_result_ref ? { verifier_result_ref: observation.verifier_result_ref } : {}),
255
+ ...(observation.invalid_reason ? { invalid_reason: observation.invalid_reason } : {}),
256
+ };
257
+ }
258
+ function trialObservation(trial, runStatus, trajectoryStatus, recordStatus, verifierRef) {
259
+ if (trial.exception_info)
260
+ return { status: "invalid", invalid_reason: "infrastructure_failure", ...(verifierRef ? { verifier_result_ref: verifierRef } : {}) };
261
+ if (runStatus === "cancelled")
262
+ return { status: "invalid", invalid_reason: "cancelled", ...(verifierRef ? { verifier_result_ref: verifierRef } : {}) };
263
+ if (runStatus !== "succeeded")
264
+ return { status: "invalid", invalid_reason: "infrastructure_failure", ...(verifierRef ? { verifier_result_ref: verifierRef } : {}) };
265
+ if (recordStatus !== "valid")
266
+ return { status: "invalid", invalid_reason: "infrastructure_failure", ...(verifierRef ? { verifier_result_ref: verifierRef } : {}) };
267
+ if (trajectoryStatus !== "valid")
268
+ return { status: "invalid", invalid_reason: "trajectory_missing_or_corrupt", ...(verifierRef ? { verifier_result_ref: verifierRef } : {}) };
269
+ const reward = primaryReward(trial);
270
+ if (reward === undefined || !verifierRef)
271
+ return { status: "invalid", invalid_reason: "verifier_result_missing" };
272
+ return { status: "valid", reward, verifier_result_ref: verifierRef };
273
+ }
274
+ function verifierResult(trial) {
275
+ return trial.verifier_result && typeof trial.verifier_result === "object" && !Array.isArray(trial.verifier_result)
276
+ ? trial.verifier_result
277
+ : null;
278
+ }
279
+ function primaryReward(trial) {
280
+ const rewards = (verifierResult(trial)?.rewards || {});
281
+ const preferred = rewards.reward;
282
+ if (typeof preferred === "number" && Number.isFinite(preferred))
283
+ return preferred;
284
+ return Object.values(rewards).find((value) => typeof value === "number" && Number.isFinite(value));
285
+ }
286
+ async function findRunBundle(root, depth = 0) {
287
+ if (depth > 5)
288
+ return null;
289
+ try {
290
+ const manifest = path.join(root, "manifest.json");
291
+ if ((await lstat(manifest)).isFile() && path.basename(root) === "hitch-run-bundle")
292
+ return root;
293
+ }
294
+ catch { /* Continue scanning. */ }
295
+ let entries;
296
+ try {
297
+ entries = await readdir(root, { withFileTypes: true });
298
+ }
299
+ catch {
300
+ return null;
301
+ }
302
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
303
+ if (!entry.isDirectory())
304
+ continue;
305
+ const found = await findRunBundle(path.join(root, entry.name), depth + 1);
306
+ if (found)
307
+ return found;
308
+ }
309
+ return null;
310
+ }
311
+ async function validateBundleTree(root) {
312
+ let files = 0;
313
+ let bytes = 0;
314
+ const walk = async (directory) => {
315
+ for (const entry of await readdir(directory, { withFileTypes: true })) {
316
+ const target = path.join(directory, entry.name);
317
+ const info = await lstat(target);
318
+ if (info.isSymbolicLink())
319
+ throw new Error(`run bundle contains a symbolic link: ${path.relative(root, target)}`);
320
+ if (info.isDirectory()) {
321
+ await walk(target);
322
+ }
323
+ else if (info.isFile()) {
324
+ files += 1;
325
+ bytes += info.size;
326
+ if (files > 100_000 || bytes > 1024 * 1024 * 1024)
327
+ throw new Error("run bundle exceeds import limits");
328
+ }
329
+ else {
330
+ throw new Error(`run bundle contains a special file: ${path.relative(root, target)}`);
331
+ }
332
+ }
333
+ };
334
+ await walk(root);
335
+ }
336
+ function trialAttempt(trialId) {
337
+ const match = trialId.match(/__(\d+)$/);
338
+ const value = Number(match?.[1]);
339
+ // Modern Harbor trial ids carry a random suffix. Each such id identifies a
340
+ // distinct trial, whose execution attempt starts at one.
341
+ return Number.isInteger(value) && value > 0 ? value : 1;
342
+ }
343
+ function nonEmpty(value) {
344
+ return typeof value === "string" && value.trim() ? value.trim() : null;
345
+ }
346
+ function isWithin(root, candidate) {
347
+ const relative = path.relative(path.resolve(root), path.resolve(candidate));
348
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
349
+ }
350
+ function withoutKeys(record, keys) {
351
+ const result = { ...record };
352
+ for (const key of keys)
353
+ delete result[key];
354
+ return result;
355
+ }
356
+ async function validateJSONLines(file) {
357
+ const content = await readFile(file, "utf8");
358
+ for (const [index, line] of content.split(/\r?\n/).entries()) {
359
+ if (!line)
360
+ continue;
361
+ try {
362
+ JSON.parse(line);
363
+ }
364
+ catch (error) {
365
+ throw new Error(`invalid JSONL at ${path.basename(file)}:${index + 1}: ${error.message}`);
366
+ }
367
+ }
368
+ }
369
+ export async function validateEvalTrialReferences(root, evalId, trials, expected) {
370
+ for (const trial of trials) {
371
+ const loaded = await loadRunRecord(path.join(statePaths(root).runs, trial.run_id), { verifyTrajectory: false });
372
+ const record = loaded.record;
373
+ if (record.context.kind !== "benchmark_task")
374
+ throw new Error(`eval trial ${trial.trial_id} references a non-benchmark run`);
375
+ if (expected && (record.context.benchmark_id !== expected.benchmarkId
376
+ || record.context.benchmark_revision !== expected.benchmarkRevision
377
+ || record.context.verifier_identity !== benchmarkVerifierIdentity(expected.benchmarkId, expected.benchmarkRevision)))
378
+ throw new Error(`eval trial ${trial.trial_id} benchmark identity mismatch`);
379
+ if (record.parent?.eval_id !== evalId || record.parent.trial_id !== trial.trial_id || record.parent.attempt !== trial.attempt) {
380
+ throw new Error(`eval trial ${trial.trial_id} parent mismatch`);
381
+ }
382
+ if (record.context.task_id !== trial.task_id)
383
+ throw new Error(`eval trial ${trial.trial_id} task mismatch`);
384
+ if (record.observation?.status !== trial.observation_status)
385
+ throw new Error(`eval trial ${trial.trial_id} observation status mismatch`);
386
+ if (record.observation?.reward !== trial.reward)
387
+ throw new Error(`eval trial ${trial.trial_id} reward mismatch`);
388
+ if (record.observation?.verifier_result_ref !== trial.verifier_result_ref)
389
+ throw new Error(`eval trial ${trial.trial_id} verifier ref mismatch`);
390
+ if (record.observation?.invalid_reason !== trial.invalid_reason)
391
+ throw new Error(`eval trial ${trial.trial_id} invalid reason mismatch`);
392
+ }
393
+ }
394
+ //# sourceMappingURL=trial-import.js.map