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.
This commit is contained in:
@@ -1291,6 +1291,7 @@ const resolveMemoryProjectId = createMemoryProjectResolver({
|
||||
return sanitizeProjects(settings?.projects || []).map((project) => project.path);
|
||||
},
|
||||
resolvePrimaryWorktreeRoot,
|
||||
managedProjectRoots: [path.join(OPENCHAMBER_USER_CONFIG_ROOT, 'chats')],
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,8 @@ const normalize = (value) => {
|
||||
};
|
||||
|
||||
export const createMemoryProjectResolver = (dependencies) => {
|
||||
const { listProjectPaths, resolvePrimaryWorktreeRoot } = dependencies;
|
||||
const { listProjectPaths, resolvePrimaryWorktreeRoot, managedProjectRoots = [] } = dependencies;
|
||||
const managedRoots = managedProjectRoots.map(normalize).filter(Boolean);
|
||||
|
||||
return async (directory) => {
|
||||
const resolved = normalize(directory);
|
||||
@@ -32,6 +33,14 @@ export const createMemoryProjectResolver = (dependencies) => {
|
||||
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);
|
||||
|
||||
@@ -51,6 +51,15 @@ describe('resolving a session directory to its project', () => {
|
||||
expect(await resolve('/tmp/loose')).toBe(createProjectIdFromPath('/tmp/loose'));
|
||||
});
|
||||
|
||||
test('managed chat session directories share the Chats root store', async () => {
|
||||
const chatsRoot = '/Users/x/.config/openchamber/chats';
|
||||
const resolve = createResolver({ managedProjectRoots: [chatsRoot] });
|
||||
|
||||
expect(await resolve(`${chatsRoot}/2026-08-21/session-a`)).toBe(createProjectIdFromPath(chatsRoot));
|
||||
expect(await resolve(`${chatsRoot}/2026-08-21/session-b`)).toBe(createProjectIdFromPath(chatsRoot));
|
||||
expect(await resolve('/Users/x/.config/openchamber/chats-other/session-a')).not.toBe(createProjectIdFromPath(chatsRoot));
|
||||
});
|
||||
|
||||
test('no directory resolves to nothing rather than to some default project', async () => {
|
||||
const resolve = createResolver();
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
Server-owned storage for the Project Notes surface: free-form notes, todos, and
|
||||
plan markdown files.
|
||||
|
||||
The managed Chats root (`~/.config/openchamber/chats`) is also one context owner. Every dated per-session directory beneath it resolves to that root, so Notes, Todo, Plans, pinned knowledge, and project memory are shared across ordinary chats without registering Chats as a user project.
|
||||
|
||||
## Ownership
|
||||
|
||||
| Path | Owner | Contents |
|
||||
|
||||
@@ -24,6 +24,8 @@ attached to that session. Pins never come from project-wide note or plan state.
|
||||
A new-session draft passes its pins into this metadata when its first message
|
||||
creates the session.
|
||||
|
||||
Directories beneath the managed `~/.config/openchamber/chats` root resolve to that root before project context and project memory are read. Every ordinary chat therefore shares one Chats knowledge owner instead of creating an unreachable context store for each dated session directory.
|
||||
|
||||
`session.metadata.openchamber.knowledge_context_delivered` holds the signature
|
||||
of what the session is carrying. It lives with the session, so it survives the
|
||||
tab closing and is visible to every sender, including the ones with no tab.
|
||||
|
||||
Reference in New Issue
Block a user