Commit Graph
354 Commits
Author SHA1 Message Date
Shawn 8ec95a280c fix(core): prevent infinite loop in drainEvents on closed channel (#152)
drainEvents uses a select loop with a default case to drain buffered
events. When the channel is closed, receives always succeed immediately
with a zero value, so the default case never triggers and the function
spins in an infinite loop, consuming 100% CPU.

Check the ok value from the receive and return immediately when the
channel is closed. Add regression tests for both closed and open
channel scenarios.
2026-03-15 19:19:53 +08:00
Shawn 6455d2a07a fix: use interactiveKey in delete mode functions for multi-workspace mode (#148)
In multi-workspace mode, interactive states are stored under a
workspace-prefixed key (interactiveKey), not the raw sessionKey.
The delete mode functions (getOrCreateDeleteModeState,
getDeleteModeState, clearDeleteModeState, executeDeleteModeAction)
were using sessionKey directly to look up interactiveStates, causing
delete mode to silently fail in multi-workspace mode because the
state could never be found.

Fix by resolving sessionKey to interactiveKey via
interactiveKeyForSessionKey() before accessing interactiveStates,
consistent with how other interactive state functions work.
2026-03-15 18:49:16 +08:00
quabugandClaude Opus 4.6 b3bc955313 feat(agent/pi): implement session management and additional interfaces (#144)
Add support for Discord commands that were previously returning
"not supported":

- ListSessions / SessionDeleter: read and delete pi session .jsonl
  files, enabling /list and /delete commands
- HistoryProvider: read conversation history from pi session files,
  enabling /history command
- ReasoningEffortSwitcher: map to pi's --thinking flag with levels
  off/minimal/low/medium/high/xhigh, enabling /reasoning command
- SkillProvider: expose .pi/skills/ directories, enabling /skills
- GetWorkDir: expose work directory for /status display

Session file discovery uses pi's path encoding convention where
absolute paths are encoded as --path-segments-- directory names
under ~/.pi/agent/sessions/.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 18:22:31 +08:00
Sean bbd5d56197 fix(cron): validate session_key in cron add to prevent silent runtime failures (#126)
When creating a cron job via `cron add`, if no session_key was provided
(neither via --session-key flag nor CC_SESSION_KEY env), the job was
created with an empty session_key. At runtime, ExecuteCronJob would fail
silently because it couldn't determine the target platform from an empty
session key.

Add server-side validation and auto-detection in handleCronAdd:
- If session_key is empty, attempt to auto-detect from the engine's
  active interactive sessions (works when exactly one session exists)
- If auto-detection fails, return a clear error to the client

Add Engine.ActiveSessionKeys() helper method to expose active session
keys for the auto-detection logic. Uses platform != nil check (instead
of agent session alive check) so sessions are discoverable even before
an agent process is started.
2026-03-15 18:20:07 +08:00
quabugandClaude Opus 4.6 cd4a85e858 fix(session): invalidate stale agent session IDs on agent type switch (#129)
* fix(session): invalidate stale agent session IDs on agent type switch

When a project's agent type is changed in config (e.g. opencode → pi),
persisted sessions still contain agent_session_id values from the old
agent. The new agent fails to resume these foreign session IDs, causing
errors and empty responses.

Add an `agent_type` field to Session that tracks which agent created the
session ID. On engine startup, InvalidateForAgent() clears session IDs
whose agent_type doesn't match the current agent. All code paths that
set AgentSessionID now also record the agent type.

Closes #128

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

* test: add unit test for SessionManager.InvalidateForAgent

Cover the three key cases: mismatched agent type (invalidated),
matching agent type (preserved), and empty agent type (migration-safe).
Also verifies sessions with no agent session ID are untouched.

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-15 14:21:15 +08:00
chenhg5andClaude 452e5fab6e feat(cron): add setup command and refactor memory file logic
Added `/cron setup` command to write instructions to agent's memory file.
Refactored memory file setup logic into shared `setupMemoryFile` function.
Added tests for cron setup and memory file operations.
Updated i18n messages for new setup flow.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-15 11:22:47 +08:00
chenhg5andClaude c253b32de9 docs: clarify AI agent installation guidance in INSTALL.md
Move AI agent interaction guidance from README files to INSTALL.md for better visibility and consolidate documentation.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-15 09:17:25 +08:00
Shawn 18eacddc44 fix: stop RateLimiter cleanup goroutine on replacement and engine shutdown (#134)
RateLimiter.cleanupLoop() runs a background goroutine that never
terminates. When SetRateLimitCfg() is called (e.g. during config
reload), a new RateLimiter is created with a new goroutine, but the
old goroutine keeps running forever — a goroutine leak.

Add a stop channel and Stop() method to RateLimiter. Call Stop() on the
previous limiter in SetRateLimitCfg() and in Engine.Stop() to ensure
clean shutdown.
2026-03-15 07:41:48 +08:00
cg33 6da33b38d1 Merge pull request #131 from quabug/feat/pi-agent
feat(agent): add pi coding agent adapter
2026-03-15 07:31:58 +08:00
cg33 a1b76929ea Merge pull request #133 from kevinWangSheng/fix/agent-session-close-events-panic
fix(agent): prevent send-on-closed-channel panic in session Close()
2026-03-14 23:58:51 +08:00
quabugandClaude Opus 4.6 102f919570 fix(agent/pi): address PR review feedback
- Fix mode bug: pass --auto-approve flag when mode is "yolo"
- Clean up previous turn's attachments at start of each Send()
- Check os.MkdirAll error in saveImagesToDisk
- Fix struct comment to match actual CLI args (--mode json -p)
- Add comment explaining Scanner vs Reader choice
- Add tests for cleanAttachments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 08:57:19 -07:00
quabugandClaude Opus 4.6 76fee98c22 test(agent/pi): add comprehensive unit tests (78.7% coverage)
Add tests covering:
- Agent constructor with default/custom options and missing binary
- normalizeMode for all mode aliases
- All Agent interface methods (Name, Model, Mode, Sessions, etc.)
- extractToolInput with all field priorities and fallback truncation
- truncStr with ASCII and multi-byte Unicode
- saveImagesToDisk with various MIME types and filenames
- handleEvent for all event types (session, lifecycle, unknown)
- handleMessageUpdate: text_delta, thinking accumulation, toolcall_end
- handleMessageEnd: toolResult, assistant errors, edge cases
- Full conversation simulation with all event types
- readLoop integration test with real process (sh -c)
- Session lifecycle (create, resume, close, send-when-closed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 08:53:14 -07:00
cg33 4a7bcfa267 Merge pull request #125 from quabug/fix/discord-duplicate-messages
fix(discord): deduplicate gateway MessageCreate events
2026-03-14 23:43:02 +08:00
cg33 3876029807 Merge pull request #127 from Steve-Rye/fix/jsonl-large-stdout
fix(codex): handle large stdout json lines
2026-03-14 23:40:56 +08:00
quabugandClaude Opus 4.6 1de79e4bfa fix(discord): deduplicate gateway MessageCreate events (#122)
Discord's gateway can deliver the same MessageCreate event twice during
reconnect/resume, causing duplicate agent responses.

Add a sync.Map-based dedup on message ID with a 2-minute TTL to the
Discord platform handler. The second delivery of the same msg_id is
silently dropped before reaching the engine.

Root cause: when discordgo's WebSocket connection drops and resumes, the
gateway may replay events already delivered before the disconnection.
Since AddHandler registers persistent handlers that survive reconnects,
both the original and replayed event fire the same callback.

Closes #122

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 08:39:29 -07:00
kevinWangSheng be74fddca2 fix(agent): prevent panic from closing events channel while readLoop may still send
In 6 agent session implementations (codex, opencode, qoder, iflow, cursor,
gemini), Close() unconditionally calls close(events) after an 8-second
timeout, even when the readLoop goroutine may still be running and
sending events to the channel. Due to Go's select non-determinism, the
readLoop goroutine can choose the send case over the ctx.Done() case,
causing a "send on closed channel" panic.

Move close(events) into the case where wg.Wait() succeeds (readLoop has
fully exited), and skip closing on timeout to prevent the race. This
follows the same safe pattern already used by claudeSession, where the
events channel is only closed after the readLoop goroutine has finished.
2026-03-14 08:36:29 -07:00
cg33 b714a3b958 Merge pull request #123 from sean2077/feat/dontask-mode
feat(claudecode): add dontAsk permission mode support
2026-03-14 23:30:44 +08:00
cg33 51d703ad1f Merge pull request #110 from cnrpman/feat/thread-isolation-discord-feishu
feat (discord, feishu): add thread isolation option
2026-03-14 23:21:03 +08:00
quabugandClaude Opus 4.6 aef607b536 feat(agent): add pi coding agent adapter
Add support for the pi coding agent CLI (@mariozechner/pi-coding-agent)
as a new agent backend. Pi uses `--mode json` for NDJSON streaming
output and supports multi-turn sessions via `--session <id>`.

Features:
- Parse pi's streaming events (text, thinking, tool calls, tool results)
- Accumulate thinking deltas into a single event to avoid message spam
- Pass images and files as @file arguments (pi's native attachment syntax)
- Support model selection, session resumption, and mode switching
- Implement AgentDoctorInfo, ModeSwitcher, and MemoryFileProvider interfaces

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 07:58:49 -07:00
Junyi Du 605a49fc9e feat: add thread isolation for discord and feishu 2026-03-14 22:46:24 +08:00
Codex 40a7d2d797 fix(codex): handle large stdout json lines 2026-03-14 21:12:11 +08:00
chenhg5andClaude 6e87769231 feat(bridge): add session management REST API endpoints
Add REST API endpoints for session management:
- GET/POST /bridge/sessions - list/create sessions
- GET/DELETE /bridge/sessions/{id} - get/delete session details
- POST /bridge/sessions/switch - switch active session
- Add authentication wrapper for endpoints
- Implement tests for all endpoints
- Update protocol documentation

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 18:54:48 +08:00
chenhg5andClaude c2681402a7 feat(management): add HTTP REST API for external management tools
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 18:46:52 +08:00
chenhg5andClaude 968cd9142d feat(bridge): add WebSocket bridge for external platform adapters
Implement bidirectional WebSocket protocol for runtime integration of external chat platform adapters. Includes:
- BridgeServer HTTP/WebSocket server with authentication
- BridgePlatform implementation of core.Platform interface

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 18:26:53 +08:00
sean2077 0f2c8607b3 feat(claudecode): add dontAsk permission mode support
Claude Code supports a `dontAsk` permission mode that auto-denies tools
unless pre-approved via `allowed_tools` or `settings.json` allow rules.
This is the recommended mode for bot deployments where no human operator
is available to approve tool calls.

Unlike `bypassPermissions` (which auto-approves everything),
`dontAsk` implements a "default deny, explicit allow" model — only
whitelisted tools execute, everything else is silently rejected.

This also enables PreToolUse hooks (which are skipped in
bypassPermissions mode), providing an additional security layer.

Changes:
- Add dontAsk + aliases (dont-ask, dont_ask) to normalizePermissionMode
- Add dontAsk to PermissionModes() display list
- Add documentation in config.example.toml
- Add TestNormalizePermissionMode covering all modes and aliases
2026-03-14 17:58:23 +08:00
chenhg5andClaude 978f03875f refactor(session): add thread-safe accessors and improve path handling
Add thread-safe GetAgentSessionID() and GetName() methods to Session. Replace string concatenation with filepath.Join() for safer path handling in Gemini agent. Add tests for concurrent access and path sanitization. Fix Telegram entity extraction edge cases.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 17:30:36 +08:00
cg33 396cad6008 Merge pull request #120 from AtticusZeller/feat/gemini-telegram-display
feat(gemini): optimize Gemini CLI display on Telegram
2026-03-14 16:39:58 +08:00
cg33 d95498281a Merge pull request #118 from kevinWangSheng/fix/session-agentid-unprotected-write
fix: protect Session.AgentSessionID writes with mutex
2026-03-14 16:34:40 +08:00
cg33 3d6b4878c2 Merge pull request #117 from octo-patch/add-minimax-provider-example
feat: add MiniMax provider support (Chat + TTS)
2026-03-14 16:33:49 +08:00
AtticuszzandClaude Opus 4.6 77c457bed7 feat(gemini): optimize Gemini CLI display on Telegram
Improve the Gemini CLI headless mode (stream-json) experience on Telegram:

Stream preview & between-tool text:
- Add detachPreview() to preserve frozen preview messages on Telegram
- Track text segments (segmentStart) to flush between-tool text as
  separate messages instead of losing them
- Flush accumulated text before tool/thinking/permission interrupts

Tool display:
- Show full tool content without truncation (was limited to 500 chars)
- Compute line-level diff for replace operations (computeLineDiff)
  showing only changed lines with context, not entire old/new blocks
- write_file: show file path + content in code block
- shell/Bash: use ```bash code blocks for syntax highlighting
- Split long tool messages with SplitMessageCodeFenceAware

Gemini session management (/list, /switch):
- Read ~/.gemini/projects.json for correct project directory slug
- Scan session directory for matching sessionId (handles Gemini's
  session-<timestamp>-<uuid> naming)
- Handle both string and array content formats in session files
- Filter out subagent and empty sessions from /list

Markdown→HTML converter:
- Merge consecutive blockquote lines into single <blockquote>
- Add table rendering (pipe-delimited → readable text)
- Add unordered/ordered list support (bullets, numbered)
- Widen horizontal rule (3 → 10 em dashes)
- Rename to MarkdownToSimpleHTML (platform-agnostic naming)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:40:00 +08:00
kevinWangSheng ab6c26407f fix: protect Session.AgentSessionID writes with mutex
cmdModel, cmdReasoning, and executeCardAction write
s.AgentSessionID = "" without holding the session mutex, while
processInteractiveEvents reads/writes the same field under lock
in a separate goroutine. Replace bare assignments with
s.SetAgentSessionID("") which properly acquires the lock.
2026-03-13 23:09:05 -07:00
PR Botandocto-patch e10ee7cba0 feat: add MiniMax TTS provider support
Add MiniMaxTTS implementation using the MiniMax T2A v2 streaming API.
The provider uses SSE streaming with hex-encoded audio chunks and returns
MP3 audio. Configurable via [tts.minimax] section in config.toml.

- core/tts.go: MiniMaxTTS struct with SSE stream parser
- config/config.go: MiniMax sub-struct in TTSConfig
- cmd/cc-connect/main.go: "minimax" case in TTS provider switch
- core/tts_test.go: 4 unit tests (success, API error, business error, empty audio)
- config.example.toml: MiniMax TTS configuration example

Co-Authored-By: octo-patch <octo-patch@users.noreply.github.com>
2026-03-14 13:50:48 +08:00
PR Botandocto-patch 8817e58f81 docs: add MiniMax as a documented provider example
Add MiniMax (OpenAI-compatible, 204K context) as a provider example in
config.example.toml and usage docs (EN/ZH), alongside existing providers
like SiliconFlow and Bedrock. This helps users discover and configure
MiniMax models (MiniMax-M2.5, MiniMax-M2.5-highspeed) with cc-connect.

Co-Authored-By: octo-patch <noreply@github.com>
2026-03-14 13:38:13 +08:00
cg33 794dd47343 Merge pull request #115 from kevinWangSheng/fix/telegram-utf16-entity-offset
fix: use UTF-16 offsets for Telegram entity extraction
2026-03-14 12:06:59 +08:00
chenhg5andClaude e1d2b35e5a feat(heartbeat): add card UI, persistence and new commands
Add heartbeat card UI with pause/resume controls
Implement persistent state storage for heartbeat scheduler
Add new /heartbeat command and integration with card actions
Include heartbeat in help cards and command listings
Add localization support for heartbeat status messages

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 11:58:53 +08:00
chenhg5andClaude 38a88f2364 feat(heartbeat): add heartbeat scheduler management commands and enhanced tracking
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 11:41:27 +08:00
chenhg5andClaude 6ae088a658 feat(webhook): add webhook server for external integrations
Add webhook server functionality with configurable authentication, path and port settings. Supports triggering agent prompts or executing shell commands via HTTP POST requests. Includes webhook configuration in config, example documentation in config.example.toml, implementation with tests, and integration into main application startup/shutdown.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 11:33:59 +08:00
chenhg5andClaude 805045501b feat(cron): add support for shell command execution in cron jobs
The changes introduce a new `--exec` flag to run shell commands directly in cron jobs, mutually exclusive with `--prompt`. This includes:
- Adding `exec` and `work_dir` fields to cron job structs
- Implementing shell command execution logic with timeout handling
- Adding new `/cron addexec` command for shell jobs
- Updating API validation and documentation
- Adding i18n support for new shell-related messages

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 11:18:45 +08:00
kevinWangSheng 726a7aac0c fix: use UTF-16 offsets for Telegram entity extraction
Telegram Bot API entity offsets and lengths are measured in UTF-16 code
units, not bytes. The previous code used these values directly as byte
offsets into Go's UTF-8 string, which produced wrong results (or panics)
when the message contained non-ASCII characters (Chinese, emoji, etc.)
before an @mention.

Add extractEntityText() helper that properly converts UTF-16 offsets to
the correct substring, and apply it to both msg.Entities and
msg.CaptionEntities in isDirectedAtBot().
2026-03-13 20:04:39 -07:00
chenhg5andClaude 1ae755ef9a feat(heartbeat): add periodic heartbeat checks with configurable intervals
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 10:38:18 +08:00
chenhg5andClaude c7aca28196 feat(opencode): enhance session handling and error reporting
Add message counts and timestamps to session listings. Improve error handling with detailed error message extraction. Enhance tool event handling with better input summaries. Add proper session completion events and step finish logging.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 09:54:52 +08:00
chenhg5andClaude 3f438f0055 feat(card): add tagged notes and improve delete mode flow
Add Tag field to CardNote struct and TaggedNote method to CardBuilder for programmatic note handling. Simplify delete mode toggle logic by returning updated cards instead of nil and using native Feishu form checkers. Improve Discord command description truncation using runes.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 08:46:38 +08:00
cg33 dc5cc3d0df Merge pull request #106 from q107580018/codex/fix-please-oh-need
feat(feishu): improve /delete card multi-select flow
2026-03-14 08:06:57 +08:00
kevinWangSheng 9567c9e33a fix: close agent session in HandleRelay to prevent resource leak
HandleRelay starts an agent session via agent.StartSession() but never
closes it on any return path. This leaks the underlying OS process,
goroutines, and file descriptors for every relay message.

Add defer agentSession.Close() immediately after successful session start
to ensure cleanup regardless of which return path is taken.
2026-03-13 17:03:18 -07:00
cg33 db83fcc21d Merge pull request #111 from kevinWangSheng/fix/split-message-utf8-safety
fix(core): use rune-based splitting in splitMessage to prevent invalid UTF-8
2026-03-14 08:01:25 +08:00
cg33 479e142ebd Merge pull request #109 from kevinWangSheng/fix/logrotate-nil-file-panic
fix(daemon): prevent nil pointer panic in RotatingWriter after failed log rotation
2026-03-14 07:58:40 +08:00
cg33 d9b7355bee Merge pull request #105 from bydsky/discord
fix(discord): rm hardcoded commands, use RegisterCommands
2026-03-14 07:56:54 +08:00
cg33 0004a4622c Merge pull request #107 from kevinWangSheng/fix/truncate-utf8-safety
fix: use rune-based truncation to prevent splitting multi-byte UTF-8 characters
2026-03-14 07:52:04 +08:00
chenhg5andClaude b91bb8388d feat(docs): enhance gitignore and revamp README with new banner and structure
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-14 07:44:13 +08:00
Xihui He 1bd7bbff0c fix(discord): cleanup dead code 2026-03-14 10:03:43 +11:00