skalpel 4.0.49 → 4.0.50
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.
- package/codex-normalize.mjs +18 -1
- package/package.json +1 -1
package/codex-normalize.mjs
CHANGED
|
@@ -141,13 +141,28 @@ export function loadCodexEntries(file, maxBytes = DEFAULT_TAIL_BYTES) {
|
|
|
141
141
|
}
|
|
142
142
|
if (e.type !== "response_item") continue;
|
|
143
143
|
const p = e.payload || {};
|
|
144
|
+
// The rollout event's OWN wall-clock timestamp (top-level ISO string on every response_item —
|
|
145
|
+
// verified against real ~/.codex rollouts). Claude transcript entries carry `.timestamp` natively
|
|
146
|
+
// (loadClaudeEntries pushes the raw object), and downstream consumers (efficacy-eval, first-run,
|
|
147
|
+
// eval-harness) index `e.timestamp` per entry to compute the MEASURED fix-delta: t(later same-proof
|
|
148
|
+
// PASS) − t(the false "done"). Codex normalized entries omitted it, so every Codex session was silently
|
|
149
|
+
// forced to count-only even when the rollout recorded the real times. Propagate each event's own
|
|
150
|
+
// timestamp — the claim entry gets the claim's time, the proof-output entry gets the completion time —
|
|
151
|
+
// so a Codex fix-delta measures from the same two real timestamps a Claude one does. Only a string is
|
|
152
|
+
// carried (matching the consumers' `typeof === "string"` guard); anything else is left absent, exactly
|
|
153
|
+
// as a Claude entry with no timestamp — never an invented time.
|
|
154
|
+
const ts = typeof e.timestamp === "string" ? e.timestamp : undefined;
|
|
144
155
|
if (p.type === "message") {
|
|
145
156
|
const role = p.role === "assistant" ? "assistant" : "user";
|
|
146
157
|
const text = Array.isArray(p.content)
|
|
147
158
|
? p.content.map((b) => (b && typeof b.text === "string" ? b.text : "")).join("")
|
|
148
159
|
: "";
|
|
149
160
|
if (text.trim())
|
|
150
|
-
entries.push({
|
|
161
|
+
entries.push({
|
|
162
|
+
type: role,
|
|
163
|
+
timestamp: ts,
|
|
164
|
+
message: { role, content: [{ type: "text", text }] },
|
|
165
|
+
});
|
|
151
166
|
} else if (p.type === "function_call" && p.name === "exec_command" && p.call_id) {
|
|
152
167
|
let cmd = "";
|
|
153
168
|
let workdir = null;
|
|
@@ -165,6 +180,7 @@ export function loadCodexEntries(file, maxBytes = DEFAULT_TAIL_BYTES) {
|
|
|
165
180
|
if (cmd)
|
|
166
181
|
entries.push({
|
|
167
182
|
cwd: workdir,
|
|
183
|
+
timestamp: ts,
|
|
168
184
|
message: {
|
|
169
185
|
role: "assistant",
|
|
170
186
|
content: [{ type: "tool_use", name: "Bash", id: p.call_id, input: { command: cmd } }],
|
|
@@ -173,6 +189,7 @@ export function loadCodexEntries(file, maxBytes = DEFAULT_TAIL_BYTES) {
|
|
|
173
189
|
} else if (p.type === "function_call_output" && p.call_id) {
|
|
174
190
|
const { exitCode, body } = parseCodexOutput(p.output);
|
|
175
191
|
entries.push({
|
|
192
|
+
timestamp: ts,
|
|
176
193
|
message: {
|
|
177
194
|
role: "user",
|
|
178
195
|
content: [
|