[WIP] feat: Session isolation for (Discord) channels/threads with separate working directories#82
[WIP] feat: Session isolation for (Discord) channels/threads with separate working directories#82vinayh wants to merge 12 commits intomoazbuilds:masterfrom
Conversation
… non-listenChannel sessions - discord.ts: guildTriggerReason() returns "guild_message" as catch-all so bot responds to all guild messages without @mention - discord.ts: Session selection now routes non-listenChannel guild channels to their own isolated sessions (like threads) - runner.ts: Add per-session working directory support — thread/channel sessions spawn Claude in `.claude/claudeclaw/sessions/<channelId>/` - runner.ts: runClaudeOnce() accepts optional cwd parameter passed to Bun.spawn() Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…docs - start.md: Add setup questions for all-channel response mode, per-channel memory isolation, and systemd service generation with auto-detected paths - docs/MULTI_SESSION.md: Rewrite to cover channel isolation, per-session working directories, and updated architecture diagram - deploy/claudeclaw.service: Example systemd user service with nvm support, resource limits, and auto-restart Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- start.md: Inline the full systemd service template with configurable constants (PROJECT_DIR, PLUGIN_ROOT, BREW_BIN) at the top. Clarify nohup as default launch method when systemd is declined. - Remove deploy/claudeclaw.service (redundant with start.md template) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- MULTI_SESSION.md: Add terminology section, consistently use "session" for conversations and "Discord channel/thread" for Discord entities. Note legacy "threads" key in sessions.json. - start.md: Update listenChannels description, Discord setup questions, and session capture step to use consistent terminology. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All sessions now route through threadSession infrastructure with
threadId defaulting to "default". This eliminates special-casing
between global and per-channel sessions, giving every session its
own working directory and isolated memory via autoMemoryDirectory.
- Remove global session functions (getSession, createSession, incrementTurn, markCompactWarned)
- Remove global queue in favor of per-thread queues
- getSessionCwd always returns sessions/{threadId}/
- Per-session memory isolation via --settings autoMemoryDirectory
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename ThreadSession → Session, threadId → key, threadQueues → sessionQueues across sessionManager, runner, send, and discord. Add migration for legacy sessions.json format. Add CLAUDE.md documenting architecture and dev workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…plication Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The header/footer used hardcoded widths (30 chars) while the content row varied based on active services. When Discord/Telegram were enabled, content overflowed past the box corners. Replace fixed-width rendering with a render() function that measures visible content width (stripping ANSI codes, accounting for emoji), then expands the header and footer to match the widest row. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Allows injecting tokens via `op run` or similar secret managers without storing them in the config file on disk. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces manual copy-to-cache workflow with the proper plugin marketplace flow: push to GitHub, update marketplace, reinstall. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
TerrysPOV
left a comment
There was a problem hiding this comment.
Since this PR is still WIP, I don’t think it is in a merge-reviewable state yet. There is useful directional feedback to give now, but the branch needs to be completed and narrowed before final review.
The main issues I see in the current PR tip are:
- This branch is no longer scoped just to session isolation. It now also includes unrelated later work (statusline changes, env-var token changes, and substantial docs/setup edits), which makes the review noisy and the intent unclear.
- The Discord control path and the main execution path are no longer consistent. Normal Discord messages are routed through the new sessionManager-based session model, but /reset and /status still use the legacy sessions.ts global-session file. So the command path is reading/resetting different state than the message path.
- The “all sessions use the same code path” refactor is still incomplete. run() / runUserMessage() accept a session key now, but the streaming path is still hard-wired to DEFAULT_SESSION_KEY, so the branch still has split session behaviour.
- This also introduces a major Discord behaviour change by responding to all guild messages without mention. That feels like a separate product decision and should be reviewed explicitly, not bundled into the session-isolation refactor.
I'm moving this back to draft given it's WIP state and issues above. I’d suggest splitting out the unrelated commits, finishing the unified session-path work, and then resubmitting once the intended behaviour is complete and testable.
|
Thanks, all quite reasonable and fair! I ended up co-opting this branch for further changes in my fork that aren't well-scoped or documented, and will hopefully try and get around to making this a proper PR soon. |
|
Hey @vinayh — just a heads-up that the Discord session isolation piece (per-channel sessions in discord.ts) has now landed in PR #163. If you want to bring this PR out of draft and continue moving it forward, you'd want to drop those discord.ts changes to avoid the overlap — the rest of the work here (sessionManager.ts refactor, runner.ts, the CLAUDE.md, docs) is distinct and still worth landing separately. No rush since this is in draft — just keeping you in the loop. |
Hello,
This is my initial (WIP, to be tested further) attempt at addressing the lack of multi-session memory and support for sessions based on Discord channels.
listenChannelsshare the global session; all other channels/threads get dedicated sessions with isolated working directories(
.claude/claudeclaw/sessions/<key>/) and memory (--settings '{"autoMemoryDirectory": ...}')DEFAULT_SESSION_KEY, eliminating theold global-vs-thread branching
Session(notThreadSession),key(notthreadId),sessionQueues(notthreadQueues). "Thread" nowonly refers to Discord threads.
loadSessions()handles the oldthreads/threadIdJSON format automaticallyCLAUDE.mddocumenting architecture, deployment, and dev workflowChanges
src/sessionManager.ts:ThreadSession→Session,threads→sessions, all functions renamed (getSession,createSession,incrementTurn, etc.),DEFAULT_SESSION_KEYconstant, legacy format migrationsrc/runner.ts:threadId→sessionKey,threadQueues→sessionQueues, default param usesDEFAULT_SESSION_KEY, fixed missingsession key in
streamUserMessagesrc/commands/discord.ts: Updated imports/calls,ts.threadId→ts.key, comments clarifiedsrc/commands/send.ts: Updated to usegetSession/DEFAULT_SESSION_KEYCLAUDE.md: New — documents architecture, session isolation, naming conventions, systemd deployment, dev workflowTest plan
listenChannel— should use global session.claude/claudeclaw/sessions/<id>/and start a new sessionthreadsformat correctly)systemctl --user restart claudeclaw— daemon starts cleanly with new code