lvwerra HF Staff Claude Opus 4.8 (1M context) commited on
Commit
68fe810
·
1 Parent(s): 40f8c9b

Exclude Codex guardian subagent rollouts from traces and pinning

Browse files

Codex >=0.142 spawns internal "guardian" safety-judge subagents whose
rollout files share the session's cwd. The trace parser attributed them
to the user's session, polluting or blanking out the Overview digest
(codex-1 showed nothing), and the ID capture could pin a guardian's
conversation id, which would also break resume.

Flag rollouts whose session_meta says thread_source=subagent (or has a
source.subagent) and skip them in trace attribution and in
tryCaptureCodexId. Sessions already pinned to a guardian or missing
rollout self-heal on next launch: the stale pin is cleared so the real
conversation is re-captured.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files changed (2) hide show
  1. server/src/runner.js +22 -1
  2. server/src/traces.js +9 -1
server/src/runner.js CHANGED
@@ -164,14 +164,35 @@ function tryCaptureCodexId(sessionId, workdir, sinceMs) {
164
  if (!m || claimed.has(m[1])) continue;
165
  let meta;
166
  try { meta = JSON.parse(firstLine(c.p)); } catch { continue; }
167
- if ((meta && meta.payload && meta.payload.cwd) !== workdir) continue;
 
 
 
 
 
168
  update(sessionId, { codexSessionId: m[1], codexRollout: c.p });
169
  return true;
170
  }
171
  return false;
172
  }
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  function scheduleCodexCapture(session, workdir) {
 
 
 
175
  if (session.codexSessionId || codexCapturing.has(session.id)) return;
176
  codexCapturing.add(session.id);
177
  const since = Date.now() - 2000;
 
164
  if (!m || claimed.has(m[1])) continue;
165
  let meta;
166
  try { meta = JSON.parse(firstLine(c.p)); } catch { continue; }
167
+ const mp = (meta && meta.payload) || {};
168
+ if (mp.cwd !== workdir) continue;
169
+ // Skip Codex's internal guardian/subagent rollouts — they share the cwd but
170
+ // aren't this agent's conversation, so pinning one would break resume and
171
+ // the Overview digest.
172
+ if (mp.thread_source === 'subagent' || (mp.source && mp.source.subagent)) continue;
173
  update(sessionId, { codexSessionId: m[1], codexRollout: c.p });
174
  return true;
175
  }
176
  return false;
177
  }
178
 
179
+ // A pin captured before subagents were filtered out (or one whose rollout was
180
+ // rotated away) may point at a guardian/missing rollout — clear it so we
181
+ // re-capture the real conversation on this launch.
182
+ function pinIsStale(session) {
183
+ if (!session.codexSessionId) return false;
184
+ const p = session.codexRollout;
185
+ if (!p || !fs.existsSync(p)) return true;
186
+ try {
187
+ const mp = (JSON.parse(firstLine(p)) || {}).payload || {};
188
+ return mp.thread_source === 'subagent' || !!(mp.source && mp.source.subagent);
189
+ } catch { return false; }
190
+ }
191
+
192
  function scheduleCodexCapture(session, workdir) {
193
+ if (session.codexSessionId && pinIsStale(session)) {
194
+ session = update(session.id, { codexSessionId: undefined, codexRollout: undefined }) || session;
195
+ }
196
  if (session.codexSessionId || codexCapturing.has(session.id)) return;
197
  codexCapturing.add(session.id);
198
  const since = Date.now() - 2000;
server/src/traces.js CHANGED
@@ -135,7 +135,14 @@ function parseCodex(txt) {
135
  let j; try { j = JSON.parse(line); } catch { continue; }
136
  if (j.timestamp) addTs(st, j.timestamp);
137
  const p = j.payload || {};
138
- if (j.type === 'session_meta' && p.cwd) st.cwd = p.cwd; // for cwd-fallback attribution
 
 
 
 
 
 
 
139
  if (j.type === 'response_item') {
140
  switch (p.type) {
141
  case 'message':
@@ -537,6 +544,7 @@ async function build() {
537
  seenFiles.add(p);
538
  const parsed = await statsFor(p, parseCodex);
539
  if (!parsed) continue;
 
540
  const m = path.basename(p).match(UUID_RE);
541
  let session = m ? byCodexId.get(m[1]) : null;
542
  if (!session && parsed.stats.cwd) {
 
135
  let j; try { j = JSON.parse(line); } catch { continue; }
136
  if (j.timestamp) addTs(st, j.timestamp);
137
  const p = j.payload || {};
138
+ if (j.type === 'session_meta') {
139
+ if (p.cwd) st.cwd = p.cwd; // for cwd-fallback attribution
140
+ // Codex >=0.142 spawns internal "guardian" safety-judge subagents whose
141
+ // rollouts share the session's cwd. They aren't the user's conversation —
142
+ // flag them so build() skips attribution (they'd otherwise pollute or
143
+ // blank out the Overview digest).
144
+ if (p.thread_source === 'subagent' || (p.source && p.source.subagent)) st.subagent = true;
145
+ }
146
  if (j.type === 'response_item') {
147
  switch (p.type) {
148
  case 'message':
 
544
  seenFiles.add(p);
545
  const parsed = await statsFor(p, parseCodex);
546
  if (!parsed) continue;
547
+ if (parsed.stats.subagent) continue; // Codex guardian subagent — not the user's thread
548
  const m = path.basename(p).match(UUID_RE);
549
  let session = m ? byCodexId.get(m[1]) : null;
550
  if (!session && parsed.stats.cwd) {