@akagilnc/pi-workflow-roles 0.1.1883 → 0.1.1918

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.
@@ -18,6 +18,12 @@ import { renderPublicAkRoleCommand } from "./public-command-renderer.ts";
18
18
  import { issueRoot, subjectPath } from "./work-subject-identity.ts";
19
19
  import { wrapPackageOwnedToolDefinition } from "./package-owned-tool-idle.ts";
20
20
  import { createReceiptDeliveryPolicy, NO_RECEIPT_LIFECYCLE_ENTRY_TYPE, RECEIPT_DELIVERY_PROMPT } from "./receipt-delivery-policy.ts";
21
+ import { recordTypedProviderHttpStatus } from "./typed-provider-http.ts";
22
+ import {
23
+ hasUpstreamErrorTestimony,
24
+ isNonSuccessHttpStatus,
25
+ projectConfirmedRemotePayload,
26
+ } from "./upstream-error-testimony.ts";
21
27
 
22
28
  export const NAVIGATOR_EVENT_TYPE = "ak-navigator-attendance" as const;
23
29
  export const NAVIGATOR_PREPARE_TOOL_NAME = "ak_navigator_prepare" as const;
@@ -1092,19 +1098,37 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1092
1098
  }
1093
1099
  assignProviderFailure(navigatorProviderFailureFromStatus(status));
1094
1100
  };
1095
- const humanProviderError = <T extends Record<string, unknown>>(error: T): T => {
1096
- const human = { ...error };
1097
- delete human.statusCode;
1098
- delete human.code;
1099
- delete human.navigatorFailure;
1100
- return human;
1101
+ /** Project only fields actually held on the call surface — never forge upstream payload. */
1102
+ const projectHeldUpstream = (error: unknown): Record<string, unknown> => {
1103
+ if (!exactRecord(error)) return {};
1104
+ // Shape reading stays local; testimony + confirmed-remote payload use shared authority.
1105
+ const status = typeof error.statusCode === "number"
1106
+ ? error.statusCode
1107
+ : typeof error.status === "number"
1108
+ ? error.status
1109
+ : typeof error.httpStatus === "number"
1110
+ ? error.httpStatus
1111
+ : undefined;
1112
+ const httpStatus = isNonSuccessHttpStatus(status) ? status : undefined;
1113
+ const diagnostics = Array.isArray(error.diagnostics) && error.diagnostics.length > 0
1114
+ ? error.diagnostics
1115
+ : undefined;
1116
+ const testimony = hasUpstreamErrorTestimony({
1117
+ ...(httpStatus === undefined ? {} : { httpStatus }),
1118
+ ...(diagnostics === undefined ? {} : { diagnostics }),
1119
+ });
1120
+ return {
1121
+ ...(httpStatus === undefined ? {} : { statusCode: httpStatus, status: httpStatus }),
1122
+ ...(diagnostics === undefined ? {} : { diagnostics }),
1123
+ ...(testimony ? projectConfirmedRemotePayload(error) : {}),
1124
+ };
1101
1125
  };
1102
- let providerFailureEvidenceNumber = 0;
1103
- let providerFailureEvidence: { id: string; error: unknown } | undefined;
1104
- const retainProviderFailure = (error: unknown): string => {
1105
- const id = `navigator-provider-failure-${++providerFailureEvidenceNumber}`;
1106
- providerFailureEvidence = { id, error };
1107
- return id;
1126
+ /** Keep held upstream fields; strip only the local navigatorFailure classification marker. */
1127
+ const retainUpstreamMessage = <T extends Record<string, unknown>>(error: T): T => {
1128
+ if (!("navigatorFailure" in error)) return error;
1129
+ const copy = { ...error };
1130
+ delete copy.navigatorFailure;
1131
+ return copy;
1108
1132
  };
1109
1133
  const setupFailureMessage = (error: unknown) => ({
1110
1134
  role: "assistant" as const,
@@ -1115,15 +1139,27 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1115
1139
  usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
1116
1140
  stopReason: "error" as const,
1117
1141
  errorMessage: error instanceof Error ? error.message : String(error),
1118
- navigatorFailureEvidenceId: retainProviderFailure(error),
1119
1142
  timestamp: Date.now(),
1143
+ ...projectHeldUpstream(error),
1120
1144
  });
1145
+ const persistNavigatorHttpObservation = async (
1146
+ status: number,
1147
+ model: unknown,
1148
+ ): Promise<void> => {
1149
+ const runDir = process.env.AK_ROLE_RUN_DIR;
1150
+ if (typeof runDir !== "string" || runDir.trim() === "") return;
1151
+ const provider = exactRecord(model) && typeof model.provider === "string" && model.provider.trim() !== ""
1152
+ ? model.provider
1153
+ : undefined;
1154
+ if (provider === undefined) return;
1155
+ await recordTypedProviderHttpStatus(runDir, { httpStatus: status, provider });
1156
+ };
1121
1157
  const instrumentProvider = <TProvider extends NonNullable<typeof provider>>(sourceProvider: TProvider): TProvider => {
1122
1158
  type StreamFn = TProvider["stream"];
1123
1159
  type StreamSimpleFn = TProvider["streamSimple"];
1124
1160
  type StreamOptionsArg = Parameters<StreamFn>[2];
1125
1161
  type StreamSimpleOptionsArg = Parameters<StreamSimpleFn>[2];
1126
- const instrumentStreamOptions = <TOptions>(options: TOptions): TOptions => {
1162
+ const instrumentStreamOptions = <TOptions>(options: TOptions, observedStatus: { value?: number }): TOptions => {
1127
1163
  const record = (exactRecord(options) ? options : {}) as Record<string, unknown>;
1128
1164
  const previous = typeof record.onResponse === "function"
1129
1165
  ? record.onResponse as (response: { status: number; headers: Record<string, string> }, model: unknown) => void | Promise<void>
@@ -1131,12 +1167,23 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1131
1167
  return {
1132
1168
  ...record,
1133
1169
  onResponse: async (response: { status: number; headers: Record<string, string> }, model: unknown) => {
1170
+ observedStatus.value = response.status;
1134
1171
  classifyProviderResponseStatus(response.status);
1172
+ // Durable run-dossier sink for typed non-success HTTP (2xx clears).
1173
+ await persistNavigatorHttpObservation(response.status, model);
1135
1174
  await previous?.(response, model);
1136
1175
  },
1137
1176
  } as TOptions;
1138
1177
  };
1139
- const wrapProviderStream = (source: ReturnType<StreamFn>): ReturnType<StreamFn> => {
1178
+ /** Attach a directly observed non-success HTTP status when the terminal message lacks one. */
1179
+ const withObservedStatus = <T extends Record<string, unknown>>(message: T, observedStatus: number | undefined): T => {
1180
+ if (observedStatus === undefined || observedStatus >= 200 && observedStatus < 300) return message;
1181
+ if (typeof message.statusCode === "number" || typeof message.status === "number" || typeof message.httpStatus === "number") {
1182
+ return message;
1183
+ }
1184
+ return { ...message, statusCode: observedStatus, status: observedStatus };
1185
+ };
1186
+ const wrapProviderStream = (source: ReturnType<StreamFn>, observedStatus: { value?: number }): ReturnType<StreamFn> => {
1140
1187
  const wrapped = createAssistantMessageEventStream();
1141
1188
  void (async () => {
1142
1189
  let result: Awaited<ReturnType<typeof source.result>> | undefined;
@@ -1147,13 +1194,14 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1147
1194
  sawTerminal = true;
1148
1195
  if (event.type === "done" && exactRecord(event.message)) {
1149
1196
  assignProviderFailure(navigatorProviderFailureFromDiagnostics(event.message.diagnostics));
1150
- result = humanProviderError(event.message) as typeof event.message;
1197
+ // Keep held message/status/body/code/errno/diagnostics on the session surface.
1198
+ result = withObservedStatus(retainUpstreamMessage(event.message), observedStatus.value) as typeof event.message;
1151
1199
  wrapped.push({ ...event, message: result });
1152
1200
  continue;
1153
1201
  }
1154
1202
  if (event.type === "error" && exactRecord(event.error)) {
1155
1203
  classifyProviderStreamError(event.error);
1156
- result = humanProviderError(event.error) as typeof event.error;
1204
+ result = withObservedStatus(retainUpstreamMessage(event.error), observedStatus.value) as typeof event.error;
1157
1205
  wrapped.push({ ...event, error: result });
1158
1206
  continue;
1159
1207
  }
@@ -1163,24 +1211,24 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1163
1211
  // Only await result after a terminal done/error event. end(undefined) leaves result() unresolved forever.
1164
1212
  if (sawTerminal) {
1165
1213
  const terminal = await source.result();
1166
- if (result === undefined && exactRecord(terminal)) result = humanProviderError(terminal) as typeof terminal;
1167
- else if (result === undefined) result = terminal;
1214
+ if (result === undefined && exactRecord(terminal)) {
1215
+ result = withObservedStatus(retainUpstreamMessage(terminal), observedStatus.value) as typeof terminal;
1216
+ } else if (result === undefined) result = terminal;
1168
1217
  }
1169
1218
  } catch (error) {
1170
- // Contract: README.md#Navigator-attendance — synthetic provider errors retain a stable pointer to the raw rejection while exposing only human-safe text.
1219
+ // Stream throw: project held upstream fields onto the synthetic terminal message (session is durable).
1171
1220
  classifyProviderStreamError(error);
1172
- if (providerFailure === undefined) providerFailure = { source: "transport", cause: "transport" };
1221
+ if (providerFailure === undefined) providerFailure = { source: "unknown", cause: "unknown" };
1173
1222
  if (!sawTerminal) {
1174
- const message = setupFailureMessage(error);
1223
+ const message = withObservedStatus(setupFailureMessage(error), observedStatus.value);
1175
1224
  wrapped.push({ type: "error", reason: "error", error: message });
1176
1225
  result = message;
1177
1226
  sawTerminal = true;
1178
1227
  }
1179
1228
  } finally {
1180
- // No terminal stream event means the provider produced no response.
1181
- // Emit a synthetic error so callers do not hang waiting for done/error, and never await an unresolved source.result().
1229
+ // No terminal stream event: unknown only — do not forge upstream payload.
1182
1230
  if (!sawTerminal) {
1183
- if (providerFailure === undefined) providerFailure = { source: "transport", cause: "transport" };
1231
+ if (providerFailure === undefined) providerFailure = { source: "unknown", cause: "unknown" };
1184
1232
  const message = setupFailureMessage(new Error("Navigator provider produced no response"));
1185
1233
  wrapped.push({ type: "error", reason: "error", error: message });
1186
1234
  result = message;
@@ -1191,15 +1239,16 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1191
1239
  })();
1192
1240
  return wrapped as ReturnType<StreamFn>;
1193
1241
  };
1194
- const invokeInstrumentedStream = (invoke: () => ReturnType<StreamFn>): ReturnType<StreamFn> => {
1242
+ const invokeInstrumentedStream = (invoke: (observedStatus: { value?: number }) => ReturnType<StreamFn>): ReturnType<StreamFn> => {
1195
1243
  // Reset before invoking the selected provider so setup throws cannot leave a prior call's fact.
1196
1244
  providerFailure = undefined;
1245
+ const observedStatus: { value?: number } = {};
1197
1246
  try {
1198
- return wrapProviderStream(invoke());
1247
+ return wrapProviderStream(invoke(observedStatus), observedStatus);
1199
1248
  } catch (error) {
1200
- // Contract: README.md#Navigator-attendance — setup failures become terminal synthetic errors, with the raw rejection retained by stable evidence pointer.
1249
+ // Setup failures become terminal synthetic errors with held upstream fields projected when present.
1201
1250
  classifyProviderStreamError(error);
1202
- if (providerFailure === undefined) providerFailure = { source: "transport", cause: "transport" };
1251
+ if (providerFailure === undefined) providerFailure = { source: "unknown", cause: "unknown" };
1203
1252
  const wrapped = createAssistantMessageEventStream();
1204
1253
  const message = setupFailureMessage(error);
1205
1254
  queueMicrotask(() => {
@@ -1212,12 +1261,16 @@ export function createNativeNavigatorSessionFactory(defaultModelSettingPath = na
1212
1261
  return {
1213
1262
  ...sourceProvider,
1214
1263
  stream(model: Parameters<StreamFn>[0], streamContext: Parameters<StreamFn>[1], options: StreamOptionsArg) {
1215
- const instrumented = instrumentStreamOptions(options);
1216
- return invokeInstrumentedStream(() => sourceProvider.stream(model, streamContext, instrumented) as ReturnType<StreamFn>) as ReturnType<StreamFn>;
1264
+ return invokeInstrumentedStream((observedStatus) => {
1265
+ const instrumented = instrumentStreamOptions(options, observedStatus);
1266
+ return sourceProvider.stream(model, streamContext, instrumented) as ReturnType<StreamFn>;
1267
+ }) as ReturnType<StreamFn>;
1217
1268
  },
1218
1269
  streamSimple(model: Parameters<StreamSimpleFn>[0], streamContext: Parameters<StreamSimpleFn>[1], options: StreamSimpleOptionsArg) {
1219
- const instrumented = instrumentStreamOptions(options);
1220
- return invokeInstrumentedStream(() => sourceProvider.streamSimple(model, streamContext, instrumented) as ReturnType<StreamFn>) as ReturnType<StreamSimpleFn>;
1270
+ return invokeInstrumentedStream((observedStatus) => {
1271
+ const instrumented = instrumentStreamOptions(options, observedStatus);
1272
+ return sourceProvider.streamSimple(model, streamContext, instrumented) as ReturnType<StreamFn>;
1273
+ }) as ReturnType<StreamSimpleFn>;
1221
1274
  },
1222
1275
  } as TProvider;
1223
1276
  };
@@ -61,6 +61,69 @@ export {
61
61
  export { CliUsageError } from "./cli-errors.ts";
62
62
  export type { CliIo } from "./cli-io.ts";
63
63
 
64
+ /**
65
+ * Sole production map: public role command → argv parser.
66
+ * cli handlers consume this table; no parallel set.
67
+ */
68
+ export const PUBLIC_ROLE_ARGV = {
69
+ judge: { parse: parseJudgeArgv },
70
+ coder: { parse: parseCoderArgv },
71
+ fixer: { parse: parseFixerArgv },
72
+ collector: { parse: parseCollectorArgv },
73
+ doctor: { parse: parseDoctorArgv },
74
+ merger: { parse: parseMergerArgv },
75
+ reviewer: { parse: parseReviewerArgv },
76
+ } as const;
77
+
78
+ type TakenPublicGlobalFlag =
79
+ | { flag: "help"; consume: 1 }
80
+ | { flag: "model"; consume: 1 | 2; value: string | undefined }
81
+ | { flag: "thinking"; consume: 1 | 2; raw: string | undefined };
82
+
83
+ /**
84
+ * If `argv[index]` is a public global flag, describe its span and payload.
85
+ * Sole global-flag grammar for parseArgv.
86
+ */
87
+ function takePublicGlobalFlag(
88
+ argv: readonly string[],
89
+ index: number,
90
+ ): TakenPublicGlobalFlag | undefined {
91
+ const token = argv[index];
92
+ if (token === undefined) return undefined;
93
+ if (token === "--help" || token === "-h") {
94
+ return { flag: "help", consume: 1 };
95
+ }
96
+ if (token === "--model") {
97
+ const value = argv[index + 1];
98
+ if (value === undefined) {
99
+ return { flag: "model", consume: 1, value: undefined };
100
+ }
101
+ return { flag: "model", consume: 2, value };
102
+ }
103
+ if (token.startsWith("--model=")) {
104
+ return {
105
+ flag: "model",
106
+ consume: 1,
107
+ value: token.slice("--model=".length),
108
+ };
109
+ }
110
+ if (token === "--thinking") {
111
+ const raw = argv[index + 1];
112
+ if (raw === undefined) {
113
+ return { flag: "thinking", consume: 1, raw: undefined };
114
+ }
115
+ return { flag: "thinking", consume: 2, raw };
116
+ }
117
+ if (token.startsWith("--thinking=")) {
118
+ return {
119
+ flag: "thinking",
120
+ consume: 1,
121
+ raw: token.slice("--thinking=".length),
122
+ };
123
+ }
124
+ return undefined;
125
+ }
126
+
64
127
  export type CliEnv = {
65
128
  home?: string;
66
129
  agentDir?: string;
@@ -167,44 +230,38 @@ function parseArgv(argv: readonly string[]): ParsedGlobal {
167
230
 
168
231
  // Global flags may appear before or after the subcommand
169
232
  // (`ak-role --model x roles` and `ak-role roles --model x`).
233
+ // Grammar authority: takePublicGlobalFlag above.
170
234
  while (args.length > 0) {
171
- const token = args.shift()!;
172
- if (token === "--") {
235
+ if (args[0] === "--") {
236
+ args.shift();
173
237
  positional.push(...args);
174
238
  break;
175
239
  }
176
- if (token === "--help" || token === "-h") {
177
- help = true;
178
- continue;
179
- }
180
- if (token === "--model") {
181
- const value = args.shift();
182
- if (value === undefined) throw new CliUsageError("--model requires a value");
183
- model = value;
184
- continue;
185
- }
186
- if (token.startsWith("--model=")) {
187
- model = token.slice("--model=".length);
188
- continue;
189
- }
190
- if (token === "--thinking") {
191
- const value = args.shift();
192
- if (value === undefined) throw new CliUsageError("--thinking requires a value");
193
- thinking = parseThinking(value);
194
- continue;
195
- }
196
- if (token.startsWith("--thinking=")) {
197
- thinking = parseThinking(token.slice("--thinking=".length));
198
- continue;
199
- }
200
- if (token.startsWith("-") && token !== "-") {
201
- // Subcommands may own additional flags later; only reject unknowns at the
202
- // top level when they appear before any positional command is collected
203
- // and the command is one that does not accept free flags (enforced below).
204
- positional.push(token);
240
+ const taken = takePublicGlobalFlag(args, 0);
241
+ if (taken !== undefined) {
242
+ if (taken.flag === "help") {
243
+ help = true;
244
+ args.splice(0, taken.consume);
245
+ continue;
246
+ }
247
+ if (taken.flag === "model") {
248
+ if (taken.value === undefined) {
249
+ throw new CliUsageError("--model requires a value");
250
+ }
251
+ model = taken.value;
252
+ args.splice(0, taken.consume);
253
+ continue;
254
+ }
255
+ if (taken.raw === undefined) {
256
+ throw new CliUsageError("--thinking requires a value");
257
+ }
258
+ thinking = parseThinking(taken.raw);
259
+ args.splice(0, taken.consume);
205
260
  continue;
206
261
  }
207
- positional.push(token);
262
+ // Subcommands may own additional flags later; unknown dashed tokens stay
263
+ // positional here (same as pre-unification parseArgv).
264
+ positional.push(args.shift()!);
208
265
  }
209
266
 
210
267
  const [command, ...rest] = positional;
@@ -626,7 +683,7 @@ export async function runAkRole(
626
683
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
627
684
  },
628
685
  io,
629
- parseJudgeArgv,
686
+ PUBLIC_ROLE_ARGV.judge.parse,
630
687
  );
631
688
  return {
632
689
  exitCode: result.exitCode,
@@ -669,7 +726,7 @@ export async function runAkRole(
669
726
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
670
727
  },
671
728
  io,
672
- parseCoderArgv,
729
+ PUBLIC_ROLE_ARGV.coder.parse,
673
730
  );
674
731
  return {
675
732
  exitCode: result.exitCode,
@@ -712,7 +769,7 @@ export async function runAkRole(
712
769
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
713
770
  },
714
771
  io,
715
- parseFixerArgv,
772
+ PUBLIC_ROLE_ARGV.fixer.parse,
716
773
  );
717
774
  return {
718
775
  exitCode: result.exitCode,
@@ -755,7 +812,7 @@ export async function runAkRole(
755
812
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
756
813
  },
757
814
  io,
758
- parseCollectorArgv,
815
+ PUBLIC_ROLE_ARGV.collector.parse,
759
816
  );
760
817
  return {
761
818
  exitCode: result.exitCode,
@@ -798,7 +855,7 @@ export async function runAkRole(
798
855
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
799
856
  },
800
857
  io,
801
- parseReviewerArgv,
858
+ PUBLIC_ROLE_ARGV.reviewer.parse,
802
859
  );
803
860
  return {
804
861
  exitCode: result.exitCode,
@@ -841,7 +898,7 @@ export async function runAkRole(
841
898
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
842
899
  },
843
900
  io,
844
- parseDoctorArgv,
901
+ PUBLIC_ROLE_ARGV.doctor.parse,
845
902
  );
846
903
  return {
847
904
  exitCode: result.exitCode,
@@ -884,7 +941,7 @@ export async function runAkRole(
884
941
  ...(env.createRunId === undefined ? {} : { createRunId: env.createRunId }),
885
942
  },
886
943
  io,
887
- parseMergerArgv,
944
+ PUBLIC_ROLE_ARGV.merger.parse,
888
945
  );
889
946
  return {
890
947
  exitCode: result.exitCode,
@@ -41,11 +41,11 @@ import {
41
41
  markRunResumable,
42
42
  markRunRunning,
43
43
  markRunTerminal,
44
- readTypedHttp429Observation,
45
44
  renderResumeCommand,
46
45
  RESUME_TRANSPORT_ENVELOPE,
47
46
  RunWriterLeaseHeldError,
48
47
  type RunWriterLease,
48
+ type TypedProviderHttpObservation,
49
49
  } from "./run-lifecycle.ts";
50
50
  import {
51
51
  classifyPostAdmissionFailure,
@@ -58,7 +58,9 @@ import {
58
58
  presentFailureTerminal,
59
59
  presentStructuralRejection,
60
60
  explicitInternalKnownFailureClassificationInput,
61
- resolveAuditedRunnerKnownFailure,
61
+ resolveAuditedRunnerFailureResolution,
62
+ resolveControlledFailureResumeObservation,
63
+ controlledFailureInputFromResolution,
62
64
  settleFailureTerminalResult,
63
65
  trySettleCoderTerminalResult,
64
66
  } from "./settlement.ts";
@@ -196,6 +198,8 @@ async function presentControlledFailure(
196
198
  readonly code?: string | number;
197
199
  };
198
200
  knownDiagnostic?: string;
201
+ typedHttpObservationSettled?: true;
202
+ typedHttpObservation?: TypedProviderHttpObservation;
199
203
  },
200
204
  io: CliIo,
201
205
  ): Promise<{
@@ -204,10 +208,23 @@ async function presentControlledFailure(
204
208
  terminal: TerminalResult;
205
209
  }> {
206
210
  const hasThrown = Object.hasOwn(failureInput, "thrown");
211
+ const resumeObservation = await resolveControlledFailureResumeObservation({
212
+ runDirectory: admitted.runDirectory,
213
+ ...(failureInput.typedHttpObservationSettled === true
214
+ ? {
215
+ typedHttpObservationSettled: true as const,
216
+ ...(failureInput.typedHttpObservation === undefined
217
+ ? {}
218
+ : { typedHttpObservation: failureInput.typedHttpObservation }),
219
+ }
220
+ : {}),
221
+ });
222
+ const knownFailure =
223
+ failureInput.knownFailure ?? resumeObservation.observationReadFailure;
207
224
  const session =
208
225
  !hasThrown &&
209
226
  !failureInput.timedOut &&
210
- failureInput.knownFailure === undefined &&
227
+ knownFailure === undefined &&
211
228
  failureInput.knownCause === undefined
212
229
  ? await inspectJudgeSession(admitted.sessionFile)
213
230
  : undefined;
@@ -216,7 +233,7 @@ async function presentControlledFailure(
216
233
  code: failureInput.code,
217
234
  stderr: failureInput.stderr,
218
235
  ...(hasThrown ? { thrown: failureInput.thrown } : {}),
219
- ...explicitInternalKnownFailureClassificationInput(failureInput.knownFailure),
236
+ ...explicitInternalKnownFailureClassificationInput(knownFailure),
220
237
  ...(failureInput.knownCause === undefined
221
238
  ? {}
222
239
  : { knownCause: failureInput.knownCause }),
@@ -230,7 +247,7 @@ async function presentControlledFailure(
230
247
  });
231
248
 
232
249
  const hasLawfulTerminalResult = await hasLawfulCoderTerminalResult(admitted);
233
- const typedHttp429 = await readTypedHttp429Observation(admitted.runDirectory);
250
+ const typedHttp429 = resumeObservation.typedHttp429;
234
251
  const sessionPrincipalAvailable = await isSessionPrincipalAvailable(
235
252
  admitted.sessionFile,
236
253
  );
@@ -296,8 +313,9 @@ async function dispatchAdmittedCoder(input: {
296
313
  PI_CODING_AGENT_DIR: env.agentDir,
297
314
  AK_ROLE_RUN_DIR: admitted.runDirectory,
298
315
  };
299
- if (env.correlationId !== undefined && env.correlationId.trim() !== "") {
300
- childEnv.AK_CORRELATION_ID = env.correlationId;
316
+ const correlationId = admitted.correlationId ?? env.correlationId;
317
+ if (correlationId !== undefined && correlationId.trim() !== "") {
318
+ childEnv.AK_CORRELATION_ID = correlationId;
301
319
  }
302
320
 
303
321
  let result: ExplicitInternalPiResult;
@@ -369,10 +387,11 @@ async function dispatchAdmittedCoder(input: {
369
387
  env.model,
370
388
  env.credentials,
371
389
  );
372
- const knownFailure = await resolveAuditedRunnerKnownFailure({
390
+ const resolution = await resolveAuditedRunnerFailureResolution({
373
391
  runner: result.knownFailure,
374
392
  sessionFile: admitted.sessionFile,
375
393
  credential: credentialFailure,
394
+ runDirectory: admitted.runDirectory,
376
395
  });
377
396
  return await presentControlledFailure(
378
397
  admitted,
@@ -380,7 +399,7 @@ async function dispatchAdmittedCoder(input: {
380
399
  timedOut: result.timedOut,
381
400
  code: result.code,
382
401
  stderr: result.stderr,
383
- ...(knownFailure === undefined ? {} : { knownFailure }),
402
+ ...controlledFailureInputFromResolution(resolution),
384
403
  },
385
404
  io,
386
405
  );
@@ -469,7 +488,10 @@ export async function runPublicCoder(
469
488
 
470
489
  return await dispatchAdmittedCoder({
471
490
  admitted,
472
- env,
491
+ env: {
492
+ ...env,
493
+ ...(admitted.correlationId === undefined ? {} : { correlationId: admitted.correlationId }),
494
+ },
473
495
  io,
474
496
  extraArgs,
475
497
  lease,
@@ -563,7 +585,10 @@ export async function runPublicCoderResume(
563
585
 
564
586
  return await dispatchAdmittedCoder({
565
587
  admitted,
566
- env,
588
+ env: {
589
+ ...env,
590
+ ...(admitted.correlationId === undefined ? {} : { correlationId: admitted.correlationId }),
591
+ },
567
592
  io,
568
593
  extraArgs,
569
594
  lease,
@@ -211,8 +211,9 @@ async function dispatchAdmittedCollector(input: {
211
211
  PI_CODING_AGENT_DIR: env.agentDir,
212
212
  AK_ROLE_RUN_DIR: admitted.runDirectory,
213
213
  };
214
- if (env.correlationId !== undefined && env.correlationId.trim() !== "") {
215
- childEnv.AK_CORRELATION_ID = env.correlationId;
214
+ const correlationId = admitted.correlationId ?? env.correlationId;
215
+ if (correlationId !== undefined && correlationId.trim() !== "") {
216
+ childEnv.AK_CORRELATION_ID = correlationId;
216
217
  }
217
218
 
218
219
  let result: ExplicitInternalPiResult;
@@ -299,6 +300,7 @@ async function dispatchAdmittedCollector(input: {
299
300
  }),
300
301
  sessionFile: admitted.sessionFile,
301
302
  credential: credentialFailure,
303
+ runDirectory: admitted.runDirectory,
302
304
  });
303
305
  return await presentControlledFailure(
304
306
  admitted,
@@ -367,7 +369,10 @@ export async function runPublicCollector(
367
369
 
368
370
  return await dispatchAdmittedCollector({
369
371
  admitted,
370
- env,
372
+ env: {
373
+ ...env,
374
+ ...(admitted.correlationId === undefined ? {} : { correlationId: admitted.correlationId }),
375
+ },
371
376
  io,
372
377
  extraArgs,
373
378
  lease,
@@ -280,6 +280,7 @@ async function dispatchAdmittedDoctor(input: {
280
280
  runner: result.knownFailure,
281
281
  sessionFile: admitted.sessionFile,
282
282
  credential: credentialFailure,
283
+ runDirectory: admitted.runDirectory,
283
284
  });
284
285
  return await presentControlledFailure(
285
286
  admitted,