@llblab/pi-actors 0.55.0 → 0.56.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 (188) hide show
  1. package/AGENTS.md +108 -20
  2. package/BACKLOG.md +198 -4
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +31 -1
  5. package/dist/index.js +1 -1
  6. package/dist/lib/async-runs.d.ts +37 -0
  7. package/dist/lib/async-runs.js +131 -62
  8. package/dist/lib/automatic-review-runtime.d.ts +2 -0
  9. package/dist/lib/automatic-review-runtime.js +3 -2
  10. package/dist/lib/command-templates.d.ts +37 -0
  11. package/dist/lib/command-templates.js +263 -129
  12. package/dist/lib/execution-files.d.ts +32 -0
  13. package/dist/lib/execution-files.js +128 -0
  14. package/dist/lib/execution-output.js +5 -4
  15. package/dist/lib/execution-sessions.d.ts +19 -1
  16. package/dist/lib/execution-sessions.js +62 -26
  17. package/dist/lib/execution.d.ts +4 -1
  18. package/dist/lib/execution.js +6 -1
  19. package/dist/lib/extension-runtime.d.ts +9 -2
  20. package/dist/lib/extension-runtime.js +92 -14
  21. package/dist/lib/file-state.js +21 -2
  22. package/dist/lib/inspector-command.js +2 -2
  23. package/dist/lib/inspector-overlay.d.ts +25 -7
  24. package/dist/lib/inspector-overlay.js +562 -262
  25. package/dist/lib/inspector.d.ts +20 -10
  26. package/dist/lib/inspector.js +80 -91
  27. package/dist/lib/limits.d.ts +1 -0
  28. package/dist/lib/limits.js +1 -0
  29. package/dist/lib/observability.d.ts +15 -1
  30. package/dist/lib/observability.js +216 -99
  31. package/dist/lib/pi.d.ts +5 -4
  32. package/dist/lib/pi.js +64 -10
  33. package/dist/lib/recipe-projection.d.ts +18 -0
  34. package/dist/lib/recipe-projection.js +220 -0
  35. package/dist/lib/recipes-context.d.ts +2 -0
  36. package/dist/lib/recipes-context.js +30 -6
  37. package/dist/lib/registry.js +280 -36
  38. package/dist/lib/run-delivery.d.ts +213 -5
  39. package/dist/lib/run-delivery.js +1909 -40
  40. package/dist/lib/run-supervision.d.ts +107 -0
  41. package/dist/lib/run-supervision.js +412 -0
  42. package/dist/lib/run-ui-runtime.d.ts +9 -2
  43. package/dist/lib/run-ui-runtime.js +283 -60
  44. package/dist/lib/runs-artifacts.d.ts +26 -1
  45. package/dist/lib/runs-artifacts.js +68 -2
  46. package/dist/lib/runs-context-exports.d.ts +451 -0
  47. package/dist/lib/runs-context-exports.js +2315 -0
  48. package/dist/lib/runs-context-index.d.ts +97 -0
  49. package/dist/lib/runs-context-index.js +368 -0
  50. package/dist/lib/runs-control-delivery.js +46 -7
  51. package/dist/lib/runs-control.d.ts +78 -2
  52. package/dist/lib/runs-control.js +334 -2
  53. package/dist/lib/runs-controls.js +4 -1
  54. package/dist/lib/runs-descendant-launches.d.ts +80 -0
  55. package/dist/lib/runs-descendant-launches.js +287 -0
  56. package/dist/lib/runs-execution-evidence.d.ts +110 -0
  57. package/dist/lib/runs-execution-evidence.js +282 -0
  58. package/dist/lib/runs-index.d.ts +66 -1
  59. package/dist/lib/runs-index.js +349 -13
  60. package/dist/lib/runs-native-launches.d.ts +73 -0
  61. package/dist/lib/runs-native-launches.js +227 -0
  62. package/dist/lib/runs-outcomes.d.ts +141 -0
  63. package/dist/lib/runs-outcomes.js +292 -0
  64. package/dist/lib/runs-ownership.d.ts +10 -1
  65. package/dist/lib/runs-ownership.js +36 -4
  66. package/dist/lib/runs-process-windows-mi.d.ts +46 -0
  67. package/dist/lib/runs-process-windows-mi.js +114 -0
  68. package/dist/lib/runs-process.d.ts +34 -1
  69. package/dist/lib/runs-process.js +158 -26
  70. package/dist/lib/runs-results.d.ts +116 -0
  71. package/dist/lib/runs-results.js +518 -0
  72. package/dist/lib/runs-retention.d.ts +15 -2
  73. package/dist/lib/runs-retention.js +148 -9
  74. package/dist/lib/runs-session-exports.d.ts +95 -0
  75. package/dist/lib/runs-session-exports.js +470 -0
  76. package/dist/lib/runs-snapshot.d.ts +58 -0
  77. package/dist/lib/runs-snapshot.js +92 -0
  78. package/dist/lib/runs-start.d.ts +2 -0
  79. package/dist/lib/runs-start.js +44 -3
  80. package/dist/lib/runs-status.d.ts +15 -1
  81. package/dist/lib/runs-status.js +337 -13
  82. package/dist/lib/runs-supervision.d.ts +105 -0
  83. package/dist/lib/runs-supervision.js +501 -0
  84. package/dist/lib/runs-trace.d.ts +1 -0
  85. package/dist/lib/runs-trace.js +14 -3
  86. package/dist/lib/runtime.js +22 -3
  87. package/dist/lib/session-evidence.d.ts +37 -0
  88. package/dist/lib/session-evidence.js +59 -3
  89. package/dist/lib/state-readers.d.ts +4 -1
  90. package/dist/lib/state-readers.js +14 -8
  91. package/dist/lib/temp.d.ts +2 -4
  92. package/dist/lib/temp.js +4 -56
  93. package/dist/lib/tools-access.d.ts +3 -0
  94. package/dist/lib/tools-access.js +13 -0
  95. package/dist/lib/tools-inspect.d.ts +1 -0
  96. package/dist/lib/tools-inspect.js +110 -43
  97. package/dist/lib/tools-local.d.ts +3 -0
  98. package/dist/lib/tools-local.js +18 -6
  99. package/dist/lib/tools-message.d.ts +4 -1
  100. package/dist/lib/tools-message.js +126 -31
  101. package/dist/lib/tools-response.d.ts +31 -3
  102. package/dist/lib/tools-response.js +311 -47
  103. package/dist/lib/tools-spawn.d.ts +3 -0
  104. package/dist/lib/tools-spawn.js +29 -7
  105. package/dist/lib/trace-projection.d.ts +20 -0
  106. package/dist/lib/trace-projection.js +192 -43
  107. package/dist/scripts/async-runner.mjs +272 -194
  108. package/dist/scripts/conformance.mjs +14 -2
  109. package/dist/skills/actors/SKILL.md +46 -2
  110. package/dist/skills/actors/references/diagnostics.md +40 -0
  111. package/dist/skills/actors/references/runs.md +55 -1
  112. package/docs/README.md +9 -1
  113. package/docs/actor-inspector.md +84 -15
  114. package/docs/agent-experience.md +216 -0
  115. package/docs/async-runs.md +18 -2
  116. package/docs/composition-transport.md +103 -0
  117. package/docs/coordinator-delivery.md +204 -4
  118. package/docs/inspection.md +38 -2
  119. package/docs/output-projection-map.md +130 -0
  120. package/docs/releasing.md +19 -2
  121. package/docs/run-supervision.md +1622 -0
  122. package/docs/template-recipes.md +4 -0
  123. package/docs/windows-process-observation.md +248 -0
  124. package/index.ts +1 -1
  125. package/lib/async-runs.ts +164 -73
  126. package/lib/automatic-review-runtime.ts +4 -2
  127. package/lib/command-templates.ts +287 -138
  128. package/lib/execution-files.ts +118 -0
  129. package/lib/execution-output.ts +4 -4
  130. package/lib/execution-sessions.ts +57 -27
  131. package/lib/execution.ts +8 -0
  132. package/lib/extension-runtime.ts +102 -12
  133. package/lib/file-state.ts +16 -2
  134. package/lib/inspector-command.ts +2 -2
  135. package/lib/inspector-overlay.ts +524 -261
  136. package/lib/inspector.ts +101 -130
  137. package/lib/limits.ts +1 -0
  138. package/lib/observability.ts +235 -116
  139. package/lib/pi.ts +59 -11
  140. package/lib/recipe-projection.ts +232 -0
  141. package/lib/recipes-context.ts +27 -6
  142. package/lib/registry.ts +275 -36
  143. package/lib/run-delivery.ts +1737 -49
  144. package/lib/run-supervision.ts +411 -0
  145. package/lib/run-ui-runtime.ts +237 -73
  146. package/lib/runs-artifacts.ts +73 -1
  147. package/lib/runs-context-exports.ts +1871 -0
  148. package/lib/runs-context-index.ts +372 -0
  149. package/lib/runs-control-delivery.ts +36 -18
  150. package/lib/runs-control.ts +297 -1
  151. package/lib/runs-controls.ts +3 -1
  152. package/lib/runs-descendant-launches.ts +338 -0
  153. package/lib/runs-execution-evidence.ts +302 -0
  154. package/lib/runs-index.ts +345 -17
  155. package/lib/runs-native-launches.ts +270 -0
  156. package/lib/runs-outcomes.ts +377 -0
  157. package/lib/runs-ownership.ts +37 -4
  158. package/lib/runs-process-windows-mi.ts +113 -0
  159. package/lib/runs-process.ts +210 -34
  160. package/lib/runs-results.ts +601 -0
  161. package/lib/runs-retention.ts +134 -10
  162. package/lib/runs-session-exports.ts +406 -0
  163. package/lib/runs-snapshot.ts +143 -0
  164. package/lib/runs-start.ts +44 -2
  165. package/lib/runs-status.ts +252 -10
  166. package/lib/runs-supervision.ts +394 -0
  167. package/lib/runs-trace.ts +14 -3
  168. package/lib/runtime.ts +22 -3
  169. package/lib/session-evidence.ts +76 -4
  170. package/lib/state-readers.ts +16 -7
  171. package/lib/temp.ts +3 -56
  172. package/lib/tools-access.ts +13 -0
  173. package/lib/tools-inspect.ts +110 -51
  174. package/lib/tools-local.ts +37 -24
  175. package/lib/tools-message.ts +120 -37
  176. package/lib/tools-response.ts +273 -50
  177. package/lib/tools-spawn.ts +31 -10
  178. package/lib/trace-projection.ts +165 -42
  179. package/package.json +10 -6
  180. package/scripts/async-runner.mjs +272 -194
  181. package/scripts/conformance.mjs +14 -2
  182. package/skills/actors/SKILL.md +46 -2
  183. package/skills/actors/references/diagnostics.md +40 -0
  184. package/skills/actors/references/runs.md +55 -1
  185. package/banner.jpg +0 -0
  186. package/dist/lib/inspector-actions.d.ts +0 -16
  187. package/dist/lib/inspector-actions.js +0 -57
  188. package/lib/inspector-actions.ts +0 -79
@@ -36,6 +36,7 @@ const {
36
36
  appendRecipeContextToPiArgs,
37
37
  attachPiSessionDir,
38
38
  materializePiPrintPromptArg,
39
+ piPrintResponseMode,
39
40
  } = await importRuntimeModule("recipes-context");
40
41
  const { buildReviewPreflightDiagnostic, formatReviewPreflightDiagnostic } =
41
42
  await importRuntimeModule("preflight-diagnostics");
@@ -44,6 +45,8 @@ const { applyOutputAcceptancePolicy, executeRegisteredTool } =
44
45
  await importRuntimeModule("execution");
45
46
  const { writeJsonAtomic } = await importRuntimeModule("file-state");
46
47
  const { appendRunTraceEvent } = await importRuntimeModule("runs-trace");
48
+ const { admitRunResultProducer, publishRunResult, withRunProducerEvidence, withRunWorkAdmission } = await importRuntimeModule("runs-results");
49
+ const { createRunPrimaryTextPublisher, prepareRunArtifactInventoryPublication, createRunResultDiagnosticsPublisher } = await importRuntimeModule("runs-context-exports");
47
50
 
48
51
  function quoteCommandDetailPart(value) {
49
52
  if (value === "") return "''";
@@ -74,14 +77,19 @@ function summarizeCommandDetail(commandDetail) {
74
77
 
75
78
  export async function runAsyncRunner(stateDir = process.argv[2]) {
76
79
  if (!stateDir) throw new Error("missing state dir");
77
- const runPath = join(stateDir, "run.json");
80
+ const { meta, producer } = admitRunResultProducer(stateDir);
81
+ const primaryText = createRunPrimaryTextPublisher(stateDir, producer);
82
+ const resultDiagnostics = createRunResultDiagnosticsPublisher(stateDir, producer);
83
+ const artifactInventory = prepareRunArtifactInventoryPublication(stateDir, producer);
84
+ if (artifactInventory.readFailure() !== undefined) console.error("Run artifact inventory publication unavailable; workload policy is unchanged.");
85
+ let primarySelectionFailed = false; let terminalPublicationStarted = false;
86
+ let unsupportedResponseFormat = false;
78
87
  const progressPath = join(stateDir, "progress.json");
79
88
  const resultPath = join(stateDir, "result.json");
80
89
  const tracePath = join(stateDir, "trace.jsonl");
81
90
  const stdoutPath = join(stateDir, "stdout.log");
82
91
  const stderrPath = join(stateDir, "stderr.log");
83
92
  const executionPath = join(stateDir, "execution.json");
84
- const meta = JSON.parse(readFileSync(runPath, "utf8"));
85
93
  function event(kind, data = {}) {
86
94
  appendRunTraceEvent(stateDir, { kind, data });
87
95
  }
@@ -114,8 +122,10 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
114
122
  writeJsonAtomic(executionPath, {
115
123
  version: 1,
116
124
  run: meta.run,
125
+ run_instance_id: producer.runInstanceId,
117
126
  status,
118
127
  ...(meta.model_policy ? { model_policy: meta.model_policy } : {}),
128
+ active_subagents: activeSubagents,
119
129
  commands: [...evidenceRecords].sort((left, right) =>
120
130
  left.id.localeCompare(right.id)
121
131
  ),
@@ -173,12 +183,31 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
173
183
  : "not_required",
174
184
  };
175
185
  }
186
+ function commandAttemptEvidence({ attempt, result, nativeExitCode, nativeSignal }) {
187
+ return {
188
+ attempt,
189
+ code: result.code,
190
+ ...(nativeExitCode !== undefined ? { native_exit_code: nativeExitCode } : {}),
191
+ ...(nativeSignal ? { native_signal: nativeSignal } : {}),
192
+ killed: result.killed,
193
+ stdout: {
194
+ ...(result.stdoutFile ? { path: relative(stateDir, result.stdoutFile).replaceAll("\\", "/") } : {}),
195
+ bytes: result.stdoutCaptureIncomplete ? result.stdoutFileBytes ?? null : result.stdoutBytes ?? Buffer.byteLength(result.stdout),
196
+ ...(result.stdoutCaptureIncomplete ? { complete: false, bytes_observed: result.stdoutBytes, error: "capture_write_failed" } : {}),
197
+ },
198
+ stderr: {
199
+ ...(result.stderrFile ? { path: relative(stateDir, result.stderrFile).replaceAll("\\", "/") } : {}),
200
+ bytes: result.stderrCaptureIncomplete ? result.stderrFileBytes ?? null : result.stderrBytes ?? Buffer.byteLength(result.stderr),
201
+ ...(result.stderrCaptureIncomplete ? { complete: false, bytes_observed: result.stderrBytes, error: "capture_write_failed" } : {}),
202
+ },
203
+ };
204
+ }
176
205
  function commandEvidenceRecord({
177
- captureDir,
178
206
  commandDetail,
179
207
  commandId,
180
208
  materialized,
181
209
  options,
210
+ attempts,
182
211
  result,
183
212
  rawExitCode,
184
213
  sessionDir,
@@ -187,28 +216,6 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
187
216
  startedAt,
188
217
  }) {
189
218
  const recipeContext = options?.actorRecipeContext;
190
- const attempts = [];
191
- const maxAttempts = Math.max(1, options?.retry || 1);
192
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
193
- const attemptDir = join(
194
- captureDir,
195
- `attempt-${String(attempt).padStart(3, "0")}`,
196
- );
197
- const stdoutFile = join(attemptDir, "stdout.log");
198
- const stderrFile = join(attemptDir, "stderr.log");
199
- if (!existsSync(stdoutFile) && !existsSync(stderrFile)) continue;
200
- attempts.push({
201
- attempt,
202
- stdout: {
203
- path: relative(stateDir, stdoutFile).replaceAll("\\", "/"),
204
- bytes: existsSync(stdoutFile) ? statSync(stdoutFile).size : 0,
205
- },
206
- stderr: {
207
- path: relative(stateDir, stderrFile).replaceAll("\\", "/"),
208
- bytes: existsSync(stderrFile) ? statSync(stderrFile).size : 0,
209
- },
210
- });
211
- }
212
219
  const expectedReviewMarker =
213
220
  options?.evidenceContext?.acceptOutput === "review_evidence";
214
221
  const expectedPreflightMarker = stage === "preflight";
@@ -250,6 +257,8 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
250
257
  : {}),
251
258
  attempts,
252
259
  exit_code: rawExitCode ?? result.code,
260
+ ...(attempts.at(-1)?.native_exit_code !== undefined
261
+ ? { native_exit_code: attempts.at(-1).native_exit_code } : {}),
253
262
  effective_exit_code: result.code,
254
263
  killed: result.killed,
255
264
  stdout_bytes: result.stdoutBytes ?? Buffer.byteLength(result.stdout),
@@ -284,8 +293,9 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
284
293
  complete_allowed: missing.length === 0,
285
294
  };
286
295
  }
287
- function progressRunning() {
288
- progress("running", {
296
+ function progressRunning(status = "running") {
297
+ if (status === "done" || status === "failed") return;
298
+ progress(status, {
289
299
  activeSubagents,
290
300
  completed: completedSubagents,
291
301
  failures: subagentFailures,
@@ -306,159 +316,207 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
306
316
  }
307
317
  }
308
318
  async function observedExec(command, args, options) {
309
- captureCounter += 1;
310
- const commandId = `command-${String(captureCounter).padStart(3, "0")}`;
311
- const contextArgs = appendRecipeContextToPiArgs(
312
- command,
313
- args,
314
- meta.recipe_context_records,
315
- options?.actorRecipeContext,
316
- );
317
- const session = attachPiSessionDir(
318
- command,
319
- contextArgs,
320
- join(stateDir, "sessions", commandId),
321
- );
322
- const materialized = materializePiPrintPromptArg(
323
- command,
324
- session.args,
325
- promptFilePath,
326
- );
327
- const execArgs = materialized.args;
328
- const commandDetail = formatCommandDetail(command, execArgs);
329
- const stage =
330
- options?.actorRecipeContext?.alias ||
331
- options?.actorRecipeContext?.name ||
332
- "command";
333
- const occurrence = (stageOccurrences.get(stage) || 0) + 1;
334
- stageOccurrences.set(stage, occurrence);
335
- if (
336
- materialized.promptFile &&
337
- ["verifier", "merger", "judge", "normalizer"].includes(stage)
338
- ) {
339
- const references = evidenceRecords
340
- .filter((record) =>
341
- ["reviewer", "verifier", "merger", "judge"].includes(record.stage)
342
- )
343
- .map((record) => `ACTOR_EVIDENCE_REF: execution.json#${record.id}`);
344
- if (references.length > 0) {
345
- appendFileSync(
346
- materialized.promptFile,
347
- `\n\nRetained actor evidence references available for citation:\n${references.join("\n")}\n`,
348
- );
349
- materialized.promptBytes = statSync(materialized.promptFile).size;
319
+ const {
320
+ captureDir, commandDetail, commandId, evidenceIndex, execArgs, materialized, occurrence, session, stage, startedEvidence,
321
+ } = withRunWorkAdmission(stateDir, producer, () => {
322
+ captureCounter += 1;
323
+ const commandId = `command-${String(captureCounter).padStart(3, "0")}`;
324
+ const contextArgs = appendRecipeContextToPiArgs(
325
+ command,
326
+ args,
327
+ meta.recipe_context_records,
328
+ options?.actorRecipeContext,
329
+ );
330
+ const session = attachPiSessionDir(
331
+ command,
332
+ contextArgs,
333
+ join(stateDir, "sessions", commandId),
334
+ );
335
+ const materialized = materializePiPrintPromptArg(
336
+ command,
337
+ session.args,
338
+ promptFilePath,
339
+ );
340
+ const execArgs = materialized.args;
341
+ const commandDetail = formatCommandDetail(command, execArgs);
342
+ const stage =
343
+ options?.actorRecipeContext?.alias ||
344
+ options?.actorRecipeContext?.name ||
345
+ "command";
346
+ const occurrence = (stageOccurrences.get(stage) || 0) + 1;
347
+ stageOccurrences.set(stage, occurrence);
348
+ if (
349
+ materialized.promptFile &&
350
+ ["verifier", "merger", "judge", "normalizer"].includes(stage)
351
+ ) {
352
+ const references = evidenceRecords
353
+ .filter((record) =>
354
+ ["reviewer", "verifier", "merger", "judge"].includes(record.stage)
355
+ )
356
+ .map((record) => `ACTOR_EVIDENCE_REF: execution.json#${record.id}`);
357
+ if (references.length > 0) {
358
+ appendFileSync(
359
+ materialized.promptFile,
360
+ `\n\nRetained actor evidence references available for citation:\n${references.join("\n")}\n`,
361
+ );
362
+ materialized.promptBytes = statSync(materialized.promptFile).size;
363
+ }
350
364
  }
351
- }
352
- activeSubagents += 1;
353
- const evidenceIndex = evidenceRecords.length;
354
- const startedEvidence = commandEvidenceStartRecord({
355
- commandDetail,
356
- commandId,
357
- materialized,
358
- options,
359
- sessionDir: session.sessionDir,
360
- stage,
361
- occurrence,
362
- });
363
- evidenceRecords.push(startedEvidence);
364
- writeExecutionManifest("running");
365
- event("command.start", {
366
- activeSubagents,
367
- command_id: commandId,
368
- command: commandDetail,
369
- ...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
370
- ...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
371
- ...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
372
- });
373
- progressRunning();
374
- const captureDir = join(stateDir, "captures", commandId);
375
- const rawResult = await execCommandTemplate(command, execArgs, {
376
- ...options,
377
- captureDir,
378
- });
379
- let result = await applyOutputAcceptancePolicy(
380
- rawResult,
381
- options?.evidenceContext?.acceptOutput,
382
- );
383
- result = {
384
- ...result,
385
- evidenceRef: `execution.json#${commandId}`,
386
- };
387
- const preflightDiagnostic = result.code !== 0
388
- ? buildReviewPreflightDiagnostic({
389
- args: execArgs,
390
- code: result.code,
391
- killed: result.killed,
392
- ...(materialized.promptFile ? { promptFile: materialized.promptFile } : {}),
393
- promptText: readPromptText(materialized.promptFile),
394
- stderr: result.stderr,
395
- stdout: result.stdout,
396
- })
397
- : undefined;
398
- if (preflightDiagnostic) {
399
- result = {
400
- ...result,
401
- stderr: [
402
- result.stderr,
403
- formatReviewPreflightDiagnostic(preflightDiagnostic),
404
- ].filter(Boolean).join("\n"),
405
- };
406
- }
407
- evidenceRecords[evidenceIndex] = commandEvidenceRecord({
408
- captureDir,
409
- commandDetail,
410
- commandId,
411
- materialized,
412
- options,
413
- result,
414
- rawExitCode: rawResult.code,
415
- sessionDir: session.sessionDir,
416
- stage,
417
- occurrence,
418
- startedAt: startedEvidence.started_at,
419
- });
420
- writeExecutionManifest("running");
421
- activeSubagents = Math.max(0, activeSubagents - 1);
422
- completedSubagents += 1;
423
- if (result.code !== 0) {
424
- subagentFailures.push({
425
- code: result.code,
426
- command: commandDetail,
427
- killed: result.killed,
428
- ...captureDetails(result),
429
- ...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
430
- ...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
365
+ activeSubagents += 1;
366
+ const evidenceIndex = evidenceRecords.length;
367
+ const startedEvidence = commandEvidenceStartRecord({
368
+ commandDetail,
369
+ commandId,
370
+ materialized,
371
+ options,
372
+ sessionDir: session.sessionDir,
373
+ stage,
374
+ occurrence,
431
375
  });
432
- }
433
- observation(
434
- "command.done",
435
- `Command ${summarizeCommandDetail(commandDetail)} completed with code ${result.code}`,
436
- {
376
+ evidenceRecords.push(startedEvidence);
377
+ writeExecutionManifest("running");
378
+ event("command.start", {
437
379
  activeSubagents,
438
- ...(meta.artifacts ? { artifacts: meta.artifacts } : {}),
439
- run_files: [stdoutPath, stderrPath, resultPath, tracePath, executionPath],
440
380
  command_id: commandId,
441
- code: result.code,
442
381
  command: commandDetail,
443
- killed: result.killed,
444
- ...captureDetails(result),
445
382
  ...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
446
383
  ...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
447
384
  ...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
385
+ });
386
+ progressRunning();
387
+ const captureDir = join(stateDir, "captures", commandId);
388
+ return { captureDir, commandDetail, commandId, evidenceIndex, execArgs, materialized, occurrence, session, stage, startedEvidence };
389
+ });
390
+ const attempts = []; const primaryAttempts = [];
391
+ let completed = false;
392
+ let completedObservation;
393
+ try {
394
+ const rawResult = await execCommandTemplate(command, execArgs, {
395
+ ...options,
396
+ captureDir,
397
+ admitSpawn: (start) => withRunWorkAdmission(stateDir, producer, start),
398
+ onAttemptComplete: (receipt) => {
399
+ primaryAttempts.push(receipt);
400
+ attempts.push(commandAttemptEvidence(receipt));
401
+ withRunProducerEvidence(stateDir, producer, ({ status }) => {
402
+ evidenceRecords[evidenceIndex] = { ...startedEvidence, attempts: [...attempts] };
403
+ writeExecutionManifest(status);
404
+ progressRunning(status);
405
+ });
406
+ },
407
+ });
408
+ let result = await applyOutputAcceptancePolicy(
409
+ rawResult,
410
+ options?.evidenceContext?.acceptOutput,
411
+ );
412
+ result = {
413
+ ...result,
414
+ evidenceRef: `execution.json#${commandId}`,
415
+ };
416
+ const preflightDiagnostic = result.code !== 0
417
+ ? buildReviewPreflightDiagnostic({
418
+ args: execArgs,
419
+ code: result.code,
420
+ killed: result.killed,
421
+ ...(materialized.promptFile ? { promptFile: materialized.promptFile } : {}),
422
+ promptText: readPromptText(materialized.promptFile),
423
+ stderr: result.stderr,
424
+ stdout: result.stdout,
425
+ })
426
+ : undefined;
427
+ if (preflightDiagnostic) {
428
+ result = {
429
+ ...result,
430
+ stderr: [
431
+ result.stderr,
432
+ formatReviewPreflightDiagnostic(preflightDiagnostic),
433
+ ].filter(Boolean).join("\n"),
434
+ };
435
+ }
436
+ evidenceRecords[evidenceIndex] = commandEvidenceRecord({
437
+ commandDetail,
438
+ commandId,
439
+ materialized,
440
+ options,
441
+ attempts,
442
+ result,
443
+ rawExitCode: rawResult.code,
444
+ sessionDir: session.sessionDir,
445
+ stage,
446
+ occurrence,
447
+ startedAt: startedEvidence.started_at,
448
+ });
449
+ completedSubagents += 1;
450
+ if (result.code !== 0) {
451
+ subagentFailures.push({
452
+ code: result.code,
453
+ command: commandDetail,
454
+ killed: result.killed,
455
+ ...captureDetails(result),
456
+ ...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
457
+ ...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
458
+ });
459
+ }
460
+ completedObservation = () => observation(
461
+ "command.done",
462
+ `Command ${summarizeCommandDetail(commandDetail)} completed with code ${result.code}`,
463
+ {
464
+ activeSubagents,
465
+ ...(meta.artifacts ? artifactInventory.snapshot() : {}),
466
+ run_files: [stdoutPath, stderrPath, resultPath, tracePath, executionPath],
467
+ command_id: commandId,
468
+ code: result.code,
469
+ command: commandDetail,
470
+ killed: result.killed,
471
+ ...captureDetails(result),
472
+ ...(materialized.promptFile ? { prompt_file: materialized.promptFile } : {}),
473
+ ...(materialized.promptBytes ? { prompt_bytes: materialized.promptBytes } : {}),
474
+ ...(session.sessionDir ? { session_dir: relative(stateDir, session.sessionDir).replaceAll("\\", "/") } : {}),
475
+ ...(commandSessionFiles(session.sessionDir).length > 0
476
+ ? { session_files: commandSessionFiles(session.sessionDir) }
477
+ : {}),
478
+ ...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
479
+ },
480
+ "log",
481
+ result.code === 0 ? "info" : "error",
482
+ );
483
+ const responseMode = piPrintResponseMode(command, execArgs);
484
+ if (responseMode === "unsupported") unsupportedResponseFormat = true;
485
+ primaryText.observe(result, { commandId, attempts: primaryAttempts, session: Boolean(session.sessionDir) || responseMode !== undefined });
486
+ completed = true;
487
+ return result;
488
+ } catch (error) {
489
+ evidenceRecords[evidenceIndex] = {
490
+ ...startedEvidence,
491
+ status: "interrupted",
492
+ completed_at: new Date().toISOString(),
493
+ attempts,
448
494
  ...(commandSessionFiles(session.sessionDir).length > 0
449
- ? { session_files: commandSessionFiles(session.sessionDir) }
450
- : {}),
451
- ...(preflightDiagnostic ? { preflight: preflightDiagnostic } : {}),
452
- },
453
- "log",
454
- result.code === 0 ? "info" : "error",
455
- );
456
- progressRunning();
457
- return result;
495
+ ? { session_files: commandSessionFiles(session.sessionDir) } : {}),
496
+ semantic_acceptance: "unavailable",
497
+ interruption: { message: error instanceof Error ? error.message : String(error) },
498
+ };
499
+ throw error;
500
+ } finally {
501
+ activeSubagents = Math.max(0, activeSubagents - 1);
502
+ try {
503
+ withRunProducerEvidence(stateDir, producer, ({ status }) => {
504
+ writeExecutionManifest(status);
505
+ if (completed) completedObservation();
506
+ progressRunning(status);
507
+ });
508
+ } catch (error) {
509
+ if (completed) throw error;
510
+ // Keep the primary execution rejection; never write evidence into a replacement generation.
511
+ console.error("Run interrupted-command evidence could not be persisted.");
512
+ }
513
+ }
458
514
  }
459
515
  try {
460
- event("run.runner.start", { pid: process.pid });
461
- progressRunning();
516
+ withRunWorkAdmission(stateDir, producer, () => {
517
+ event("run.runner.start", { pid: process.pid });
518
+ progressRunning();
519
+ });
462
520
  const result = await executeRegisteredTool(
463
521
  {
464
522
  name: "run_actor",
@@ -470,6 +528,13 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
470
528
  meta.values || {},
471
529
  observedExec,
472
530
  meta.cwd,
531
+ undefined,
532
+ { onPrimaryCommandSelected: selected => {
533
+ try { primaryText.select(selected); } catch (error) { primarySelectionFailed = true; throw error; }
534
+ }, onFinalResponseSelected: (text, upstreamTruncated) => {
535
+ if (unsupportedResponseFormat) return;
536
+ try { primaryText.selectResponse(text, upstreamTruncated); } catch (error) { primarySelectionFailed = true; throw error; }
537
+ } },
473
538
  );
474
539
  const text = result.content?.[0]?.text || "";
475
540
  appendFileSync(stdoutPath, text);
@@ -487,35 +552,32 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
487
552
  };
488
553
  throw error;
489
554
  }
490
- writeExecutionManifest("done");
491
- progress("done", {
492
- completed: 1,
493
- failures: result.details.nonCriticalFailures || [],
494
- });
495
- writeJsonAtomic(resultPath, {
555
+ terminalPublicationStarted = true;
556
+ publishRunResult(stateDir, producer, {
496
557
  code: result.details.code,
497
558
  command: result.details.command,
498
559
  killed: result.details.killed,
499
560
  ...(meta.model_policy ? { model_policy: meta.model_policy } : {}),
500
561
  truncated: result.details.truncated,
501
- completedAt: new Date().toISOString(),
562
+ }, {
563
+ primaryText: primaryText.snapshot,
564
+ artifactInventory: artifactInventory.snapshot,
565
+ resultDiagnostics: resultDiagnostics.publish,
566
+ trace: { kind: "run.done", data: { code: result.details.code } },
567
+ prepare: ({ stopping }) => {
568
+ writeExecutionManifest(stopping ? "stopping" : "done");
569
+ progress(stopping ? "stopping" : "done", {
570
+ completed: 1,
571
+ failures: result.details.nonCriticalFailures || [],
572
+ });
573
+ },
502
574
  });
503
- event("run.done", { code: result.details.code });
504
575
  } catch (error) {
576
+ // Authority refusal and failed terminal publication must not become an invented workload code or implicit retry.
577
+ if (primarySelectionFailed || terminalPublicationStarted) throw error;
505
578
  const message = error instanceof Error ? error.message : String(error);
506
579
  const details = error && typeof error === "object" ? error.details : undefined;
507
- appendFileSync(stderrPath, `${message}\n`);
508
- writeExecutionManifest("failed");
509
- progress("failed", {
510
- completed: 0,
511
- failures: Array.isArray(details?.branches) && details.branches.length > 0
512
- ? details.branches
513
- : subagentFailures.length > 0
514
- ? subagentFailures
515
- : [{ message }],
516
- ...(details?.failureReason ? { failureReason: details.failureReason } : {}),
517
- });
518
- writeJsonAtomic(resultPath, {
580
+ publishRunResult(stateDir, producer, {
519
581
  code: typeof details?.code === "number" ? details.code : 1,
520
582
  error: message,
521
583
  killed: Boolean(details?.killed),
@@ -523,11 +585,27 @@ export async function runAsyncRunner(stateDir = process.argv[2]) {
523
585
  ...(details?.failureReason ? { failure_reason: details.failureReason } : {}),
524
586
  ...(meta.model_policy ? { model_policy: meta.model_policy } : {}),
525
587
  ...(details?.softQuorum ? { soft_quorum: details.softQuorum } : {}),
526
- completedAt: new Date().toISOString(),
527
- });
528
- event("run.failed", {
529
- error: message,
530
- ...(details?.failureReason ? { failure_reason: details.failureReason } : {}),
588
+ }, {
589
+ primaryText: () => ({ output: null, output_unavailable: "primary_not_selected_for_failure" }),
590
+ artifactInventory: artifactInventory.snapshot,
591
+ resultDiagnostics: resultDiagnostics.publish,
592
+ trace: {
593
+ kind: "run.failed",
594
+ data: { error: message, ...(details?.failureReason ? { failure_reason: details.failureReason } : {}) },
595
+ },
596
+ prepare: ({ stopping }) => {
597
+ appendFileSync(stderrPath, `${message}\n`);
598
+ writeExecutionManifest(stopping ? "stopping" : "failed");
599
+ progress(stopping ? "stopping" : "failed", {
600
+ completed: 0,
601
+ failures: Array.isArray(details?.branches) && details.branches.length > 0
602
+ ? details.branches
603
+ : subagentFailures.length > 0
604
+ ? subagentFailures
605
+ : [{ message }],
606
+ ...(details?.failureReason ? { failureReason: details.failureReason } : {}),
607
+ });
608
+ },
531
609
  });
532
610
  throw error;
533
611
  }
@@ -13,21 +13,32 @@ import { fileURLToPath } from "node:url";
13
13
 
14
14
  const conformanceSuites = [
15
15
  "tests/agent-journeys.test.ts",
16
+ "tests/file-state.test.ts",
16
17
  "tests/control.test.ts",
17
18
  "tests/runs-controls.test.ts",
19
+ "tests/runs-control-delivery.test.ts",
20
+ "tests/runs-retention.test.ts",
18
21
  "tests/runs-trace.test.ts",
22
+ "tests/trace-projection.test.ts",
23
+ "tests/session-evidence.test.ts",
24
+ "tests/execution-sessions.test.ts",
19
25
  "tests/observability.test.ts",
20
- "tests/async-run-control.test.ts",
26
+ "tests/run-delivery.test.ts",
27
+ "tests/run-ui-runtime.test.ts",
21
28
  "tests/run-kernel-dogfood.test.ts",
22
29
  "tests/installed.scripts.test.ts",
23
30
  "tests/inspector-overlay.test.ts",
24
- "tests/tools-inspect-kernel.test.ts",
31
+ "tests/tools-inspect.test.ts",
25
32
  "tests/recipes-discovery.test.ts",
26
33
  "tests/review-swarm-dogfood.test.ts",
27
34
  "tests/registry.test.ts",
28
35
  "tests/runtime.test.ts",
29
36
  "tests/async-runs.test.ts",
30
37
  "tests/tools.test.ts",
38
+ "tests/tools-response.test.ts",
39
+ "tests/tools-message.test.ts",
40
+ "tests/tools-spawn.test.ts",
41
+ "tests/tools-local.test.ts",
31
42
  ];
32
43
 
33
44
  function packageRoot() {
@@ -37,6 +48,7 @@ function packageRoot() {
37
48
  const result = spawnSync(
38
49
  process.execPath,
39
50
  [
51
+ "--import", "./tests/test-environment.mjs",
40
52
  "--experimental-strip-types",
41
53
  "--test",
42
54
  "--test-concurrency=1",