Files
openchamber/packages/web/server/lib/agent-memory/project-resolution.js
T
Bohdan Triapitsyn 9e87d7fdb9 feat(chats): add managed projectless chat sessions
Create projectless chat sessions under a managed, date-scoped Chats directory and clean abandoned or deleted session folders.

Add Chats to sidebar state, startup cache, shared context, and Electron Mini Chat while keeping VS Code project-only. Resolve managed chat directories to one server-side memory owner and document the runtime contracts.
2026-08-21 12:12:40 +03:00

66 lines
2.3 KiB
JavaScript

/**
* Which project's memory a session directory belongs to.
*
* A session often runs in a worktree, whose path is not the project's path.
* Keying memory by the session directory filed a worktree's memories under a
* project the panel never reads, so the agent stored them and the user never
* saw them. Every worktree of a repository shares one project memory, which is
* also what the user means by "this project".
*
* A directory that is itself a configured project is taken as-is; anything else
* resolves to its primary worktree. The configured check comes first because a
* user may register a worktree as a project in its own right, and that choice
* has to win over the git topology.
*/
import path from 'node:path';
import { createProjectIdFromPath } from '../projects/project-id.js';
const normalize = (value) => {
if (typeof value !== 'string') return '';
const trimmed = value.trim();
return trimmed ? path.resolve(trimmed) : '';
};
export const createMemoryProjectResolver = (dependencies) => {
const { listProjectPaths, resolvePrimaryWorktreeRoot, managedProjectRoots = [] } = dependencies;
const managedRoots = managedProjectRoots.map(normalize).filter(Boolean);
return async (directory) => {
const resolved = normalize(directory);
if (!resolved) {
return '';
}
const managedRoot = managedRoots.find((root) => {
const relative = path.relative(root, resolved);
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
});
if (managedRoot) {
return createProjectIdFromPath(managedRoot);
}
let configured = [];
try {
configured = ((await listProjectPaths()) || []).map(normalize).filter(Boolean);
} catch {
// An unreadable project list must not lose the memory: the git-derived
// root below still converges every worktree of the repository on one
// store rather than scattering one per checkout.
}
if (configured.includes(resolved)) {
return createProjectIdFromPath(resolved);
}
let primaryRoot = '';
try {
primaryRoot = normalize((await resolvePrimaryWorktreeRoot(resolved))?.root);
} catch {
// Not a git checkout, or git is unavailable.
}
return createProjectIdFromPath(primaryRoot || resolved);
};
};