@akagilnc/pi-workflow-roles 0.1.4782 → 0.1.4802

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 (35) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/dist/acp-host/production-host.js +104 -215
  4. package/dist/headless-host/production-host.js +149 -229
  5. package/dist/migrate-book-topology.js +1 -1
  6. package/dist/pi/role-turn-host.js +1 -1
  7. package/dist/public-cli/auto-resume.js +9 -15
  8. package/dist/public-cli/countersign-run.js +75 -87
  9. package/dist/public-cli/diarist-run.js +6 -26
  10. package/dist/public-cli/inspector-run.js +1 -1
  11. package/dist/public-cli/main.js +64 -177
  12. package/dist/public-cli/notary-run.js +0 -3
  13. package/dist/public-cli/option-definitions.js +1 -1
  14. package/dist/public-cli/post-admission.js +35 -65
  15. package/dist/public-cli/reviewer-run.js +1 -1
  16. package/dist/public-cli/run-lifecycle.js +14 -46
  17. package/dist/public-cli/seat-ticket-binding.js +11 -25
  18. package/dist/public-role-summons.js +2 -0
  19. package/package.json +1 -1
  20. package/souls/coder.md +1 -2
  21. package/src/headless-host/role-turn-host.ts +49 -12
  22. package/src/pi/role-turn-host.ts +1 -1
  23. package/src/public-cli/auto-resume.ts +19 -18
  24. package/src/public-cli/countersign-run.ts +76 -129
  25. package/src/public-cli/diarist-run.ts +6 -33
  26. package/src/public-cli/inspector-run.ts +1 -1
  27. package/src/public-cli/invocation.ts +5 -0
  28. package/src/public-cli/notary-run.ts +3 -7
  29. package/src/public-cli/option-definitions.ts +1 -1
  30. package/src/public-cli/post-admission.ts +37 -69
  31. package/src/public-cli/reviewer-run.ts +1 -1
  32. package/src/public-cli/run-lifecycle.ts +18 -66
  33. package/src/public-cli/seat-ticket-binding.ts +12 -28
  34. package/src/public-role-summons.ts +15 -6
  35. package/src/role-runtime.ts +9 -0
@@ -417,7 +417,7 @@ function normalizeReviewComment(raw) {
417
417
  function createGhApiRunner(options = {}) {
418
418
  const spawnImpl = options.spawnImpl ?? spawn;
419
419
  return async (args, runOptions = {}) => {
420
- return await new Promise((resolve21, reject) => {
420
+ return await new Promise((resolve22, reject) => {
421
421
  const signal = runOptions.signal;
422
422
  if (signal?.aborted) {
423
423
  reject(signal.reason ?? new Error("aborted"));
@@ -490,11 +490,11 @@ function createGhApiRunner(options = {}) {
490
490
  const value = line2.slice(idx + 1).trim();
491
491
  headers[name] = value;
492
492
  }
493
- resolve21({ status, headers, bodyText });
493
+ resolve22({ status, headers, bodyText });
494
494
  return;
495
495
  }
496
496
  if (code === 0) {
497
- resolve21({ status: 200, headers: {}, bodyText: stdout });
497
+ resolve22({ status: 200, headers: {}, bodyText: stdout });
498
498
  return;
499
499
  }
500
500
  const failure2 = new Error(
@@ -2312,7 +2312,7 @@ async function spawnEngineDetourOnce(input) {
2312
2312
  }
2313
2313
  const command = input.argv[0];
2314
2314
  const args = input.argv.slice(1);
2315
- return await new Promise((resolve21, reject) => {
2315
+ return await new Promise((resolve22, reject) => {
2316
2316
  let settled = false;
2317
2317
  const signal = input.signal;
2318
2318
  const child = spawn2(command, args, {
@@ -2342,7 +2342,7 @@ async function spawnEngineDetourOnce(input) {
2342
2342
  if (signal !== void 0) {
2343
2343
  signal.removeEventListener("abort", onAbort);
2344
2344
  }
2345
- resolve21(result);
2345
+ resolve22(result);
2346
2346
  };
2347
2347
  const onAbort = () => {
2348
2348
  fail4(signal !== void 0 ? abortReasonError(signal) : new Error("aborted"));
@@ -3926,7 +3926,7 @@ function piUserDialogueBody(request) {
3926
3926
  const _exhaustive = request.continuation;
3927
3927
  return _exhaustive;
3928
3928
  })();
3929
- return request.continuation.kind === "resume" && request.activation.role === "reviewer" ? rawPrompt : applyPiNativeSkillInvocation(request.methods, rawPrompt);
3929
+ return request.continuation.kind === "resume" ? rawPrompt : applyPiNativeSkillInvocation(request.methods, rawPrompt);
3930
3930
  }
3931
3931
  async function resolveSelectedPi(command, cwd, env) {
3932
3932
  const searchPath = env.PATH ?? (platform === "win32" ? process.env.PATH ?? "" : "/usr/bin:/bin");
@@ -5047,7 +5047,6 @@ __export(run_lifecycle_exports, {
5047
5047
  acquireRunWriterLease: () => acquireRunWriterLease,
5048
5048
  autopsyWriterLock: () => autopsyWriterLock,
5049
5049
  buildAutoResumeContinuationPrompt: () => buildAutoResumeContinuationPrompt,
5050
- buildResumeContinuationPrompt: () => buildResumeContinuationPrompt,
5051
5050
  clearCurrentCourt: () => clearCurrentCourt,
5052
5051
  clearTypedProviderHttpObservation: () => clearTypedProviderHttpObservation,
5053
5052
  describeErrorIdentity: () => describeErrorIdentity,
@@ -5084,32 +5083,18 @@ __export(run_lifecycle_exports, {
5084
5083
  recordCurrentCourt: () => recordCurrentCourt,
5085
5084
  recordTypedProviderHttpStatus: () => recordTypedProviderHttpStatus,
5086
5085
  renderResumeCommand: () => renderResumeCommand,
5087
- selectResumeContinuationPrompt: () => selectResumeContinuationPrompt,
5088
5086
  writeRoleRunState: () => writeRoleRunState
5089
5087
  });
5090
5088
  import { chmod, lstat as lstat2, open, readdir as readdir4, readFile as readFile10, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
5091
5089
  import { basename as basename5, join as join14 } from "node:path";
5092
- function selectResumeContinuationPrompt(message, engineMaterial) {
5093
- const lines = message !== void 0 ? [message] : [];
5094
- return appendEngineSessionMaterial(lines, engineMaterial).join("\n");
5095
- }
5096
- function buildResumeContinuationPrompt(options) {
5097
- return selectResumeContinuationPrompt(
5098
- options.message,
5099
- engineSessionMaterialFromOptions({
5100
- packageRoot: options.packageRoot,
5101
- ...pickEngineAxis(options)
5102
- })
5103
- );
5104
- }
5105
5090
  function buildAutoResumeContinuationPrompt(options) {
5106
- return selectResumeContinuationPrompt(
5107
- RESUME_TRANSPORT_ENVELOPE,
5091
+ return appendEngineSessionMaterial(
5092
+ [RESUME_TRANSPORT_ENVELOPE],
5108
5093
  engineSessionMaterialFromOptions({
5109
5094
  packageRoot: options.packageRoot,
5110
5095
  ...pickEngineAxis(options)
5111
5096
  })
5112
- );
5097
+ ).join("\n");
5113
5098
  }
5114
5099
  function isV1ResumableProvider(provider) {
5115
5100
  return V1_RESUMABLE_PROVIDERS.includes(provider);
@@ -5654,7 +5639,7 @@ async function runHasFormedSessionPrincipal(runDirectory) {
5654
5639
  }
5655
5640
  }
5656
5641
  async function findLatestRunIdForSeatTicket(input) {
5657
- if (input.ticketNumber === void 0 && input.parentRunPath === void 0) {
5642
+ if (input.parentRunPath.trim() === "") {
5658
5643
  return void 0;
5659
5644
  }
5660
5645
  const ledgerHome = resolveActivationLedgerHome(input.home);
@@ -5673,13 +5658,8 @@ async function findLatestRunIdForSeatTicket(input) {
5673
5658
  if (!entry.endsWith(suffix)) continue;
5674
5659
  const runId = entry.slice(0, entry.length - suffix.length);
5675
5660
  if (runId.length === 0) continue;
5676
- if (input.parentRunPath !== void 0) {
5677
- const parentPath = await readRunParentPath(runDirectory);
5678
- if (parentPath !== input.parentRunPath) continue;
5679
- } else if (input.ticketNumber !== void 0) {
5680
- const ticketNumber = await readBoardTicketNumber(runDirectory);
5681
- if (ticketNumber !== input.ticketNumber) continue;
5682
- }
5661
+ const parentPath = await readRunParentPath(runDirectory);
5662
+ if (parentPath !== input.parentRunPath) continue;
5683
5663
  if (!await runHasFormedSessionPrincipal(runDirectory)) continue;
5684
5664
  if (best === void 0 || runId > best) best = runId;
5685
5665
  }
@@ -7542,7 +7522,7 @@ var init_option_definitions = __esm({
7542
7522
  },
7543
7523
  resume: {
7544
7524
  command: "resume",
7545
- summary: "Resume a role run under the live seat table (model/host/engine); session principal must still exist. [message] applies only to seats that accept caller instruction; Notary/\u7B26\u5B9D\u90CE must omit message and derives evidence from the existing source-run/dossier binding. Global --model/--thinking/--host/--engine must be placed before <runId> (either before `resume` or between `resume` and <runId>); the one argv after <runId> is the opaque message, not a flag position (#471).",
7525
+ summary: "Resume a role run under the live seat table (model/host/engine); session principal must still exist. Every callable seat, including Notary/\u7B26\u5B9D\u90CE, accepts the optional caller message and passes it through unchanged as the continuation prompt. Notary explicit new still accepts only its source-run locator, not a caller prompt. Global --model/--thinking/--host/--engine must be placed before <runId> (either before `resume` or between `resume` and <runId>); the one argv after <runId> is the opaque message, not a flag position (#471).",
7546
7526
  usage: ["ak-role resume <runId> [message]"],
7547
7527
  examples: [
7548
7528
  "ak-role resume 01abc\u2026",
@@ -12139,12 +12119,12 @@ function createSystemCollectorClock() {
12139
12119
  return {
12140
12120
  wallNow: () => /* @__PURE__ */ new Date(),
12141
12121
  monoNow: () => Number(process.hrtime.bigint() - start) / 1e6,
12142
- sleep: (ms, signal) => new Promise((resolve21, reject) => {
12122
+ sleep: (ms, signal) => new Promise((resolve22, reject) => {
12143
12123
  if (signal?.aborted) {
12144
12124
  reject(signal.reason ?? new Error("aborted"));
12145
12125
  return;
12146
12126
  }
12147
- const timer = setTimeout(resolve21, ms);
12127
+ const timer = setTimeout(resolve22, ms);
12148
12128
  const onAbort = () => {
12149
12129
  clearTimeout(timer);
12150
12130
  reject(signal?.reason ?? new Error("aborted"));
@@ -16499,10 +16479,10 @@ function presentFailureTerminal(terminal, io) {
16499
16479
  }
16500
16480
  function defaultNavigatorGraceSleep() {
16501
16481
  let timer;
16502
- const sleep = ((ms) => new Promise((resolve21) => {
16482
+ const sleep = ((ms) => new Promise((resolve22) => {
16503
16483
  timer = setTimeout(() => {
16504
16484
  timer = void 0;
16505
- resolve21();
16485
+ resolve22();
16506
16486
  }, ms);
16507
16487
  }));
16508
16488
  sleep.cancel = () => {
@@ -16514,7 +16494,7 @@ function defaultNavigatorGraceSleep() {
16514
16494
  return sleep;
16515
16495
  }
16516
16496
  function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep = defaultNavigatorGraceSleep()) {
16517
- return new Promise((resolve21, reject) => {
16497
+ return new Promise((resolve22, reject) => {
16518
16498
  let settled = false;
16519
16499
  const finish = (action) => {
16520
16500
  if (settled) return;
@@ -16523,11 +16503,11 @@ function raceNavigatorGrace(work, graceMs = NAVIGATOR_POST_ROLE_GRACE_MS, sleep
16523
16503
  action();
16524
16504
  };
16525
16505
  void work.then(
16526
- (value) => finish(() => resolve21({ status: "done", value })),
16506
+ (value) => finish(() => resolve22({ status: "done", value })),
16527
16507
  (error) => finish(() => reject(error))
16528
16508
  );
16529
16509
  void sleep(graceMs).then(() => {
16530
- finish(() => resolve21({ status: "timeout" }));
16510
+ finish(() => resolve22({ status: "timeout" }));
16531
16511
  });
16532
16512
  });
16533
16513
  }
@@ -16598,15 +16578,12 @@ var init_settlement = __esm({
16598
16578
  // src/public-cli/seat-ticket-binding.ts
16599
16579
  async function tryResumeSameTicketSeatRun(input) {
16600
16580
  if (input.freshSummons === true) return void 0;
16601
- if (input.parentRunPath === void 0 && input.ticketNumber === void 0) {
16602
- return void 0;
16603
- }
16581
+ if (input.parentRunPath.trim() === "") return void 0;
16604
16582
  const previousRunId = await findLatestRunIdForSeatTicket({
16605
16583
  home: input.home,
16606
16584
  bookKey: resolveBookKeyFromGit(input.projectRoot),
16607
16585
  role: input.role,
16608
- ...input.parentRunPath === void 0 ? {} : { parentRunPath: input.parentRunPath },
16609
- ...input.ticketNumber === void 0 ? {} : { ticketNumber: input.ticketNumber }
16586
+ parentRunPath: input.parentRunPath
16610
16587
  });
16611
16588
  if (previousRunId === void 0) return void 0;
16612
16589
  return await input.resume(previousRunId, input.summons);
@@ -17753,22 +17730,10 @@ async function runWithAutoResumeLoop(options) {
17753
17730
  let everyAttemptThrew = true;
17754
17731
  const retainedErrorFiles = [];
17755
17732
  while (true) {
17756
- let lease;
17757
- try {
17758
- lease = await acquireRunWriterLease(
17759
- options.admitted.runDirectory,
17760
- (diagnostic) => options.io.stderr(diagnostic)
17761
- );
17762
- } catch (error) {
17763
- if (error instanceof RunWriterLeaseHeldError) {
17764
- presentStructuralRejection(error, options.io);
17765
- return { exitCode: 2 };
17766
- }
17767
- throw error;
17768
- }
17769
17733
  let result;
17770
17734
  let turnStartedBeforeThrow = false;
17771
17735
  try {
17736
+ const lease = isFirst ? await acquireRunWriterLease(options.admitted.runDirectory) : void 0;
17772
17737
  result = await options.dispatch(currentPayload, lease, isFirst, dummyIo);
17773
17738
  } catch (error) {
17774
17739
  lastThrownError = error;
@@ -17902,8 +17867,8 @@ async function runWithAutoResumeLoop(options) {
17902
17867
  autoResumeAttempts++;
17903
17868
  if (result?.turnDispatched === true || turnStartedBeforeThrow) {
17904
17869
  currentPayload = options.buildResumePayload();
17870
+ isFirst = false;
17905
17871
  }
17906
- isFirst = false;
17907
17872
  }
17908
17873
  }
17909
17874
  var dummyIo, TurnDispatchedFailure, DISPATCH_ERROR_RETENTION_ENTRY_TYPE;
@@ -18393,7 +18358,7 @@ async function dispatchPostAdmissionTurn(input) {
18393
18358
  await recordBestEffortPostDispatchDiagnostic(
18394
18359
  admitted,
18395
18360
  env,
18396
- `current-court cleanup failed after accepted settlement (best-effort continue, self-heals on next resume): ${describeErrorIdentity(error)}`,
18361
+ `current-court cleanup failed after accepted settlement (best-effort continue): ${describeErrorIdentity(error)}`,
18397
18362
  io
18398
18363
  );
18399
18364
  }
@@ -18569,21 +18534,14 @@ function resumeTurnRequestProjectionOptions(admitted, request, env, summonsPrepa
18569
18534
  } else if (request.summons !== void 0) {
18570
18535
  prompt = request.message;
18571
18536
  } else {
18572
- prompt = buildResumeContinuationPrompt({
18573
- packageRoot: env.packageRoot,
18574
- ...pickEngineAxis(env),
18575
- message: request.message
18576
- });
18537
+ prompt = request.message;
18577
18538
  }
18578
18539
  } else if (summonsPrepared !== void 0) {
18579
18540
  prompt = officerDialogue ? summonsPrepared.instructionEmpty ? "" : summonsPrepared.instruction : buildInstructionTransportPrompt(summonsPrepared);
18580
18541
  } else if (request.summons !== void 0) {
18581
18542
  prompt = "";
18582
18543
  } else {
18583
- prompt = buildResumeContinuationPrompt({
18584
- packageRoot: env.packageRoot,
18585
- ...pickEngineAxis(env)
18586
- });
18544
+ prompt = "";
18587
18545
  }
18588
18546
  return {
18589
18547
  packageRoot: env.packageRoot,
@@ -18608,7 +18566,7 @@ async function dispatchAfterWriterLease(input) {
18608
18566
  handedOffToDispatch = true;
18609
18567
  return await input.dispatch(request);
18610
18568
  } finally {
18611
- if (!handedOffToDispatch) {
18569
+ if (!handedOffToDispatch && input.lease !== void 0) {
18612
18570
  await input.lease.release();
18613
18571
  }
18614
18572
  }
@@ -18661,20 +18619,11 @@ async function runPostAdmissionSeatResume(input) {
18661
18619
  let preparedCleanup;
18662
18620
  const buildRequestAfterLease = async () => {
18663
18621
  let openCourtAttemptId;
18664
- let admittedForBuild = loaded.admitted;
18622
+ const admittedForBuild = loaded.admitted;
18665
18623
  if (request.summons === void 0) {
18666
18624
  const openCourt = await readCurrentCourt(admittedForBuild.runDirectory);
18667
18625
  if (openCourt !== void 0) {
18668
18626
  openCourtAttemptId = openCourt.courtAttemptId;
18669
- request = {
18670
- runId: request.runId,
18671
- ...request.message === void 0 ? {} : { message: request.message },
18672
- ...openCourt.summons === void 0 ? {} : { summons: openCourt.summons }
18673
- };
18674
- if (openCourt.summons !== void 0) {
18675
- const reloaded = await input.load(request);
18676
- admittedForBuild = reloaded.admitted;
18677
- }
18678
18627
  }
18679
18628
  }
18680
18629
  if (request.summons !== void 0) {
@@ -18736,7 +18685,7 @@ async function runPostAdmissionSeatResume(input) {
18736
18685
  // Same as public manual resume: prior-court sealed acceptance is not a
18737
18686
  // redispatch brake (#833). New-court station-child turns still auto-resume.
18738
18687
  dispatch: async (payload, lease, _isFirst, attemptIo) => dispatchAfterWriterLease({
18739
- lease,
18688
+ ...lease === void 0 ? {} : { lease },
18740
18689
  build: async () => {
18741
18690
  if (payload.resumeTurn && firstTurn !== void 0) {
18742
18691
  return {
@@ -18760,7 +18709,7 @@ async function runPostAdmissionSeatResume(input) {
18760
18709
  env,
18761
18710
  io: attemptIo,
18762
18711
  request: turnRequest,
18763
- lease,
18712
+ ...lease === void 0 ? {} : { lease },
18764
18713
  adapters: stationAdapters,
18765
18714
  persistRunState: false,
18766
18715
  ...input.effectiveEngine === void 0 ? {} : { effectiveEngine: input.effectiveEngine }
@@ -18846,7 +18795,7 @@ async function runPostAdmissionResumable(input) {
18846
18795
  },
18847
18796
  io: attemptIo,
18848
18797
  request,
18849
- lease,
18798
+ ...lease === void 0 ? {} : { lease },
18850
18799
  adapters,
18851
18800
  persistRunState: false,
18852
18801
  // #600: every attempt (initial + auto-resume) writes seat engine when present.
@@ -19088,11 +19037,6 @@ async function runPublicNotaryResume(request, env, io) {
19088
19037
  env,
19089
19038
  io,
19090
19039
  load: async (effective) => {
19091
- if (effective.message !== void 0) {
19092
- throw new CliUsageError(
19093
- "notary rejects caller prompt/instruction; only zero caller-prompt continuation admitted"
19094
- );
19095
- }
19096
19040
  const loaded = await loadResumableNotaryRun(
19097
19041
  env.home,
19098
19042
  effective.runId,
@@ -19846,29 +19790,7 @@ async function runPublicDiarist(argv, env, io, parseDiaristArgv2) {
19846
19790
  }
19847
19791
  throw error;
19848
19792
  }
19849
- const projectRoot = parsed.project ?? env.cwd;
19850
19793
  const handoffTicket = env.boundTicketNumber;
19851
- if (typeof handoffTicket === "number" && Number.isSafeInteger(handoffTicket) && handoffTicket >= 1) {
19852
- const summons = {
19853
- instruction: parsed.instruction,
19854
- instructionEmpty: parsed.instruction.trim() === "",
19855
- attachmentPaths: parsed.attachmentPaths
19856
- };
19857
- const resumed = await tryResumeSameTicketSeatRun({
19858
- home: env.home,
19859
- projectRoot,
19860
- role: "diarist",
19861
- ticketNumber: handoffTicket,
19862
- freshSummons: env.freshSummons,
19863
- summons,
19864
- resume: (runId, materials) => runPublicDiaristResume(
19865
- { runId, ...materials === void 0 ? {} : { summons: materials } },
19866
- env,
19867
- io
19868
- )
19869
- });
19870
- if (resumed !== void 0) return resumed;
19871
- }
19872
19794
  let admitted;
19873
19795
  try {
19874
19796
  admitted = await admitDiaristInvocation({
@@ -19953,7 +19875,6 @@ var init_diarist_run = __esm({
19953
19875
  init_invocation();
19954
19876
  init_post_admission();
19955
19877
  init_run_lifecycle();
19956
- init_seat_ticket_binding();
19957
19878
  init_settlement();
19958
19879
  init_turn_request();
19959
19880
  init_run_ticket_number();
@@ -19969,6 +19890,7 @@ __export(countersign_run_exports, {
19969
19890
  runPublicCountersign: () => runPublicCountersign,
19970
19891
  runPublicCountersignResume: () => runPublicCountersignResume
19971
19892
  });
19893
+ import { resolve as resolve14 } from "node:path";
19972
19894
  function buildCountersignTurnRequest(admitted, options) {
19973
19895
  return projectRoleTurnRequest(
19974
19896
  admitted,
@@ -20163,6 +20085,28 @@ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
20163
20085
  throw error;
20164
20086
  }
20165
20087
  let admitted;
20088
+ const gateParentRunPath = typeof env.parentRunPath === "string" && env.parentRunPath.trim() !== "" ? env.parentRunPath : void 0;
20089
+ if (gateParentRunPath !== void 0) {
20090
+ const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction ?? parsed.instruction;
20091
+ const resumed = await tryResumeSameTicketSeatRun({
20092
+ home: env.home,
20093
+ projectRoot: resolve14(parsed.project ?? env.cwd),
20094
+ role: "countersign",
20095
+ parentRunPath: gateParentRunPath,
20096
+ freshSummons: env.freshSummons,
20097
+ summons: {
20098
+ sourceRunPath: gateParentRunPath,
20099
+ instruction: resumeInstruction,
20100
+ instructionEmpty: resumeInstruction.trim() === ""
20101
+ },
20102
+ resume: (runId, materials) => runPublicCountersignResume(
20103
+ { runId, ...materials === void 0 ? {} : { summons: materials } },
20104
+ env,
20105
+ io
20106
+ )
20107
+ });
20108
+ if (resumed !== void 0) return resumed;
20109
+ }
20166
20110
  try {
20167
20111
  admitted = await admitCountersignInvocation({
20168
20112
  home: env.home,
@@ -20206,7 +20150,7 @@ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
20206
20150
  instruction: parsed.instruction,
20207
20151
  projectRoot: admitted.projectRoot,
20208
20152
  failureLabel: "unbound summons",
20209
- // #969: parent durable ticket is the resume/bind key (ADR 0079).
20153
+ // #969: parent durable ticket is the bind key (not a resume lookup).
20210
20154
  ...env.boundTicketNumber === void 0 ? {} : { boundTicketNumber: env.boundTicketNumber }
20211
20155
  },
20212
20156
  env,
@@ -20262,59 +20206,13 @@ async function runPublicCountersign(argv, env, io, parseCountersignArgv2) {
20262
20206
  } else if (isSafePositiveTicketNumber(env.boundTicketNumber)) {
20263
20207
  typedTicket = env.boundTicketNumber;
20264
20208
  }
20265
- if (typedTicket !== void 0) {
20266
- const resumeInstruction = env.reviewReask ?? env.gateReviewInstruction ?? parsed.instruction;
20267
- const summons = {
20268
- instruction: resumeInstruction,
20269
- instructionEmpty: resumeInstruction.trim() === ""
20270
- };
20271
- const resumed = await tryResumeSameTicketSeatRun({
20272
- home: env.home,
20273
- projectRoot: admitted.projectRoot,
20274
- role: "countersign",
20275
- ticketNumber: typedTicket,
20276
- freshSummons: env.freshSummons,
20277
- summons,
20278
- resume: async (runId, materials) => {
20279
- const retained = await loadResumableCountersignRun(
20280
- env.home,
20281
- runId,
20282
- env.principalAuthority
20283
- );
20284
- if (retained.admitted === void 0) {
20285
- throw new Error(
20286
- `retained countersign run disappeared before resume: ${runId}`
20287
- );
20288
- }
20289
- const frozenPaths = (await freezePreparedAttachmentsIntoRun(
20290
- preparedAttachments,
20291
- retained.admitted.runDirectory,
20292
- `s-${Date.now().toString(36)}`
20293
- )).map((attachment) => attachment.frozenPath);
20294
- const preparedMaterials = {
20295
- ...materials ?? {},
20296
- ...frozenPaths.length === 0 ? {} : { attachmentPaths: frozenPaths }
20297
- };
20298
- return await runPublicCountersignResume(
20299
- {
20300
- runId,
20301
- summons: preparedMaterials
20302
- },
20303
- {
20304
- ...env,
20305
- ...typedCourtTicketNumbers === void 0 ? {} : { pendingCourtTicketNumbers: typedCourtTicketNumbers }
20306
- },
20307
- io
20308
- );
20309
- }
20310
- });
20311
- if (resumed !== void 0) {
20312
- return resumed;
20313
- }
20314
- }
20315
20209
  }
20316
20210
  await materializeAdmission();
20317
20211
  await markRunAdmitted(admitted, env.principalAuthority);
20212
+ if (gateParentRunPath !== void 0) {
20213
+ await persistAdmittedSourceRunPath(admitted, gateParentRunPath);
20214
+ admitted = { ...admitted, sourceRunPath: gateParentRunPath };
20215
+ }
20318
20216
  if (identityDiaristRan && typedTicket !== void 0) {
20319
20217
  try {
20320
20218
  await bindAdmittedTicketNumber(admitted, typedTicket);
@@ -20433,22 +20331,7 @@ async function runPublicCountersignResume(request, env, io) {
20433
20331
  )
20434
20332
  );
20435
20333
  },
20436
- adapters: countersignAdapters({
20437
- beforeDispatch: async (admitted) => {
20438
- if (admitted.courtTicketNumbersDamage !== void 0) {
20439
- throw new StationChildExhaustedError(
20440
- admitted.courtTicketNumbersDamage
20441
- );
20442
- }
20443
- if (env.pendingCourtTicketNumbers !== void 0) {
20444
- await bindCourtTicketNumbersOnAdmitted(
20445
- admitted,
20446
- env.pendingCourtTicketNumbers
20447
- );
20448
- }
20449
- await runCountersignCourtDiaristStation(admitted, env, io);
20450
- }
20451
- }),
20334
+ adapters: countersignAdapters(),
20452
20335
  ...env.engine === void 0 ? {} : { effectiveEngine: env.engine }
20453
20336
  });
20454
20337
  }
@@ -20476,7 +20359,7 @@ __export(reviewer_run_exports, {
20476
20359
  runPublicReviewer: () => runPublicReviewer,
20477
20360
  runPublicReviewerResume: () => runPublicReviewerResume
20478
20361
  });
20479
- import { resolve as resolve14 } from "node:path";
20362
+ import { resolve as resolve15 } from "node:path";
20480
20363
  function reviewerMethods(packageRoot) {
20481
20364
  return [{ kind: "skill", path: resolvePackagedMethodSkillPath(packageRoot, "ak-cross-m-review") }];
20482
20365
  }
@@ -20568,7 +20451,7 @@ async function runPublicReviewer(argv, env, io, parseReviewerArgv2) {
20568
20451
  // Do not substitute the resolved project path — relative --project must
20569
20452
  // not be re-resolved against a shifted cwd.
20570
20453
  cwd: env.cwd,
20571
- projectRoot: resolve14(parsed.project ?? env.cwd),
20454
+ projectRoot: resolve15(parsed.project ?? env.cwd),
20572
20455
  // Typed base from the public parse — precheck only; child argv stays verbatim.
20573
20456
  baseRevision: parsed.baseRevision,
20574
20457
  home: env.home,
@@ -20741,7 +20624,7 @@ async function runPublicReviewerResume(request, env, io) {
20741
20624
  ...sandbox.executionCwd === void 0 ? {} : { cwd: sandbox.executionCwd },
20742
20625
  continuation: {
20743
20626
  kind: "resume",
20744
- prompt: reviewerResumePrompt(activeEnv, effective.message)
20627
+ prompt: effective.message ?? ""
20745
20628
  }
20746
20629
  });
20747
20630
  },
@@ -21007,6 +20890,7 @@ async function summonPublicRole(options) {
21007
20890
  ...options.reviewReask === void 0 ? {} : { reviewReask: options.reviewReask },
21008
20891
  ...options.gateReviewInstruction === void 0 ? {} : { gateReviewInstruction: options.gateReviewInstruction },
21009
20892
  ...options.boundTicketNumber === void 0 ? {} : { boundTicketNumber: options.boundTicketNumber },
20893
+ ...options.parentRunPath === void 0 || options.parentRunPath.trim() === "" ? {} : { parentRunPath: options.parentRunPath },
21010
20894
  ...options.correlationId === void 0 || options.correlationId.trim() === "" ? {} : { correlationId: options.correlationId },
21011
20895
  ...options.createRunId === void 0 ? {} : { createRunId: options.createRunId },
21012
20896
  ...options.executionCwd === void 0 ? {} : { executionCwd: options.executionCwd }
@@ -21509,6 +21393,7 @@ async function summonGateOfficer(options) {
21509
21393
  argv: ["--project", options.cwd, "--", instruction],
21510
21394
  ...common,
21511
21395
  correlationId,
21396
+ parentRunPath: options.sourceRunDirectory,
21512
21397
  ...parentTicket === void 0 ? {} : { boundTicketNumber: parentTicket }
21513
21398
  });
21514
21399
  }
@@ -21750,7 +21635,7 @@ import {
21750
21635
  statSync as statSync3,
21751
21636
  writeFileSync as writeFileSync2
21752
21637
  } from "node:fs";
21753
- import { dirname as dirname18, resolve as resolve15, join as join35 } from "node:path";
21638
+ import { dirname as dirname18, resolve as resolve16, join as join35 } from "node:path";
21754
21639
  import { StringDecoder } from "node:string_decoder";
21755
21640
  import { SessionManager } from "@earendil-works/pi-coding-agent";
21756
21641
  function readCurrentSession(sessionDir) {
@@ -21858,8 +21743,8 @@ function parseSessionHeaderLine(line2) {
21858
21743
  };
21859
21744
  }
21860
21745
  function mostRecentNavigatorSessionFile(sessionDir, cwd) {
21861
- const absoluteSessionDir = resolve15(sessionDir);
21862
- const absoluteCwd = resolve15(cwd);
21746
+ const absoluteSessionDir = resolve16(sessionDir);
21747
+ const absoluteCwd = resolve16(cwd);
21863
21748
  let names;
21864
21749
  try {
21865
21750
  names = readdirSync2(absoluteSessionDir);
@@ -21894,7 +21779,7 @@ function mostRecentNavigatorSessionFile(sessionDir, cwd) {
21894
21779
  }
21895
21780
  if (header === void 0) continue;
21896
21781
  if (typeof header.cwd !== "string" || header.cwd.length === 0) continue;
21897
- if (resolve15(header.cwd) !== absoluteCwd) continue;
21782
+ if (resolve16(header.cwd) !== absoluteCwd) continue;
21898
21783
  matched.push({ path: filePath, mtimeMs: st.mtimeMs });
21899
21784
  }
21900
21785
  if (matched.length === 0) return void 0;
@@ -21924,8 +21809,8 @@ function resolveNavigatorNestContinuation(sessionDir, cwd) {
21924
21809
  }
21925
21810
  }
21926
21811
  function assertRecentFinalFileUnderSessionDir(sessionDir, recentFile) {
21927
- const absoluteSessionDir = resolve15(sessionDir);
21928
- const absoluteFile = resolve15(recentFile);
21812
+ const absoluteSessionDir = resolve16(sessionDir);
21813
+ const absoluteFile = resolve16(recentFile);
21929
21814
  if (absoluteFile !== absoluteSessionDir && !pathContainedIn(absoluteSessionDir, absoluteFile)) {
21930
21815
  throw new ActivationLedgerError(
21931
21816
  `archivist record session must be under the authorized nest (${sessionDir}): ${recentFile}`
@@ -21980,7 +21865,7 @@ function createRecordSessionOpen(options) {
21980
21865
  throw new Error("Durable record ownership requires a parent session inside the ledger home");
21981
21866
  } else {
21982
21867
  ledgerHome = resolveActivationLedgerHomeForPath(parentFile);
21983
- const parentResolved = resolve15(parentFile);
21868
+ const parentResolved = resolve16(parentFile);
21984
21869
  if (!physicallyContainedIn(ledgerHome, parentResolved)) {
21985
21870
  throw new Error("Durable record ownership requires a parent session inside the ledger home");
21986
21871
  }
@@ -22147,7 +22032,7 @@ import { createHash as createHash9 } from "node:crypto";
22147
22032
  init_navigator_invocation_identity();
22148
22033
  init_packaged_role_registry();
22149
22034
  init_activation_ledger_topology();
22150
- import { resolve as resolve16 } from "node:path";
22035
+ import { resolve as resolve17 } from "node:path";
22151
22036
  import "@earendil-works/pi-coding-agent";
22152
22037
  import { Type as Type13 } from "typebox";
22153
22038
 
@@ -22594,7 +22479,7 @@ function navigatorSubjectKey(subjectRoot, subject, provenance = "role_input") {
22594
22479
  }
22595
22480
  function navigatorSubjectKeyForInput(subjectRoot, reference, cwd = process.cwd()) {
22596
22481
  if (issueRoot(subjectRoot) !== void 0 || !subjectRoot.includes("/.ak/work/")) return subjectRoot;
22597
- const resolvedReference = resolve16(cwd, reference);
22482
+ const resolvedReference = resolve17(cwd, reference);
22598
22483
  const marker = "/runs/";
22599
22484
  if (resolvedReference.includes(marker)) {
22600
22485
  return subjectRoot;
@@ -23107,7 +22992,7 @@ ${helpContext}
23107
22992
  init_doctor_evidence();
23108
22993
  init_host_contracts();
23109
22994
  import { readFile as readFile20 } from "node:fs/promises";
23110
- import { resolve as resolve17 } from "node:path";
22995
+ import { resolve as resolve18 } from "node:path";
23111
22996
  init_notary_source_run();
23112
22997
  init_packaged_role_registry();
23113
22998
  init_invocation();
@@ -23115,7 +23000,7 @@ function navigatorInputReference(getFlag, role) {
23115
23000
  if (getFlag === void 0) return void 0;
23116
23001
  const name = packagedRoleInputFlag(role);
23117
23002
  const value = name === void 0 ? void 0 : getFlag(name);
23118
- return typeof value === "string" && value !== "" ? resolve17(value) : void 0;
23003
+ return typeof value === "string" && value !== "" ? resolve18(value) : void 0;
23119
23004
  }
23120
23005
  async function loadNavigatorWorkContext(options) {
23121
23006
  const reference = navigatorInputReference(options.getFlag, options.role);
@@ -23136,7 +23021,7 @@ async function loadNavigatorWorkContext(options) {
23136
23021
  }
23137
23022
  const publicRunDir = runDirectoryFromHostContext(options.context);
23138
23023
  const currentSessionDir = options.context.sessionManager.getSessionDir();
23139
- const isBoundPublicRun = publicRunDir !== void 0 && resolve17(currentSessionDir) === resolve17(publicRunDir, "session");
23024
+ const isBoundPublicRun = publicRunDir !== void 0 && resolve18(currentSessionDir) === resolve18(publicRunDir, "session");
23140
23025
  if (options.role === "judge" && isBoundPublicRun) {
23141
23026
  let admitted;
23142
23027
  try {
@@ -23169,9 +23054,9 @@ async function loadNavigatorWorkContext(options) {
23169
23054
  }
23170
23055
  const workRoot = subjectRoot.includes("/.ak/work/") ? subjectRoot : void 0;
23171
23056
  const authorityFiles = workRoot === void 0 ? [] : [
23172
- resolve17(workRoot, "authority.md"),
23173
- resolve17(workRoot, "authority.txt"),
23174
- resolve17(workRoot, "design-v2/owner-direction.md")
23057
+ resolve18(workRoot, "authority.md"),
23058
+ resolve18(workRoot, "authority.txt"),
23059
+ resolve18(workRoot, "design-v2/owner-direction.md")
23175
23060
  ];
23176
23061
  let authorityMaterial;
23177
23062
  for (const path of authorityFiles) {
@@ -23287,7 +23172,7 @@ import {
23287
23172
  statSync as statSync4,
23288
23173
  writeFileSync as writeFileSync3
23289
23174
  } from "node:fs";
23290
- import { isAbsolute as isAbsolute9, resolve as resolve18 } from "node:path";
23175
+ import { isAbsolute as isAbsolute9, resolve as resolve19 } from "node:path";
23291
23176
  var ActivationSessionFileMissingError = class extends Error {
23292
23177
  code = "AK_ACTIVATION_SESSION_FILE_MISSING";
23293
23178
  path;
@@ -23332,7 +23217,7 @@ function durableSessionPointer(sessionManager) {
23332
23217
  `Workflow role activation requires an absolute durable session file path; got relative path: ${file}`
23333
23218
  );
23334
23219
  }
23335
- const resolvedFile = resolve18(file);
23220
+ const resolvedFile = resolve19(file);
23336
23221
  try {
23337
23222
  lstatSync2(resolvedFile);
23338
23223
  } catch (error) {
@@ -25181,7 +25066,7 @@ init_submission_errors();
25181
25066
  init_submission_errors();
25182
25067
  import { execFileSync as execFileSync4 } from "node:child_process";
25183
25068
  import { existsSync as existsSync11, lstatSync as lstatSync3, readdirSync as readdirSync3, readFileSync as readFileSync5, rmdirSync, rmSync } from "node:fs";
25184
- import { resolve as resolve19 } from "node:path";
25069
+ import { resolve as resolve20 } from "node:path";
25185
25070
  var WORKER_SUBMISSION_GATE_RECORD_KIND = WORKER_SUBMISSION_GATE_KIND;
25186
25071
  var WORKER_COMMIT_BASELINE_ENTRY_TYPE = "commit-baseline";
25187
25072
  var WORKER_COMMIT_REMINDER_BOUNCE_ENTRY_TYPE = "commit-reminder-bounce";
@@ -25230,7 +25115,7 @@ function escapeGitConfigValueRegex(value) {
25230
25115
  function unsetOwnedHooksPath(file) {
25231
25116
  const owned = [];
25232
25117
  for (const value of tryGetAll(file, "core.hooksPath")) {
25233
- if (!ownedHook(resolve19(value, HOOK_FILE))) continue;
25118
+ if (!ownedHook(resolve20(value, HOOK_FILE))) continue;
25234
25119
  try {
25235
25120
  gitFile(file, [
25236
25121
  "--unset-all",
@@ -25245,15 +25130,15 @@ function unsetOwnedHooksPath(file) {
25245
25130
  return owned;
25246
25131
  }
25247
25132
  function rmOwnedDir(dir) {
25248
- const hookPath = resolve19(dir, HOOK_FILE);
25133
+ const hookPath = resolve20(dir, HOOK_FILE);
25249
25134
  if (!ownedHook(hookPath)) return;
25250
25135
  rmSync(hookPath, { force: true });
25251
25136
  if (existsSync11(dir) && readdirSync3(dir).length === 0) rmdirSync(dir);
25252
25137
  }
25253
25138
  function linkedGitDirs(commonDir) {
25254
- const root = resolve19(commonDir, "worktrees");
25139
+ const root = resolve20(commonDir, "worktrees");
25255
25140
  if (!existsSync11(root)) return [];
25256
- return readdirSync3(root).map((name) => resolve19(root, name)).filter((dir) => lstatSync3(dir).isDirectory());
25141
+ return readdirSync3(root).map((name) => resolve20(root, name)).filter((dir) => lstatSync3(dir).isDirectory());
25257
25142
  }
25258
25143
  function uninstallPackageWorkerHooks(cwd) {
25259
25144
  let inside;
@@ -25267,14 +25152,14 @@ function uninstallPackageWorkerHooks(cwd) {
25267
25152
  const clear = (configFile) => {
25268
25153
  for (const hooks of unsetOwnedHooksPath(configFile)) rmOwnedDir(hooks);
25269
25154
  };
25270
- clear(resolve19(commonDir, "config"));
25271
- clear(resolve19(commonDir, "config.worktree"));
25272
- rmOwnedDir(resolve19(commonDir, HOOKS_DIR));
25273
- const legacy = resolve19(commonDir, "hooks", HOOK_FILE);
25155
+ clear(resolve20(commonDir, "config"));
25156
+ clear(resolve20(commonDir, "config.worktree"));
25157
+ rmOwnedDir(resolve20(commonDir, HOOKS_DIR));
25158
+ const legacy = resolve20(commonDir, "hooks", HOOK_FILE);
25274
25159
  if (ownedHook(legacy)) rmSync(legacy, { force: true });
25275
25160
  for (const gitDir of linkedGitDirs(commonDir)) {
25276
- clear(resolve19(gitDir, "config.worktree"));
25277
- rmOwnedDir(resolve19(gitDir, HOOKS_DIR));
25161
+ clear(resolve20(gitDir, "config.worktree"));
25162
+ rmOwnedDir(resolve20(gitDir, HOOKS_DIR));
25278
25163
  }
25279
25164
  }
25280
25165
  function isRecord15(value) {
@@ -26432,11 +26317,15 @@ function createSecretariatRoleRuntime(roleHost, dependencies, hostActions) {
26432
26317
  const summoned = summon === void 0 ? await (async () => {
26433
26318
  const coordinates = readRoleRunCoordinates(ctx, "secretariat summons");
26434
26319
  const { summonPublicRole: summonPublicRole2 } = await Promise.resolve().then(() => (init_public_role_summons(), public_role_summons_exports));
26320
+ const { readBoardTicketNumber: readBoardTicketNumber2 } = await Promise.resolve().then(() => (init_run_ticket_number(), run_ticket_number_exports));
26321
+ const parentTicket = await readBoardTicketNumber2(coordinates.runDirectory);
26435
26322
  return summonPublicRole2({
26436
26323
  role: "countersign",
26437
26324
  argv: ["--project", coordinates.projectRoot, "--", instruction],
26438
26325
  cwd: coordinates.projectRoot,
26439
26326
  home: coordinates.home,
26327
+ parentRunPath: coordinates.runDirectory,
26328
+ ...parentTicket === void 0 ? {} : { boundTicketNumber: parentTicket },
26440
26329
  ...signal === void 0 ? {} : { signal },
26441
26330
  ...correlationId === void 0 ? {} : { correlationId },
26442
26331
  ...dependencies.packageRoot === void 0 ? {} : { packageRoot: dependencies.packageRoot },
@@ -27748,11 +27637,11 @@ init_role_activation_flags();
27748
27637
  init_submission_correctable_error();
27749
27638
  init_navigator_invocation_identity();
27750
27639
  async function listen(server, path) {
27751
- await new Promise((resolve21, reject) => {
27640
+ await new Promise((resolve22, reject) => {
27752
27641
  server.once("error", reject);
27753
27642
  server.listen(path, () => {
27754
27643
  server.off("error", reject);
27755
- resolve21();
27644
+ resolve22();
27756
27645
  });
27757
27646
  });
27758
27647
  }
@@ -28194,8 +28083,8 @@ async function prepareRoleEnvelope(options) {
28194
28083
  try {
28195
28084
  const closeAll = server.closeAllConnections;
28196
28085
  if (typeof closeAll === "function") closeAll.call(server);
28197
- await new Promise((resolve21, reject) => {
28198
- server.close((error) => error ? reject(error) : resolve21());
28086
+ await new Promise((resolve22, reject) => {
28087
+ server.close((error) => error ? reject(error) : resolve22());
28199
28088
  });
28200
28089
  } catch (error) {
28201
28090
  cleanupFailures.push(error);
@@ -28601,7 +28490,7 @@ import { spawn as spawn4, spawnSync } from "node:child_process";
28601
28490
  import { randomUUID as randomUUID10 } from "node:crypto";
28602
28491
  import { existsSync as existsSync12 } from "node:fs";
28603
28492
  import { writeFile as writeFile12 } from "node:fs/promises";
28604
- import { dirname as dirname24, isAbsolute as isAbsolute10, join as join44, resolve as resolve20 } from "node:path";
28493
+ import { dirname as dirname24, isAbsolute as isAbsolute10, join as join44, resolve as resolve21 } from "node:path";
28605
28494
 
28606
28495
  // src/external-host-turn-loop.ts
28607
28496
  init_host_contracts();
@@ -28850,10 +28739,10 @@ function resolveGitCommonDir(cwd) {
28850
28739
  if (raw === "") {
28851
28740
  throw new Error("git rev-parse --git-common-dir returned empty stdout");
28852
28741
  }
28853
- return isAbsolute10(raw) ? raw : resolve20(cwd, raw);
28742
+ return isAbsolute10(raw) ? raw : resolve21(cwd, raw);
28854
28743
  }
28855
28744
  function spawnHeadlessTurn(options) {
28856
- return new Promise((resolve21, reject) => {
28745
+ return new Promise((resolve22, reject) => {
28857
28746
  if (options.signal?.aborted) {
28858
28747
  reject(hostAbortedError("headless host aborted"));
28859
28748
  return;
@@ -28928,7 +28817,7 @@ function spawnHeadlessTurn(options) {
28928
28817
  reject(stdinDeliveryError);
28929
28818
  return;
28930
28819
  }
28931
- resolve21({ code, stdout: resultStdout, stderr, timedOut });
28820
+ resolve22({ code, stdout: resultStdout, stderr, timedOut });
28932
28821
  };
28933
28822
  const onAbort = () => {
28934
28823
  child.kill("SIGTERM");
@@ -29104,7 +28993,7 @@ function createHeadlessRoleTurnHost(config) {
29104
28993
  args,
29105
28994
  cwd: request.cwd,
29106
28995
  env,
29107
- stdin: applyMethodPrompt(prompt),
28996
+ stdin: request.continuation.kind === "resume" ? prompt : applyMethodPrompt(prompt),
29108
28997
  ...abortSignal === void 0 ? {} : { signal: abortSignal },
29109
28998
  ...request.timeoutMs === void 0 ? {} : { timeoutMs: request.timeoutMs },
29110
28999
  onStdoutLine(line2) {
@@ -29116,6 +29005,7 @@ function createHeadlessRoleTurnHost(config) {
29116
29005
  } catch {
29117
29006
  return;
29118
29007
  }
29008
+ codexObserver?.observe(event);
29119
29009
  reportHostSessionEvent({
29120
29010
  host: config.hostName,
29121
29011
  cwd: request.cwd,
@@ -29123,12 +29013,22 @@ function createHeadlessRoleTurnHost(config) {
29123
29013
  source: "headless-host",
29124
29014
  event
29125
29015
  });
29126
- codexObserver?.observe(event);
29127
29016
  }
29128
29017
  });
29129
29018
  } catch (error) {
29130
29019
  if (isHostAbortedError(error)) throw error;
29131
29020
  const message = error instanceof Error ? error.message : String(error);
29021
+ const observedHostFailure = codexObserver?.result().failureDiagnostic;
29022
+ if (observedHostFailure !== void 0) {
29023
+ return {
29024
+ status: "terminal",
29025
+ result: failure("output", "HeadlessCliError", "codex-turn-failed", {
29026
+ diagnostic: observedHostFailure,
29027
+ sessionRecordDiagnostic: message,
29028
+ sessionId
29029
+ }, observedHostFailure)
29030
+ };
29031
+ }
29132
29032
  const isRecordFailure = typeof error === "object" && error !== null && (error.knownCause === "session" || error.name === "SitianInfrastructureError");
29133
29033
  if (isRecordFailure) {
29134
29034
  return {
@@ -29159,22 +29059,28 @@ function createHeadlessRoleTurnHost(config) {
29159
29059
  }
29160
29060
  if (codex) {
29161
29061
  const observation = codexObserver.result();
29162
- if (observation.threadId === void 0 || observation.threadId === "") {
29163
- return terminalFromSpawned(spawned, {
29164
- cause: "session",
29165
- identity: { name: "HeadlessMissingThreadId", code: "missing-thread-id" },
29166
- diagnostic: "codex exec emitted no thread.started thread_id",
29167
- details: { sessionId, exitCode: spawned.code }
29168
- });
29062
+ if (observation.threadId !== void 0 && observation.threadId !== "") {
29063
+ sessionId = observation.threadId;
29064
+ }
29065
+ let sessionBindingDiagnostic;
29066
+ const hostFailed = observation.failureDiagnostic !== void 0 || spawned.code !== 0 && spawned.code !== null;
29067
+ if (hostFailed && observation.threadId !== void 0 && observation.threadId !== "") {
29068
+ try {
29069
+ await config.sessionIdentity.bind(request.principal, observation.threadId);
29070
+ } catch (error) {
29071
+ sessionBindingDiagnostic = error instanceof Error ? error.message : String(error);
29072
+ }
29169
29073
  }
29170
- sessionId = observation.threadId;
29171
- await config.sessionIdentity.bind(request.principal, sessionId);
29172
29074
  if (observation.failureDiagnostic !== void 0) {
29173
29075
  return terminalFromSpawned(spawned, {
29174
29076
  cause: "output",
29175
29077
  identity: { name: "HeadlessCliError", code: "codex-turn-failed" },
29176
29078
  diagnostic: observation.failureDiagnostic,
29177
- details: { sessionId, exitCode: spawned.code }
29079
+ details: {
29080
+ sessionId,
29081
+ exitCode: spawned.code,
29082
+ ...sessionBindingDiagnostic === void 0 ? {} : { sessionBindingDiagnostic }
29083
+ }
29178
29084
  });
29179
29085
  }
29180
29086
  if (spawned.code !== 0 && spawned.code !== null) {
@@ -29182,9 +29088,23 @@ function createHeadlessRoleTurnHost(config) {
29182
29088
  cause: "output",
29183
29089
  identity: { name: "HeadlessCliError", code: "codex-nonzero-exit" },
29184
29090
  diagnostic: spawned.stderr.trim() || `codex exec exited ${String(spawned.code)}`,
29091
+ details: {
29092
+ sessionId,
29093
+ exitCode: spawned.code,
29094
+ ...sessionBindingDiagnostic === void 0 ? {} : { sessionBindingDiagnostic }
29095
+ }
29096
+ });
29097
+ }
29098
+ if (observation.threadId === void 0 || observation.threadId === "") {
29099
+ return terminalFromSpawned(spawned, {
29100
+ cause: "session",
29101
+ identity: { name: "HeadlessMissingThreadId", code: "missing-thread-id" },
29102
+ diagnostic: "codex exec emitted no thread.started thread_id",
29185
29103
  details: { sessionId, exitCode: spawned.code }
29186
29104
  });
29187
29105
  }
29106
+ sessionId = observation.threadId;
29107
+ await config.sessionIdentity.bind(request.principal, sessionId);
29188
29108
  if (!observation.turnCompleted) {
29189
29109
  return terminalFromSpawned(spawned, {
29190
29110
  cause: "output",