Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/storage/src/__tests__/foreign-session-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { mkdir, mkdtemp, symlink, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { join, win32 } from 'node:path';
import { DatabaseSync } from 'node:sqlite';
import {
FOREIGN_SESSION_SCAN_MAX_SESSIONS,
type ForeignSessionSummary,
} from '@maka/core/foreign-session';
import {
codexCwdSqlVariants,
createForeignSessionStore,
isClaudeCodeImportEnabled,
isCodexImportEnabled,
Expand Down Expand Up @@ -294,6 +295,13 @@ describe('foreign session store — Claude scan', () => {
});

describe('foreign session store — Codex scan', () => {
it('includes POSIX-shaped SQL variants for a native Windows cwd', () => {
const native = win32.join('C:\\', 'Users', 'me', 'project');
const variants = codexCwdSqlVariants(native);
assert.ok(variants.includes('C:/Users/me/project'));
assert.ok(variants.includes('C:/Users/me/project/'));
});

it('lists threads from sqlite, dropping archived and foreign-source rows', async () => {
const home = await tempHome();
await seedCodexSqlite(home, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ describe('SqliteSessionMetadataStore', () => {
turnId: 'turn-1',
expansion: {
filesystem: {
entries: [{ path: join(tmpdir(), 'maka-output'), access: 'write', scope: 'exact' }],
entries: [{ path: '/tmp/maka-output', access: 'write', scope: 'exact' }],
},
},
justification: 'Write a temporary output.',
Expand Down
30 changes: 23 additions & 7 deletions packages/storage/src/foreign-session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -569,14 +569,13 @@ async function readCodexThreadRows(
// Filter cwd IN SQL, before LIMIT: otherwise a multi-project store with
// many newer threads from other directories fills the LIMIT window and
// the target project's older thread never reaches the JS-side filter.
// This is a COARSE pre-filter — the exact normalized path or its
// trailing-separator variant — so a stored `/target/` isn't dropped
// before the authoritative two-sided normalizePath() comparison in
// codexRowsToSummaries(); SQL cannot run normalizePath on the stored side.
// This is a COARSE pre-filter across source-native and host-normalized
// separator forms. The authoritative two-sided normalizePath()
// comparison still runs in codexRowsToSummaries().
if (cwdFilter !== undefined && columns.has('cwd')) {
const norm = normalizePath(cwdFilter);
where.push('(cwd = ? OR cwd = ?)');
params.push(norm, norm + sep);
const variants = codexCwdSqlVariants(cwdFilter);
where.push(`cwd IN (${variants.map(() => '?').join(', ')})`);
params.push(...variants);
}
const orderColumn = columns.has('updated_at_ms')
? 'updated_at_ms'
Expand Down Expand Up @@ -684,3 +683,20 @@ function normalizePath(path: string): string {
const resolved = resolve(path);
return resolved.endsWith(sep) && resolved !== sep ? resolved.slice(0, -1) : resolved;
}

export function codexCwdSqlVariants(path: string): string[] {
const variants = new Set<string>();
for (const candidate of [path, normalizePath(path)]) {
for (const separatorForm of [
candidate,
candidate.replaceAll('\\', '/'),
candidate.replaceAll('/', '\\'),
]) {
const withoutTrailingSeparator = separatorForm.replace(/[\\/]+$/, '') || separatorForm;
variants.add(withoutTrailingSeparator);
variants.add(`${withoutTrailingSeparator}/`);
variants.add(`${withoutTrailingSeparator}\\`);
}
}
return [...variants];
}
Loading