- Add global [[providers]] config with project-level provider_refs
- Add provider management page in web admin with preset support
- Implement card-based /provider add flow: preset selection card
with sponsor presets, API key prompt, and manual "Other" option
- Add inline button support for Telegram-style platforms
- Add remote provider-presets.json with sponsor data
- Add REST API endpoints for global providers and provider refs
- Update project detail providers tab with unified list UI
Made-with: Cursor
Quoted message prefix was prepended directly to Content, making
the first character not '/' and breaking command parsing. Move the
quoted prefix to ExtraContent which is merged after command detection.
Made-with: Cursor
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
Previously done_emoji only fired when stream preview had been updated
in-place at least once, which meant quick responses never triggered
the reaction. Remove the needsDoneReaction() gate so the platform's
AddDoneReaction is always called on EventResult.
Made-with: Cursor
Remove the standalone /agentsid command and fold its output (Agent SID)
into /status. Also add work_dir to the status output so users can see
the current working directory at a glance.
Changes:
- Remove /agentsid command registration, handler, card button, help
text, and all i18n entries (MsgAgentSIDWarning, MsgBuiltinCmdAgentSID)
- Add Agent SID line to /status (both plain-text and card variants),
shown only when a session is active
- Add Work Dir line to /status output (i18n: 5 languages)
- Add MsgStatusAgentSID i18n key
Made-with: Cursor
Previously, /switch overwrote the active session's AgentSessionID,
causing the old agent session to disappear from KnownAgentSessionIDs
and thus from /list output.
Add SwitchToAgentSession() which finds an existing internal session
for the target agent session ID or creates a new one, preserving all
previous session references.
Made-with: Cursor
When quiet mode is enabled (ThinkingMessages=false, ToolMessages=false),
EventThinking and EventToolUse events are suppressed but the stream
preview card is not frozen. This causes multiple Claude text outputs
to accumulate into a single preview card.
Fix: when thinking/tool events arrive in quiet mode and there is
accumulated text, freeze + detachPreview to preserve the current text
as a permanent message. When the preview is already degraded (after
the first freeze), send accumulated text directly as a regular message
instead of silently advancing segmentStart past it.
Co-authored-by: leoliang <leoliang@futunn.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tables were rendered as proportional-width plain text, causing columns
to be misaligned in Telegram clients. Wrap tables in <pre> with
per-column padding so they display correctly in monospace.
Related: openclaw/openclaw#36323
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
H1: Kimi readLoop — move cmd.Wait() before EventResult to prevent
out-of-order EventError after turn completion.
H2: Kimi tests — add skipUnlessKimiAvailable guard so CI without
kimi binary skips gracefully instead of failing.
H3: Claude shellJoinArgs — add comprehensive unit tests for shell
quoting (spaces, single quotes, backslashes, newlines).
M1: cli_path — document that paths with spaces are not supported.
M3: needsDoneReaction — add streaming preview tests covering
UpdateMessage trigger, discard reset, and disabled state.
M5: /agentsid — add security warning about session ID sensitivity
in the command response.
M7: i18n — localize reply_footer_remaining for ZH/ZH-TW/JA/ES;
add MsgAgentSIDWarning translations for all 5 languages.
Made-with: Cursor
* feat(cli): add `cc-connect agent-sid` command to query agent session ID
Reads the agent session ID (e.g. Claude Code / Codex / Gemini CLI session
ID used with --resume) from persisted session files. Auto-detects project
and session key from CC_PROJECT and CC_SESSION_KEY env vars, making it
usable directly inside agent subprocess sessions.
Handles all session file naming variants: plain, hashed, workspace (_ws_),
legacy paths, and .sessions.json. When multiple files contain the same key,
prefers the one with the newest UpdatedAt. Includes nil-safety, file modtime
fallback for zero-UpdatedAt entries, and proper error propagation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(core): add /agentsid chat command to show agent session ID
Registers /agentsid (alias: /agent-sid) as a builtin chat command so
users can query the agent session ID directly from Discord/Telegram/
Feishu etc. without asking the agent to run a Bash command.
- Added to builtinCommands registry, handleCommand switch, and help
card (session group)
- Added to text-based /help output for all 5 languages (EN/ZH/ZH-TW/JA/ES)
- Added i18n description key MsgBuiltinCmdAgentSID with translations
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(lint): use strings.TrimPrefix instead of HasPrefix+slice
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `cli_path` and `cli_args_flag` config options so cc-connect can
spawn third-party Claude-compatible CLIs (and wrappers that need Claude
flags bundled through a passthrough flag) instead of hardcoding `claude`.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Trace parent_id links up to 5 levels deep when a user replies to a message,
providing the full conversation chain as context for the agent.
- Add fetchSingleMessage() to retrieve a single message with parent_id
- Add fetchReplyChain() for iterative chain traversal with cycle detection
- Add formatReplyChain() with backward-compatible single-message format
- Refactor fetchQuotedMessage() as a thin wrapper
- Show 'Bot' for app senders, 'User' for unresolved user names
Co-authored-by: MeiShaoqing <meishaoqing@xiaomi.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the agent's response is delivered via in-place card update (Feishu
streaming preview), the user gets no push notification. This adds a
configurable done_emoji option (e.g. "Done") that adds a reaction after
the "OnIt" reaction is removed, so the user knows the agent finished.
Only activates in quiet mode when the preview card was updated in-place
at least once (via UpdateMessage). Non-quiet mode is unaffected as
intermediate messages already trigger push notifications. Single-round
responses that only use SendPreviewStart are also unaffected, as the
initial send already triggers a push notification.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat(core): add optional reply footer metadata
* feat(config): enable reply footer by default
* feat(codex): use runtime footer metadata from app-server
* feat(codex): resolve exec footer metadata at runtime
* feat(core): style reply footer and prefer context left
* feat(codex): use real runtime context for reply footer
* feat(core): use english left footer text
* fix(codex): use current context tokens for footer left
* fix(codex): match cli context left calculation
* test(codex): adapt footer tests to session base url arg
buildReplyContent previously used a three-tier strategy: card only for
"complex" markdown (code blocks/tables), post for everything else. This
caused most markdown content (headings, blockquotes, links, dividers,
lists) to be rendered via post format, which has poor markdown support —
links aren't clickable, blockquotes don't render, dividers are ignored.
Simplify to two paths:
- Has markdown → card (best rendering, supports all markdown features)
- No markdown → text
- Fallback to post md tag only when table count exceeds card limit (>5)
Removes the hasComplexMarkdown gate and the buildPostJSON path from
the rendering decision. hasComplexMarkdown and buildPostJSON are kept
as functions for backward compatibility but are no longer called by
buildReplyContent.
Co-authored-by: leoliang <leoliang@futunn.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(claudecode): cover readLoop interruption regressions
- TestReadLoop_ChildHoldsStdoutPipe: a descendant holds the agent's
stdout fd, so scanner.Scan() blocks forever and readLoop's defer
never runs. Asserts the agent's last buffered event is delivered.
- TestReadLoop_CtxCancelClosesChannels: ctx cancelled while stderrBuf
is non-empty exercises the defer's <-ctx.Done() branch that used to
return without closing events/done.
Both hang on the current code and pass after the fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(claudecode): unblock readLoop on agent exit and ctx cancel
When a tool subprocess inherits the agent's stdout fd, the pipe stays
open after the agent dies and scanner.Scan() blocks forever. readLoop's
defer never runs, cs.done never closes, and Close() blocks until the
130s timeout in closeAgentSessionWithTimeout fires.
Restructure readLoop:
- Reaper goroutine owns cmd.Wait() and signals waitDone on exit.
- Stdout-closer goroutine force-closes the read end on either
<-ctx.Done() or <-waitDone, unblocking scanner in both paths.
- Defer reads waitErrCh instead of calling cmd.Wait() itself, and
unconditionally closes events/done (the previous <-ctx.Done()
branch returned early and stranded the engine event loop).
- Scanner error suppresses the expected ErrClosed from the
reaper-triggered close via a channel check.
Also splits readLoop into named helpers per AGENTS.md style guide.
Close() now returns in microseconds in all paths, shrinking the
cleanupInteractiveState window from up to 130 s to negligible.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(claudecode): align readLoop with os/exec stdout pipe semantics
* Revert "fix(claudecode): align readLoop with os/exec stdout pipe semantics"
This reverts commit 9008b57f9d16b1dd9400310d8a2c93f9c3165d9a.
* fix(claudecode): add grace period for startReadLoopWait to avoid cutoff json in stdout.close
* “fix(claudecode): fix minor issues by agent reviewer
* style(claudecode): fmt case with space
* fix(claudecode): avoid waiting for 50ms in done branch
* style(claudecode): improve comments
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move AIGoCode to first position (highest sponsor)
- Update AICodeMirror description: "CC users" instead of "cc-connect users"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
H1: Prevent workspace path traversal via ../
Add resolveLocalDirPath() that validates resolved path stays under
baseDir, expands ~/... via os.UserHomeDir, rejects escaping paths.
M2: Sanitize sender_name in inject_sender header
Replace quotes with single quotes, strip newlines/CR to prevent
prompt injection via display names.
M3: Warn on unset env var placeholders in config
resolveEnvPlaceholders now logs slog.Warn when ${VAR} references
an environment variable that is not set.
M4: Validate WeCom api_base_url
Reject non-http(s) or malformed URLs at New() to prevent credential
exfiltration via misconfigured base URL.
M5: Quote systemd Environment= values
Wrap values in double quotes to handle spaces, #, and special chars
in proxy URLs and paths.
M7: Discord table detection now requires separator row
Only wrap pipe-delimited blocks that contain a markdown separator
row (| --- | --- |), reducing false positives on decorative pipes.
Made-with: Cursor
- AICodeMirror: Claude Code/Codex/Gemini CLI relay service
- AIGoCode: All-in-one Claude Code/Codex/Gemini platform
- Code0: AI model API relay for Chinese developers
- claudeapi.com: Premium direct Claude connection service
Sponsor section moved to front (after language switch), similar to cc-switch layout.
Full descriptions preserved for AICodeMirror and AIGoCode.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
H1: Prevent double-Close on concurrent cleanupInteractiveState calls
by nil'ing state.agentSession under the lock before closing.
H2: Weixin StartTyping sends typingStatusStop on ctx.Done() and on
repeat-failure exit, preventing stuck "typing" indicator.
M3: Feishu @mention resolution: downgrade PII log to Debug, HTML-escape
display names in at tags, skip ambiguous (duplicate) names.
M4: Narrow segmentStart==0 condition in EventResult to only apply when
tool messages are hidden (!e.display.ToolMessages).
M5: Log warning when cron workspace agent creation fails instead of
silently falling back to global agent.
M7: Discord file download capped at 50 MiB via io.LimitReader.
M8: Weixin getConfig now checks ret/errcode fields and returns error
on non-zero values.
Made-with: Cursor
The BoundAt field in WorkspaceBinding uses time.Time which requires
strict RFC3339 format. When workspace_bindings.json is hand-edited
(e.g. by an AI agent), a missing timezone suffix causes json.Unmarshal
to fail for the entire file, breaking ALL workspace bindings.
Introduce FlexTime, a time.Time wrapper with lenient UnmarshalJSON
that tries multiple time formats and falls back to zero value on
failure instead of returning an error. This ensures a non-critical
metadata field cannot take down the workspace routing.