@akagilnc/pi-workflow-roles 0.1.5406 → 0.1.5475

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 (93) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +42 -55
  3. package/README.zh-CN.md +59 -73
  4. package/THIRD_PARTY_NOTICES.md +4 -7
  5. package/dist/acp-host/production-host.js +8261 -9073
  6. package/dist/collector-tool-schemas.js +1 -1
  7. package/dist/compliance-transport.js +2 -5
  8. package/dist/engine-detour-usage.js +3 -30
  9. package/dist/gatekeeper-role.js +4 -18
  10. package/dist/headless-host/description.js +0 -209
  11. package/dist/headless-host/production-host.js +6973 -8035
  12. package/dist/host-contracts.js +8 -0
  13. package/dist/migrate-book-topology.js +30 -27
  14. package/dist/packaged-role-registry.js +1 -9
  15. package/dist/pi/role-turn-host.js +37 -19
  16. package/dist/public-cli/auto-resume.js +29 -66
  17. package/dist/public-cli/countersign-run.js +4 -18
  18. package/dist/public-cli/instruction-seat-run.js +59 -76
  19. package/dist/public-cli/main.js +9844 -10720
  20. package/dist/public-cli/option-definitions.js +1 -1
  21. package/dist/public-cli/post-admission.js +204 -285
  22. package/dist/public-cli/process-cancel.js +8 -0
  23. package/dist/public-cli/run-lifecycle.js +24 -94
  24. package/dist/public-cli/settlement.js +614 -1524
  25. package/dist/public-cli/terminal.js +23 -53
  26. package/dist/public-role-summons.js +3 -8
  27. package/dist/receipt-delivery-policy.js +11 -12
  28. package/dist/run-terminal-artifacts.js +19 -14
  29. package/dist/serialize-thrown-value.js +7 -1
  30. package/dist/submission-ledger.js +21 -89
  31. package/package.json +1 -1
  32. package/packets/fixer-repair.md +3 -1
  33. package/packets/judge-apply.md +7 -16
  34. package/packets/judge-authority.md +7 -17
  35. package/packets/judge-plan.md +7 -18
  36. package/packets/judge-review.md +8 -22
  37. package/packets/judge-submission.md +4 -7
  38. package/resources/collector-bot-handbook.md +2 -2
  39. package/resources/diarist-collect.md +2 -2
  40. package/resources/engine-dispatch.md +2 -2
  41. package/resources/navigator-route-playbook.md +1 -1
  42. package/resources/secretariat-ticket-identity.md +1 -1
  43. package/scripts/build-package.mjs +0 -2
  44. package/souls/coder-output-guide.md +3 -3
  45. package/souls/coder.md +2 -3
  46. package/souls/doctor-auditor.md +0 -3
  47. package/souls/fixer-auditor.md +0 -2
  48. package/souls/fixer-output-guide.md +3 -3
  49. package/souls/fixer.md +2 -2
  50. package/souls/gate-output-guide.md +1 -1
  51. package/souls/quality-law.md +2 -0
  52. package/src/acp-host/role-turn-host.ts +168 -75
  53. package/src/collector-tool-schemas.ts +1 -1
  54. package/src/compliance-transport.ts +3 -6
  55. package/src/engine-detour-usage.ts +3 -33
  56. package/src/external-host-turn-loop.ts +81 -36
  57. package/src/gatekeeper-role.ts +5 -20
  58. package/src/headless-host/description.ts +4 -214
  59. package/src/headless-host/production-host.ts +2 -1
  60. package/src/headless-host/role-turn-host.ts +153 -186
  61. package/src/host-contracts.ts +13 -10
  62. package/src/navigator-public-session.ts +1 -1
  63. package/src/navigator-session-contracts.ts +1 -1
  64. package/src/package-contracts/gatekeeper-output.ts +1 -1
  65. package/src/package-contracts/navigator-output.ts +1 -1
  66. package/src/package-contracts/terminating-infrastructure.ts +1 -1
  67. package/src/packaged-role-registry.ts +2 -10
  68. package/src/pi/adapter.ts +0 -3
  69. package/src/pi/role-turn-host.ts +42 -28
  70. package/src/prepared-role-turn.ts +2 -0
  71. package/src/public-cli/auto-resume.ts +27 -66
  72. package/src/public-cli/countersign-run.ts +4 -22
  73. package/src/public-cli/instruction-seat-run.ts +79 -80
  74. package/src/public-cli/option-definitions.ts +1 -1
  75. package/src/public-cli/post-admission.ts +264 -340
  76. package/src/public-cli/process-cancel.ts +9 -0
  77. package/src/public-cli/run-lifecycle.ts +25 -126
  78. package/src/public-cli/settlement.ts +792 -1912
  79. package/src/public-cli/terminal.ts +29 -69
  80. package/src/public-role-summons.ts +3 -8
  81. package/src/receipt-delivery-policy.ts +11 -12
  82. package/src/role-envelope.ts +16 -9
  83. package/src/role-runtime.ts +14 -81
  84. package/src/run-terminal-artifacts.ts +21 -17
  85. package/src/serialize-thrown-value.ts +7 -1
  86. package/src/submission-ledger.ts +30 -116
  87. package/dist/audit-escalation.js +0 -7
  88. package/dist/pi/known-failure.js +0 -51
  89. package/dist/typed-provider-http.js +0 -57
  90. package/dist/upstream-error-testimony.js +0 -19
  91. package/src/pi/known-failure.ts +0 -76
  92. package/src/typed-provider-http.ts +0 -89
  93. package/src/upstream-error-testimony.ts +0 -45
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Controlled post-admission failure classes (ADR 0052 / #107). Owner = host contract.
3
+ * Closed set of typed facts only — never a fabricated "could not classify" label (#881).
4
+ * When no typed confirmation exists, omit cause and keep the original diagnostic / error pointer.
5
+ */
6
+ export const CONTROLLED_FAILURE_CAUSES = [
7
+ "activation", "provider", "session", "output", "timeout",
8
+ ];
1
9
  /**
2
10
  * Thrown activation failure with a production-owned typed cause.
3
11
  * Prefer this over ad-hoc Error property tags so settlement retains typed identity.
@@ -682,14 +682,6 @@ var init_cli_errors = __esm({
682
682
  }
683
683
  });
684
684
 
685
- // src/typed-provider-http.ts
686
- var init_typed_provider_http = __esm({
687
- "src/typed-provider-http.ts"() {
688
- "use strict";
689
- init_unknown_value();
690
- }
691
- });
692
-
693
685
  // src/sha256.ts
694
686
  import { createHash } from "node:crypto";
695
687
  function sha256Hex(bytes) {
@@ -14057,7 +14049,6 @@ var init_packaged_role_registry = __esm({
14057
14049
  outputTool: JUDGE_OUTPUT_TOOL_NAME,
14058
14050
  auditTool: JUDGE_AUDIT_TOOL_NAME,
14059
14051
  settlement: "sealed",
14060
- runnerFailure: "engine-detour-known-first",
14061
14052
  /** Factory board: court until another station has started, then marshal. */
14062
14053
  boardPlacement: "judge-history",
14063
14054
  /** Navigator subject is the public admitted instruction when the run is bound. */
@@ -14175,7 +14166,6 @@ var init_packaged_role_registry = __esm({
14175
14166
  },
14176
14167
  /** Frozen base/lens/authority become the initial prompt; instruction follows. */
14177
14168
  transportPrompt: "skill-args",
14178
- runnerFailure: "engine-detour-record-first",
14179
14169
  boardPlacement: "marshal",
14180
14170
  activationFlags: [
14181
14171
  { field: "baseRevision", flag: "ak-review-base" },
@@ -14270,10 +14260,7 @@ var init_packaged_role_registry = __esm({
14270
14260
  /** Method-material load failure keeps the activation cause (not reviewer/fixer). */
14271
14261
  phases: [null],
14272
14262
  outputTool: MERGER_OUTPUT_TOOL_NAME,
14273
- settlement: "residual",
14274
- /** #836: residual scan stays on the whole host session. */
14275
- residualScan: "session",
14276
- residualTool: MERGER_OUTPUT_TOOL_NAME,
14263
+ settlement: "sealed",
14277
14264
  artifactFace: {
14278
14265
  evidenceRole: true,
14279
14266
  leaves: [
@@ -14450,7 +14437,6 @@ var init_packaged_role_registry = __esm({
14450
14437
  phases: [null],
14451
14438
  outputTool: GATEKEEPER_OUTPUT_TOOL_NAME,
14452
14439
  settlement: "accepted",
14453
- runnerFailure: "engine-detour-record-first",
14454
14440
  activationStage: "load-and-install",
14455
14441
  // Province materials; officers reuse their own public records below.
14456
14442
  sessionMaterials: ["CLAUDE.md", "souls/gatekeeper.md", "souls/quality-law.md", "souls/gate-output-guide.md"]
@@ -14465,7 +14451,6 @@ var init_packaged_role_registry = __esm({
14465
14451
  phases: [null],
14466
14452
  outputTool: NAVIGATOR_OUTPUT_TOOL_NAME,
14467
14453
  settlement: "accepted",
14468
- runnerFailure: "engine-detour-record-first",
14469
14454
  activationStage: "load-and-install",
14470
14455
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"]
14471
14456
  },
@@ -14488,7 +14473,6 @@ var init_packaged_role_registry = __esm({
14488
14473
  settlement: "accepted",
14489
14474
  /** Court reask replaces the initial prompt. */
14490
14475
  reaskPrompt: true,
14491
- runnerFailure: "engine-detour-record-first",
14492
14476
  gateStageLabel: "\u5BA1\u5211\u9662",
14493
14477
  /** Gate summon binds --subject judge and --source-run. */
14494
14478
  gateSummon: "subject-source",
@@ -14690,12 +14674,38 @@ var init_role_submission_declarations = __esm({
14690
14674
  }
14691
14675
  });
14692
14676
 
14677
+ // src/serialize-thrown-value.ts
14678
+ var init_serialize_thrown_value = __esm({
14679
+ "src/serialize-thrown-value.ts"() {
14680
+ "use strict";
14681
+ }
14682
+ });
14683
+
14684
+ // src/public-cli/terminal.ts
14685
+ var init_terminal = __esm({
14686
+ "src/public-cli/terminal.ts"() {
14687
+ "use strict";
14688
+ init_serialize_thrown_value();
14689
+ }
14690
+ });
14691
+
14693
14692
  // src/run-terminal-artifacts.ts
14693
+ var RUN_TERMINAL_ERROR_SETTLEMENT_FILE, RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS, UNIQUE_ERROR_FALLBACK_STEM, UNIQUE_ERROR_FALLBACK_NAME;
14694
14694
  var init_run_terminal_artifacts = __esm({
14695
14695
  "src/run-terminal-artifacts.ts"() {
14696
14696
  "use strict";
14697
14697
  init_role_run_placement();
14698
14698
  init_unknown_value();
14699
+ RUN_TERMINAL_ERROR_SETTLEMENT_FILE = "error.settlement.json";
14700
+ RUN_TERMINAL_ERROR_FALLBACK_RELATIVE_PATHS = [
14701
+ `artifacts/${RUN_TERMINAL_ERROR_SETTLEMENT_FILE}`,
14702
+ RUN_TERMINAL_ERROR_SETTLEMENT_FILE
14703
+ ];
14704
+ UNIQUE_ERROR_FALLBACK_STEM = "error";
14705
+ UNIQUE_ERROR_FALLBACK_NAME = new RegExp(
14706
+ `^${UNIQUE_ERROR_FALLBACK_STEM}\\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\.json$`,
14707
+ "i"
14708
+ );
14699
14709
  }
14700
14710
  });
14701
14711
 
@@ -14711,20 +14721,15 @@ var init_gatekeeper_role = __esm({
14711
14721
  init_review_submission();
14712
14722
  init_gatekeeper_output();
14713
14723
  init_role_submission_declarations();
14724
+ init_terminal();
14714
14725
  init_run_terminal_artifacts();
14715
14726
  init_unknown_value();
14727
+ init_serialize_thrown_value();
14716
14728
  init_submission_errors();
14717
14729
  GATEKEEPER_TOOL_SPEC = roleSubmissionDeclaration("gatekeeper");
14718
14730
  }
14719
14731
  });
14720
14732
 
14721
- // src/public-cli/terminal.ts
14722
- var init_terminal = __esm({
14723
- "src/public-cli/terminal.ts"() {
14724
- "use strict";
14725
- }
14726
- });
14727
-
14728
14733
  // src/host-descriptions.ts
14729
14734
  var HOST_DESCRIPTIONS, HEADLESS_HOST_DESCRIPTIONS;
14730
14735
  var init_host_descriptions = __esm({
@@ -16057,7 +16062,7 @@ var init_option_definitions = __esm({
16057
16062
  },
16058
16063
  notary: {
16059
16064
  command: "notary",
16060
- summary: "Direct Notary document check (quote fidelity + ticket alignment); zero prompt/attachment.",
16065
+ summary: "Direct Notary summons; duties: souls/notary.md. Zero prompt/attachment.",
16061
16066
  usage: ["ak-role notary --source-run <runId@role|path> [options]"],
16062
16067
  examples: [
16063
16068
  "ak-role notary --source-run 01a034f1-75bf-71a6-bcf5-d1299145b1a5@judge",
@@ -16220,8 +16225,6 @@ var init_run_lifecycle = __esm({
16220
16225
  init_role_run_placement();
16221
16226
  init_run_ticket_number();
16222
16227
  init_cli_errors();
16223
- init_typed_provider_http();
16224
- init_typed_provider_http();
16225
16228
  init_collector_config();
16226
16229
  init_role_run_relocation();
16227
16230
  init_engine_material();
@@ -59,7 +59,6 @@ const PUBLIC_ROLE_RECORDS = [
59
59
  outputTool: JUDGE_OUTPUT_TOOL_NAME,
60
60
  auditTool: JUDGE_AUDIT_TOOL_NAME,
61
61
  settlement: "sealed",
62
- runnerFailure: "engine-detour-known-first",
63
62
  /** Factory board: court until another station has started, then marshal. */
64
63
  boardPlacement: "judge-history",
65
64
  /** Navigator subject is the public admitted instruction when the run is bound. */
@@ -177,7 +176,6 @@ const PUBLIC_ROLE_RECORDS = [
177
176
  },
178
177
  /** Frozen base/lens/authority become the initial prompt; instruction follows. */
179
178
  transportPrompt: "skill-args",
180
- runnerFailure: "engine-detour-record-first",
181
179
  boardPlacement: "marshal",
182
180
  activationFlags: [
183
181
  { field: "baseRevision", flag: "ak-review-base" },
@@ -272,10 +270,7 @@ const PUBLIC_ROLE_RECORDS = [
272
270
  /** Method-material load failure keeps the activation cause (not reviewer/fixer). */
273
271
  phases: [null],
274
272
  outputTool: MERGER_OUTPUT_TOOL_NAME,
275
- settlement: "residual",
276
- /** #836: residual scan stays on the whole host session. */
277
- residualScan: "session",
278
- residualTool: MERGER_OUTPUT_TOOL_NAME,
273
+ settlement: "sealed",
279
274
  artifactFace: {
280
275
  evidenceRole: true,
281
276
  leaves: [
@@ -452,7 +447,6 @@ const PUBLIC_ROLE_RECORDS = [
452
447
  phases: [null],
453
448
  outputTool: GATEKEEPER_OUTPUT_TOOL_NAME,
454
449
  settlement: "accepted",
455
- runnerFailure: "engine-detour-record-first",
456
450
  activationStage: "load-and-install",
457
451
  // Province materials; officers reuse their own public records below.
458
452
  sessionMaterials: ["CLAUDE.md", "souls/gatekeeper.md", "souls/quality-law.md", "souls/gate-output-guide.md"]
@@ -467,7 +461,6 @@ const PUBLIC_ROLE_RECORDS = [
467
461
  phases: [null],
468
462
  outputTool: NAVIGATOR_OUTPUT_TOOL_NAME,
469
463
  settlement: "accepted",
470
- runnerFailure: "engine-detour-record-first",
471
464
  activationStage: "load-and-install",
472
465
  sessionMaterials: ["CLAUDE.md", "souls/navigator.md"]
473
466
  },
@@ -490,7 +483,6 @@ const PUBLIC_ROLE_RECORDS = [
490
483
  settlement: "accepted",
491
484
  /** Court reask replaces the initial prompt. */
492
485
  reaskPrompt: true,
493
- runnerFailure: "engine-detour-record-first",
494
486
  gateStageLabel: "\u5BA1\u5211\u9662",
495
487
  /** Gate summon binds --subject judge and --source-run. */
496
488
  gateSummon: "subject-source",
@@ -14,6 +14,8 @@ import { ExplicitInternalActivationError } from "../host-contracts.js";
14
14
  import { applyEngineChildEnv, ENGINE_MODEL_FLAG_NAME, normalizeEngineName } from "../engine-detour.js";
15
15
  import { projectActivationFlags } from "../role-activation-flags.js";
16
16
  import { encodeUserDialogueStdin } from "../user-dialogue-stdin.js";
17
+ import { projectThrownFailureLeaf, retainPackageFault } from "../public-cli/settlement.js";
18
+ import { describeErrorIdentity } from "../public-cli/run-lifecycle.js";
17
19
  import { RECEIPT_DELIVERY_LIMIT_ENV } from "../receipt-delivery-policy.js";
18
20
  /** Package-relative Internal role entrypoint (ADR 0052; same path as public-cli registry). */
19
21
  const INTERNAL_ROLE_ENTRYPOINT_RELATIVE = "extensions/role-runtime.ts";
@@ -191,7 +193,6 @@ export function createDefaultPiSpawnRunner(options) {
191
193
  // fall back to rejecting on `error` if `close` never fires (e.g. spawn
192
194
  // never succeeded so no `close` event will arrive).
193
195
  let hasSpawned = false;
194
- let executionError;
195
196
  const armTimeoutAfterChildReady = () => {
196
197
  if (spawnOptions.timeoutMs === undefined)
197
198
  return;
@@ -207,6 +208,8 @@ export function createDefaultPiSpawnRunner(options) {
207
208
  child.kill("SIGTERM");
208
209
  };
209
210
  parentSignal?.addEventListener("abort", terminateForParentAbort, { once: true });
211
+ const packageErrors = [];
212
+ let identityFailure;
210
213
  let identityRecorded = Promise.resolve();
211
214
  child.once("spawn", () => {
212
215
  hasSpawned = true;
@@ -217,7 +220,10 @@ export function createDefaultPiSpawnRunner(options) {
217
220
  if (typeof runDirectory === "string" &&
218
221
  runDirectory !== "" &&
219
222
  options.recordLaunchedPiIdentity !== undefined) {
220
- identityRecorded = options.recordLaunchedPiIdentity(runDirectory, piIdentity);
223
+ identityRecorded = Promise.resolve().then(() => options.recordLaunchedPiIdentity(runDirectory, piIdentity)).catch((error) => {
224
+ packageErrors.push(error);
225
+ identityFailure = projectThrownFailureLeaf(error);
226
+ });
221
227
  }
222
228
  });
223
229
  child.stderr.setEncoding("utf8").on("data", (chunk) => {
@@ -227,8 +233,10 @@ export function createDefaultPiSpawnRunner(options) {
227
233
  // A pre-spawn error has no child lifecycle to close. After spawn,
228
234
  // retain the execution error and let the mandatory close event own
229
235
  // cleanup and the single settlement.
230
- if (settled || hasSpawned) {
231
- executionError = error;
236
+ if (settled)
237
+ return;
238
+ if (hasSpawned) {
239
+ packageErrors.push(error);
232
240
  return;
233
241
  }
234
242
  if (timer !== undefined)
@@ -237,33 +245,43 @@ export function createDefaultPiSpawnRunner(options) {
237
245
  settled = true;
238
246
  reject(error);
239
247
  });
240
- child.on("close", (code) => {
248
+ // `close` carries (code, signal): a signal-killed child reports code null.
249
+ // Both travel out so a signal death is never read as a clean exit.
250
+ child.on("close", (code, closeSignal) => {
241
251
  if (timer !== undefined)
242
252
  clearTimeout(timer);
243
253
  parentSignal?.removeEventListener("abort", terminateForParentAbort);
244
- void identityRecorded.then(() => {
254
+ void (async () => {
245
255
  if (settled)
246
256
  return;
247
257
  settled = true;
248
- if (executionError !== undefined) {
249
- reject(executionError);
250
- return;
251
- }
252
- if (stdinDeliveryError !== undefined) {
253
- reject(stdinDeliveryError);
254
- return;
258
+ await identityRecorded;
259
+ if (stdinDeliveryError !== undefined)
260
+ packageErrors.push(stdinDeliveryError);
261
+ for (const error of packageErrors) {
262
+ const diagnostic = `Pi transport handling failed beside host terminal: ${describeErrorIdentity(error)}`;
263
+ const runDirectory = spawnOptions.env.AK_ROLE_RUN_DIR;
264
+ if (runDirectory !== undefined) {
265
+ await retainPackageFault({ runDirectory, diagnostic, error });
266
+ }
267
+ else {
268
+ // Bare runner callers have no admitted artifact directory.
269
+ try {
270
+ process.stderr.write(`${diagnostic}\n`);
271
+ }
272
+ catch { /* Best-effort presentation beside the already closed child. */ }
273
+ }
255
274
  }
256
275
  resolveResult({
257
276
  code,
258
277
  stderr,
259
278
  timedOut,
279
+ ...(closeSignal === null ? {} : { signal: closeSignal }),
280
+ ...(code === 0 && !timedOut && closeSignal === null && identityFailure !== undefined
281
+ ? { knownFailure: identityFailure }
282
+ : {}),
260
283
  });
261
- }, (error) => {
262
- if (settled)
263
- return;
264
- settled = true;
265
- reject(error);
266
- });
284
+ })();
267
285
  });
268
286
  });
269
287
  };
@@ -13,45 +13,46 @@
13
13
  */
14
14
  import { constants as fsConstants } from "node:fs";
15
15
  import { randomUUID } from "node:crypto";
16
- import { lstat, mkdir, open } from "node:fs/promises";
16
+ import { open } from "node:fs/promises";
17
17
  import { join } from "node:path";
18
- import { roleRunArtifactsDirectory } from "../role-run-placement.js";
19
- import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunResumable, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
18
+ import { AUTO_RESUME_LIMIT, describeErrorIdentity, acquireRunWriterLease, markRunTerminal, RunWriterLeaseHeldError, } from "./run-lifecycle.js";
20
19
  import { parseAutoResumeLimit } from "./config.js";
21
20
  import { processCancelSignalName } from "./process-cancel.js";
22
21
  import { isLawfulTypedTerminalOutcome, formatTerminalResult } from "./terminal.js";
23
- import { attachRecordedSubmissions, presentFailureTerminal, resolveControlledFailureResumeObservation, } from "./settlement.js";
22
+ import { attachRecordedSubmissions, ensureRealArtifactsDirectory, presentFailureTerminal, retainPackageFault, } from "./settlement.js";
23
+ export { ensureRealArtifactsDirectory };
24
24
  import { serializeThrownValue } from "../serialize-thrown-value.js";
25
- import { errorText, isEnoent } from "../unknown-value.js";
25
+ import { errorText } from "../unknown-value.js";
26
26
  const dummyIo = { stdout: () => { }, stderr: () => { } };
27
27
  /**
28
28
  * Persist run-state after a host-turn result, outside the retried dispatch try.
29
- * Lawful settlement always seals terminal — a typed 429 observation must not
30
- * win (#416 成功即停). 429 only marks resumable on the controlled-failure path.
31
- * Write failure must escape the retried dispatch try instead of becoming a
32
- * synthetic host-turn terminal.
29
+ * The host outcome already decided the terminal. This write only seals the run.
33
30
  */
34
- export async function persistReturnedRunState(admitted, options) {
35
- if (options?.lawful === true) {
36
- await markRunTerminal(admitted.runDirectory);
37
- return;
38
- }
39
- const resumeObservation = await resolveControlledFailureResumeObservation({
40
- runDirectory: admitted.runDirectory,
41
- });
42
- const typedHttp429 = resumeObservation.typedHttp429;
43
- if (admitted.principal !== undefined && typedHttp429 !== undefined) {
44
- await markRunResumable(admitted.runDirectory, typedHttp429);
45
- return;
46
- }
31
+ export async function persistReturnedRunState(admitted) {
47
32
  await markRunTerminal(admitted.runDirectory);
48
33
  }
49
- function presentTerminal(terminal, io) {
50
- if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
51
- presentFailureTerminal(terminal, io);
34
+ export async function presentTerminal(terminal, io, runDirectory, held) {
35
+ try {
36
+ if (held !== undefined) {
37
+ for (const value of held)
38
+ io.stdout(value);
39
+ }
40
+ else if (terminal.roleOutcome.kind === "failure" || terminal.roleOutcome.kind === "no_receipt") {
41
+ presentFailureTerminal(terminal, io);
42
+ }
43
+ else {
44
+ io.stdout(formatTerminalResult(terminal));
45
+ }
52
46
  }
53
- else {
54
- io.stdout(formatTerminalResult(terminal));
47
+ catch (error) {
48
+ // Presentation consumes a settled report; it never reopens dispatch or
49
+ // replaces the host's cause, identity or open details.
50
+ await retainPackageFault({
51
+ runDirectory,
52
+ diagnostic: `terminal presentation failed beside host terminal: ${describeErrorIdentity(error)}`,
53
+ error,
54
+ stderr: (text) => io.stderr(text),
55
+ });
55
56
  }
56
57
  }
57
58
  /**
@@ -89,39 +90,6 @@ export class TurnDispatchedFailure extends Error {
89
90
  }
90
91
  /** Session custom-entry type carrying the pointer to one dispatch error file. */
91
92
  export const DISPATCH_ERROR_RETENTION_ENTRY_TYPE = "ak_run_dispatch_error_retention";
92
- /**
93
- * #182-A hardened path identity, mirrored from settlement.ts's
94
- * ensureAuditEvidenceDirectory: a planted symlink at the run directory or the
95
- * artifacts path must not receive a durable run artifact (O_NOFOLLOW only
96
- * protects the final file name; recursive mkdir would accept a symlinked
97
- * parent). Fails loudly with the true cause instead. Shared by every caller
98
- * that needs a durable run-artifacts write independent of session/dossier
99
- * health — dispatch-error retention here, and post-admission's best-effort
100
- * cleanup diagnostic (#840 bounce class 2).
101
- */
102
- export async function ensureRealArtifactsDirectory(runDirectory) {
103
- const runStat = await lstat(runDirectory);
104
- if (runStat.isSymbolicLink() || !runStat.isDirectory()) {
105
- throw new Error("run artifact retention: run directory is not a real directory");
106
- }
107
- const artifactsDir = roleRunArtifactsDirectory(runDirectory);
108
- try {
109
- const existing = await lstat(artifactsDir);
110
- if (existing.isSymbolicLink() || !existing.isDirectory()) {
111
- throw new Error("run artifact retention: artifacts path is not a real directory");
112
- }
113
- }
114
- catch (error) {
115
- if (!isEnoent(error))
116
- throw error;
117
- await mkdir(artifactsDir, { recursive: true });
118
- const created = await lstat(artifactsDir);
119
- if (created.isSymbolicLink() || !created.isDirectory()) {
120
- throw new Error("run artifact retention: artifacts directory is not a real directory");
121
- }
122
- }
123
- return artifactsDir;
124
- }
125
93
  /** Cycle- and bigint-safe JSON replacer so serialization itself cannot drop data. */
126
94
  function jsonSafeReplacer() {
127
95
  const seen = new WeakSet();
@@ -406,12 +374,7 @@ export async function runWithAutoResumeLoop(options) {
406
374
  } }
407
375
  : terminal;
408
376
  if (complete !== undefined) {
409
- if (isLawfulTypedTerminalOutcome(complete.roleOutcome)) {
410
- options.io.stdout(formatTerminalResult(complete));
411
- }
412
- else {
413
- presentTerminal(complete, options.io);
414
- }
377
+ await presentTerminal(complete, options.io, options.admitted.runDirectory);
415
378
  }
416
379
  return stoppedResult === undefined
417
380
  ? { exitCode: 1, terminal: complete }
@@ -1,24 +1,11 @@
1
1
  import { readableGateItem } from "../readable-gate-item.js";
2
+ import { currentReplyRows } from "./terminal.js";
2
3
  import { isRecord } from "../unknown-value.js";
3
4
  function isEscalatePayload(payload) {
4
5
  if (!isRecord(payload))
5
6
  return false;
6
7
  return (payload.status === "escalate");
7
8
  }
8
- /**
9
- * Rows a parent may read from a nested diarist terminal (#836 / #953).
10
- * Accepted/audit use role result payloads; failure history is submissions only.
11
- */
12
- function courtDiaristPayloadRows(roleOutcome, submissions) {
13
- if (roleOutcome === undefined)
14
- return submissions ?? [];
15
- if (roleOutcome.kind === "accepted" || roleOutcome.kind === "audit_escalation") {
16
- return roleOutcome.payloads ?? [];
17
- }
18
- if (roleOutcome.kind === "failure")
19
- return submissions ?? [];
20
- return [];
21
- }
22
9
  /**
23
10
  * Parent diagnostic for court-diarist escalate (#953 / 失败诚实 / 传话).
24
11
  * Relays diarist escalate payload(s) via readableGateItem — never invents
@@ -29,8 +16,8 @@ function courtDiaristPayloadRows(roleOutcome, submissions) {
29
16
  * no courtAttempt sole-filter on diarist). Same law as secretariat parent face:
30
17
  * every escalate receipt as-is, last = currentConclusion.
31
18
  */
32
- function courtDiaristEscalateDiagnostic(roleOutcome, submissions) {
33
- const payloads = courtDiaristPayloadRows(roleOutcome, submissions);
19
+ function courtDiaristEscalateDiagnostic(terminal) {
20
+ const payloads = currentReplyRows(terminal);
34
21
  const escalatePayloads = [];
35
22
  for (const payload of payloads) {
36
23
  if (isEscalatePayload(payload))
@@ -100,14 +87,13 @@ export async function invokeCourtDiarist(input, env, io) {
100
87
  : { hostAdapters: env.hostAdapters }),
101
88
  });
102
89
  const roleOutcome = result.terminal?.roleOutcome;
103
- const submissions = result.terminal?.submissions;
104
90
  // Escalate routing is a boolean over the preserved sequence (#881). Reasons and
105
91
  // payload bodies stay on roleOutcome — never rewritten into a sole identity reason.
106
92
  if (latestPayloadEscalated(roleOutcome)) {
107
93
  return {
108
94
  identity: {
109
95
  kind: "escalate",
110
- diagnostic: courtDiaristEscalateDiagnostic(roleOutcome, submissions),
96
+ diagnostic: courtDiaristEscalateDiagnostic(result.terminal),
111
97
  },
112
98
  ...(result.admitted === undefined ? {} : { admitted: result.admitted }),
113
99
  ...(result.terminal === undefined ? {} : { terminal: result.terminal }),