mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 22:49:41 +02:00
fix(review): add Codex last-response test coverage + minor hardening (PR #152)
- Add test/routes/session-routes-codex-last-response.test.ts (app.inject +
temp CODEX_HOME fixture rollouts): originator match beats cwd fallback when
two panes share a dir, cwd fallback excludes sibling-claimed/foreign-cwd
rollouts, resume-uuid filename match, history.jsonl pin outranks originator,
event_msg/legacy user-turn dedup keeps old-codex turns, injected-context
filtering, image placeholder, envelope shape ({success:true,data:{text,
timestamp[,messages]}}), and a Claude-mode regression guard (codex reader
never consulted for claude sessions)
- Replace clear-at-cap Map caches (codexHistoryPinCache, codexRolloutMetaCache)
with the repo-standard LRUMap so a full cache wipe can't thrash hot entries
on large rollout collections
- Join multi-block assistant/user text with a blank line instead of no
separator (extractCodexBlockText)
- Re-enable the terminal-buffer eye fallback for shell sessions (they have no
transcript source at all); TUI modes keep the clear placeholder
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1613,11 +1613,12 @@ class CodemanApp {
|
||||
let lastResponse = data.text || '';
|
||||
|
||||
// Source 2: Terminal buffer fallback — strip ANSI, drop Claude CLI chrome.
|
||||
// Claude-only: _cleanTerminalBuffer knows Claude CLI's output; for TUI
|
||||
// modes (codex/opencode/gemini) it yields repaint garbage, so a clear
|
||||
// Claude + shell only: _cleanTerminalBuffer knows Claude CLI's output, and
|
||||
// shell sessions have no transcript source at all; for TUI modes
|
||||
// (codex/opencode/gemini) it yields repaint garbage, so a clear
|
||||
// placeholder beats a messy screen dump there.
|
||||
const sessionMode = this.sessions.get(this.activeSessionId)?.mode || 'claude';
|
||||
if (!lastResponse && sessionMode === 'claude') {
|
||||
if (!lastResponse && (sessionMode === 'claude' || sessionMode === 'shell')) {
|
||||
const termRes = await fetch(`/api/sessions/${this.activeSessionId}/terminal`);
|
||||
const termData = (await termRes.json())?.data ?? {};
|
||||
if (termData.terminalBuffer) {
|
||||
|
||||
@@ -62,6 +62,7 @@ import { RunSummaryTracker } from '../../run-summary.js';
|
||||
import { MAX_INPUT_LENGTH, MAX_SESSION_NAME_LENGTH } from '../../config/terminal-limits.js';
|
||||
import { MAX_PASTE_IMAGE_BYTES } from '../../config/buffer-limits.js';
|
||||
import { dataPath } from '../../config/instance.js';
|
||||
import { LRUMap } from '../../utils/lru-map.js';
|
||||
|
||||
// Path to linked-cases registry (same file used by case-routes resolveCasePath)
|
||||
const LINKED_CASES_FILE = dataPath('linked-cases.json');
|
||||
@@ -1030,7 +1031,7 @@ export function registerSessionRoutes(
|
||||
// codex TUI itself. An entry is credited to this pane only when its Enter is
|
||||
// the closest among all codex panes, so a menu keystroke in another pane
|
||||
// can't steal the attribution.
|
||||
const codexHistoryPinCache = new Map<string, { submitAt: number; threadId: string }>();
|
||||
const codexHistoryPinCache = new LRUMap<string, { submitAt: number; threadId: string }>({ maxSize: 1024 });
|
||||
async function resolveCodexThreadFromHistory(
|
||||
session: { id: string; codexLastSubmitAt?: number },
|
||||
codexHome: string
|
||||
@@ -1071,7 +1072,6 @@ export function registerSessionRoutes(
|
||||
if (!best || dist < best.dist) best = { threadId: e.session_id, dist };
|
||||
}
|
||||
if (!best) return null;
|
||||
if (codexHistoryPinCache.size >= 1024) codexHistoryPinCache.clear();
|
||||
codexHistoryPinCache.set(session.id, { submitAt, threadId: best.threadId });
|
||||
return best.threadId;
|
||||
}
|
||||
@@ -1168,7 +1168,7 @@ export function registerSessionRoutes(
|
||||
// rewritten (verified: resume appends without touching it), so the parsed
|
||||
// identity of a given path can be cached forever. This turns the per-request
|
||||
// scan into stat calls plus head reads for new files only.
|
||||
const codexRolloutMetaCache = new Map<string, { cwd?: string; originator?: string }>();
|
||||
const codexRolloutMetaCache = new LRUMap<string, { cwd?: string; originator?: string }>({ maxSize: 4096 });
|
||||
async function readCodexRolloutMetaCached(
|
||||
filePath: string,
|
||||
headBuf: Buffer
|
||||
@@ -1181,7 +1181,6 @@ export function registerSessionRoutes(
|
||||
// Don't cache a still-incomplete head: a rollout being created may not
|
||||
// have flushed session_meta/turn_context yet.
|
||||
if (!meta.cwd && !meta.originator) return meta;
|
||||
if (codexRolloutMetaCache.size >= 4096) codexRolloutMetaCache.clear();
|
||||
codexRolloutMetaCache.set(filePath, meta);
|
||||
return meta;
|
||||
}
|
||||
@@ -1198,7 +1197,7 @@ export function registerSessionRoutes(
|
||||
typeof (b as { text?: string }).text === 'string'
|
||||
)
|
||||
.map((b) => b.text)
|
||||
.join('');
|
||||
.join('\n\n');
|
||||
}
|
||||
|
||||
// Single pass over a Codex rollout: track the last assistant message (for the
|
||||
|
||||
Reference in New Issue
Block a user