@riddledc/riddle-proof 0.5.47 → 0.5.49

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.
@@ -10,10 +10,10 @@ import {
10
10
  createRunStatusSnapshot,
11
11
  normalizeRunParams,
12
12
  setRunStatus
13
- } from "./chunk-JOXTKWX6.js";
13
+ } from "./chunk-U7Q3RB5D.js";
14
14
  import {
15
15
  createRiddleProofRunCard
16
- } from "./chunk-PLSGW2GI.js";
16
+ } from "./chunk-CI2F66EE.js";
17
17
  import {
18
18
  authorPacketPayloadFromCheckpointResponse,
19
19
  buildCheckpointPacketForEngineResult,
@@ -281,6 +281,20 @@ function checkpointContinueStage(result) {
281
281
  const resume = recordValue(result.checkpointContract?.resume);
282
282
  return nonEmptyString(resume?.continue_with_stage);
283
283
  }
284
+ function checkpointRecommendedStage(result) {
285
+ const resumeStage = checkpointContinueStage(result);
286
+ if (resumeStage) return resumeStage;
287
+ return nonEmptyString(result.checkpointContract?.recommended_advance_stage) || nonEmptyString(result.decisionRequest?.continue_with_stage) || nonEmptyString(result.decisionRequest?.recommended_advance_stage) || nonEmptyString(recordValue(result.decisionRequest)?.continueWithStage) || nonEmptyString(recordValue(result.decisionRequest)?.recommendedAdvanceStage);
288
+ }
289
+ function stageCheckpointContinuation(result) {
290
+ const stage = checkpointRecommendedStage(result);
291
+ if (!stage) return null;
292
+ return {
293
+ action: "run",
294
+ state_path: String(result.state_path || ""),
295
+ advance_stage: stage
296
+ };
297
+ }
284
298
  function recommendedContinuation(result) {
285
299
  const continueStage = checkpointContinueStage(result);
286
300
  if (!continueStage) return null;
@@ -323,6 +337,18 @@ function defaultStageForProofCheckpointDecision(decision) {
323
337
  if (decision === "needs_richer_proof") return "author";
324
338
  return null;
325
339
  }
340
+ function checkpointContractFromPacket(packet) {
341
+ return recordValue(packet.evidence_excerpt?.checkpoint_contract) || recordValue(recordValue(packet.state_excerpt?.stage_decision_request)?.checkpoint_contract) || null;
342
+ }
343
+ function stageFromCheckpointResponse(response, packet) {
344
+ if (response.decision === "needs_recon") return "recon";
345
+ if (response.decision === "needs_implementation") return "implement";
346
+ const payload = recordValue(response.payload) || {};
347
+ const contract = checkpointContractFromPacket(packet);
348
+ const resume = recordValue(contract?.resume);
349
+ const stage = response.continue_with_stage || nonEmptyString(payload.continue_with_stage) || nonEmptyString(payload.recommended_stage) || nonEmptyString(resume?.continue_with_stage) || (response.decision === "retry_stage" ? packet.stage : "");
350
+ return stage ? stage : null;
351
+ }
326
352
  function proofAssessmentPayloadFromCheckpointResponse(response) {
327
353
  if (![
328
354
  "ready_to_ship",
@@ -425,6 +451,13 @@ function visualDeltaEvidenceRecoveryAssessment(state, payload, blocker) {
425
451
  blockers: [...blockers, blocker]
426
452
  };
427
453
  }
454
+ function isRecoverableStageCheckpoint(checkpoint) {
455
+ return [
456
+ "ship_gate_blocked",
457
+ "verify_required",
458
+ "verify_supervisor_judgment_required"
459
+ ].includes(checkpoint);
460
+ }
428
461
  function contextFor(request, state, result) {
429
462
  return {
430
463
  request,
@@ -771,6 +804,18 @@ function checkpointResponseContinuation(state, value) {
771
804
  };
772
805
  }
773
806
  }
807
+ if (response.decision === "continue_stage" || response.decision === "retry_stage" || response.decision === "needs_implementation") {
808
+ const stage = stageFromCheckpointResponse(response, packet);
809
+ if (stage) {
810
+ appendCheckpointResponse(state, response);
811
+ return {
812
+ next: {
813
+ ...base,
814
+ advance_stage: stage
815
+ }
816
+ };
817
+ }
818
+ }
774
819
  appendCheckpointResponse(state, response, { clear_packet: false });
775
820
  return {
776
821
  blocker: {
@@ -853,7 +898,10 @@ async function handleImplementation(request, state, result, agent) {
853
898
  worktree_path: workdir || null
854
899
  }
855
900
  });
901
+ const implementationStartedAt = timestamp();
902
+ const implementationStartedMs = Date.now();
856
903
  const implementation = await agent.implementChange({ ...context, workdir });
904
+ const implementationDurationMs = Date.now() - implementationStartedMs;
857
905
  if (implementation.blocker || implementation.ok === false) {
858
906
  recordEvent(state, {
859
907
  kind: "agent.implementation.blocked",
@@ -862,6 +910,8 @@ async function handleImplementation(request, state, result, agent) {
862
910
  summary: implementation.summary || implementation.blocker?.message || "Implementation adapter reported a blocker.",
863
911
  details: compactRecord({
864
912
  worktree_path: workdir || null,
913
+ started_at: implementationStartedAt,
914
+ duration_ms: implementationDurationMs,
865
915
  changed_files: implementation.changedFiles || [],
866
916
  tests_run: implementation.testsRun || [],
867
917
  implementation_notes: implementation.implementationNotes || null,
@@ -887,24 +937,32 @@ async function handleImplementation(request, state, result, agent) {
887
937
  summary: implementation.summary || "Implementation adapter returned without leaving a detectable git diff.",
888
938
  details: compactRecord({
889
939
  worktree_path: workdir || null,
940
+ started_at: implementationStartedAt,
941
+ duration_ms: implementationDurationMs,
890
942
  changed_files: implementation.changedFiles || [],
891
943
  tests_run: implementation.testsRun || [],
892
944
  implementation_notes: implementation.implementationNotes || null,
893
945
  adapter_details: implementation.details || null
894
946
  })
895
947
  });
896
- return {
897
- blocker: {
898
- code: "implementation_diff_missing",
948
+ recordEvent(state, {
949
+ kind: "agent.implementation.retry_requested",
950
+ checkpoint: result.checkpoint || null,
951
+ stage: "implement",
952
+ summary: "Implementation adapter left no detectable diff; retrying the implement checkpoint inside the bounded loop.",
953
+ details: compactRecord({
954
+ worktree_path: workdir || null,
899
955
  checkpoint: result.checkpoint || null,
900
- message: "The implementation adapter returned, but the after worktree has no detectable git diff. The harness will not advance to verify.",
901
- details: compactRecord({
902
- worktree_path: workdir || null,
903
- changed_files: implementation.changedFiles || [],
904
- tests_run: implementation.testsRun || [],
905
- implementation_notes: implementation.implementationNotes || null,
906
- adapter_details: implementation.details || null
907
- })
956
+ next_stage: "implement",
957
+ previous_duration_ms: implementationDurationMs
958
+ })
959
+ });
960
+ return {
961
+ next: {
962
+ action: "run",
963
+ state_path: String(result.state_path || ""),
964
+ advance_stage: "implement",
965
+ implementation_notes: implementation.implementationNotes || implementation.summary || "Implementation adapter returned without a detectable git diff; retry the implement checkpoint."
908
966
  }
909
967
  };
910
968
  }
@@ -915,6 +973,8 @@ async function handleImplementation(request, state, result, agent) {
915
973
  summary: implementation.summary || "Implementation adapter reported code changes.",
916
974
  details: {
917
975
  worktree_path: workdir || null,
976
+ started_at: implementationStartedAt,
977
+ duration_ms: implementationDurationMs,
918
978
  diffDetected,
919
979
  changed_files: implementation.changedFiles || [],
920
980
  tests_run: implementation.testsRun || [],
@@ -940,16 +1000,32 @@ async function routeCheckpoint(request, state, result, agent, input) {
940
1000
  terminal: terminalResult(state, "completed", result, result.summary || "Riddle Proof engine completed.")
941
1001
  };
942
1002
  }
1003
+ if (isRecoverableStageCheckpoint(checkpoint) && !input.dry_run && !request.dry_run) {
1004
+ if (input.checkpoint_mode === "yield") {
1005
+ return { terminal: checkpointAwaitingResult(state, result, input.checkpoint_visibility) };
1006
+ }
1007
+ const next = stageCheckpointContinuation(result);
1008
+ if (next) {
1009
+ recordEvent(state, {
1010
+ kind: "checkpoint.recovery_continuation",
1011
+ checkpoint,
1012
+ stage: stageFromCheckpoint(result),
1013
+ summary: `Routing recoverable checkpoint ${checkpoint} back to ${next.advance_stage}.`,
1014
+ details: {
1015
+ next,
1016
+ checkpointContract: result.checkpointContract || null
1017
+ }
1018
+ });
1019
+ return { next };
1020
+ }
1021
+ }
943
1022
  const failureBlocker = engineFailureBlocker(result, checkpoint);
944
1023
  if (failureBlocker) {
945
1024
  return { blocker: failureBlocker };
946
1025
  }
947
1026
  if ([
948
1027
  "recon_human_escalation",
949
- "verify_human_escalation",
950
- "ship_gate_blocked",
951
- "verify_required",
952
- "verify_supervisor_judgment_required"
1028
+ "verify_human_escalation"
953
1029
  ].includes(checkpoint) && result.ok === false) {
954
1030
  return {
955
1031
  blocker: {
@@ -1015,15 +1091,44 @@ async function routeCheckpoint(request, state, result, agent, input) {
1015
1091
  if (input.checkpoint_mode === "yield") {
1016
1092
  return { terminal: checkpointAwaitingResult(state, result, input.checkpoint_visibility) };
1017
1093
  }
1094
+ const startedAt = timestamp();
1095
+ const startedMs = Date.now();
1096
+ recordEvent(state, {
1097
+ kind: "agent.recon_assessment.started",
1098
+ checkpoint,
1099
+ stage: "recon",
1100
+ summary: "Recon assessment agent started.",
1101
+ details: { started_at: startedAt }
1102
+ });
1018
1103
  const assessment = await agent.assessRecon(context);
1104
+ const durationMs = Date.now() - startedMs;
1019
1105
  const blocker = requirePayload("recon_assessment", assessment, state, result);
1020
- if (blocker) return { blocker };
1106
+ if (blocker) {
1107
+ recordEvent(state, {
1108
+ kind: "agent.recon_assessment.blocked",
1109
+ checkpoint,
1110
+ stage: "recon",
1111
+ summary: blocker.message,
1112
+ details: compactRecord({
1113
+ started_at: startedAt,
1114
+ duration_ms: durationMs,
1115
+ blocker,
1116
+ adapter_details: assessment.details || null
1117
+ })
1118
+ });
1119
+ return { blocker };
1120
+ }
1021
1121
  recordEvent(state, {
1022
1122
  kind: "agent.recon_assessment.completed",
1023
1123
  checkpoint,
1024
1124
  stage: "recon",
1025
1125
  summary: assessment.summary,
1026
- details: { payload: assessment.payload }
1126
+ details: compactRecord({
1127
+ payload: assessment.payload,
1128
+ started_at: startedAt,
1129
+ duration_ms: durationMs,
1130
+ adapter_details: assessment.details || null
1131
+ })
1027
1132
  });
1028
1133
  return {
1029
1134
  next: { ...baseContinuation(result), recon_assessment_json: jsonParam(assessment.payload) }
@@ -1035,15 +1140,44 @@ async function routeCheckpoint(request, state, result, agent, input) {
1035
1140
  if (input.checkpoint_mode === "yield") {
1036
1141
  return { terminal: checkpointAwaitingResult(state, result, input.checkpoint_visibility) };
1037
1142
  }
1143
+ const startedAt = timestamp();
1144
+ const startedMs = Date.now();
1145
+ recordEvent(state, {
1146
+ kind: "agent.author_packet.started",
1147
+ checkpoint,
1148
+ stage: "author",
1149
+ summary: "Proof authoring agent started.",
1150
+ details: { started_at: startedAt }
1151
+ });
1038
1152
  const packet = await agent.authorProofPacket(context);
1153
+ const durationMs = Date.now() - startedMs;
1039
1154
  const blocker = requirePayload("author_packet", packet, state, result);
1040
- if (blocker) return { blocker };
1155
+ if (blocker) {
1156
+ recordEvent(state, {
1157
+ kind: "agent.author_packet.blocked",
1158
+ checkpoint,
1159
+ stage: "author",
1160
+ summary: blocker.message,
1161
+ details: compactRecord({
1162
+ started_at: startedAt,
1163
+ duration_ms: durationMs,
1164
+ blocker,
1165
+ adapter_details: packet.details || null
1166
+ })
1167
+ });
1168
+ return { blocker };
1169
+ }
1041
1170
  recordEvent(state, {
1042
1171
  kind: "agent.author_packet.completed",
1043
1172
  checkpoint,
1044
1173
  stage: "author",
1045
1174
  summary: packet.summary,
1046
- details: { payload: packet.payload }
1175
+ details: compactRecord({
1176
+ payload: packet.payload,
1177
+ started_at: startedAt,
1178
+ duration_ms: durationMs,
1179
+ adapter_details: packet.details || null
1180
+ })
1047
1181
  });
1048
1182
  return {
1049
1183
  next: { ...baseContinuation(result), author_packet_json: jsonParam(packet.payload) }
@@ -1066,9 +1200,31 @@ async function routeCheckpoint(request, state, result, agent, input) {
1066
1200
  if (input.checkpoint_mode === "yield") {
1067
1201
  return { terminal: checkpointAwaitingResult(state, result, input.checkpoint_visibility) };
1068
1202
  }
1203
+ const startedAt = timestamp();
1204
+ const startedMs = Date.now();
1205
+ recordEvent(state, {
1206
+ kind: "agent.proof_assessment.started",
1207
+ checkpoint,
1208
+ stage: "verify",
1209
+ summary: "Proof assessment agent started.",
1210
+ details: { started_at: startedAt }
1211
+ });
1069
1212
  const assessment = await agent.assessProof(context);
1213
+ const durationMs = Date.now() - startedMs;
1070
1214
  const blocker = requirePayload("proof_assessment", assessment, state, result);
1071
1215
  if (blocker) {
1216
+ recordEvent(state, {
1217
+ kind: "agent.proof_assessment.blocked",
1218
+ checkpoint,
1219
+ stage: "verify",
1220
+ summary: blocker.message,
1221
+ details: compactRecord({
1222
+ started_at: startedAt,
1223
+ duration_ms: durationMs,
1224
+ blocker,
1225
+ adapter_details: assessment.details || null
1226
+ })
1227
+ });
1072
1228
  if (blocker.code === "main_agent_proof_review_required") {
1073
1229
  recordEvent(state, {
1074
1230
  kind: "checkpoint.packet.requested",
@@ -1087,7 +1243,12 @@ async function routeCheckpoint(request, state, result, agent, input) {
1087
1243
  checkpoint,
1088
1244
  stage: "verify",
1089
1245
  summary: assessment.summary,
1090
- details: { payload }
1246
+ details: compactRecord({
1247
+ payload,
1248
+ started_at: startedAt,
1249
+ duration_ms: durationMs,
1250
+ adapter_details: assessment.details || null
1251
+ })
1091
1252
  });
1092
1253
  const visualBlocker = proofAssessmentVisualBlocker({
1093
1254
  ...context.fullRiddleState || {},
@@ -1108,7 +1269,8 @@ async function routeCheckpoint(request, state, result, agent, input) {
1108
1269
  recovery_stage: "verify",
1109
1270
  evidence_issue_code: recoveryAssessment.evidence_issue_code || null,
1110
1271
  visual_delta: recoveryAssessment.visual_delta || null,
1111
- proof_assessment: recoveryAssessment
1272
+ proof_assessment: recoveryAssessment,
1273
+ agent_duration_ms: durationMs
1112
1274
  })
1113
1275
  });
1114
1276
  return { next: proofAssessmentContinuation(result, recoveryAssessment) };
@@ -30,6 +30,68 @@ function compactText(value, limit = 600) {
30
30
  if (!text) return void 0;
31
31
  return text.length <= limit ? text : `${text.slice(0, limit - 20).trimEnd()}...`;
32
32
  }
33
+ function numericValue(value) {
34
+ const number = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
35
+ return Number.isFinite(number) ? number : void 0;
36
+ }
37
+ function eventDetails(event) {
38
+ return recordValue(recordValue(event)?.details) || {};
39
+ }
40
+ function collectRunnerMetrics(details) {
41
+ const metrics = [];
42
+ const adapterDetails = recordValue(details.adapter_details);
43
+ const direct = recordValue(adapterDetails?.runner_metrics);
44
+ if (direct) metrics.push(direct);
45
+ const attempts = Array.isArray(adapterDetails?.attempt_summaries) ? adapterDetails.attempt_summaries : [];
46
+ for (const attempt of attempts) {
47
+ const attemptMetrics = recordValue(recordValue(attempt)?.runner_metrics);
48
+ if (attemptMetrics) metrics.push(attemptMetrics);
49
+ }
50
+ return metrics;
51
+ }
52
+ function observabilityFrom(state) {
53
+ const engineEvents = state.events.filter((event) => event.kind === "engine.result").map((event) => ({ event, details: eventDetails(event) }));
54
+ const agentEvents = state.events.filter((event) => event.kind.startsWith("agent.")).map((event) => ({ event, details: eventDetails(event) })).filter(({ details }) => numericValue(details.duration_ms) !== void 0);
55
+ const retryEvents = state.events.filter((event) => /retry|recovery/i.test(event.kind) || /retry|recovery/i.test(event.summary || "")).slice(-8);
56
+ const runnerMetrics = agentEvents.flatMap(({ details }) => collectRunnerMetrics(details));
57
+ const promptChars = runnerMetrics.map((metrics) => numericValue(metrics.prompt_chars)).filter((value) => value !== void 0);
58
+ const sum = (values) => values.reduce((total, value) => total + (value ?? 0), 0);
59
+ const recentEngineTimings = engineEvents.slice(-8).map(({ event, details }) => compactRecord({
60
+ checkpoint: event.checkpoint || null,
61
+ stage: event.stage || null,
62
+ duration_ms: numericValue(details.duration_ms),
63
+ ok: details.ok ?? null
64
+ }));
65
+ const recentAgentTimings = agentEvents.slice(-8).map(({ event, details }) => {
66
+ const metrics = collectRunnerMetrics(details);
67
+ const localPromptChars = metrics.map((item) => numericValue(item.prompt_chars)).filter((value) => value !== void 0);
68
+ return compactRecord({
69
+ kind: event.kind,
70
+ checkpoint: event.checkpoint || null,
71
+ stage: event.stage || null,
72
+ duration_ms: numericValue(details.duration_ms),
73
+ prompt_chars: localPromptChars.length ? Math.max(...localPromptChars) : void 0,
74
+ attempt_count: numericValue(recordValue(details.adapter_details)?.attempt_count)
75
+ });
76
+ });
77
+ return compactRecord({
78
+ engine_call_count: engineEvents.length,
79
+ agent_call_count: agentEvents.length,
80
+ engine_total_ms: sum(engineEvents.map(({ details }) => numericValue(details.duration_ms))),
81
+ agent_total_ms: sum(agentEvents.map(({ details }) => numericValue(details.duration_ms))),
82
+ max_agent_prompt_chars: promptChars.length ? Math.max(...promptChars) : void 0,
83
+ total_agent_prompt_chars: promptChars.length ? sum(promptChars) : void 0,
84
+ recent_engine_timings: recentEngineTimings.length ? recentEngineTimings : void 0,
85
+ recent_agent_timings: recentAgentTimings.length ? recentAgentTimings : void 0,
86
+ retry_event_count: retryEvents.length,
87
+ recent_retry_reasons: retryEvents.length ? retryEvents.map((event) => compactRecord({
88
+ kind: event.kind,
89
+ checkpoint: event.checkpoint || null,
90
+ stage: event.stage || null,
91
+ summary: compactText(event.summary, 240)
92
+ })) : void 0
93
+ });
94
+ }
33
95
  function visualDeltaFrom(input) {
34
96
  const fullState = input.fullRiddleState || {};
35
97
  const bundle = recordValue(fullState.evidence_bundle);
@@ -142,6 +204,7 @@ function createRiddleProofRunCard(state, input = {}) {
142
204
  proof_evidence_present: fullState.proof_evidence_present === true || Boolean(bundle?.proof_evidence || bundle?.proof_evidence_sample),
143
205
  artifacts
144
206
  }),
207
+ observability: observabilityFrom(state),
145
208
  stop_condition: compactRecord({
146
209
  status: state.status,
147
210
  terminal: isTerminalStatus(state.status),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createRiddleProofRunCard
3
- } from "./chunk-PLSGW2GI.js";
3
+ } from "./chunk-CI2F66EE.js";
4
4
  import {
5
5
  compactRecord,
6
6
  isTerminalStatus,
@@ -3,7 +3,7 @@ import {
3
3
  appendStageHeartbeat,
4
4
  createRunState,
5
5
  setRunStatus
6
- } from "./chunk-JOXTKWX6.js";
6
+ } from "./chunk-U7Q3RB5D.js";
7
7
  import {
8
8
  createRunResult
9
9
  } from "./chunk-DUFDZJOF.js";
@@ -1,3 +1,7 @@
1
+ import {
2
+ compactRecord
3
+ } from "./chunk-DUFDZJOF.js";
4
+
1
5
  // src/codex-exec-agent.ts
2
6
  import { execFileSync, spawnSync } from "child_process";
3
7
  import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
@@ -128,10 +132,10 @@ var PROOF_SCHEMA = {
128
132
  source: { type: "string", enum: ["supervising_agent"] }
129
133
  }
130
134
  };
131
- var PROMPT_STRING_LIMIT = 2e3;
132
- var PROMPT_ARRAY_LIMIT = 12;
133
- var PROMPT_OBJECT_KEY_LIMIT = 70;
134
- var PROMPT_BLOCK_LIMIT = 12e4;
135
+ var PROMPT_STRING_LIMIT = 1400;
136
+ var PROMPT_ARRAY_LIMIT = 8;
137
+ var PROMPT_OBJECT_KEY_LIMIT = 50;
138
+ var PROMPT_BLOCK_LIMIT = 7e4;
135
139
  var PROMPT_KEY_PRIORITY = [
136
140
  "ok",
137
141
  "status",
@@ -153,6 +157,16 @@ var PROMPT_KEY_PRIORITY = [
153
157
  "after_cdn",
154
158
  "before_baseline",
155
159
  "prod_baseline",
160
+ "route_hints",
161
+ "route_candidates",
162
+ "keyword_hits",
163
+ "observations",
164
+ "latest_attempt",
165
+ "attempt_history",
166
+ "current_plan",
167
+ "plan_history",
168
+ "decision_history",
169
+ "refined_inputs",
156
170
  "recon_assessment",
157
171
  "baseline_understanding",
158
172
  "supervisor_author_packet",
@@ -176,6 +190,7 @@ var PROMPT_KEY_PRIORITY = [
176
190
  "shipGate",
177
191
  "last_error",
178
192
  "errors",
193
+ "runtime_events",
179
194
  "events"
180
195
  ];
181
196
  var PROMPT_PRIORITY_INDEX = new Map(PROMPT_KEY_PRIORITY.map((key, index) => [key, index]));
@@ -192,7 +207,7 @@ function compactPromptValue(value, depth = 0, key = "") {
192
207
  if (!looksLikeUrl && (lowerKey.includes("base64") || lowerKey.includes("data_url") || lowerKey.includes("screenshot_blob"))) {
193
208
  return `[omitted ${value.length} chars from ${key || "large artifact"}]`;
194
209
  }
195
- const limit = looksLikeUrl ? 1e3 : depth <= 1 ? PROMPT_STRING_LIMIT : 1200;
210
+ const limit = looksLikeUrl ? 1e3 : depth <= 1 ? PROMPT_STRING_LIMIT : 900;
196
211
  return truncatePromptString(value, limit);
197
212
  }
198
213
  if (Array.isArray(value)) {
@@ -323,11 +338,39 @@ function isHarnessVerificationOnlyBlocker(blocker) {
323
338
  const text = blocker.toLowerCase();
324
339
  return (text.includes("erofs") || text.includes("read-only file system")) && text.includes("node_modules") && (text.includes(".vite-temp") || text.includes("vite.config"));
325
340
  }
341
+ function runnerMetrics(input) {
342
+ const schemaText = JSON.stringify(input.request.schema);
343
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
344
+ return compactRecord({
345
+ purpose: input.request.purpose,
346
+ workdir: input.request.workdir,
347
+ started_at: input.startedAt,
348
+ finished_at: finishedAt,
349
+ duration_ms: Date.now() - input.startedMs,
350
+ prompt_chars: input.request.prompt.length,
351
+ prompt_lines: input.request.prompt.split(/\r?\n/).length,
352
+ schema_chars: schemaText.length,
353
+ stdout_chars: (input.stdout || "").length,
354
+ stderr_chars: (input.stderr || "").length,
355
+ final_message_chars: (input.finalText || "").length,
356
+ exit_status: input.status ?? null,
357
+ timed_out: input.timedOut || false,
358
+ error_code: input.errorCode,
359
+ codex_command: input.config.codexCommand || "codex",
360
+ codex_model: input.config.codexModel,
361
+ codex_sandbox: input.config.codexSandbox || "workspace-write",
362
+ codex_full_auto: input.config.codexFullAuto !== false,
363
+ timeout_ms: Number(input.config.codexTimeoutMs || 6e5)
364
+ });
365
+ }
326
366
  function createCodexExecJsonRunner(config = {}) {
327
367
  return (request) => {
368
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
369
+ const startedMs = Date.now();
328
370
  if (!request.workdir || !existsSync(request.workdir)) {
329
371
  return {
330
372
  ok: false,
373
+ metrics: runnerMetrics({ request, config, startedAt, startedMs, errorCode: "workdir_missing" }),
331
374
  blocker: {
332
375
  code: "codex_workdir_missing",
333
376
  message: `Codex workdir does not exist for ${request.purpose}.`,
@@ -372,6 +415,17 @@ function createCodexExecJsonRunner(config = {}) {
372
415
  ok: false,
373
416
  stdout: proc.stdout || "",
374
417
  stderr: proc.stderr || "",
418
+ metrics: runnerMetrics({
419
+ request,
420
+ config,
421
+ startedAt,
422
+ startedMs,
423
+ stdout: proc.stdout || "",
424
+ stderr: proc.stderr || "",
425
+ status: proc.status,
426
+ timedOut,
427
+ errorCode: proc.error.code || "spawn_error"
428
+ }),
375
429
  blocker: {
376
430
  code: timedOut ? "codex_timeout" : "codex_exec_error",
377
431
  message: timedOut ? `Codex timed out during ${request.purpose}.` : `Codex failed to start or complete ${request.purpose}.`,
@@ -384,6 +438,16 @@ function createCodexExecJsonRunner(config = {}) {
384
438
  ok: false,
385
439
  stdout: proc.stdout || "",
386
440
  stderr: proc.stderr || "",
441
+ metrics: runnerMetrics({
442
+ request,
443
+ config,
444
+ startedAt,
445
+ startedMs,
446
+ stdout: proc.stdout || "",
447
+ stderr: proc.stderr || "",
448
+ status: proc.status,
449
+ errorCode: "nonzero_exit"
450
+ }),
387
451
  blocker: {
388
452
  code: "codex_nonzero_exit",
389
453
  message: `Codex exited with status ${proc.status} during ${request.purpose}.`,
@@ -398,6 +462,17 @@ function createCodexExecJsonRunner(config = {}) {
398
462
  ok: false,
399
463
  stdout: proc.stdout || "",
400
464
  stderr: proc.stderr || "",
465
+ metrics: runnerMetrics({
466
+ request,
467
+ config,
468
+ startedAt,
469
+ startedMs,
470
+ stdout: proc.stdout || "",
471
+ stderr: proc.stderr || "",
472
+ finalText,
473
+ status: proc.status,
474
+ errorCode: "invalid_json"
475
+ }),
401
476
  blocker: {
402
477
  code: "codex_invalid_json",
403
478
  message: `Codex completed ${request.purpose}, but did not return valid JSON.`,
@@ -409,7 +484,17 @@ function createCodexExecJsonRunner(config = {}) {
409
484
  ok: true,
410
485
  json: parsed,
411
486
  stdout: proc.stdout || "",
412
- stderr: proc.stderr || ""
487
+ stderr: proc.stderr || "",
488
+ metrics: runnerMetrics({
489
+ request,
490
+ config,
491
+ startedAt,
492
+ startedMs,
493
+ stdout: proc.stdout || "",
494
+ stderr: proc.stderr || "",
495
+ finalText,
496
+ status: proc.status
497
+ })
413
498
  };
414
499
  } finally {
415
500
  rmSync(tmpDir, { recursive: true, force: true });
@@ -430,14 +515,21 @@ function payloadOrBlocker(raw, checkpoint) {
430
515
  ok: false,
431
516
  blocker: {
432
517
  ...blocker,
433
- checkpoint
518
+ checkpoint,
519
+ details: {
520
+ ...blocker.details || {},
521
+ runner_metrics: raw.metrics || null
522
+ }
434
523
  }
435
524
  };
436
525
  }
437
526
  return {
438
527
  ok: true,
439
528
  payload: raw.json,
440
- summary: typeof raw.json.summary === "string" ? raw.json.summary : void 0
529
+ summary: typeof raw.json.summary === "string" ? raw.json.summary : void 0,
530
+ details: compactRecord({
531
+ runner_metrics: raw.metrics || null
532
+ })
441
533
  };
442
534
  }
443
535
  function stringArray(value) {
@@ -573,14 +665,16 @@ function createCodexExecAgentAdapter(config = {}, runner = createCodexExecJsonRu
573
665
  agent_summary: summary,
574
666
  agent_changed_files: changedFiles,
575
667
  agent_tests_run: testsRun,
576
- agent_blockers: blockers
668
+ agent_blockers: blockers,
669
+ runner_metrics: raw.metrics || null
577
670
  };
578
671
  attemptSummaries.push({
579
672
  purpose,
580
673
  summary,
581
674
  changed_files: changedFiles,
582
675
  tests_run: testsRun,
583
- blockers
676
+ blockers,
677
+ runner_metrics: raw.metrics || null
584
678
  });
585
679
  if (hardBlockers.length) {
586
680
  return {