[codex] add Codex usage indicator#537
Conversation
There was a problem hiding this comment.
Findings
-
[Major] Codex session listing bypasses the runner workspace scope — the new machine RPC returns every transcript under
CODEX_HOME, including title/path, without using the existing workspace-root guard that protects machine directory listing and spawn. On a scoped runner, this lets the web UI enumerate Codex sessions outside the allowed workspace. Evidencecli/src/modules/common/handlers/codexSessions.ts:15.
Suggested fix:const allowed = options?.isPathAllowed const sessions = [] for (const session of result.sessions) { if (!allowed || await allowed(session.path)) { sessions.push(session) } } return { success: true, sessions, nextCursor: result.nextCursor }
-
[Major]
importHistoryis dropped before the runner builds Codex args — the hub now sendsimportHistorytospawn-happy-session, but the machine RPC handler does not destructure or forward it tospawnSession, sobuildCliArgs()never sees it and never adds--hapi-import-history. Resuming a Codex session from the new selector starts the Codex thread but does not import the transcript into HAPI. Evidencehub/src/sync/rpcGateway.ts:158.
Suggested fix:const { directory, sessionId, resumeSessionId, machineId, approvedNewDirectoryCreation, agent, model, effort, modelReasoningEffort, yolo, permissionMode, token, sessionType, worktreeName, importHistory } = params || {} const result = await spawnSession({ directory, sessionId, resumeSessionId, importHistory, // existing fields... })
-
[Minor] Local
--hapi-import-historycan replay the same transcript twice —runCodex()imports the transcript before launching, then the local scanner is started withreplayExistingEvents: session.importHistoryand sends those same existing user/agent messages again. Evidencecli/src/codex/codexLocalLauncher.ts:73.
Suggested fix:const createdScanner = await createCodexSessionScanner({ transcriptPath, replayExistingEvents: false, onSessionId: (sessionId) => { session.onSessionFound(sessionId) }, // existing onEvent... })
Questions
- None.
Summary
Review mode: initial
3 findings: one workspace-scope privacy regression, one broken import-history propagation path, and one local duplicate-history edge case.
Testing
- Not run (automation); static
git diff --checkpassed.
HAPI Bot
| logger.debug('List Codex sessions request'); | ||
|
|
||
| try { | ||
| const result = await listCodexSessions(data ?? {}); |
There was a problem hiding this comment.
[MAJOR] This handler returns every Codex transcript under CODEX_HOME with title/path, but it is registered as a machine RPC and does not receive the runner's workspaceRoot guard. A scoped runner already blocks machine directory listing/spawn outside the root; this endpoint now bypasses that boundary and exposes sessions from unrelated directories.
Suggested fix:
const allowed = options?.isPathAllowed
const sessions = []
for (const session of result.sessions) {
if (!allowed || await allowed(session.path)) {
sessions.push(session)
}
}
return { success: true, sessions, nextCursor: result.nextCursor }| machineId, | ||
| 'spawn-happy-session', | ||
| { type: 'spawn-in-directory', directory, agent, model, modelReasoningEffort, yolo, sessionType, worktreeName, resumeSessionId, effort, permissionMode } | ||
| { type: 'spawn-in-directory', directory, agent, model, modelReasoningEffort, yolo, sessionType, worktreeName, resumeSessionId, importHistory, effort, permissionMode } |
There was a problem hiding this comment.
[MAJOR] importHistory is sent here, but the receiving machine RPC handler currently destructures params without importHistory and does not include it in the spawnSession() options. As a result, runner-spawned Codex resumes never pass --hapi-import-history, so the new selector resumes the Codex thread without importing prior transcript messages into HAPI.
Suggested fix:
const { directory, sessionId, resumeSessionId, machineId, approvedNewDirectoryCreation, agent, model, effort, modelReasoningEffort, yolo, permissionMode, token, sessionType, worktreeName, importHistory } = params || {}
const result = await spawnSession({
directory,
sessionId,
resumeSessionId,
importHistory,
machineId,
approvedNewDirectoryCreation,
agent,
model,
effort,
modelReasoningEffort,
yolo,
permissionMode,
token,
sessionType,
worktreeName
})| } | ||
| const createdScanner = await createCodexSessionScanner({ | ||
| transcriptPath, | ||
| replayExistingEvents: session.importHistory, |
There was a problem hiding this comment.
[MINOR] runCodex() already imports the transcript before launch when --hapi-import-history is set. In local mode this scanner then replays the existing transcript and sends the same user/agent messages again, producing duplicate history in HAPI for hapi codex resume <id> --hapi-import-history.
Suggested fix:
const createdScanner = await createCodexSessionScanner({
transcriptPath,
replayExistingEvents: false,
onSessionId: (sessionId) => {
session.onSessionFound(sessionId)
},
// existing onEvent...
})
Summary
Adds live Codex usage state and a composer usage ring for Codex sessions.
Changes
token_countevents from app-server notifications and transcript tailing.session.metadata.codexUsageand sends metadata patches over SSE.Validation
bun run test:cli -- src/codex/codexRemoteLauncher.test.ts src/codex/utils/codexUsage.test.tscd shared && bun test src/codexUsageSchema.test.tscd web && bun run test -- src/components/AssistantChat/codexUsageDisplay.test.tsbun typecheck