* feat(platform/max): add MAX messenger bridge with bidirectional file and voice support
- Implement core.Platform plus InlineButtonSender, ImageSender, FileSender,
AudioSender, MessageUpdater, FormattingInstructionProvider,
ReplyContextReconstructor
- Two-step attachment upload via POST /uploads + multipart to CDN
- Route incoming audio (type=audio and audio/* file mime) to
core.Message.Audio so the speech pipeline transcribes it for the agent
- Outgoing voice replies via SendAudio for the TTS pipeline
- Retry on attachment.not.ready with exponential backoff
- Long poll /updates with reconnect backoff and allow_from filtering
- Register max in Makefile ALL_PLATFORMS and document in config.example.toml
- Tighten .gitignore: ignore config.*.toml (except example) and *.old
- 19 unit/integration tests with httptest mock, passes -race
Refs #741
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(platform/max): handle real MAX CDN response shapes and audio download
Issues discovered when testing end-to-end with the live MAX API:
1. Image CDN returns nested {"photos": {<id>: {"token": ...}}}, not a flat
{"token": ...}. Parse the right shape per kind.
2. Audio CDN returns XML "<retval>1</retval>" rather than JSON. The real
attachment token is delivered in the /uploads response itself for
video/audio, so we use urlInfo.Token as the source of truth and only
scan the CDN body opportunistically.
3. /audios/{token} endpoint does not exist in MAX (returns 404). Incoming
voice payloads already carry the download URL directly, mirroring
images/files. Use payload.url; fall back to resolveMediaURL only if
empty (for video where token resolution may still apply).
Also raise the speech HTTP client timeout from 60s to 5m — enough for the
first-request cold-start case when a local Whisper backend has to download
or JIT-load the model before responding.
Tests updated to emit the real CDN response shapes per attachment type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(platform/max): address review — dedup + idiomatic session key parse
- Add core.MessageDedup to Platform and check msg.Body.Mid / callback ID
in handleMessage and handleCallback. Guards against reprocessing on
poll-loop reconnect edge cases, matching the pattern used by feishu/
dingtalk/wecom platforms.
- Replace fmt.Sscanf in ReconstructReplyCtx with strings.CutPrefix +
strings.Cut — more idiomatic Go, simpler to read, and handles the
"max:" prefix and optional ":userID" suffix without a manual loop.
- Add TestHandleMessageDedupsByID regression test.
Addresses review feedback on #742.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds native support for Devin CLI (Cognition, https://cli.devin.ai/)
as a named cc-connect agent, alongside Claude Code / Codex / Cursor /
Gemini / iFlow / Qoder / OpenCode. Users can now configure Devin with
a minimal `type = "devin"` block rather than hand-rolling a generic
ACP config.
[[projects]]
name = "my-project"
[projects.agent]
type = "devin" # NEW
[projects.agent.options]
work_dir = "/path/to/code"
# command / args / display_name default to "devin" / ["acp"] / "Devin"
# mode, auth_method, env (for WINDSURF_API_KEY) passed through to ACP
Credentials come from the local `devin auth login` store — cc-connect
never sees or forwards any token. Windsurf Enterprise users can inject
WINDSURF_API_KEY via the agent env option.
The new `agent/devin/` package is a thin wrapper around `agent/acp/`:
Devin speaks the Agent Client Protocol over stdio via its `devin acp`
subcommand, so the session/prompt, session/update, permission, and
tool-call plumbing is shared. Embedding *acp.Agent and shadowing only
Name() gives us identity and defaulting without duplicating transport
code:
type Agent struct { *acp.Agent }
func (a *Agent) Name() string { return "devin" }
Wiring Devin revealed two protocol features that agent/acp had left
as MVP-skipped TODOs. Both are needed for `/list` and `/mode` to work
in IM, so this PR fills them in as part of the Devin integration —
Cursor Agent, OpenClaw, and Copilot CLI all pick up the same
improvements automatically.
ListSessions now spawns a short-lived `<command>` process that runs
just initialize + session/list, then tears down. Capability is
discovered via `agentCapabilities.sessionCapabilities.list`, and a
negative result is cached on the Agent so non-supporting servers pay
the spawn cost only once. Soft-degrades on JSON-RPC -32601 (method
not found) and -32600 (invalid request). Measured cost against
`devin acp` 2026.4.9-0: ~40 ms round trip.
Agent implements core.ModeSwitcher (SetMode / GetMode /
PermissionModes); the modes list is sourced from the modes block
returned by session/new or session/load. GetMode precedence: the most
recent explicit SetMode wins over the last server-advertised
currentModeId, so `/mode plan` reports the new mode back to the user
immediately rather than the cached previous one.
acpSession implements core.LiveModeSwitcher via session/set_mode with
client-side modeId validation against the cached availableModes.
Client-side validation is mandatory because at least one ACP server
(devin acp 2026.4.9-0) silently accepts unknown modeIds with an empty
result — server-only validation would let typos go undetected.
session/update notifications with sessionUpdate = current_mode_update
are absorbed so cc-connect stays in sync when the user switches modes
from another client (e.g. the Windsurf IDE).
newACPSession moved from seven positional args to an acpSessionConfig
struct. Private API, not a breaking change externally; the one
exported consumer (cursor_integration_test.go) is updated.
A sessionCallbacks interface lets running acpSession instances report
handshake-derived capabilities and modes back to the parent Agent,
since the engine (not the agent) owns the session lifetime.
- README.md / README.zh-CN.md: Devin added to the Support Matrix and
the agent count bumped from 7 to 8.
- config.example.toml: dedicated Devin section placed before the
generic ACP examples, with prerequisites and override notes.
- web/src/pages/Projects/ProjectList.tsx: Devin added to the agent
dropdown so the web admin UI surfaces it alongside other
first-class agents.
- Makefile: ALL_AGENTS += devin (enables selective compilation tags
`no_devin` / AGENTS=devin,...).
22 new tests total (7 for agent/devin, 15 for agent/acp). All pass
with -race. agent/acp wrapper tests avoid requiring `devin` in PATH
by pinning `command = "true"` to satisfy the upstream exec.LookPath
check while still exercising the Name() shadow and default injection.
End-to-end verified against live `devin acp` 2026.4.9-0 through the
cc-connect bridge:
- /mode lists Devin's five modes with ▶ Code marked current
- /mode plan switches and the engine reports "switched to **Plan**"
- /list shows "devin Sessions (1)" (confirms Name() propagates to UI)
- agent field in engine logs = "devin" (not "acp")
Closes none — net-new agent support.
Generated with [Devin](https://cli.devin.ai/docs)
Co-authored-by: youngshook <youngshook@users.noreply.github.com>
Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Implement Weibo private message platform via the Open IM WebSocket
protocol (open-im.api.weibo.com). Supports app_id/app_secret auth,
automatic token refresh, heartbeat keep-alive, reconnect with
exponential backoff, message dedup, and chunked sending for long
messages (2000 char limit).
Includes plugin registration, build tag (no_weibo), unit tests,
setup documentation, and cross-references in all platform docs.
Made-with: Cursor
Replace the npm-based install/upgrade workflow with compile-time
embedding. The web/dist/ directory is now embedded directly into the
binary using go:embed and registered through core's plugin registry
pattern (web/embed.go → core.RegisterWebAssets).
- Add web/embed.go (go:build !no_web) and web/embed_stub.go
- Add core/web_assets.go as the embedded FS registry
- Add cmd/cc-connect/plugin_web.go to import the web package
- Rewrite management.go static serving to use io/fs instead of disk
- Simplify /web command to setup + status (remove install/upgrade/uninstall)
- Clean up web_manager.go (remove npm logic, keep GenerateToken)
- Update Makefile with web build target and NO_WEB/build-noweb support
- Remove .npmignore and npm publishing fields from package.json
Binary size impact: ~1MB (16M → 17M). Use `no_web` tag to exclude.
Made-with: Cursor
* feat(agent): add ACP stdio JSON-RPC adapter (MVP)
Implement agent/acp as an ACP client over newline-delimited JSON-RPC: initialize,
session/new or session/load, session/prompt, session/update mapping to core.Event,
and session/request_permission wired to RespondPermission.
Includes plugin_agent_acp.go (!no_acp), Makefile ALL_AGENTS entry, config example,
unit tests for transport and mapping, and AgentDoctorInfo for doctor.
Made-with: Cursor
* fix(acp): Cursor CLI compatibility — authenticate + cursor/* RPC ack
- Optional auth_method (e.g. cursor_login) triggers JSON-RPC authenticate after initialize
- initialize includes minimal clientCapabilities (fs/terminal) per Cursor ACP docs
- Acknowledge cursor/* server requests with empty result to avoid blocking tool flows
- Add integration test against ~/.local/bin/agent when present
- Document auth_method and args in config.example.toml
* fix(telegram): use Request for sendChatAction typing indicator
Telegram returns result=true for sendChatAction; BotAPI.Send unmarshals
result as Message and fails. Request only checks ok and avoids that decode.
* fix(core): surface EventToolResult in IM; extend ACP tool/thinking mapping
- Engine: handle EventToolResult in interactive loop (was silently dropped);
compress and relay paths append tool output too. Add MsgToolResult i18n.
- ACP: stream tool_call_update for in_progress/pending; optional reasoning_*
sessionUpdate kinds -> EventThinking; fallback hook for unknown kinds.
* fix(core): run agent Send concurrently with interactive event loop
Agents that block inside Send (e.g. ACP session/prompt) can emit
EventPermissionRequest while blocked; the engine must consume Events()
in parallel or permission prompts never reach the platform.
- processInteractiveMessageWith: async Send + pass sendDone into processInteractiveEvents
- processInteractiveEvents: select on sendDone for errors and sync at turn boundaries
- drainPendingMessages: same async Send pattern as the main path
- Add TestProcessInteractiveEvents_PermissionWhileSendBlocked regression test
* docs(acp): document OpenClaw and Copilot CLI; add display_name option
- config.example.toml: Cursor, OpenClaw (`openclaw acp`), Copilot (`copilot --acp --stdio`) examples with upstream doc links
- acp agent: optional display_name for doctor (AgentDoctorInfo)
- agent_test.go: cover default and custom display_name
* fix(core): remove ineffectual pendingSend assignments (ineffassign)
pendingSend was set to nil immediately before return or before
reassignment to nextSend; golangci-lint flags these as ineffectual.
* test(core): fix race in PermissionWhileSendBlocked under -race
The test goroutine polled stubInlineButtonPlatform.buttonRows while
processInteractiveEvents wrote them from another goroutine. Use a
permSignalInlinePlatform wrapper that signals on perm:allow via a channel
instead of racy field reads.
---------
Co-authored-by: Claude <noreply@anthropic.com>
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>
Implement build tag system for agents/platforms with Makefile support:
- Move imports to plugin_*.go files with build tags
- Add Makefile logic for AGENTS/PLATFORMS_INCLUDE/EXCLUDE
- Update documentation with usage examples
- Remove direct imports from main.go
generated by llmgit
Co-Authored-By: Claude <noreply@anthropic.com>
Added cross-platform build targets in Makefile and updated .gitignore. Introduced version, commit, and build time tracking via LDFLAGS. Added clean dist directory target and organized build artifacts.
generated by llmgit
Co-Authored-By: Claude <noreply@anthropic.com>