refactor(agents): remove transcript token cost enrichment#1994
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b93dfad18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Pull request overview
This PR updates the Codex agent plugin to avoid expensive JSONL streaming/parsing on request paths for sessions that are already terminal (or runtime-missing) by returning a cheap AgentSessionInfo derived from persisted restore metadata (codexThreadId / codexModel). It also adds a short-lived cache so repeated getSessionInfo() / getRestoreCommand() calls within the same refresh window don’t duplicate JSONL parsing work.
Changes:
- Add a metadata-only fast path for
getSessionInfo()whencodexThreadIdis already persisted (especially for terminal/runtime-missing sessions). - Add a module-level cache + in-flight dedupe for streamed Codex session data to avoid repeated parsing within a TTL window.
- Extend tests to cover “no JSONL streaming for terminal sessions” and “streamed data is cached across repeated calls”.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/plugins/agent-codex/src/index.ts | Adds metadata-only getSessionInfo() path and introduces cached/deduped JSONL streaming for session data. |
| packages/plugins/agent-codex/src/index.test.ts | Adds coverage for metadata-only fast path and the new streamed-session-data cache behavior. |
Greptile SummaryThis PR fixes a Codex-session-manager OOM path by avoiding full-file JSONL streaming for sessions that already have
Confidence Score: 5/5Safe to merge — the OOM fix is well-scoped and the bounded-read approach correctly prevents large JSONL files from being loaded into memory. All changes are additive safeguards (byte limits, fast paths) or pure deletions of cost-estimation code with no remaining callers. The fast path for terminal sessions is covered by new tests, and the bounded JSONL scan mirrors the existing cwd-match scan approach. The two observations are minor ordering/optimization nits that do not affect correctness for real-world session files. packages/plugins/agent-codex/src/index.ts — specifically the byte-limit ordering in readJsonlPrefixLines and the missing early-exit in readCodexSessionMetadata.
|
| Filename | Overview |
|---|---|
| packages/plugins/agent-codex/src/index.ts | Core change: replaces unbounded JSONL streaming with a bounded prefix scan, adds buildPersistedCodexSessionInfo fast path, and removes cost estimation. Logic is correct; minor ordering note on byte-limit break vs. newline processing. |
| packages/plugins/agent-codex/src/index.test.ts | Test suite updated to replace stream mocks with file-handle mocks, adds coverage for terminal fast-path, live sessions with/without model metadata, and lifecycle-driven terminal detection. |
| packages/core/src/types.ts | Removes CostEstimate interface and its cost field from AgentSessionInfo; doc-comment updates only. |
| packages/plugins/agent-claude-code/src/index.ts | Removes extractCost and cost-related JSONL fields; simplifies parseJsonlFileTail to always use a file handle with bounded reads. Behavior for tail-read logic is unchanged. |
| packages/plugins/agent-aider/src/index.ts | Switches extractAiderSummary from readFile to a bounded 64 KB head read via open. Correct since the first user message (#### prefix) is always near the start of .aider.chat.history.md. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getSessionInfo called] --> B{buildPersistedCodexSessionInfo}
B --> C{codexThreadId in metadata?}
C -- No --> D[findCodexSessionFileCached]
C -- Yes --> E{codexModel in metadata?}
E -- Yes --> F[Return info from metadata no I/O]
E -- No --> G{isTerminalSession?}
G -- Yes --> H[Return info from metadata no I/O, summary=null]
G -- No --> D
D --> I{Session file found?}
I -- No --> J[return null]
I -- Yes --> K[readCodexSessionMetadata first 100 lines / 1 MB]
K --> L{threadId or model found?}
L -- No --> J
L -- Yes --> M[buildCodexSessionInfo persists codexModel to metadata]
M --> N[Return AgentSessionInfo]
F --> N
H --> N
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/plugins/agent-codex/src/index.ts:158-163
The byte-limit guard fires **before** the newline-processing loop, so all complete lines that happen to land in the chunk that pushes `totalBytesRead` past `maxBytes` are silently discarded. In the worst case a batch of lines that would have been within the effective budget (accumulated in `partialLine`) is lost when the loop breaks. Moving the byte-limit check to after the newline loop ensures every complete line already decoded from the last chunk is captured.
```suggestion
partialLine += decoder.write(buffer.subarray(0, bytesRead));
let newlineIndex = partialLine.indexOf("\n");
while (newlineIndex !== -1 && lines.length < maxLines) {
const line = partialLine.slice(0, newlineIndex).trim();
if (line) lines.push(line);
partialLine = partialLine.slice(newlineIndex + 1);
newlineIndex = partialLine.indexOf("\n");
}
if (totalBytesRead > maxBytes) {
break;
}
newlineIndex = partialLine.indexOf("\n");
```
### Issue 2 of 2
packages/plugins/agent-codex/src/index.ts:321-334
`readCodexSessionMetadata` continues iterating through all 100 lines even after both `threadId` and `model` have been populated. Since `session_meta` (with the thread ID) and `turn_context` (with the model) are nearly always the first two lines of a Codex JSONL file, adding an early-exit saves unnecessary JSON parsing for the remaining lines on every refresh that takes the live path.
```suggestion
if (entry.type === "turn_context" && typeof payload.model === "string" && payload.model) {
data.model = payload.model;
} else if (!data.model && typeof payload.model === "string" && payload.model) {
data.model = payload.model;
}
if (data.threadId && data.model) break;
} catch {
// Skip malformed lines
}
}
} catch {
return null;
}
return data.threadId || data.model ? data : null;
```
Reviews (3): Last reviewed commit: "refactor(agents): remove session token c..." | Re-trigger Greptile
Summary
AgentSessionInfo.cost/CostEstimateand delete live token/cost aggregation from agent session-info pathsThis is a follow-up cleanup after #1992 and #1996. It keeps the Codex thread-id lookup/restore behavior needed for legacy sessions while making dashboard/session-list enrichment safe by construction.
Related: #1991, #1935.
Tests
pnpm --filter @aoagents/ao-plugin-agent-codex testpnpm --filter @aoagents/ao-plugin-agent-codex typecheckpnpm --filter @aoagents/ao-plugin-agent-claude-code testpnpm --filter @aoagents/ao-plugin-agent-claude-code typecheckpnpm --filter @aoagents/ao-plugin-agent-aider testpnpm --filter @aoagents/ao-plugin-agent-aider typecheckpnpm --filter @aoagents/ao-plugin-agent-cursor testpnpm --filter @aoagents/ao-plugin-agent-cursor typecheckpnpm --filter @aoagents/ao-plugin-agent-kimicode testpnpm --filter @aoagents/ao-plugin-agent-kimicode typecheckpnpm --filter @aoagents/ao-plugin-agent-opencode testpnpm --filter @aoagents/ao-plugin-agent-opencode typecheckpnpm --filter @aoagents/ao-plugin-agent-grok testpnpm --filter @aoagents/ao-plugin-agent-grok typecheckpnpm --filter @aoagents/ao-core typecheckpnpm --filter @aoagents/ao-core exec vitest run src/__tests__/activity-events-migration.test.tspnpm --filter @aoagents/ao-web test -- src/lib/__tests__/serialize.test.tspnpm --filter @aoagents/ao-web typecheckpnpm lintgit diff --checkNotes: full
pnpm buildis blocked locally by existingpackages/notifier-macosSwift build environment error (SOURCE_DATE_EPOCHempty). Fullpnpm --filter @aoagents/ao-core testis blocked locally by timeouts inactivity-events-migration.test.ts; the same test file passes when run directly as listed above.