12 Commits
Author SHA1 Message Date
Leigh StillardandClaude Opus 4.6 e04247e576 feat(core): default reset_on_idle_mins to 30 to prevent context drift (#494)
The workspace pool evicts idle session entries after ~15 minutes
(core/engine.go newWorkspacePool TTL). The next message after eviction
rehydrates the session via Claude Code's --continue flag, which re-ingests
the entire prior transcript. Over many cycles this accumulates stale chat
history (failed commands, debugging noise, abandoned tangents) that starts
to dominate the model's attention, causing the original architectural
intent to drift — see #492.

#400 (xukp20) already added reset_on_idle_mins as the right mechanism for
this: it rotates to a fresh active session after a configurable idle gap
while preserving the previous session for /list and /switch. The only gap
was that it was opt-in and defaulted to disabled, so existing deployments
hit the drift loop unless the user knew to set it.

This change:

- defaults reset_on_idle_mins to 30 minutes when unset (twice the pool TTL,
  long enough that a coffee break doesn't rotate, short enough that a
  "come back after lunch" does)
- emits a one-time slog.Info nudge per project at startup when the default
  is applied, pointing users at docs/usage.md
- introduces resolveResetOnIdle() so both call sites in main.go share the
  default-resolution logic, with a unit test covering nil / 0 / explicit
  positive value
- documents the new default and opt-out (reset_on_idle_mins = 0) in
  README.md, docs/usage.md, and config.example.toml

Refs: #492
Credit: xukp20 for #400, which this builds on

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 22:42:46 +08:00
Leigh StillardandClaude Opus 4.6 afec8cbcd1 feat(claudecode): implement ReasoningEffortSwitcher for /effort command
The Claude Code agent now supports the /reasoning and /effort commands
that were previously only available for Codex. This passes --effort to
the Claude CLI when starting sessions.

Available levels: low, medium, high, max
Configurable via reasoning_effort in [projects.agent.options].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 00:50:01 +00:00
Leigh StillardandClaude Opus 4.6 eadc52a096 fix: graceful session close — stdin EOF before signals (#509) (#510)
* fix: graceful session shutdown — close stdin before signals

The old Close() called context.Cancel() which sends SIGKILL immediately
via Go's exec.CommandContext, giving Claude Code zero time to run Stop
hooks. This broke claude-mem session summaries (120s timeout) and any
other cleanup that depends on a clean exit.

New three-phase shutdown:
1. Close stdin (EOF) — Claude Code exits cleanly, running Stop hooks
2. SIGTERM after 30s grace period if still alive
3. SIGKILL after 5s more as last resort

Also increases engine-level close timeout from 10s to 45s to accommodate
the longer graceful window.

Tested: all agent/claudecode and core tests pass. Verified Claude Code
exits cleanly on stdin EOF (exit code 0).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 120s graceful close + hold message during session transition

Increases the default graceful stop timeout from 30s to 120s to match
claude-mem's Stop hook timeout. The wait ends as soon as the process
exits — typical shutdowns take seconds, not the full 120s.

When a user messages during an idle-reset that triggers a graceful
close, they now see "Wrapping up your previous session (usually a few
seconds, up to 2 minutes). Your new session will start automatically."
instead of a silent delay. The hold message is sent before the blocking
close, and the new session starts immediately when the old one finishes.

Engine-level close timeout raised to 130s (120s grace + 5s SIGTERM +
5s buffer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 14:31:01 +08:00
Leigh StillardandClaude Sonnet 4.6 326fa258ff fix(core): strip workspace prefix from cron/heartbeat session keys (#474)
In multi-workspace mode, stored session keys may be prefixed with the
workspace path (e.g. "/home/user/project:slack:C123:U456"). The naive
first-colon split produced a path as the platform name, causing
ExecuteCronJob and ExecuteHeartbeat to fail with "platform not found".

Add a fallback loop that searches for a registered platform name within
the key and strips the prefix before proceeding.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 22:26:31 +08:00
Leigh StillardandClaude Opus 4.6 401525931b feat: session resilience — --continue, resume recovery, context indicator
* feat: add session resilience with --continue, resume recovery, and context indicator

First connection after engine startup uses --continue to pick up the most
recent CLI session, bridging direct CLI usage and cc-connect. If resume
fails (e.g. prompt too long), automatically falls back to a fresh session.

Appends [ctx: ~XX%] to agent responses using SDK token counts, with
fallback to agent self-reported context percentage when SDK values are
unreliable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: --continue logic bug — respect fresh session intent after first connection

Remove the startSessionID == "" condition that forced --continue even
when users explicitly requested fresh sessions (e.g. via /new). Now only
the first connection after engine startup uses --continue; subsequent
connections respect the stored session ID or "" for fresh.

Add TestFreshSessionRespectedAfterFirstConnection to verify.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(npm): correct pre-release version comparison

The isNewerOrEqual function was comparing pre-release strings
lexicographically (e.g., "beta.2" > "beta.10" due to string comparison),
causing incorrect downgrade detection. Now parses pre-release into
id and numeric parts for proper numeric comparison.

Fixes: beta version upgrade being treated as outdated and downgrading to stable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(npm): bump version to 1.2.2-beta.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: make hasConnectedOnce per-workspace so idle-reaped workspaces resume with --continue

hasConnectedOnce was engine-global, so after the first connection set it
to true, any workspace that got idle-reaped and later recreated would
skip --continue and spawn a fresh session — losing all prior context.

Move the flag into workspaceState so each workspace independently tracks
its first connection. When a workspace is reaped and GetOrCreate makes a
new one, the flag starts false again, enabling --continue on reconnection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: fork session on resume/continue to prevent context interleaving

When cc-connect picks up a session via --continue or --resume, add
--fork-session so the platform conversation gets its own independent
context branch. This prevents history interleaving when the user is
also working in the same session from the CLI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:30:19 +08:00
Leigh StillardandClaude Opus 4.6 df5cc64ef4 feat: fork session on --continue to prevent context contamination (#244)
When cc-connect starts up and picks up the most recent CLI session via
--continue, it now adds --fork-session to create an independent context
branch. This prevents history interleaving when the user is concurrently
working in the same workspace from both the CLI and a messaging platform.

Without this, resuming into an active CLI session corrupts both context
windows — the model sees interleaved tool calls and responses from two
different conversations, leading to hallucinations about files and state.

The fork only applies to --continue (first connection after startup,
where we grab "whatever is latest" which may be an active CLI session).
Plain --resume of cc-connect's own stored session ID is unaffected,
since that session belongs to cc-connect and has no interleaving risk.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 20:32:48 +08:00
Leigh StillardandClaude Opus 4.6 080c5a90fc feat: add workspace path normalization to prevent pool key mismatches (#140)
Add normalizeWorkspacePath() which cleans and resolves workspace paths
(trailing slashes, symlinks, relative segments) to prevent duplicate
pool entries for the same workspace. Applied at all Bind and resolve
call sites in the engine.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:27:07 +08:00
Leigh StillardandClaude Opus 4.6 b709294262 feat: Slack typing indicator and mrkdwn formatting (#139)
* feat: add Slack typing indicator and mrkdwn formatting instructions

Implement TypingIndicator interface for Slack using emoji reactions as a
heartbeat (eyes immediately, clock after 2min, extras every 5min). All
reactions are cleaned up when the agent completes.

Also update the agent system prompt to instruct Slack mrkdwn formatting
instead of standard Markdown (single *bold*, no ## headings, etc.).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make formatting instructions platform-specific via optional interfaces

Move Slack mrkdwn formatting from the global AgentSystemPrompt() into a
platform-specific FormattingInstructionProvider interface. The engine
wires platform formatting to agents via PlatformPromptInjector before
session start, so only Slack sessions receive Slack formatting.

Also fix StartTyping comment: "random emoji" → "sequential from extras list".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:25:47 +08:00
Leigh StillardandClaude Opus 4.6 a8ac04c4b4 feat: add /btw command to inject messages into busy sessions and Slack slash command support (#138)
When the agent is processing and the user sends /btw <message>, the
content is injected into the running session mid-turn instead of being
rejected. The busy message now tells users about /btw.

Also adds a Slack slash command handler (socketmode.EventTypeSlashCommand)
so native Slack slash commands work without needing text prefixes.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 07:23:50 +08:00
Leigh StillardandClaude Opus 4.6 e285668897 docs: add design plans, session resilience docs, and Slack app manifest (#136)
Design and planning documents for multi-workspace support and session
resilience features. Includes Slack app manifest for slash command
registration and a feature inventory of Slack platform work.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 22:10:39 +08:00
Leigh StillardandClaude Opus 4.6 647398f765 feat: multi-workspace mode — single bot, multiple workspaces per channel (#79)
* feat: add multi-workspace mode and base_dir config fields

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add WorkspaceBindingManager for channel-to-workspace persistence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add workspacePool for managing per-workspace agent state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add ChannelNameResolver interface and Slack implementation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add multi-workspace fields, SetMultiWorkspace, and idle reaper to Engine

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add resolveWorkspace method for channel-to-directory mapping

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: wire multi-workspace mode setup in main.go

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add workspace init flow for cloning repos and binding channels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add /workspace commands (init, unbind, list, status)

Add handleWorkspaceCommand with subcommands:
- (no args): show current workspace binding for the channel
- init <git-url>: clone repo and bind workspace to channel
- unbind: remove workspace binding from channel
- list: show all workspace bindings for the project

Only available when multi-workspace mode is enabled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: wire multi-workspace routing into handleMessage with per-workspace agents

Add workspace resolution to handleMessage that resolves channels to workspace
directories, creates per-workspace agents with correct work_dir, and routes
messages through workspace-specific session managers. The normal (single-workspace)
code path is unchanged — all new logic is gated behind e.multiWorkspace.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* test: add integration tests for multi-workspace resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve multi-workspace bugs found during live testing

Three bugs fixed:

1. App mention events not handled - Slack sends `app_mention` events
   (not `message.channels`) when the bot is @mentioned. Added
   AppMentionEvent handler in the Slack platform.

2. Bot mention prefix not stripped - app_mention text includes
   `<@UBOTID> ` prefix, breaking command parsing and workspace
   resolution. Now stripped before passing to engine.

3. Slash command syntax incompatible with Slack - `/workspace` commands
   were being intercepted by Slack as slash commands. Changed all
   user-facing references and prefix detection to use `workspace`
   without the leading slash.

Also added debug logging to handleEvent for diagnosing event delivery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: instruct agent to use Slack mrkdwn formatting

Add Slack formatting guidelines to the agent system prompt so responses
use Slack's mrkdwn syntax instead of standard Markdown (e.g. *bold*
instead of **bold**, no ## headings).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 19:38:58 +08:00
Leigh StillardandClaude Opus 4.6 6ec4199e70 chore: update golang.org/x/crypto and golang.org/x/sys to latest (#77)
Update outdated standard library dependencies:
- golang.org/x/crypto: v0.0.0-20210421170649 → v0.48.0
- golang.org/x/sys: v0.0.0-20201119102817 → v0.42.0
- Go minimum version: 1.24.2 → 1.25.0 (required by x/sys v0.42.0)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 07:34:51 +08:00