mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-05 23:19:43 +02:00
feat(codex): list codex conversations and resume them
Codex conversations never appeared in the session list, and the resume path skipped codex, so picking one back up meant finding its thread id by hand and POSTing codexConfig.resumeSessionId to /api/sessions. Two gaps caused it: - The unified list is built from ~/.claude/projects plus omp's own store. Codex writes to neither: its rollouts live in ~/.codex/sessions/<y>/<m>/<d>. - terminal-ui.js sends a continuation only for the CLIs with a "continue most recent" flag. Codex has no such flag — it names a thread by an exact id — and nothing supplied one. Add codex-transcript.ts, the codex analog of omp-transcript.ts, and wire it into gatherUnifiedInputs() beside the omp scan. A rollout row carries `resumeId`, the thread id `codex resume` takes, and the resume path sends it as codexConfig.resumeSessionId. `resumeId` is what keeps the two kinds of row apart: only a transcript scanner sets it, so a LIVE codex row — whose sessionId is Codeman's own uuid — can never ask codex for a thread that does not exist. Three things measured against a real store of 519 rollouts rather than assumed: - Rollouts are far too large to read whole (median 407 KiB, p90 1.3 MiB, max 25 MiB, 381 MiB total), so this reads a 128 KiB head for the identity and the opening prompt and a bounded tail for the most recent one. session_meta is written once and never rewritten, so per-path identity is cached; a warm rescan of that store costs ~75ms against ~470ms cold. - codex 0.152.1 emits no event_msg/user_message rows at all. It writes event_msg/item_completed carrying an item.type of UserMessage. Both shapes are read, plus response_item as a last resort. - That last resort sees injected context, and the first such row is the repo's AGENTS.md every time, so injections are dropped rather than used as titles. Sub-agent threads (thread_source: 'subagent') are left out; codex spawns them for itself and on a real store they outnumber the resumable threads.
This commit is contained in:
@@ -14,6 +14,30 @@ import {
|
||||
} from '../../src/services/unified-session-service.js';
|
||||
|
||||
describe('mergeUnifiedSessions', () => {
|
||||
it("carries a transcript row's own resume token, and stamps none on a live row", () => {
|
||||
// codex names a thread by an id in its rollout, not by Codeman's session id.
|
||||
// The scanner sets `resumeId`; a live session never does, which is what stops
|
||||
// a resume from asking codex for a thread whose id is really Codeman's uuid.
|
||||
const merged = mergeUnifiedSessions({
|
||||
live: [{ id: 'codeman-uuid', status: 'idle', mode: 'codex' }],
|
||||
history: [
|
||||
{
|
||||
sessionId: 'codex-thread-id',
|
||||
workingDir: '/w',
|
||||
sizeBytes: 4000,
|
||||
lastModified: '2026-09-02T00:00:00.000Z',
|
||||
mode: 'codex',
|
||||
resumeId: 'codex-thread-id',
|
||||
},
|
||||
],
|
||||
});
|
||||
const fromTranscript = merged.find((m) => m.sessionId === 'codex-thread-id');
|
||||
const fromLive = merged.find((m) => m.sessionId === 'codeman-uuid');
|
||||
expect(fromTranscript?.resumeId).toBe('codex-thread-id');
|
||||
expect(fromTranscript?.mode).toBe('codex');
|
||||
expect(fromLive?.resumeId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('dedupes the same sessionId across live + persisted into one item', () => {
|
||||
const merged = mergeUnifiedSessions({
|
||||
live: [{ id: 's1', status: 'working', isWorking: true }],
|
||||
|
||||
Reference in New Issue
Block a user