Skip to content

[WIP] feat: Session isolation for (Discord) channels/threads with separate working directories#82

Draft
vinayh wants to merge 12 commits intomoazbuilds:masterfrom
vinayh:master
Draft

[WIP] feat: Session isolation for (Discord) channels/threads with separate working directories#82
vinayh wants to merge 12 commits intomoazbuilds:masterfrom
vinayh:master

Conversation

@vinayh
Copy link
Copy Markdown

@vinayh vinayh commented Apr 8, 2026

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.

  • The bot responds to all Discord guild messages without requiring @mention
  • listenChannels share the global session; all other channels/threads get dedicated sessions with isolated working directories
    (.claude/claudeclaw/sessions/<key>/) and memory (--settings '{"autoMemoryDirectory": ...}')
  • Unified session handling: all sessions (including the default) go through the same code path via DEFAULT_SESSION_KEY, eliminating the
    old global-vs-thread branching
  • Consistent naming throughout: Session (not ThreadSession), key (not threadId), sessionQueues (not threadQueues). "Thread" now
    only refers to Discord threads.
  • Migration in loadSessions() handles the old threads/threadId JSON format automatically
  • Added CLAUDE.md documenting architecture, deployment, and dev workflow

Changes

  • src/sessionManager.ts: ThreadSessionSession, threadssessions, all functions renamed (getSession, createSession,
    incrementTurn, etc.), DEFAULT_SESSION_KEY constant, legacy format migration
  • src/runner.ts: threadIdsessionKey, threadQueuessessionQueues, default param uses DEFAULT_SESSION_KEY, fixed missing
    session key in streamUserMessage
  • src/commands/discord.ts: Updated imports/calls, ts.threadIdts.key, comments clarified
  • src/commands/send.ts: Updated to use getSession/DEFAULT_SESSION_KEY
  • CLAUDE.md: New — documents architecture, session isolation, naming conventions, systemd deployment, dev workflow

Test plan

  • Send message in a listenChannel — should use global session
  • Send message in a non-listenChannel — should create .claude/claudeclaw/sessions/<id>/ and start a new session
  • Send message in a Discord thread — should get its own dedicated session
  • Verify sessions persist across daemon restarts (migration reads old threads format correctly)
  • Run systemctl --user restart claudeclaw — daemon starts cleanly with new code

vinayh and others added 5 commits April 8, 2026 10:56
… 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>
@vinayh vinayh changed the title [WIP] Session isolation for (Discord) channels/threads with separate working directories [WIP] feat: Session isolation for (Discord) channels/threads with separate working directories Apr 9, 2026
vinayh and others added 7 commits April 9, 2026 09:54
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>
Copy link
Copy Markdown
Collaborator

@TerrysPOV TerrysPOV left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

@TerrysPOV TerrysPOV marked this pull request as draft April 24, 2026 15:58
@vinayh
Copy link
Copy Markdown
Author

vinayh commented Apr 25, 2026

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.

@TerrysPOV
Copy link
Copy Markdown
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants