Repository navigation
[Feature] A working keyless local thread store (follow-up to #73) #179
Description
What outcome should OpenMuse handle?
Self-hosting OpenMuse without CopilotKit Intelligence, with conversations kept in the deployment's own Postgres or PGlite: the request in #73.
I built this as a fork and released it as OpenMuse Standalone v0.1.0: https://github.com/caspianchan31/openmuse-standalone (MIT, based on main at 1ac68f3). I'm sharing it in case it helps consolidate #94 and #166, and I'm happy to open a PR or split pieces out, whichever the maintainers prefer.
Proposed behavior
- Without
CPK_INTELLIGENCE_API_KEYthe API starts in local mode. With the key, behavior is unchanged from upstream, and upstream's config tests for the keyed path are kept. - The runtime runs in SSE mode on a persistent
AgentRunner(run/connect/isRunning/stop) backed by the app's own store (chat_threads,chat_runs,chat_events). It does not implementLocalThreadEndpointRunner, whose synchronous methods would force an in-memory store. /api/copilotkit/threads*(list, messages, rename, archive, restore, delete) and/api/main-threadare served by the Hono layer and scoped to the signed-in owner;/info.threadEndpointsis rewritten so the app uses them.- A send on a busy thread gets
409 agent_thread_lockedbefore any SSE response, so the app holds the message as it does withAgentThreadLockedError. - Replies are journaled while running, and the terminal event waits for the durable save; a failed save surfaces as
persistence_failed. Interrupted replies are recovered from the journal (PGlite at startup, Postgres once the lock is stale). OptionalCHAT_RETENTION_DAYS. - Edits to upstream files are small and marked
// Standalone:; new code lives inapps/server/src/standalone/.
On the points raised in the #166 review: new side chats persist through the agent-clone path with restart coverage; owner checks apply to every thread route and to run/connect/stop; failed and concurrent runs end cleanly and completion stays behind the durable write; there is no in-memory eviction, the database is the history.
How would we verify it works?
- 583 tests (typecheck, biome, node:test); the runner and store suites also run against Postgres 17.
- Keyless end-to-end in a browser: streaming, reload mid-reply resumes, Stop keeps the partial reply, a second tab's send is held via 409, side chats rename/archive/restore/delete, history survives an API restart and a
kill -9. - Docs:
docs/STANDALONE.md(architecture, upstream merge procedure) anddocs/STANDALONE-THREADS.md(routes, schema, validation).
Known limits: no live cross-device thread metadata sync; live resume needs a single API instance.
Activity