@post-print/agent-test 1.1.9 → 2.0.0

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 (238) hide show
  1. package/README.md +18 -66
  2. package/dist/cli.d.ts +2 -64
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +26 -691
  5. package/dist/cli.js.map +1 -1
  6. package/dist/compare-scenario.d.ts +1 -1
  7. package/dist/compare-scenario.d.ts.map +1 -1
  8. package/dist/compare-scenario.js +72 -75
  9. package/dist/compare-scenario.js.map +1 -1
  10. package/dist/failures.d.ts +5 -1
  11. package/dist/failures.d.ts.map +1 -1
  12. package/dist/failures.js +1 -1
  13. package/dist/failures.js.map +1 -1
  14. package/dist/host-login.d.ts +1 -26
  15. package/dist/host-login.d.ts.map +1 -1
  16. package/dist/host-login.js +1 -92
  17. package/dist/host-login.js.map +1 -1
  18. package/dist/index.d.ts +6 -28
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +4 -26
  21. package/dist/index.js.map +1 -1
  22. package/dist/scenario-story.d.ts.map +1 -1
  23. package/dist/scenario-story.js +57 -39
  24. package/dist/scenario-story.js.map +1 -1
  25. package/dist/sdk/cli.d.ts +3 -0
  26. package/dist/sdk/cli.d.ts.map +1 -0
  27. package/dist/sdk/cli.js +20 -0
  28. package/dist/sdk/cli.js.map +1 -0
  29. package/dist/sdk/definitions.d.ts +22 -0
  30. package/dist/sdk/definitions.d.ts.map +1 -0
  31. package/dist/sdk/definitions.js +58 -0
  32. package/dist/sdk/definitions.js.map +1 -0
  33. package/dist/sdk/expect.d.ts +31 -0
  34. package/dist/sdk/expect.d.ts.map +1 -0
  35. package/dist/sdk/expect.js +68 -0
  36. package/dist/sdk/expect.js.map +1 -0
  37. package/dist/sdk/judge.d.ts +16 -0
  38. package/dist/sdk/judge.d.ts.map +1 -0
  39. package/dist/sdk/judge.js +82 -0
  40. package/dist/sdk/judge.js.map +1 -0
  41. package/dist/sdk/metrics.d.ts +3 -0
  42. package/dist/sdk/metrics.d.ts.map +1 -0
  43. package/dist/sdk/metrics.js +24 -0
  44. package/dist/sdk/metrics.js.map +1 -0
  45. package/dist/sdk/reporter.d.ts +12 -0
  46. package/dist/sdk/reporter.d.ts.map +1 -0
  47. package/dist/sdk/reporter.js +58 -0
  48. package/dist/sdk/reporter.js.map +1 -0
  49. package/dist/sdk/runtime.d.ts +33 -0
  50. package/dist/sdk/runtime.d.ts.map +1 -0
  51. package/dist/sdk/runtime.js +188 -0
  52. package/dist/sdk/runtime.js.map +1 -0
  53. package/dist/sdk/test.d.ts +27 -0
  54. package/dist/sdk/test.d.ts.map +1 -0
  55. package/dist/sdk/test.js +92 -0
  56. package/dist/sdk/test.js.map +1 -0
  57. package/dist/sdk/types.d.ts +75 -0
  58. package/dist/sdk/types.d.ts.map +1 -0
  59. package/dist/sdk/types.js +2 -0
  60. package/dist/sdk/types.js.map +1 -0
  61. package/dist/sdk/usage.d.ts +10 -0
  62. package/dist/sdk/usage.d.ts.map +1 -0
  63. package/dist/sdk/usage.js +13 -0
  64. package/dist/sdk/usage.js.map +1 -0
  65. package/dist/sdk/viewer-results.d.ts +43 -0
  66. package/dist/sdk/viewer-results.d.ts.map +1 -0
  67. package/dist/sdk/viewer-results.js +126 -0
  68. package/dist/sdk/viewer-results.js.map +1 -0
  69. package/dist/sdk/viewer.d.ts +11 -0
  70. package/dist/sdk/viewer.d.ts.map +1 -0
  71. package/dist/sdk/viewer.js +160 -0
  72. package/dist/sdk/viewer.js.map +1 -0
  73. package/dist/sdk/workspace.d.ts +33 -0
  74. package/dist/sdk/workspace.d.ts.map +1 -0
  75. package/dist/sdk/workspace.js +100 -0
  76. package/dist/sdk/workspace.js.map +1 -0
  77. package/dist/types.d.ts +4 -40
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/viewer/catalog.d.ts +2 -8
  80. package/dist/viewer/catalog.d.ts.map +1 -1
  81. package/dist/viewer/catalog.js +23 -128
  82. package/dist/viewer/catalog.js.map +1 -1
  83. package/dist/viewer/client.d.ts.map +1 -1
  84. package/dist/viewer/client.js +12 -12
  85. package/dist/viewer/client.js.map +1 -1
  86. package/dist/viewer/run-controller.d.ts +4 -2
  87. package/dist/viewer/run-controller.d.ts.map +1 -1
  88. package/dist/viewer/run-controller.js +297 -248
  89. package/dist/viewer/run-controller.js.map +1 -1
  90. package/dist/viewer/server.d.ts +4 -3
  91. package/dist/viewer/server.d.ts.map +1 -1
  92. package/dist/viewer/server.js +149 -112
  93. package/dist/viewer/server.js.map +1 -1
  94. package/dist/viewer/styles.d.ts +2 -0
  95. package/dist/viewer/styles.d.ts.map +1 -0
  96. package/dist/viewer/styles.js +444 -0
  97. package/dist/viewer/styles.js.map +1 -0
  98. package/dist/worker-pool.d.ts +5 -1
  99. package/dist/worker-pool.d.ts.map +1 -1
  100. package/dist/worker-pool.js +1 -1
  101. package/dist/worker-pool.js.map +1 -1
  102. package/package.json +7 -7
  103. package/dist/check.d.ts +0 -38
  104. package/dist/check.d.ts.map +0 -1
  105. package/dist/check.js +0 -169
  106. package/dist/check.js.map +0 -1
  107. package/dist/command-allowlist.d.ts +0 -9
  108. package/dist/command-allowlist.d.ts.map +0 -1
  109. package/dist/command-allowlist.js +0 -29
  110. package/dist/command-allowlist.js.map +0 -1
  111. package/dist/debug-bundle.d.ts +0 -81
  112. package/dist/debug-bundle.d.ts.map +0 -1
  113. package/dist/debug-bundle.js +0 -392
  114. package/dist/debug-bundle.js.map +0 -1
  115. package/dist/discover-suites.d.ts +0 -2
  116. package/dist/discover-suites.d.ts.map +0 -1
  117. package/dist/discover-suites.js +0 -19
  118. package/dist/discover-suites.js.map +0 -1
  119. package/dist/doctor.d.ts +0 -24
  120. package/dist/doctor.d.ts.map +0 -1
  121. package/dist/doctor.js +0 -272
  122. package/dist/doctor.js.map +0 -1
  123. package/dist/expect.d.ts +0 -59
  124. package/dist/expect.d.ts.map +0 -1
  125. package/dist/expect.js +0 -518
  126. package/dist/expect.js.map +0 -1
  127. package/dist/host-log.d.ts +0 -17
  128. package/dist/host-log.d.ts.map +0 -1
  129. package/dist/host-log.js +0 -154
  130. package/dist/host-log.js.map +0 -1
  131. package/dist/hosts.d.ts +0 -22
  132. package/dist/hosts.d.ts.map +0 -1
  133. package/dist/hosts.js +0 -84
  134. package/dist/hosts.js.map +0 -1
  135. package/dist/html-report.d.ts +0 -18
  136. package/dist/html-report.d.ts.map +0 -1
  137. package/dist/html-report.js +0 -1276
  138. package/dist/html-report.js.map +0 -1
  139. package/dist/live-isolation.d.ts +0 -82
  140. package/dist/live-isolation.d.ts.map +0 -1
  141. package/dist/live-isolation.js +0 -399
  142. package/dist/live-isolation.js.map +0 -1
  143. package/dist/live-timeout.d.ts +0 -11
  144. package/dist/live-timeout.d.ts.map +0 -1
  145. package/dist/live-timeout.js +0 -29
  146. package/dist/live-timeout.js.map +0 -1
  147. package/dist/load-adapters.d.ts +0 -13
  148. package/dist/load-adapters.d.ts.map +0 -1
  149. package/dist/load-adapters.js +0 -76
  150. package/dist/load-adapters.js.map +0 -1
  151. package/dist/load-suite.d.ts +0 -22
  152. package/dist/load-suite.d.ts.map +0 -1
  153. package/dist/load-suite.js +0 -154
  154. package/dist/load-suite.js.map +0 -1
  155. package/dist/mcp-config.d.ts +0 -13
  156. package/dist/mcp-config.d.ts.map +0 -1
  157. package/dist/mcp-config.js +0 -121
  158. package/dist/mcp-config.js.map +0 -1
  159. package/dist/preflight.d.ts +0 -3
  160. package/dist/preflight.d.ts.map +0 -1
  161. package/dist/preflight.js +0 -17
  162. package/dist/preflight.js.map +0 -1
  163. package/dist/progress.d.ts +0 -24
  164. package/dist/progress.d.ts.map +0 -1
  165. package/dist/progress.js +0 -111
  166. package/dist/progress.js.map +0 -1
  167. package/dist/record-trace.d.ts +0 -64
  168. package/dist/record-trace.d.ts.map +0 -1
  169. package/dist/record-trace.js +0 -124
  170. package/dist/record-trace.js.map +0 -1
  171. package/dist/report-preview-server.d.ts +0 -2
  172. package/dist/report-preview-server.d.ts.map +0 -1
  173. package/dist/report-preview-server.js +0 -24
  174. package/dist/report-preview-server.js.map +0 -1
  175. package/dist/report-preview.d.ts +0 -35
  176. package/dist/report-preview.d.ts.map +0 -1
  177. package/dist/report-preview.js +0 -205
  178. package/dist/report-preview.js.map +0 -1
  179. package/dist/run-suite.d.ts +0 -139
  180. package/dist/run-suite.d.ts.map +0 -1
  181. package/dist/run-suite.js +0 -1688
  182. package/dist/run-suite.js.map +0 -1
  183. package/dist/scenario-finalizer.d.ts +0 -27
  184. package/dist/scenario-finalizer.d.ts.map +0 -1
  185. package/dist/scenario-finalizer.js +0 -39
  186. package/dist/scenario-finalizer.js.map +0 -1
  187. package/dist/scenario-retry.d.ts +0 -15
  188. package/dist/scenario-retry.d.ts.map +0 -1
  189. package/dist/scenario-retry.js +0 -52
  190. package/dist/scenario-retry.js.map +0 -1
  191. package/dist/scenario-seed.d.ts +0 -13
  192. package/dist/scenario-seed.d.ts.map +0 -1
  193. package/dist/scenario-seed.js +0 -72
  194. package/dist/scenario-seed.js.map +0 -1
  195. package/dist/suite-summary.d.ts +0 -16
  196. package/dist/suite-summary.d.ts.map +0 -1
  197. package/dist/suite-summary.js +0 -218
  198. package/dist/suite-summary.js.map +0 -1
  199. package/dist/theme.d.ts +0 -96
  200. package/dist/theme.d.ts.map +0 -1
  201. package/dist/theme.js +0 -446
  202. package/dist/theme.js.map +0 -1
  203. package/dist/validate-seeds.d.ts +0 -20
  204. package/dist/validate-seeds.d.ts.map +0 -1
  205. package/dist/validate-seeds.js +0 -72
  206. package/dist/validate-seeds.js.map +0 -1
  207. package/dist/validate-suite.d.ts +0 -24
  208. package/dist/validate-suite.d.ts.map +0 -1
  209. package/dist/validate-suite.js +0 -542
  210. package/dist/validate-suite.js.map +0 -1
  211. package/dist/viewer/cli.d.ts +0 -17
  212. package/dist/viewer/cli.d.ts.map +0 -1
  213. package/dist/viewer/cli.js +0 -38
  214. package/dist/viewer/cli.js.map +0 -1
  215. package/dist/viewer/compare-metrics.d.ts +0 -30
  216. package/dist/viewer/compare-metrics.d.ts.map +0 -1
  217. package/dist/viewer/compare-metrics.js +0 -101
  218. package/dist/viewer/compare-metrics.js.map +0 -1
  219. package/dist/viewer/context-files.d.ts +0 -8
  220. package/dist/viewer/context-files.d.ts.map +0 -1
  221. package/dist/viewer/context-files.js +0 -57
  222. package/dist/viewer/context-files.js.map +0 -1
  223. package/dist/viewer/emit.d.ts +0 -7
  224. package/dist/viewer/emit.d.ts.map +0 -1
  225. package/dist/viewer/emit.js +0 -25
  226. package/dist/viewer/emit.js.map +0 -1
  227. package/dist/viewer/live-runner.d.ts +0 -27
  228. package/dist/viewer/live-runner.d.ts.map +0 -1
  229. package/dist/viewer/live-runner.js +0 -166
  230. package/dist/viewer/live-runner.js.map +0 -1
  231. package/dist/viewer/session-server.d.ts +0 -2
  232. package/dist/viewer/session-server.d.ts.map +0 -1
  233. package/dist/viewer/session-server.js +0 -71
  234. package/dist/viewer/session-server.js.map +0 -1
  235. package/dist/warnings.d.ts +0 -3
  236. package/dist/warnings.d.ts.map +0 -1
  237. package/dist/warnings.js +0 -13
  238. package/dist/warnings.js.map +0 -1
@@ -20,113 +20,135 @@ export function followViewerRun(controller, runId, onEvent) {
20
20
  flush();
21
21
  return unsubscribe;
22
22
  }
23
- export function createViewerRunController(options) {
24
- const maxParallel = options.maxParallelAgents ?? VIEWER_MAX_PARALLEL_AGENTS;
25
- const sessions = new Map();
26
- let activeId;
27
- for (const record of options.initialRuns ?? []) {
28
- sessions.set(record.id, {
29
- record: structuredClone(record),
30
- events: [],
31
- listeners: new Set(),
32
- done: Promise.resolve(),
33
- armResults: new Map(),
34
- finalizing: new Set(),
35
- });
23
+ class RunController {
24
+ options;
25
+ sessions = new Map();
26
+ activeId;
27
+ maxParallel;
28
+ constructor(options) {
29
+ this.options = options;
30
+ this.maxParallel = this.options.maxParallelAgents ?? VIEWER_MAX_PARALLEL_AGENTS;
31
+ for (const record of this.options.initialRuns ?? []) {
32
+ this.sessions.set(record.id, {
33
+ record: structuredClone(record),
34
+ events: [],
35
+ listeners: new Set(),
36
+ done: Promise.resolve(),
37
+ armResults: new Map(),
38
+ finalizing: new Set(),
39
+ });
40
+ }
36
41
  }
37
- const emit = (target, event) => {
42
+ emit = (target, event) => {
38
43
  target.events.push(event);
39
44
  if (event.type === "scenario_result") {
40
- addScenarioResult(target, event, options.catalog);
45
+ addScenarioResult(target, event, this.options.catalog);
41
46
  }
42
47
  for (const listener of target.listeners)
43
48
  listener(event);
44
49
  };
45
- return {
46
- activeRunId: () => activeId,
47
- maxParallelAgents: () => maxParallel,
48
- runs: () => [...sessions.values()].map(({ record }) => structuredClone(record)),
49
- run: (runId) => {
50
- const record = sessions.get(runId)?.record;
51
- return record ? structuredClone(record) : undefined;
52
- },
53
- start(request) {
54
- if (activeId)
55
- throw new Error("A viewer run is already in progress");
56
- if (request.workers !== undefined &&
57
- (!Number.isInteger(request.workers) || request.workers < 1 || request.workers > maxParallel)) {
58
- throw new Error(`workers must be an integer 1-${maxParallel}`);
59
- }
60
- const jobs = expandViewerJobs(options.catalog, request);
61
- if (jobs.length === 0)
62
- throw new Error("No matching scenarios to run");
63
- const runId = `run-${Date.now()}`;
64
- const abort = new AbortController();
65
- const current = {
66
- record: {
67
- id: runId,
68
- request: structuredClone(request),
69
- status: "running",
70
- startedAt: new Date().toISOString(),
71
- reports: [],
72
- },
73
- events: [],
74
- listeners: new Set(),
75
- abort,
76
- done: Promise.resolve(),
77
- armResults: new Map(),
78
- finalizing: new Set(),
79
- };
80
- sessions.set(runId, current);
81
- activeId = runId;
82
- emit(current, { type: "run_started", runId });
83
- current.done = runJobBatches({
84
- jobs,
85
- catalog: options.catalog,
86
- hosts: request.hosts ?? options.catalog.defaultSelectedHosts,
87
- parallelHosts: request.parallelHosts === true,
88
- maxParallel: request.workers ?? maxParallel,
89
- signal: abort.signal,
90
- runner: options.runner,
91
- forward: (event) => emit(current, event),
92
- afterJob: () => finalizeScheduledCompares(current, options.catalog, options.runner, emit),
93
- })
94
- .catch((error) => emit(current, {
95
- type: "error",
96
- message: error instanceof Error ? error.message : String(error),
97
- }))
98
- .finally(() => {
99
- const totals = summarizeRun(current.record.reports, current.events);
100
- current.record.status = abort.signal.aborted ? "cancelled" : "completed";
101
- current.record.finishedAt = new Date().toISOString();
102
- emit(current, { type: "run_finished", runId, status: current.record.status, ...totals });
103
- if (activeId === runId)
104
- activeId = undefined;
105
- });
106
- return { runId };
107
- },
108
- cancel(runId) {
109
- const target = sessions.get(runId);
110
- if (!target?.abort || target.record.status !== "running")
111
- return false;
112
- target.record.status = "cancelling";
113
- target.abort.abort();
114
- return true;
115
- },
116
- history(runId) {
117
- const target = sessions.get(runId);
118
- return target ? [...target.events] : undefined;
119
- },
120
- subscribe(runId, listener) {
121
- const target = sessions.get(runId);
122
- if (!target)
123
- return () => undefined;
124
- target.listeners.add(listener);
125
- return () => {
126
- target.listeners.delete(listener);
127
- };
128
- },
50
+ activeRunId = () => this.activeId;
51
+ maxParallelAgents = () => this.maxParallel;
52
+ runs = () => [...this.sessions.values()].map(({ record }) => structuredClone(record));
53
+ run = (runId) => {
54
+ const record = this.sessions.get(runId)?.record;
55
+ return record ? structuredClone(record) : undefined;
129
56
  };
57
+ start(request) {
58
+ if (this.activeId)
59
+ throw new Error("A viewer run is already in progress");
60
+ if (request.workers !== undefined &&
61
+ (!Number.isInteger(request.workers) ||
62
+ request.workers < 1 ||
63
+ request.workers > this.maxParallel)) {
64
+ throw new Error(`workers must be an integer 1-${this.maxParallel}`);
65
+ }
66
+ const jobs = expandViewerJobs(this.options.catalog, request);
67
+ if (jobs.length === 0)
68
+ throw new Error("No matching scenarios to run");
69
+ const runId = `run-${Date.now()}`;
70
+ const abort = new AbortController();
71
+ const current = {
72
+ record: {
73
+ id: runId,
74
+ request: structuredClone(request),
75
+ status: "running",
76
+ startedAt: new Date().toISOString(),
77
+ reports: [],
78
+ },
79
+ events: [],
80
+ listeners: new Set(),
81
+ abort,
82
+ done: Promise.resolve(),
83
+ armResults: new Map(),
84
+ finalizing: new Set(),
85
+ };
86
+ this.sessions.set(runId, current);
87
+ this.activeId = runId;
88
+ this.emit(current, { type: "run_started", runId });
89
+ this.schedule(current, jobs, abort);
90
+ return { runId };
91
+ }
92
+ schedule(current, jobs, abort) {
93
+ const runId = current.record.id;
94
+ const request = current.record.request;
95
+ current.done = runJobBatches({
96
+ jobs,
97
+ catalog: this.options.catalog,
98
+ hosts: request.hosts ?? this.options.catalog.defaultSelectedHosts,
99
+ parallelHosts: request.parallelHosts === true,
100
+ maxParallel: request.workers ?? this.maxParallel,
101
+ signal: abort.signal,
102
+ runner: this.options.runner,
103
+ forward: (event) => this.emit(current, event),
104
+ afterJob: () => finalizeScheduledCompares(current, {
105
+ catalog: this.options.catalog,
106
+ runner: this.options.runner,
107
+ emit: this.emit,
108
+ }),
109
+ })
110
+ .catch((error) => this.emit(current, {
111
+ type: "error",
112
+ message: error instanceof Error ? error.message : String(error),
113
+ }))
114
+ .finally(() => {
115
+ const totals = summarizeRun(current.record.reports, current.events);
116
+ current.record.status = abort.signal.aborted ? "cancelled" : "completed";
117
+ current.record.finishedAt = new Date().toISOString();
118
+ this.emit(current, {
119
+ type: "run_finished",
120
+ runId,
121
+ status: current.record.status,
122
+ ...totals,
123
+ });
124
+ if (this.activeId === runId)
125
+ this.activeId = undefined;
126
+ });
127
+ }
128
+ cancel(runId) {
129
+ const target = this.sessions.get(runId);
130
+ if (!target?.abort || target.record.status !== "running")
131
+ return false;
132
+ target.record.status = "cancelling";
133
+ target.abort.abort();
134
+ return true;
135
+ }
136
+ history(runId) {
137
+ const target = this.sessions.get(runId);
138
+ return target ? [...target.events] : undefined;
139
+ }
140
+ subscribe(runId, listener) {
141
+ const target = this.sessions.get(runId);
142
+ if (!target)
143
+ return () => undefined;
144
+ target.listeners.add(listener);
145
+ return () => {
146
+ target.listeners.delete(listener);
147
+ };
148
+ }
149
+ }
150
+ export function createViewerRunController(options) {
151
+ return new RunController(options);
130
152
  }
131
153
  function addScenarioResult(session, event, catalog) {
132
154
  const report = ensureReport(session.record, event.suite, event.host);
@@ -151,8 +173,8 @@ function addScenarioResult(session, event, catalog) {
151
173
  group.results.set(event.arm, event.result);
152
174
  session.armResults.set(key, group);
153
175
  }
154
- function buildViewerCompareResult(suite, scenarioName, scenario, stored) {
155
- const arms = (scenario.compare ?? []).flatMap((arm) => {
176
+ function viewerCompareArms(scenario, stored) {
177
+ return (scenario.compare ?? []).flatMap((arm) => {
156
178
  const result = stored.get(arm.id);
157
179
  return result
158
180
  ? [
@@ -170,6 +192,9 @@ function buildViewerCompareResult(suite, scenarioName, scenario, stored) {
170
192
  ]
171
193
  : [];
172
194
  });
195
+ }
196
+ function buildViewerCompareResult(suite, { scenarioName, scenario, stored, }) {
197
+ const arms = viewerCompareArms(scenario, stored);
173
198
  const { compare, failures } = finalizeCompareOutcome(arms, scenario.gates);
174
199
  const passed = failures.length === 0;
175
200
  const result = {
@@ -206,7 +231,7 @@ function buildViewerCompareResult(suite, scenarioName, scenario, stored) {
206
231
  };
207
232
  return result;
208
233
  }
209
- async function finalizeScheduledCompares(session, catalog, runner, emit) {
234
+ async function finalizeScheduledCompares(session, { catalog, runner, emit, }) {
210
235
  for (const [key, group] of session.armResults) {
211
236
  if (session.abort?.signal.aborted)
212
237
  return;
@@ -218,79 +243,97 @@ async function finalizeScheduledCompares(session, catalog, runner, emit) {
218
243
  if (!scenario?.compare?.length || group.results.size < scenario.compare.length)
219
244
  continue;
220
245
  session.finalizing.add(key);
221
- emit(session, {
222
- type: "cell_started",
223
- suite: group.suite,
224
- scenario: group.scenario,
225
- host: group.host,
226
- });
227
- emit(session, {
228
- type: "scenario_finalizing",
229
- suite: group.suite,
230
- scenario: group.scenario,
231
- host: group.host,
232
- });
233
- emit(session, {
234
- type: "status",
235
- suite: group.suite,
236
- scenario: group.scenario,
237
- host: group.host,
238
- text: "Finalizing comparison and judge.",
239
- });
240
- let result;
241
- try {
242
- const armResults = new Map();
243
- for (const [armId, armResult] of group.results) {
244
- const resultWithWorkspace = structuredClone(armResult);
245
- const workspace = group.judgeWorkspaces.get(armId);
246
- if (workspace) {
247
- Object.defineProperty(resultWithWorkspace, "judgeWorkspace", {
248
- value: workspace,
249
- enumerable: false,
250
- });
251
- }
252
- armResults.set(armId, resultWithWorkspace);
253
- }
254
- result = runner.finalizeCompare
255
- ? await runner.finalizeCompare({
256
- suite: group.suite,
257
- scenario,
258
- host: group.host,
259
- armResults,
260
- })
261
- : buildViewerCompareResult(group.suite, group.scenario, scenario, group.results);
262
- }
263
- catch (error) {
264
- result = buildViewerCompareResult(group.suite, group.scenario, scenario, group.results);
265
- result.failures.push({
266
- matcher: "compareFinalize",
267
- message: error instanceof Error ? error.message : String(error),
268
- category: "agent_runtime",
246
+ await finalizeGroup(session, { group, scenario, runner, emit });
247
+ }
248
+ }
249
+ function comparisonInputs(group) {
250
+ const armResults = new Map();
251
+ for (const [armId, armResult] of group.results) {
252
+ const resultWithWorkspace = structuredClone(armResult);
253
+ const workspace = group.judgeWorkspaces.get(armId);
254
+ if (workspace) {
255
+ Object.defineProperty(resultWithWorkspace, "judgeWorkspace", {
256
+ value: workspace,
257
+ enumerable: false,
269
258
  });
270
- result.passed = false;
271
259
  }
272
- if (session.abort?.signal.aborted)
273
- return;
274
- emit(session, {
275
- type: "cell_finished",
276
- suite: group.suite,
277
- scenario: group.scenario,
278
- host: group.host,
279
- passed: result.passed,
280
- durationMs: result.durationMs,
281
- failures: result.failures.map((failure) => ({
282
- matcher: failure.matcher,
283
- message: failure.message,
284
- })),
260
+ armResults.set(armId, resultWithWorkspace);
261
+ }
262
+ return armResults;
263
+ }
264
+ function publishComparison(session, { group, result, emit, }) {
265
+ emit(session, {
266
+ type: "cell_finished",
267
+ suite: group.suite,
268
+ scenario: group.scenario,
269
+ host: group.host,
270
+ passed: result.passed,
271
+ durationMs: result.durationMs,
272
+ failures: result.failures.map((failure) => ({
273
+ matcher: failure.matcher,
274
+ message: failure.message,
275
+ })),
276
+ });
277
+ emit(session, {
278
+ type: "scenario_result",
279
+ suite: group.suite,
280
+ scenario: group.scenario,
281
+ host: group.host,
282
+ result,
283
+ });
284
+ }
285
+ async function finalizeGroup(session, { group, scenario, runner, emit }) {
286
+ emit(session, {
287
+ type: "cell_started",
288
+ suite: group.suite,
289
+ scenario: group.scenario,
290
+ host: group.host,
291
+ });
292
+ emit(session, {
293
+ type: "scenario_finalizing",
294
+ suite: group.suite,
295
+ scenario: group.scenario,
296
+ host: group.host,
297
+ });
298
+ emit(session, {
299
+ type: "status",
300
+ suite: group.suite,
301
+ scenario: group.scenario,
302
+ host: group.host,
303
+ text: "Finalizing comparison and judge.",
304
+ });
305
+ let result;
306
+ try {
307
+ const armResults = comparisonInputs(group);
308
+ result = runner.finalizeCompare
309
+ ? await runner.finalizeCompare({
310
+ suite: group.suite,
311
+ scenario,
312
+ host: group.host,
313
+ armResults,
314
+ })
315
+ : buildViewerCompareResult(group.suite, {
316
+ scenarioName: group.scenario,
317
+ scenario: scenario,
318
+ stored: group.results,
319
+ });
320
+ }
321
+ catch (error) {
322
+ result = buildViewerCompareResult(group.suite, {
323
+ scenarioName: group.scenario,
324
+ scenario: scenario,
325
+ stored: group.results,
285
326
  });
286
- emit(session, {
287
- type: "scenario_result",
288
- suite: group.suite,
289
- scenario: group.scenario,
290
- host: group.host,
291
- result,
327
+ result.failures.push({
328
+ matcher: "compareFinalize",
329
+ message: error instanceof Error ? error.message : String(error),
330
+ category: "agent_runtime",
292
331
  });
332
+ result.passed = false;
293
333
  }
334
+ if (session.abort?.signal.aborted)
335
+ return;
336
+ publishComparison(session, { group, result, emit });
294
337
  }
295
338
  function ensureReport(record, suite, host) {
296
339
  let report = record.reports.find((item) => item.suite === suite && item.host === host);
@@ -341,86 +384,92 @@ async function runJobBatches(options) {
341
384
  for (const batch of batches) {
342
385
  if (options.signal.aborted)
343
386
  return;
344
- await runWorkerPool(batch, options.maxParallel, async (job) => {
345
- try {
346
- let completedResult;
347
- await options.runner.runJob(job, (event) => {
348
- if (event.type === "scenario_result" && !job.arm) {
349
- completedResult = event.result;
350
- return;
351
- }
352
- options.forward(event);
353
- }, options.signal);
354
- if (!completedResult ||
355
- job.arm ||
356
- !options.runner.finalizeScenario ||
357
- options.signal.aborted) {
358
- if (completedResult && !options.signal.aborted)
359
- options.forward({ type: "scenario_result", ...job, result: completedResult });
360
- return;
361
- }
362
- const scenario = options.catalog.suites
363
- .find((suite) => suite.name === job.suite)
364
- ?.scenarios.find((item) => item.name === job.scenario);
365
- if (!scenario || scenario.compare?.length) {
366
- options.forward({ type: "scenario_result", ...job, result: completedResult });
367
- return;
368
- }
369
- options.forward({ type: "scenario_finalizing", ...job });
370
- const finalized = await options.runner.finalizeScenario({ suite: job.suite, scenario, host: job.host, result: completedResult }, options.forward);
371
- // The child emits an agent-only completion before parent-process judging.
372
- // Replace that provisional status so the cell cannot remain green when
373
- // judging later fails or becomes unavailable.
374
- options.forward({
375
- type: "cell_finished",
376
- ...job,
377
- passed: finalized.passed,
378
- durationMs: finalized.durationMs,
379
- failures: finalized.failures.map((failure) => ({
380
- matcher: failure.matcher,
381
- message: failure.message,
382
- })),
383
- });
384
- options.forward({ type: "scenario_result", ...job, result: finalized });
385
- }
386
- catch (error) {
387
- if (options.signal.aborted)
388
- return;
389
- const message = error instanceof Error ? error.message : String(error);
390
- const envelope = {
391
- suite: job.suite,
392
- scenario: job.scenario,
393
- host: job.host,
394
- ...(job.arm ? { arm: job.arm } : {}),
395
- };
396
- const failures = [
397
- { matcher: "liveScenario", message, category: "agent_runtime" },
398
- ];
399
- options.forward({ type: "error", ...envelope, message });
400
- options.forward({
401
- type: "cell_finished",
402
- ...envelope,
403
- passed: false,
404
- failures,
405
- durationMs: 0,
406
- });
407
- options.forward({
408
- type: "scenario_result",
409
- ...envelope,
410
- result: {
411
- suite: job.suite,
412
- scenario: job.scenario,
413
- prompt: job.prompt,
414
- passed: false,
415
- failures,
416
- durationMs: 0,
417
- },
418
- });
419
- }
420
- finally {
421
- await options.afterJob();
387
+ await runWorkerPool(batch, {
388
+ workers: options.maxParallel,
389
+ worker: (job) => runViewerJob(job, options),
390
+ signal: options.signal,
391
+ });
392
+ }
393
+ }
394
+ async function runViewerJob(job, options) {
395
+ try {
396
+ let completedResult;
397
+ await options.runner.runJob(job, (event) => {
398
+ if (event.type === "scenario_result" && !job.arm) {
399
+ completedResult = event.result;
400
+ return;
422
401
  }
402
+ options.forward(event);
423
403
  }, options.signal);
404
+ await finalizeViewerJob(job, options, completedResult);
405
+ }
406
+ catch (error) {
407
+ publishJobFailure(job, options, error);
424
408
  }
409
+ finally {
410
+ await options.afterJob();
411
+ }
412
+ }
413
+ async function finalizeViewerJob(job, options, completedResult) {
414
+ if (!completedResult || job.arm || !options.runner.finalizeScenario || options.signal.aborted) {
415
+ if (completedResult && !options.signal.aborted)
416
+ options.forward({ type: "scenario_result", ...job, result: completedResult });
417
+ return;
418
+ }
419
+ const scenario = options.catalog.suites
420
+ .find((suite) => suite.name === job.suite)
421
+ ?.scenarios.find((item) => item.name === job.scenario);
422
+ if (!scenario || scenario.compare?.length) {
423
+ options.forward({ type: "scenario_result", ...job, result: completedResult });
424
+ return;
425
+ }
426
+ options.forward({ type: "scenario_finalizing", ...job });
427
+ const finalized = await options.runner.finalizeScenario({ suite: job.suite, scenario, host: job.host, result: completedResult }, options.forward);
428
+ // The child emits an agent-only completion before parent-process judging.
429
+ // Replace that provisional status so the cell cannot remain green when
430
+ // judging later fails or becomes unavailable.
431
+ options.forward({
432
+ type: "cell_finished",
433
+ ...job,
434
+ passed: finalized.passed,
435
+ durationMs: finalized.durationMs,
436
+ failures: finalized.failures.map((failure) => ({
437
+ matcher: failure.matcher,
438
+ message: failure.message,
439
+ })),
440
+ });
441
+ options.forward({ type: "scenario_result", ...job, result: finalized });
442
+ }
443
+ function publishJobFailure(job, options, error) {
444
+ if (options.signal.aborted)
445
+ return;
446
+ const message = error instanceof Error ? error.message : String(error);
447
+ const envelope = {
448
+ suite: job.suite,
449
+ scenario: job.scenario,
450
+ host: job.host,
451
+ ...(job.arm ? { arm: job.arm } : {}),
452
+ };
453
+ const failures = [{ matcher: "liveScenario", message, category: "agent_runtime" }];
454
+ options.forward({ type: "error", ...envelope, message });
455
+ options.forward({
456
+ type: "cell_finished",
457
+ ...envelope,
458
+ passed: false,
459
+ failures,
460
+ durationMs: 0,
461
+ });
462
+ options.forward({
463
+ type: "scenario_result",
464
+ ...envelope,
465
+ result: {
466
+ suite: job.suite,
467
+ scenario: job.scenario,
468
+ prompt: job.prompt,
469
+ passed: false,
470
+ failures,
471
+ durationMs: 0,
472
+ },
473
+ });
425
474
  }
426
475
  //# sourceMappingURL=run-controller.js.map