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.
@@ -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({ type: role, message: { role, content: [{ type: "text", text }] } });
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: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skalpel",
3
- "version": "4.0.49",
3
+ "version": "4.0.50",
4
4
  "description": "Behavioral graph for AI-assisted coding — learns how you work and steers Claude Code + Codex in real time.",
5
5
  "type": "module",
6
6
  "bin": {