From 433189a79b71b19cc1427d2b6b0a69a4e8a078d9 Mon Sep 17 00:00:00 2001 From: suraj-markup Date: Wed, 25 Feb 2026 16:25:24 +0530 Subject: [PATCH] perf(agent-codex): cache findCodexSessionFile to avoid double scan Add a 30-second TTL cache for session file lookups so that getActivityState, getSessionInfo, and getRestoreCommand called in the same refresh cycle reuse the result instead of re-scanning ~/.codex/sessions/ independently. Co-Authored-By: Claude Opus 4.6 --- .../plugins/agent-codex/src/index.test.ts | 3 +- packages/plugins/agent-codex/src/index.ts | 30 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/plugins/agent-codex/src/index.test.ts b/packages/plugins/agent-codex/src/index.test.ts index 1a7d383e4..363825244 100644 --- a/packages/plugins/agent-codex/src/index.test.ts +++ b/packages/plugins/agent-codex/src/index.test.ts @@ -62,7 +62,7 @@ vi.mock("node:os", () => ({ })); import { Readable } from "node:stream"; -import { create, manifest, default as defaultExport, resolveCodexBinary } from "./index.js"; +import { create, manifest, default as defaultExport, resolveCodexBinary, _resetSessionFileCache } from "./index.js"; // --------------------------------------------------------------------------- // Test helpers @@ -166,6 +166,7 @@ function setupMockStream(content: string) { beforeEach(() => { vi.clearAllMocks(); + _resetSessionFileCache(); mockHomedir.mockReturnValue("/mock/home"); // Default: open() returns a handle with empty content (no session_meta match). // Session tests call setupMockOpen(content) to override. diff --git a/packages/plugins/agent-codex/src/index.ts b/packages/plugins/agent-codex/src/index.ts index c1dd03d75..0ebb69039 100644 --- a/packages/plugins/agent-codex/src/index.ts +++ b/packages/plugins/agent-codex/src/index.ts @@ -538,6 +538,25 @@ function appendModelFlags(parts: string[], model: string | undefined): void { } } +/** TTL for session file path cache (ms). Prevents redundant filesystem scans + * when getActivityState and getSessionInfo are called in the same refresh cycle. */ +const SESSION_FILE_CACHE_TTL_MS = 30_000; + +/** Module-level session file cache shared across the agent instance lifetime. + * Keyed by workspace path, stores the resolved file path and an expiry timestamp. */ +const sessionFileCache = new Map(); + +/** Find session file with caching to avoid double scans per refresh cycle */ +async function findCodexSessionFileCached(workspacePath: string): Promise { + const cached = sessionFileCache.get(workspacePath); + if (cached && Date.now() < cached.expiry) { + return cached.path; + } + const result = await findCodexSessionFile(workspacePath); + sessionFileCache.set(workspacePath, { path: result, expiry: Date.now() + SESSION_FILE_CACHE_TTL_MS }); + return result; +} + function createCodexAgent(): Agent { /** Cached resolved binary path (populated by init or first getLaunchCommand) */ let resolvedBinary: string | null = null; @@ -621,7 +640,7 @@ function createCodexAgent(): Agent { // modified file means the agent is active. if (!session.workspacePath) return null; - const sessionFile = await findCodexSessionFile(session.workspacePath); + const sessionFile = await findCodexSessionFileCached(session.workspacePath); if (!sessionFile) return null; try { @@ -695,7 +714,7 @@ function createCodexAgent(): Agent { async getSessionInfo(session: Session): Promise { if (!session.workspacePath) return null; - const sessionFile = await findCodexSessionFile(session.workspacePath); + const sessionFile = await findCodexSessionFileCached(session.workspacePath); if (!sessionFile) return null; // Stream the file line-by-line to avoid loading potentially huge @@ -727,7 +746,7 @@ function createCodexAgent(): Agent { if (!session.workspacePath) return null; // Find the Codex session file for this workspace - const sessionFile = await findCodexSessionFile(session.workspacePath); + const sessionFile = await findCodexSessionFileCached(session.workspacePath); if (!sessionFile) return null; // Stream the file line-by-line to avoid loading potentially huge @@ -782,6 +801,11 @@ export function create(): Agent { return createCodexAgent(); } +/** @internal Clear the session file cache. Exported for testing only. */ +export function _resetSessionFileCache(): void { + sessionFileCache.clear(); +} + export { CodexAppServerClient } from "./app-server-client.js"; export type { AppServerClientOptions,