Commit Graph

1139 Commits

Author SHA1 Message Date
dev-claudecode
2754d715c1 docs(sponsors): update VisionCoder entry
- Switch domain to visioncoder.cn (per VisionCoder request)
- Refresh logo asset (assets/sponsors/visioncoder.png)
- Update sponsor copy to reflect current VisionCoder offerings
  (retail channels for Claude Max 200 / GPT Pro 200 premium accounts)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 10:49:56 +08:00
dev-claudecode
7915635f10 docs(sponsors): add APINEBULA as new sponsor
- Insert APINEBULA at the top of the sponsor table in both
  README.md and README.zh-CN.md (position 1).
- Add assets/sponsors/apinebula.png (logo supplied by
  APINEBULA via their Galaxy Video Bureau platform).
- Sponsor copy and registration link provided by APINEBULA.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 22:41:52 +08:00
cg33
e993ba7c05 fix(kimi): conditionally pass --print so newer Kimi Code CLI works (#1456) (#1461)
* fix(kimi): conditionally pass --print so newer Kimi Code CLI works (#1456)

## Problem

`cc-connect` unconditionally passes `--print --output-format stream-json
--prompt …` to the Kimi CLI. The newer Kimi Code CLI removed the
standalone `--print` flag and now exits immediately with

    error: unknown option '--print' (Did you mean --prompt?)

so every Kimi chat is broken for users on the new CLI. Issue #1456.

## Root cause

The two CLIs in the wild have opposing requirements for non-interactive
runs:

| CLI            | --print? | --output-format requires |
|----------------|----------|--------------------------|
| legacy kimi-cli| yes      | --print                  |
| new Kimi Code  | removed  | --prompt                 |

A fixed arg list cannot satisfy both, and `--print` cannot be emulated
the way #1253 emulates `--quiet` — it is the CLI's own non-interactive
mode toggle, not output formatting.

## Fix

Probe `kimi --help` once when the Agent is constructed and cache the
detected flag set. `buildArgs()` (newly extracted from `Send()` so it is
unit-testable) only emits `--print` when the installed binary still
advertises it. Probe failure / timeout falls back to "no --print", which
matches the direction the Kimi CLI is moving and avoids the hard-failure
mode of the current code.

- `agent/kimi/probe.go` — `parseKimiHelpFlags()` pure parser + bounded
  `probeKimiFlags()` (5s timeout, swallows errors).
- `agent/kimi/kimi.go` — Agent caches `kimiFlagSupport`, threads it into
  `newKimiSession`; doc comments updated.
- `agent/kimi/session.go` — `kimiSession.flagSupport` + `buildArgs()`
  helper; conditional `--print`; permission-mode comment updated to
  cover both legacy (`--print` implicit `--yolo`) and modern (`--prompt`
  auto-permission) behaviors.

## Tests

- `TestBuildArgs_NoPrintSupportOmitsPrintFlag` — regression test for
  #1456; fails on pre-fix code, passes on this branch.
- `TestBuildArgs_PrintSupportIncludesPrintFlag` — legacy CLI branch.
- `TestBuildArgs_PlanMode` / `TestBuildArgs_ResumeSession` — sanity
  coverage for other arg paths.
- `TestParseKimiHelpFlags_LegacyAdvertisesPrint` /
  `TestParseKimiHelpFlags_ModernHidesPrint` — parser handles both typer
  box-drawing and standard click `-p, --prompt` layouts.
- `TestParseKimiHelpFlags_IgnoresPositionalAndShortOnly` — robustness.
- `TestProbeKimiFlags_FallbackOnMissingBinary` — probe failure path
  returns zero value (modern-CLI default).

Full suite verified: `go test ./agent/kimi/...` (and `-race`), plus
`go test ./core/ -run TestCUJ`. Pre-existing `agent/acp` integration
test still fails on `origin/main` (requires `cursor agent login`) —
unrelated to this change.

## Non-goals

- Does not touch the cursor agent; Claude Code CLI still supports
  `--print` so cursor's hard-coded flag is fine.
- Does not change `--quiet` handling; PR #1253 owns that fix for #806.
- Does not introduce new config schema.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(kimi): wrap deferred Close in func to satisfy errcheck

CI lint failed with 3 (and a 4th caught locally) `defer ks.Close()`
sites that ignore the returned error. Wrap in a closure that
explicitly discards the return value so golangci-lint errcheck is
happy and the tests still run.

No functional change.

---------

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 21:22:12 +08:00
cg33
5e2d501f27 fix(core): align drainPendingMessages agentSession capture with #1436
Follow-up to #1436. The third call site in drainPendingMessages was
modeled correctly in spirit (capture into local var before goroutine)
but missed two details that #1436 applied to the first two sites:

1. The capture `as := state.agentSession` happened without holding
   state.mu, so the same race the PR set out to fix could still nil
   the field between the unlock above and the capture.
2. The Send goroutine did not have a defensive `if as == nil` check,
   unlike the other two sites; a nil capture would still panic when
   the goroutine ran.

Also folds the existing nil/Alive check into the post-capture path so
the gating uses the local copy (consistent with the new contract).

No behavior change for the happy path; in the racy path the goroutine
returns an error instead of dereferencing nil, which the existing
error handling already covers.

Verified locally:
- go vet ./... clean
- go build clean
- go test -race ./core -run TestCUJ_H2_TwoPlatformsConcurrentNoBleed -count=10 PASS
- go test ./core -run "Drain|Queue" PASS
2026-06-28 20:56:25 +08:00
qvictl
e739e2eb57 fix(core): throttle message recall fallback probes (#1321)
* fix(core): throttle message recall fallback probes

Avoid repeatedly probing the platform for the same active message while retaining recall detection for new turns.

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

* chore(core): address recall probe review feedback

Document the Feishu recall probe fix and keep the monitor interval unchanged while relying on per-message throttling.

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

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-06-28 20:55:01 +08:00
gotang
a4b4659316 fix(core): fix race between Send goroutines and cleanupInteractiveState (#1436)
cleanupInteractiveState sets state.agentSession = nil under state.mu,
but three Send goroutines read state.agentSession without holding the
lock. When an agent process exits before the Send goroutine is
scheduled, cleanup can nil agentSession, causing a nil pointer
dereference panic.

Fix: capture agentSession into a local variable under state.mu, then
use the local in the goroutine. If the captured value is nil, the
goroutine returns an error instead of panicking.

Co-authored-by: tanghongliang <tanghongliang@citos.cn>
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-28 20:54:58 +08:00
cg33
303fd46de0 fix(claudecode): chmod 0o644 on per-spawn system-prompt temp file (#1429) (#1433)
writeTempAppendPromptFile (the 1% edge-case path for prompts that have
session-specific platform formatting or user append_system_prompt) used
os.CreateTemp, which leaves the file at mode 0600 owned by the
cc-connect process user (often root under systemd). When the agent
was spawned under a different run_as_user, the target user got EACCES
and the agent exited before reading any prompt.

Fix: f.Chmod(0o644) immediately after write, mirroring the shared
ensureSharedSystemPromptFile path (which already writes 0o644 via
writeFileAtomic). The per-spawn content is a superset of the already
shared base prompt, so 0644 is consistent with the shared file.

The shared-file path (ensureSharedSystemPromptFile, used for the
common 99% case where no platform/user append is set) is already 0644
and untouched here. The daemon-mode path resolution fix from #1419
and the v1.3.4 cmdline 8192 fix from #1376 are independent and not
modified.

Test: TestWriteTempAppendPromptFile_ReadableByOtherUser asserts the
on-disk mode is 0o644 (the run_as_user contract) and that an
O_RDONLY open succeeds — same access path the spawned agent uses
for --append-system-prompt-file.

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-28 20:34:02 +08:00
clingnet
9301cfaaa2 feat(providers): add Zhipu GLM (z.ai + bigmodel CN) presets (#1412)
Adds two provider presets so users can one-click select Zhipu GLM via the
GLM Coding Plan subscription, both the international (z.ai) and mainland
China (open.bigmodel.cn) endpoints.

- claudecode: Anthropic-compatible endpoint (/api/anthropic)
- codex / opencode: OpenAI-compatible coding endpoint (/api/coding/paas/v4),
  codex wire_api = "chat"
- default model glm-4.7 (works across all plan tiers); models list also
  exposes glm-5.2 / glm-5.2[1m] / glm-4.6 / glm-4.5-air

Data-only change to provider-presets.json (fetched at runtime); no Go code
paths are affected.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 20:23:29 +08:00
cg33
fec094f9f0 chore(gitignore): add .worktrees/ — local-only multi-agent scratch dirs (#1443)
Multiple AI agents (qa-*, dev-*, release-*) routinely create ad-hoc git
worktrees under .worktrees/<task-name> for parallel branch work (PR
review, hotfix prep, release cherry-pick, etc.). These directories are
local-only by convention and must not be staged or committed.

Without this rule, `git add .` or `git add -A` from the repo root can
accidentally stage tens of worktrees (897 MB locally as of writing).

Establishes .worktrees/ as the canonical location for such scratch
checkouts, documented inline so future contributors / agents see the
intent.

Co-authored-by: qa-cursor <qa-cursor@cc-connect.local>
2026-06-28 20:21:06 +08:00
mchenziyi
fd6dbcc320 feat: add reasonix agent adapter (#1281)
* feat: add reasonix agent adapter

Adds a new agent adapter for Reasonix, a multi-model coding agent.

The adapter communicates with a running 'reasonix serve' instance via its HTTP API:
- Submits prompts via POST /submit
- Consumes agent events via SSE /events
- Handles tool approval via POST /approve
- Supports mode switching (default/yolo/plan)
- Accumulates incremental reasoning chunks into single events

* fix: handle resp.Body.Close errors (errcheck lint) and fix close-before-status order

* fix: add SSE auto-reconnect with backoff when reasonix serve restarts

* fix: add unit tests, static assertions, error body, reconnect limit

10 unit tests with httptest. All pass -race. P2 fixes: static assertions, error body, max reconnect.

* fix: lint: check error returns in session_test.go (errcheck)

* fix: lint: check remaining fmt.Fprintf error in TestSSEReconnect

* fix: add reasonix to ALL_AGENTS in Makefile, fix CHANGELOG, add doc comments

P1: Added reasonix to ALL_AGENTS in Makefile so make build includes it by default.

P2: Fixed CHANGELOG Unreleased entry, added formatImages doc comment, serve_url normalize comment, normalizeMode auto/force comment.

* chore: retrigger CI
2026-06-25 07:46:06 +08:00
xxb
d7f06db3e5 fix(core): create queue placeholder before session lock (#1389) 2026-06-25 07:42:31 +08:00
Yuanhao Luo
6fc59a3c6e feat(feishu): support mention_map for outbound bot-to-bot @ resolution (Fixes #1322) (#1341)
* feat(feishu): support mention_map for outbound bot-to-bot @ resolution

* feat(feishu): support mention_map for outbound bot-to-bot @ resolution (Fixes #1322)

* fix(feishu): update stale @mention test and doc comment for text-format output

After 40406328 made resolveMentionsInContent always emit the MsgTypeText
at syntax (<at user_id="...">), the legacy TestResolveMentions_CardFormat
assertion (expecting the card variant <at id=...>) started failing. Rename
the test to TestResolveMentions_MarkdownContent and assert the text-format
output instead — the behavior under test (markdown @name resolution) is
unchanged, only the expected at-tag format moved.

Also update the doc comment on resolveMentionsInContent that still
referenced "predicted message type" (that branch was removed).

No production behavior change.

* update comment of TestResolveMentions_MarkdownContent fun

* fix(feishu): restore lost test bodies from broken merge conflict resolution

* fix(feishu): restore missing closing braces broken by merge conflict resolution

The merge of main (26d897c) re-introduced the lost-brace bug: the new
TestNewPlatform_ImageBatchWindow was spliced in before the closing braces
of TestSendWithStatusFooter_NoFallbackOnNonMentionAt, leaving the for-loop
and function unclosed. This caused 10 typecheck errors (expected '(',
found TestXxx) and failed CI lint.

Add the two missing closing braces so the file compiles. All feishu
package tests pass (go test ./...).

* style(feishu): apply gofmt to fix trailing whitespace and map alignment

- platform_test.go: remove trailing whitespace on line 1789 comment
- feishu.go: drop blank line in footerElements map literal and realign
  colon spacing (gofmt); main is clean so this issue was introduced in
  this PR, not pre-existing

* docs(changelog): fix Feishu mention_map issue ref (PR #1341 -> Issue #1322)
2026-06-25 07:40:39 +08:00
dev-claudecode
1e0d8f7fff docs(sponsors): update unity2.ai registration link
Replace placeholder link with the affiliate-tracked
registration URL on unity2.ai's request.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:49:07 +08:00
dev-claudecode
5c19196825 docs(sponsors): add unity2.ai as new sponsor
- Insert unity2.ai between CaMeL and AnyRoute.io in both
  README.md and README.zh-CN.md (position 7).
- Add assets/sponsors/unity2ai.png.
- Sponsor copy and link provided by unity2.ai.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:32:23 +08:00
dev-claudecode
5cf237941a fix(dingtalk): recover panic in stream loop to prevent process crash (#1390)
The upstream dingtalk-stream-sdk-go v0.9.1 has a race condition in
processLoop (client.go:161) that causes "send on closed channel" panic
after overnight idle. The fix exists in upstream PRs but is not yet
merged/released.

Add defer/recover around streamClient.Start() so the panic is caught
and the existing reconnect loop can re-establish the connection instead
of crashing the entire cc-connect process.

Closes #1390

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 07:30:20 +08:00
coolrockin
a5d441e98e fix(claudecode): emit EventToolResult so tool output reaches progress card (#1407)
* fix(claudecode): emit EventToolResult so tool output reaches progress card

## 背景

cc-connect 的飞书卡片(以及其他平台的 progress card)渲染工具结果
依赖 engine 收到 EventToolResult 事件。engine.go:4745 的 case
EventToolResult 会调用 formatProgressToolResult / cp.AppendEvent
把工具输出渲染到卡片。

## 问题

agent/claudecode/session.go 的 handleUser 函数处理 stream-json
里的 tool_result content block 时,原本只做了一件事:

  if isError { slog.Debug("claudeSession: tool error", ...) }

也就是 **error 情况只记日志,正常情况完全静默丢弃**,始终没有
emit EventToolResult 事件给 engine。

对照 agent/codex/session.go:522 case "command_execution":codex
agent 在工具完成时正确构造了 EventToolResult 事件并发送到
cs.events channel,engine 收到后才能渲染工具结果卡片。

## 用户感知的现象

- claude-multi 飞书群里调工具后,**只看到最终文字回复,看不到
  工具调用面板和工具结果**
- codex-multi 飞书群里调工具后,能看到完整的 "工具调用 + 工具结果"
  卡片渲染
- 两个 project 的 cc-connect 配置完全一致,差异只在 agent type

之前误以为是 stream preview / 飞书 universal card payload
(ErrCode 200800) 等问题,实际那些是次要现象 —— 真正的根因是
claudecode agent 根本没把工具结果事件转发给 engine。

## 方案

最小化修复:在 if contentType == "tool_result" 分支里,无论
isError 与否都构造 EventToolResult 事件 emit 出去。

实现细节:
- content 字段兼容两种格式:string 直接用;array of {type:"text",
  text:"..."} 拼接所有 text 块(Anthropic SDK 两种格式都可能出现)
- 工具名留空:Anthropic 的 tool_result block 只带 tool_use_id 不
  带工具名,反查 id→name 映射需要额外缓存且容易出错。视觉上工具
  调用面板和结果面板按顺序排列,用户能自行配对,因此留空可接受
  (飞书 buildToolDisplay 对空名 fallback 显示为 "Tool")
- exit code: isError → 1, 否则 0;success: !isError
- 复用包内已有的 truncateStr 截断到 500 字符(与 codex agent 一致)

## 影响范围

- 只改 agent/claudecode/session.go 一处(handleUser 函数)
- 不影响 codex / gemini / opencode 等其他 agent
- 不影响 engine 及下游渲染逻辑(只是补上之前缺失的事件源)

* test(claudecode): regression test for EventToolResult emit
2026-06-23 07:22:13 +08:00
cg33
79e3132fd8 fix(core): queue post-restart notification and dispatch on platform ready (#1383) (#1388)
cc-connect /restart used to send the success notification immediately
after engine startup, racing the platform's async connect window
(Telegram: ~2.6s). On a not-yet-ready platform the send was silently
dropped at debug log level — Tony-ooo reported this in #1383.

The notify is now queued on the engine and dispatched when the target
platform reaches OnPlatformReady, with bounded retry (3 attempts,
0/500/1500 ms backoff) on transient send failure. Failed sends log at
warn level. A 10s safety timeout drops the notify with a warning if
the platform never reaches ready, so startup is never blocked
indefinitely.

Covers all AsyncRecoverablePlatform implementations (Telegram, Discord,
Weixin, Matrix) for free via the existing OnPlatformReady hook.

Tests:
- TestRestartNotify_DispatchesAfterPlatformReady
- TestRestartNotify_AlreadyReadySucceedsImmediately
- TestRestartNotify_RetriesOnSendFailure
- TestRestartNotify_ExhaustsRetriesNoHang
- TestRestartNotify_TimesOutIfPlatformNeverReady
- TestRestartNotify_NilNotifyIgnored

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
2026-06-23 07:21:42 +08:00
Marco
560635c14e Add send cwd workdir support (#1380) 2026-06-23 07:20:17 +08:00
Han
355793eaee refactor(agent): centralize cmd/env option parsing into core with unified cmd field (#1297)
* refactor: centralize cmd/env option parsing into core

- Add core.ParseCmdOpts() to unify cmd/cli_path/command option
  parsing across all agents, with deprecation warnings for old keys
- Add core.ParseConfigEnv() to parse [projects.agent.options.env]
  from config into []string KEY=VALUE format
- Rename cliBin/command struct fields to cmd consistently
- Add cliExtraArgs support so cmd="binary arg1 arg2" works
- Add configEnv field for static env that persists across SetSessionEnv
- Fix env merge order: configEnv < providerEnv < sessionEnv (was
  inconsistent in copilot and opencode agents)
- Update all tests for renamed fields

* fix(claudecode): add backward compat for deprecated cli_args_flag

Users with cli_args_flag in their config.toml will see a deprecation
warning directing them to the new cmd_args_flag key.

* docs(config): update config examples to use cmd instead of deprecated keys

- config.example.toml: cli_path → cmd, command(S) → S(6 occurrences)
- claudecode/claudecode.go: comment cli_path → cmd
- copilot/copilot_test.go: comment cliBin/cli_path → cmd

* test(core): add direct unit tests for ParseCmdOpts and ParseConfigEnv

Address review feedback on PR #1297 (P2). The two helper functions in
core/cmdopts.go are depended on by 13 agent packages; previously their
behavior was only covered indirectly via agent-level New() tests. This
commit adds direct unit tests covering:

- ParseCmdOpts: four-tier priority (cmd / cli_path / command / default),
  empty/whitespace boundaries, tokenization of extra args, no-warning
  for canonical cmd field, and capture of deprecation warnings.
- ParseConfigEnv: nil / missing key, map[string]string, map[string]any
  (TOML parser output), non-string value filtering, unsupported types,
  and input non-mutation.

Also adds structured slog attrs (deprecated_key, new_key) to all three
deprecation warnings (cli_path, command, cli_args_flag) so that future
log aggregation can scan deprecated-key usage without code changes
(review feedback P3).

Ref: PR #1297 review.

* fix(codex): use local cmd var (not stale cliBin name) in struct init

Post-rebase fixup: the struct field was renamed cliBin -> cmd in
PR #1297, and the local variable returned by core.ParseCmdOpts is
also named cmd. The rebased struct initializer still referenced
the old name 'cliBin', which no longer exists in scope. Assign
the local cmd variable to the cmd struct field.
2026-06-23 07:05:16 +08:00
Yu Zhang
fa86932f29 fix(core): make /history entry truncation configurable (#1291)
* fix(core): make history entry truncation configurable

* test(core): cover history truncation emoji boundary
2026-06-23 07:01:41 +08:00
Han
8e3203a7b7 feat(codex): prefer Codex model_catalog_json as highest-priority model source (#1074)
- Add readCodexModelCatalog() that reads $CODEX_HOME/config.toml,
  resolves model_catalog_json path (expand ~, relative to CODEX_HOME),
  and parses the JSON as the primary model list
- Extract shared parseCodexModelsJSON() to deduplicate JSON parsing
  logic between readCodexCachedModels and readCodexModelCatalog
- Refactor readCodexCachedModels() to reuse resolveCodexHome(nil)
- Update AvailableModels() priority:
  1. model_catalog_json (new, highest)
  2. provider config
  3. GET /v1/models API
  4. models_cache.json
  5. hardcoded fallback
- Add tests: TestAvailableModels_UsesModelCatalog, TestReadCodexModelCatalog_NoConfigFile
2026-06-23 06:58:40 +08:00
dev-claudecode
809038720a fix(slack): stop chat.update once payload exceeds size limit; deliver full reply via fresh postMessage
Surfaced during v1.4.0-beta.1 QA: when an agent produced a ~7.3 KB
Markdown reply in Slack, the streaming card (#1333) silently failed
mid-stream with `msg_too_long`, then Finalize tried to chat.update the
full reply and also failed, forcing the engine onto its emergency
fallback path. The user perceived the turn as a non-streaming "one-shot
output preceded by ~30s of silence".

Root cause: Slack's `chat.update` text parameter is capped at ~4000
bytes (server-side, despite docs saying chars). The streaming card had
no size guard and Update() also swallowed errors with no log, so the
half-broken state was invisible from logs until Finalize blew up.

Fix:
- Introduce `slackUpdateMaxText = 3500` (conservative; leaves headroom
  for multi-byte CJK content where Go's len() overshoots Slack's limit).
- `Update()`: once rendered exceeds the limit, silently skip the
  chat.update tick (leave the streaming card at its last fitting
  snapshot) and emit a Debug log instead of staying invisible.
- `Update()`: also Debug-log transient render errors (previously
  swallowed entirely).
- `Finalize()`: when the final payload exceeds the limit AND a card has
  already been posted, deliver the full reply via a *fresh*
  chat.postMessage (which has a 40 KB ceiling), so the engine sees
  success and skips its duplicate-fallback emission. The partial
  streaming card stays visible above the new full-content message.
- Extract `postFresh()` helper to share the postMessage logic between
  the lazy first-post path and the new overflow path.

Tests:
- `TestStreamingCard_FinalizeFallsBackToFreshPostOnOversize`: oversized
  Finalize must NOT touch chat.update and must postMessage the full
  payload (and not mark the card failed).
- `TestStreamingCard_UpdateSkipsOversizedPayload`: oversized Update
  tick must silently no-op (no chat.update call).

Net behaviour:
- Short replies (< ~3500 B mrkdwn): unchanged — chat.update streaming
  keeps working as #1333 intended.
- Long replies: no more `msg_too_long` errors, no more duplicate
  fallback messages from the engine. UX is "partial streaming card +
  one final full-content message" instead of "broken card + duplicate
  full-content message".

Closes the third user-visible bug surfaced during v1.4.0-beta.1 QA
pairing (after #1395 image batch window default and the nav.cron i18n
miss).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 22:14:26 +08:00
dev-claudecode
d6c0053a01 fix(i18n): translate nav.cron for ko, ja, es (was leaking raw "Cron")
#1343 added Korean (ko.json) but missed the nav.cron key on the left
sidebar — surfaced during v1.4.0-beta.1 QA pairing (owner spotted the
raw "Cron" label while sanity-checking the Korean UI). Same key was
also missing in ja.json (regressed earlier) and es.json. All three
files already carried the correct translation at `settings.cron`
(line 297) so this just propagates it to the nav label:

- ko: 예약 작업
- ja: スケジュールジョブ
- es: Tareas programadas

en, zh, zh-TW already correct.

No code paths or types touched; only locale string values.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 20:49:31 +08:00
dev-claudecode
54a3195f2c fix(feishu): make image batch coalesce window configurable; bump default 150ms→500ms
The hardcoded 150ms `imageBatchWindow` introduced in #1395 turned out to be
too narrow for real-world Feishu mobile sending behaviour. In QA we
observed ~330ms between consecutive image sends from the mobile client
when a user taps "send" repeatedly, which exceeds 150ms and defeats the
coalescing logic — each image was dispatched as its own turn, and turns
after the first hit the engine's "busy" guard and returned the
"will process after the current task finishes" notice.

Changes:
- Promote `imageBatchWindow` from a package const to a per-Platform field
  populated from a new platform option `image_batch_window_ms`.
- Bump the default from 150ms to 500ms based on observed mobile send
  intervals.
- Add `batchWindow()` helper so zero-initialised Platforms (constructed
  directly in tests) still fall back to the default and never schedule a
  zero-duration timer.
- Validate the option: reject negative or non-numeric values.
- Document the new knob in `config.example.toml` and `docs/feishu.md`.
- Add `TestNewPlatform_ImageBatchWindow` covering default, custom, zero
  (fallback) and validation paths; update existing batch tests to read
  the effective window via `batchWindow()`.

Operators who need a longer or shorter window can override per-platform:
  [projects.platforms.options]
  image_batch_window_ms = 800

Closes follow-up surfaced during v1.4.0-beta.1 QA pairing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-22 19:33:37 +08:00
Bryant
cb0e2fa172 feat: add Cisco Webex platform adapter (#1402)
* docs: add Webex platform adapter design spec

* docs: add Webex platform implementation plan

* feat(webex): add API types and REST client

* feat(webex): add Platform struct, New, and allowlist parsing

* feat(webex): add message gating and mention stripping

* feat(webex): build core.Message with attachment handling

* feat(webex): add Reply, Send, chunking, and file senders

* feat(webex): add Start/Stop and reconnecting WebSocket loop

* feat(webex): wire platform into build, Makefile, and config example

* test(webex): add interface conformance checks; docs: add no_webex tag

* fix(webex): address review — selfID race, allow_from wildcard, 429/401, token redaction, rune-safe chunking, goroutine + device cleanup

* chore(webex): remove internal design/plan docs from PR branch

* fix(webex): parse Mercury conversation.activity frames and fetch decrypted messages

* fix(webex): accept 'share' verb for file/image uploads, ignore re-notifications

* fix(webex): check Close() return values to satisfy errcheck lint

---------

Co-authored-by: Bryant Barzola <bryant.barzola@gmail.com>
2026-06-22 09:24:29 +08:00
cg33
66a6353c55 fix(feishu): coalesce batch images into single multi-image dispatch (#1395) (#1408)
The Feishu mobile client sends N batch-selected images as N separate
message events with very close create_time values. Dispatching each
event immediately caused core/engine's create_time watermark (PR #1168)
to drop the oldest image, so the agent only ever saw N-1 images
(issue #1395).

A per-session image buffer with a 150ms quiet window now merges the
burst into one core.Message carrying all images in send order. The
buffer only coalesces plain image messages (no parent_id) — quoted
images and replies keep the existing single-message path so the
PR #944 quoted-image behavior is preserved.

Stop() now flushes any pending batches synchronously so buffered
images aren't lost on shutdown.

Tests:
- TestDispatchMessageCoalescesImageBatch (2/3/4 image subtests)
- TestDispatchMessageSingleImageRegression
- TestDispatchMessageQuotedImageNotBatched (PR #944 path)
- TestFlushImageBatchesStopsPendingTimers
- TestFlushImageBatchesEmptySafe

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-21 23:15:33 +08:00
Wupei
8343c310b6 feat(send): configurable attachment size limit via max_attachment_size_mb (#1392)
* feat(core): configurable attachment size limit for /send API

Introduce a max_attachment_size_mb config option (default 50 MiB, 0 keeps the
default) and make the /send API body limit track it. Attachments travel
base64-encoded inside the JSON body (~4/3 expansion), so the request body
limit is now derived from the per-attachment limit rather than the previous
hard-coded 52 MB cap — which was smaller than a single 50 MB attachment after
base64 and would silently reject valid sends.

- config: add MaxAttachmentSizeMB field
- core: APIServer.SetMaxAttachmentSize setter + DefaultMaxAttachmentSize const;
  sendBodyLimit() returns limit*4/3 + envelope, falling back to the default
  when unset. The limit field is guarded by s.mu because handleSend reads it
  concurrently with reload-time writes (CI runs `go test -race`).

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

* feat(send): honor max_attachment_size_mb from cc-connect send + daemon

Resolve the per-attachment limit as CC_MAX_ATTACHMENT_SIZE_MB env (MiB) >
config max_attachment_size_mb (MiB) > core.DefaultMaxAttachmentSize, and apply
it on both sides of the socket. The env var deliberately uses the same MiB
unit (and a mirroring name) as the config field, so the two knobs cannot
silently disagree by a factor of 1<<20. A malformed or non-positive env value
falls through to config/default with a stderr warning, mirroring resolveLogMaxSize.

- daemon: set the limit on the API server at startup and on config reload.
  The engine's reload closure cannot see the API server, so a package-global
  reference re-applies the setting (mirroring existing globals like
  config.ConfigPath).
- send: the `cc-connect send` subcommand is a separate process with no loaded
  config, so it best-effort re-reads config.toml to honour the same limit; the
  guard runs before the file is read into memory.
- tests: cover env/config/default resolution (MiB) and the readAttachment guard.

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

* docs: document configurable attachment size limit

Surface max_attachment_size_mb (default 50 MiB) and the CC_MAX_ATTACHMENT_SIZE
override in the user-facing docs and README. The /send attachment sections
previously mentioned only platform-side limits, not cc-connect's own cap.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-21 21:26:09 +08:00
shellus
2f5b111faa fix: persist workspace model selections (#1372) 2026-06-21 21:24:43 +08:00
SXongin
2d4a0bbfe3 fix(acp): add AgentSessionCanceller interface for graceful /stop (#1275)
When /stop is issued against an ACP session, the engine was killing the
entire subprocess via Process.Kill(), destroying the session. For ACP
agents like OpenCode, the subprocess is a long-lived server that should
survive /stop — only the current turn should be cancelled.

This change introduces a clean, opt-in mechanism:

1. Adds AgentSessionCanceller interface in core/interfaces.go with a
   single CancelTurn() method. Sessions that implement it signal that
   /stop should cancel only the current turn, not kill the process.

2. Adds transport.sendNotification() in agent/acp/rpc.go for sending
   JSON-RPC 2.0 notifications (no response expected).

3. Implements CancelTurn() on acpSession: sends an ACP session/cancel
   notification to abort the current LLM request while keeping the
   process alive for the next user message.

4. Modifies stopInteractiveSessionWithOptions in the engine: when the
   agent session implements AgentSessionCanceller, calls CancelTurn()
   instead of markStopped + delete + close. Sets eventsNeedResync=true
   so stale events from the cancelled turn are drained before the next
   turn starts.

The engine falls back to the existing Close() path if CancelTurn()
returns an error, ensuring backward compatibility.

Co-authored-by: SXongin <sxongin@users.noreply.github.com>
2026-06-21 08:32:10 +08:00
gotang
32b100920b fix(feishu): remove text_color from markdown element, keep on plain_text (#1278)
text_color is valid per Feishu docs only for plain_text elements.
The upstream PR #1204 incorrectly added it to a markdown (lark_md)
element, causing Feishu to reject the card JSON with error:
  unknown property text_color, path: ...(tag: markdown)

Keep text_color on all plain_text elements where it is correct per
Feishu card API documentation.

Co-authored-by: tanghongliang <tanghongliang@citos.cn>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-21 08:12:12 +08:00
Haiyi
c1466312e5 feat(codex): support custom system_prompt / append_system_prompt config (#1345)
* feat(codex): support custom system_prompt / append_system_prompt config

Codex has no native system-prompt CLI flag, so these project options are
synthesized into a preamble and prepended to the first message of each new
session. Resumed sessions are not re-injected (preambleSent is preset for
resume IDs). Covers both the exec and app_server backends.

Adds config.example.toml documentation for the two new options.

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

* test(codex): check cs.Close return value to satisfy errcheck

CI lints new lines with golangci-lint --new-from-rev; the freshly added
TestSend_PrependsProjectPromptOnFreshSession used an unchecked defer
cs.Close(). Wrap it in a deferred closure that explicitly ignores the
error, matching the errcheck-clean pattern.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 12:15:19 +08:00
Yu Zhang
5fb98a46c9 fix(core): drain queued messages in FIFO order (#1286)
* fix(core): drain queued messages in FIFO order

* test(core): cover queued drain stale watermark

* fix(core): preserve queued messages during session startup
2026-06-20 12:13:02 +08:00
moduvoice
e1884d7917 feat(i18n): add Korean (ko) translation (#1343)
Add a full Korean locale (web/src/i18n/locales/ko.json, all 360 keys from en.json) and register it in i18n/index.ts, the Header/Login language switchers, and GlobalSettings. i18next {{interpolation}} preserved.
2026-06-20 12:09:16 +08:00
cg33
e8f0ba5de6 fix(skill): scan only depth-1 SKILL.md, ignore nested files (#1304) (#1317)
Previously `discoverSkillsInDir` recursed into every subdirectory looking
for SKILL.md. Any skill that shipped templates or examples under its own
subtree (e.g. `frontend-design/references/finance-report/SKILL.md`) would
leak those nested SKILL.md paths as phantom slash commands into platform
command menus — issue #1304 reports 101 leaked commands from one skill.

Switch the scan to depth-1 only: each immediate subdirectory of a skill
root is either a skill (has its own SKILL.md) or ignored. This matches
the Claude Code CLI convention and means the file under
`<root>/<skill>/references/...` is treated as a skill asset rather than
a sibling skill.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-20 07:58:12 +08:00
cg33
1d84c1b0b1 docs(sponsors): remove four lapsed sponsor entries (#1404)
Drop sponsor blocks whose relationships have lapsed:
- AIHubMix
- NekoCode
- AICodeMirror
- DDS Hub

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:08:30 +08:00
cg33
cf2d4f1b81 fix(claudecode): use --append-system-prompt-file to fix Windows cmdline limit (#1376) (#1378)
cc-connect's built-in AgentSystemPrompt is ~9KB, which on its own
exceeds Windows cmd.exe's 8192-byte command-line limit when claude
is spawned with --append-system-prompt <inline content>. The
claude.exe child immediately fails with GBK stderr "命令行太长。"
(command line too long) before any user message reaches the agent.
On v1.3.3 this breaks every Feishu / IM message on Windows hosts.

Pass the merged prompt through Claude Code CLI's
--append-system-prompt-file <path> flag instead of the inline form.
Only the short file path travels on the command line, so the cap is
no longer a function of prompt size.

Common case (99% of users — no platform formatting hints, no
user-configured append_system_prompt):

  * On agent construction, write the full AgentSystemPrompt() once to
    <data_dir>/agent-prompts/cc-connect-system.md (default
    ~/.cc-connect/agent-prompts/cc-connect-system.md). claude reads
    this file at every spawn; cc-connect never rewrites it unless the
    cc-connect version actually changes the prompt content.
  * Every spawn reuses the same file, so there is no per-spawn write
    and no concurrency race (claude is a strict reader).

Edge case (Slack / Weixin / MAX with FormattingInstructions, or
project-level append_system_prompt):

  * Write a per-spawn temp file under <data_dir>/agent-prompts/
    holding the merged content, passed via the same flag.
  * Cleaned up on session Close to avoid leaking files.

Notes for reviewers:

  * claude CLI refuses --append-system-prompt and
    --append-system-prompt-file together
    ("Cannot use both ... Please use only one"), so the merged content
    must travel as a single file.
  * Only the claudecode agent runtime is affected. Other agents
    (codex/opencode/qoder/cursor/gemini/kimi) inject AgentSystemPrompt
    via memory files (CLAUDE.md/AGENTS.md/...), which already have no
    cmdline cap.
  * core/interfaces.go (AgentSystemPrompt content) is unchanged from
    v1.3.3; the full prompt is preserved so agent behaviour does not
    regress.

Tests:

  * TestEnsureSharedSystemPromptFile_WritesOnceAndReuses — verifies the
    shared file is written once and skipped on identical content.
  * TestEnsureSharedSystemPromptFile_RewritesOnContentChange — verifies
    cc-connect upgrades refresh the shared file.
  * TestEnsureSharedSystemPromptFile_EmptyDirUsesTempDir — verifies the
    fallback when ccDataDir is unset.
  * TestWriteTempAppendPromptFile_UniquePerCall — verifies concurrent
    edge-case spawns get unique paths.
  * Existing claudecode + core suites all pass (one pre-existing flake
    in TestCUJ_H2_TwoPlatformsConcurrentNoBleed is unrelated to this
    change and still passes when run in isolation).

E2E (Windows, MiniMax-M3 backend, Feishu platform):

  * Before fix: every Feishu message produces no reply; claude.exe
    fails with "命令行太长。" on stderr.
  * After fix: "你好" → MiniMax intro reply; "3 分钟后帮我看看
    ipconfig" → timer correctly created in data/timers/jobs.json;
    "每天早上 6 点提醒我吃药" → cron correctly created in
    data/crons/jobs.json. Agent uses /timer and /cron commands as
    instructed by the prompt.

Fixes #1376.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 16:21:26 +08:00
cg33
ac037338cb test(core): add regression test for issue #814 (queued message uses own replyCtx via outer drain) (#1261)
Adds a deterministic test that exercises the full ReceiveMessage →
handleMessage → processInteractiveMessageWith → processInteractiveEvents
→ drainPendingMessages pipeline for two back-to-back messages A and B,
where B arrives while A is mid-flight. The test asserts that the reply
to A carries ctx-A and the reply to B carries ctx-B.

This complements the existing
TestProcessInteractiveEvents_QueuedMessageUsesItsOwnReplyCtx, which
covers the queue drain inside processInteractiveEvents. The new test
covers the outer drain driven from handleMessage, which is what real
users hit when sending a follow-up message while the agent is still
answering.

The test passes 100/100 times on current main, so the bug reported in
#814 is not reproduced by this scenario. The test is still useful as a
regression guard if any of the replyCtx bindings in the queue-drain
path change.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-16 10:10:57 +08:00
Haiyi
9b675c6cd3 feat(feishu): support after_click card replacement for cmd: actions (#1299)
When a cmd: button value includes an after_click object with a non-empty
title, onCardAction now returns a CardActionTriggerResponse that replaces
the original card in-place, while still dispatching the command to the
agent as before.

after_click fields:
- title (required): card header text
- color (optional, default "green"): header template color
- markdown (optional): body markdown content
- link_text + link_url (optional): appended as a markdown link after a divider

If after_click is absent or has no title, behavior is unchanged (returns nil).

Related: https://github.com/chenhg5/cc-connect/issues/871
Related: https://github.com/chenhg5/cc-connect/issues/936

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:05:33 +08:00
Spinsirr
679fbbc7a8 feat(slack): streaming preview + aggregated turn card (#1333)
* feat(slack): real-time streaming preview via chat.update

Slack was the only major platform without cc-connect's streaming-preview
interfaces (discord/feishu/telegram/max all have them), so Slack replies
arrived only as one message at turn end. This adds the minimal pair the
engine needs (same as telegram):

- SendPreviewStart (core.PreviewStarter): posts the initial preview message
  (threaded like a normal reply) and returns a {channel, ts} handle.
- UpdateMessage (core.MessageUpdater): edits it in place via chat.update as
  the engine streams.

No config needed (stream preview is on by default); the engine throttles
edits (~1500ms/30 chars) so this stays within chat.update rate limits, the
per-turn footer is preserved (appended inline at finish), and there's no
double-post (engine finalizes via the preview, only falling back to Send when
the preview is inactive).

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

* feat(slack): aggregated streaming turn card (StreamingCardPlatform)

Implements core.StreamingCardPlatform/StreamingCard for Slack: the whole agent
turn (thinking + tool steps + answer, assembled by the engine's buildCardContent)
renders as ONE Slack message that updates in place — like DingTalk's AI Card.
chat.update is throttled internally (~1.2s). When active it takes precedence over
the plain streaming preview (engine routes via handledByStreamCard, mutually
exclusive — no double-post); the preview remains the fallback.

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

* fix(slack): lazy-post the streaming card + throttle to 3s

Post the card on first content (not at turn start) so a native "is thinking…"
status can show until the bot has something to say, and bump the chat.update
throttle 1.2s → 3s to respect Slack's streaming rate-limit guidance.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 10:03:59 +08:00
Byctor
2548b66c46 feat: add plugin_dir option to load Claude Code plugins via --plugin-dir (#1325)
Adds `plugin_dir` support to `agent.options` in config.toml so that
cc-connect can pass `--plugin-dir` flags to Claude Code when spawning
sessions, enabling installed plugins (like superpowers, last30days) to
be loaded in IM-bridged sessions.

Supports both single string and array:
  plugin_dir = "/path/to/plugins/cache/xyz"       # single
  plugin_dir = ["/path/a", "/path/b"]             # multiple

The option propagates to multi-workspace agents via
WorkspaceAgentOptions().

Closes #1324
2026-06-16 10:02:37 +08:00
Baocang Nie
a1a9f706cf fix(engine): keep workspace binding when supervisor cannot stat it under run_as_user (#1316)
Under run_as_user the workspace may live in the target user's private space, so the
supervisor's os.Stat hits EACCES. The old code treated any stat error as 'directory
missing' and Unbind()'d, permanently dropping a valid binding. Skip the supervisor-side
existence check under isolation, and for the non-isolation path only treat
os.IsNotExist as missing.

Fixes #1313
2026-06-16 10:01:44 +08:00
Baocang Nie
58c7e27cc3 fix(runas): run agent in its workspace under run_as_user (#1315)
sudo -i simulates a login and runs the command from the target user's HOME,
silently overriding cmd.Dir. Under run_as_user the agent therefore ignored its
(multi-workspace) working directory and started in HOME, so it couldn't see the
bound workspace or its CLAUDE.md.

Re-establish the intended cwd inside the spawn: when SpawnOptions.WorkDir is set,
BuildSpawnCommand wraps the command to 'cd "$CC_RUNAS_CHDIR" && exec "$@"'.
The path travels via the preserved env var (not argv) so non-ASCII/space paths
survive sudo's command re-quoting.

Fixes #1312
2026-06-16 10:01:32 +08:00
cg33
1c92a03fdc feat(daemon): add CC_LOG_MAX_BACKUPS env var support (#1260)
* feat(daemon): add CC_LOG_MAX_BACKUPS env var support (#1222)

PR #1243 only addressed CC_LOG_MAX_SIZE while leaving the backup
count hard-wired to one (.log.1). That still loses any post-mortem
context older than one rotation, which is the same class of failure
users reported on #1222. This change adds the matching knob so the
post-mortem trail is configurable, with the same flag > env >
default priority used for size.

- daemon: add ParseLogBackups(s) (>=1, no unit suffix, error echoes
  input) and DefaultLogMaxBackups = 3.
- daemon: extend RotatingWriter with maxBackups; rotateLocked walks
  the chain (delete .N, shift .(N-1) -> .N .. .1 -> .2, rename
  active -> .1, reopen) and a public Rotate() hook for tests/SIGHUP.
- daemon: Config/Meta gain LogMaxBackups; Resolve() defaults to 3.
- cmd/cc-connect: resolveLogMaxBackups + preScanLogMaxBackupsFlag +
  --log-max-backups flag; startup log now reports max_backups and
  its source. The rotating-writer setup happens before flag.Parse so
  the pre-scan keeps the flag effective there too.
- daemon/launchd.go, daemon/systemd.go, daemon/windows.go: thread
  CC_LOG_MAX_BACKUPS through the service templates so a fresh
  install picks it up.
- tests: TestParseLogBackups (19 subtests + error-echo), three new
  RotatingWriter tests (chain, disabled, fallback), four resolver
  tests + pre-scan tests in cmd/cc-connect. TestIssue1222_BackupRetention
  pins the new env-var behaviour as the regression test for the
  follow-up to #1222.

* fix(daemon): silence errcheck on logrotate_test.go defer Close

QA review (run 27109765660) flagged defer w.Close() in the 4
backup-related tests added by #1260. Wrap each in
defer func() { _ = w.Close() }() so errcheck is satisfied without
changing test semantics (temp-dir cleanup is best-effort).

Verified locally:
- golangci-lint --new-from-rev origin/main ./daemon/... -> 0 issues
- go test -count=1 -tags no_web ./daemon/ ./cmd/cc-connect/ -> ok

---------

Co-authored-by: cc-connect dev-claudecode <dev-claudecode@cc-connect.local>
Co-authored-by: Claude <noreply@anthropic.com>
2026-06-16 09:59:09 +08:00
cg33
23ebad3b02 fix(claudecode): keep turn running on mid-turn compaction event (fixes #481) (#1272)
* fix(claudecode): keep turn running on mid-turn compaction event (fixes #481)

Claude Code's stream-json protocol emits a `type:"result"` event with
`subtype:"compact"` (newer CLI) or `subtype:"compaction"` (older
CLI) when it performs automatic context compaction mid-turn. The
existing handleResult treated every result event as turn completion
(Done=true), so the engine's processInteractiveEvents loop would
return early and drop any subsequent tool calls and assistant
messages for the same turn.

Recognize the compaction subtypes and emit EventResult with
Done=false so the event loop keeps reading from the CLI process. Add
a default case to the readLoop event switch that logs unrecognized
event types at debug level (with the full payload) so future new
event shapes are diagnosable from the log stream.

Rebuild of #483. The original PR also referenced adding diagnostic
logging for unknown event types; that change is included as the new
default case.

- Add isCompactionResult / resultSubtype helpers in session.go.
- Set Done=!isCompaction in handleResult's emitted EventResult.
- Add default case to readLoop's event switch with slog.Debug.
- Add TestHandleResultCompactionSubtypeIsNotTerminal and
  TestIsCompactionResult regression tests; tighten existing
  TestHandleResultParsesUsage with a Done=true assertion.

* fix(core): gate EventResult case body on event.Done (fixes #481)

The previous PR #1272 fix changed the agent-side handleResult to set
Done=!isCompaction, but processInteractiveEvents in core/engine.go never
read event.Done — every EventResult unconditionally ran
cp.Finalize(Completed), AddHistory, noteUserTurnCompleted, and a final
return. So a mid-turn compaction result still caused the engine loop to
exit early, dropping subsequent tool calls and assistant messages.

Gate the entire EventResult case body on event.Done: when an agent
emits a non-terminal result (Done=false) — e.g. mid-turn auto-compact —
slog.Debug it and continue the outer loop to read the next event. All
existing turn-completion side effects and the trailing return only run
when Done=true. The agent-side Done=!isCompaction logic from #1272 is
now effective end-to-end.

Test:
- New TestProcessInteractiveEvents_NonTerminalResultContinuesTurn
  pins issue #481: emits EventResult{Done:false} → EventText →
  EventResult{Done:true} and asserts noteUserTurnCompleted runs exactly
  once (on the terminal result) and the final reply reaches the
  platform. Verified manually that without the engine fix the test
  fails because the compaction event produces an empty '(empty
  response)' message and the loop returns before the final result.
- Existing tests in core/engine_test.go (line 12741, 13026) and
  core/relay_test.go (line 102, 184, 223, 284) that previously sent
  EventResult without setting Done now set Done:true — they were
  relying on the implicit-on EventResult semantics that this change
  inverts.

Verified:
- go test -count=1 -tags no_web -short ./core/...    ok 42s
- go test -count=1 -tags no_web ./agent/claudecode/...  ok
- golangci-lint run --new-from-rev origin/main ./core/...   0 issues

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-16 09:57:21 +08:00
Wupei
6f8d3a2bd8 feat(matrix): add Matrix platform adapter with E2EE support (#834)
* feat(matrix): add Matrix platform adapter using mautrix-go

Implements a full Matrix protocol adapter so users can interact with
their coding agent through any Matrix homeserver (matrix.org, self-hosted
Synapse/Dendrite, etc.). Uses mautrix-go SDK for Client-Server API.

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

* docs(matrix): add Matrix platform documentation

Update README.md, README.zh-CN.md, INSTALL.md, CLAUDE.md and add
docs/matrix.md with setup guide, config reference and FAQ.

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

* test(matrix): add unit tests and fix session key parsing

35 tests covering config validation, message handling, lifecycle,
concurrency, and error paths. Race detector clean.

Fixes two bugs found by tests:
- stripBotMention: strip matrix.to URL before plain user ID to avoid
  partial replacement inside the URL path
- ReconstructReplyCtx: room IDs contain colons, so colon-based SplitN
  corrupted them; use ":@" boundary detection instead

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

* docs(matrix): add Chinese setup guide

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

* feat(matrix): add E2EE support via mautrix cryptohelper

Add Olm/Megolm encryption support using mautrix-go's built-in
cryptohelper. Encrypted rooms are now fully supported for both
sending and receiving messages.

Key implementation details:
- Crypto DB stored per device ID (~/.cc-connect/matrix-crypto-<device>.db)
- Auto-recovers from stale device keys by force-uploading new keys
- All outbound messages encrypted via sendRoomEvent when room has E2EE
- DecryptErrorCallback logs decryption failures through slog
- Cleanup client stores on failed crypto init to prevent DB panics

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

* docs(matrix): update setup guide with E2EE instructions

- Recommend creating dedicated device via curl login (avoids E2EE issues)
- Add E2EE section with startup log example showing device_id
- Add FAQ entries for common E2EE problems and troubleshooting
- Document crypto DB location and cleanup steps

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

* feat(matrix): add SAS verification and cross-signing support

Enable auto-accept SAS key verification so users can verify the bot's
device from Element. Bootstrap cross-signing to eliminate "encrypted by
a device not verified by its owner" warning on bot messages.

- Add verification helper with auto-accept/auto-confirm SAS flow
- Workaround mautrix MAC verification bug for cross-user verification
- Fix in-room verification transaction ID lookup from m.relates_to
- Persist cross-signing seeds to disk for stable keys across restarts
- Support m.login.password UIA fallback for publishing cross-signing keys
- Add auto_verify and cross_signing_password config options

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

* docs(matrix): add verification and cross-signing documentation

Add SAS verification and cross-signing setup instructions to both
English and Chinese Matrix guides. Document new config options
(auto_verify, cross_signing_password) and FAQ entries for device
verification and the red exclamation mark warning.

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

* docs(matrix): fix inaccurate documentation and capability table

- Fix Matrix "Markdown / cards" from  to ⚠️ (no CardSender)
- Change "red exclamation mark" to "red question mark" per actual UI
- Recommend curl with dedicated device_id for token (not Element)
- Add cross-signing seeds file to E2EE reset instructions
- Clarify that cross-signing may need cross_signing_password

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

* fix(matrix): reduce verbose logging in production

Downgrade per-message and internal workaround logs from Info to Debug,
and use structured fields for SAS verification log.

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

* fix(matrix): fix formatting, rename field, and add token redaction

- Run gofmt to fix struct definition and literal alignment
- Rename shareSessionInChan to shareSessionInChannel for consistency
  with other platforms (Telegram, Discord, Slack)
- Add core.RedactToken() in connectLoop error logging to prevent
  access token leakage in logs

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

* ci: install libolm-dev for Matrix E2EE crypto

The mautrix-go crypto package depends on libolm (C library) for
end-to-end encryption. The CI runners don't have it pre-installed,
causing golangci-lint to fail when loading the CGO package.

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

* fix(matrix): use build-tagged E2EE to avoid CGO/libolm dependency

Split E2EE (end-to-end encryption) code behind a `goolm` build tag so
the default build compiles without CGO or libolm-dev. This fixes CI
lint failures caused by the mautrix-go crypto package pulling in the
CGO-based libolm backend by default.

- Default build: no E2EE, no CGO, CI lint passes
- Build with `-tags goolm`: full E2EE using pure-Go olm backend

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

* build: enable goolm tag by default in Makefile

Matrix E2EE requires the goolm build tag. Add it as a default
tag in the Makefile so `make build` includes E2EE automatically.

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

* fix(matrix): suppress unused field lint for cryptoHelper

The cryptoHelper field is only accessed in e2ee.go (goolm build tag),
so the default build's linter flags it as unused. Add //nolint:unused
since this is intentional.

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

* feat(matrix): add Matrix platform adapter using mautrix-go

Implements a full Matrix protocol adapter so users can interact with
their coding agent through any Matrix homeserver (matrix.org, self-hosted
Synapse/Dendrite, etc.). Uses mautrix-go SDK for Client-Server API.

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

* docs(matrix): add Matrix platform documentation

Update README.md, README.zh-CN.md, INSTALL.md, CLAUDE.md and add
docs/matrix.md with setup guide, config reference and FAQ.

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

* test(matrix): add unit tests and fix session key parsing

35 tests covering config validation, message handling, lifecycle,
concurrency, and error paths. Race detector clean.

Fixes two bugs found by tests:
- stripBotMention: strip matrix.to URL before plain user ID to avoid
  partial replacement inside the URL path
- ReconstructReplyCtx: room IDs contain colons, so colon-based SplitN
  corrupted them; use ":@" boundary detection instead

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

* docs(matrix): add Chinese setup guide

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

* feat(matrix): add E2EE support via mautrix cryptohelper

Add Olm/Megolm encryption support using mautrix-go's built-in
cryptohelper. Encrypted rooms are now fully supported for both
sending and receiving messages.

Key implementation details:
- Crypto DB stored per device ID (~/.cc-connect/matrix-crypto-<device>.db)
- Auto-recovers from stale device keys by force-uploading new keys
- All outbound messages encrypted via sendRoomEvent when room has E2EE
- DecryptErrorCallback logs decryption failures through slog
- Cleanup client stores on failed crypto init to prevent DB panics

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

* docs(matrix): update setup guide with E2EE instructions

- Recommend creating dedicated device via curl login (avoids E2EE issues)
- Add E2EE section with startup log example showing device_id
- Add FAQ entries for common E2EE problems and troubleshooting
- Document crypto DB location and cleanup steps

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

* feat(matrix): add SAS verification and cross-signing support

Enable auto-accept SAS key verification so users can verify the bot's
device from Element. Bootstrap cross-signing to eliminate "encrypted by
a device not verified by its owner" warning on bot messages.

- Add verification helper with auto-accept/auto-confirm SAS flow
- Workaround mautrix MAC verification bug for cross-user verification
- Fix in-room verification transaction ID lookup from m.relates_to
- Persist cross-signing seeds to disk for stable keys across restarts
- Support m.login.password UIA fallback for publishing cross-signing keys
- Add auto_verify and cross_signing_password config options

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

* docs(matrix): add verification and cross-signing documentation

Add SAS verification and cross-signing setup instructions to both
English and Chinese Matrix guides. Document new config options
(auto_verify, cross_signing_password) and FAQ entries for device
verification and the red exclamation mark warning.

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

* docs(matrix): fix inaccurate documentation and capability table

- Fix Matrix "Markdown / cards" from  to ⚠️ (no CardSender)
- Change "red exclamation mark" to "red question mark" per actual UI
- Recommend curl with dedicated device_id for token (not Element)
- Add cross-signing seeds file to E2EE reset instructions
- Clarify that cross-signing may need cross_signing_password

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

* fix(matrix): reduce verbose logging in production

Downgrade per-message and internal workaround logs from Info to Debug,
and use structured fields for SAS verification log.

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

* fix(matrix): fix formatting, rename field, and add token redaction

- Run gofmt to fix struct definition and literal alignment
- Rename shareSessionInChan to shareSessionInChannel for consistency
  with other platforms (Telegram, Discord, Slack)
- Add core.RedactToken() in connectLoop error logging to prevent
  access token leakage in logs

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

* ci: install libolm-dev for Matrix E2EE crypto

The mautrix-go crypto package depends on libolm (C library) for
end-to-end encryption. The CI runners don't have it pre-installed,
causing golangci-lint to fail when loading the CGO package.

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

* fix(matrix): use build-tagged E2EE to avoid CGO/libolm dependency

Split E2EE (end-to-end encryption) code behind a `goolm` build tag so
the default build compiles without CGO or libolm-dev. This fixes CI
lint failures caused by the mautrix-go crypto package pulling in the
CGO-based libolm backend by default.

- Default build: no E2EE, no CGO, CI lint passes
- Build with `-tags goolm`: full E2EE using pure-Go olm backend

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

* build: enable goolm tag by default in Makefile

Matrix E2EE requires the goolm build tag. Add it as a default
tag in the Makefile so `make build` includes E2EE automatically.

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

* fix(matrix): suppress unused field lint for cryptoHelper

The cryptoHelper field is only accessed in e2ee.go (goolm build tag),
so the default build's linter flags it as unused. Add //nolint:unused
since this is intentional.

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

* fix(matrix): resolve lint errors for CI (errcheck + staticcheck)

* feat(agent): add GitHub Copilot as a first-class agent (#865)

* chore: ignore web/tsconfig.tsbuildinfo (TS incremental build cache)

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(qqbot): add inline keyboard buttons and INTERACTION_CREATE event handling (#1131)

* feat(qqbot): add support for inline keyboard buttons and interaction events

- Update default intents to include INTERACTION_CREATE
- Implement SendWithButtons to send messages with inline keyboard buttons
- Encode permission decisions and session key in button_data for routing callbacks
- Handle INTERACTION_CREATE events to process button clicks as permission responses
- Create synthetic message for permission decisions and forward to engine
- Add ackInteraction to acknowledge INTERACTION_CREATE events via API call
- Add extensive tests for sending buttons, handling interactions, and edge cases

* fix(qqbot): ignore ackInteraction error and correct test URL check

- Suppress error from ackInteraction to avoid unused error variable warning
- Update test to expect corrected interaction URL path "/interactions/interact-1" instead of "/v2/interactions/interact-1"

* - Add sessionKey field to replyContext to embed in button_data and session routing
- Refactor SendWithButtons to use sessionKey from replyContext and validate emptiness
- Enhance replyContext construction with sessionKey in various message scenarios
- Implement new tests for sharing session keys in channel, empty session key errors,
  and interaction_create event permission routing
- Update config.example.toml to document new required intents for interaction support
- Modify changelog with instructions on enabling INTERACTION_CREATE intent (bit 26) for QQ Bot
- Permission requests now use clickable inline buttons instead of text replies, requiring
  enabling the INTERACTION capability in QQ Open Platform settings

* test(qqbot): ignore errors and returned values in test HTTP handlers

* feat: integrate Google Antigravity CLI (agy) agent

* Harden antigravity session discovery and attachment handling to preserve resume correctness under contention

Constraint: Keep external behavior and interfaces unchanged while improving runtime robustness
Rejected: Full process-level session correlation | Requires upstream CLI contract not available here
Confidence: medium
Scope-risk: narrow
Directive: Keep session-id detection conservative unless agy exposes explicit correlation metadata
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/...
Not-tested: Real concurrent agy process race on multi-session host

* Fix antigravity prompt execution and shutdown safety without changing user-facing flow

Constraint: Preserve existing antigravity integration surface and core event handling contracts
Rejected: Introduce a custom permission event parser for agy stdout | No stable upstream protocol contract yet
Confidence: medium
Scope-risk: narrow
Directive: If agy publishes structured permission I/O, replace y/n terminal fallback with typed request/response framing
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/...
Not-tested: End-to-end interactive agy permission prompt against a live CLI binary

* Stabilize antigravity permission flow so Discord approvals consistently drive CLI execution

Constraint: Preserve current antigravity stream/event contracts while improving permission reliability
Rejected: Full structured agy protocol parser | Upstream schema is not yet stable/public
Confidence: medium
Scope-risk: narrow
Directive: Replace regex prompt detection with typed protocol once agy exposes machine-readable permission events
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/... && go build ./cmd/cc-connect
Not-tested: Live Discord permission prompts across all locales/terminal themes

* Prevent agy hangs and unsupported flag crashes in cross-platform antigravity runs

Constraint: Keep antigravity adapter behavior compatible across Telegram/Discord while avoiding CLI-specific crashes
Rejected: Keep passing -m and rely on user wrapper scripts | Breaks agy v1.0.2 directly for normal users
Confidence: high
Scope-risk: narrow
Directive: Re-enable explicit model flag only after agy documents stable model flag support
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/... && go build ./cmd/cc-connect
Not-tested: Live agy v1.0.2 interactive permission prompts on Telegram with real long-running tool tasks

* Fix lint-blocking errcheck findings in antigravity adapter without behavior changes

Constraint: Keep runtime behavior unchanged while satisfying CI lint gates
Rejected: Broader refactor around file/stream lifecycle | Unnecessary for this blocking lint failure
Confidence: high
Scope-risk: narrow
Directive: Keep close/remove calls explicitly checked or intentionally ignored with clear intent in this package
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/...
Not-tested: Full GitHub Actions rerun output

* Fix remaining errcheck close-handling findings from CI lint

Constraint: Resolve lint blockers without behavioral changes
Rejected: Broad lifecycle refactor | unnecessary for this CI-only failure class
Confidence: high
Scope-risk: narrow
Directive: Keep file/stream close sites explicitly checked or intentionally ignored for errcheck
Tested: go test ./agent/antigravity/... && go test ./core/... && go test ./cmd/cc-connect/...
Not-tested: GitHub Actions rerun not yet executed

* docs: update MiniMax banner to M3 release

- Replace minimax-en.jpeg and minimax-zh.jpeg with new M3 PNG banners
- Update MiniMax description in both READMEs to reflect M3 benchmarks
  (SWE-Bench Pro 59.0, Terminal Bench 2.1 66.0, VIBE V2 60.1, etc.)
- Update tagline: "Build, Learn & Ship" / "Mini 价格 Max 性能"

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

* chore: update MiniMax model to M3 in provider presets

- Switch primary model from MiniMax-M2.7 to MiniMax-M3
- Add MiniMax-M3-highspeed variant
- Keep M2.7 as fallback for compatibility
- Update descriptions to reflect M3 features (Sparse Attention, Multimodal)
- Bump updated_at to 2026-06-03

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

* fix(core): /stop preserves AgentSessionID so next message can resume (#1196)

`cmdStop` no longer clears `AgentSessionID`; the next message can `--resume` the
conversation, matching the card-button Stop path and eliminating the
inconsistency described in #1189.

- Session-ID write-back now always follows the live forked ID Claude reports on
  every `--resume` (was: skip if already set); name binding still fires only on
  first assignment to avoid polluting `sessionNames`
- Removed `clearStaleSessionID` helper and `CompareAndSetAgentSessionID` guard;
  replaced with unconditional `SetAgentSessionID` + `wasEmpty` name-binding gate
- Updated / renamed affected tests:
  - TestSessionIDWriteback_TracksLiveForkedID
  - TestInteractiveWriteBack_TracksForkedSessionID
  - TestInteractiveWriteBack_NamingBindsOnlyOnFirstAssignment
  - TestCmdStop_PreservesAgentSessionID

Closes #1189

* fix: three bug fixes — WPS ChannelKey, Telegram text_link, workspace bind path

fix(wps-xiezuo): set ChannelKey on inbound messages for per-group session
isolation (#1217). Without ChannelKey, messages from different WPS groups
were routed to the same session.

fix(telegram): forward text_link entity URLs to agent as [label](url) (#1207).
Telegram passes inline hyperlinks as entities (not in the plain text), so the
agent never saw the URL. enrichTextLinks() rewrites text_link spans in-place
using UTF-16 offsets (Telegram's coordinate system).

fix(core): splitCommandArgs() respects quoted paths in /workspace bind (#1211).
strings.Fields splits on every space, truncating paths like
'/workspace bind "/my project/foo"'. The new parser honours single and
double quotes so space-containing paths work correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(pi): correct agent directory paths (SkillDirs + GlobalMemoryFile) (#1206)

* fix(pi): correct SkillDirs paths for pi agent skill discovery

cc-connect's pi agent reported wrong skill directories (~/.pi/skills/
instead of ~/.pi/agent/skills/), causing SkillRegistry to never find
pi skills. This meant slash commands like /caveman were not intercepted
by cc-connect's command routing — they fell through to pi's print mode
as raw text, and the LLM never received the skill instructions.

Fix:
- ~/.pi/skills/ -> ~/.pi/agent/skills/ (pi's default agent dir)
- Add ~/.agents/skills/ (common shared skill dir)
- Add /skills/ if env var is set

* fix(pi): correct GlobalMemoryFile path

pi loads global AGENTS.md from getAgentDir() which defaults to
~/.pi/agent/, not ~/.pi/. Respect  if set.

* chore(pi): consistent homeDir variable naming in SkillDirs

* fix(pi): add SetWorkDir to enable /dir command (#1177)

pi agent implemented GetWorkDir() but not SetWorkDir(), so the
WorkDirSwitcher interface was incomplete and /dir always returned
'current agent does not support dynamic work directory switching'.

* fix(pi): pipe prompt via stdin to avoid CLI option parsing (#1185)

Reply chain headers start with "---", which pi's CLI parser
interprets as an option flag, producing "Unknown option" errors.
Pass the prompt via stdin instead of as a positional argument,
consistent with how gemini and codex agents handle it.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* fix(pi): read enabledModels from settings.json for /model (#1178)

* fix(pi): read enabledModels from settings.json for /model

AvailableModels() now reads ~/.pi/agent/settings.json enabledModels
instead of returning nil. New() also falls back to defaultModel
from settings when opts don't specify model.

Add helpers: piSettingsDir, settingsPath, readSettings,
readSettingsModels, readDefaultModel. Respects PI_CODING_AGENT_DIR.

* fix(pi): check err return values in tests to pass errcheck linter

* fix(pi): silence errcheck for os.Setenv/Unsetenv in defer cleanup blocks

* feat(dingtalk): add reaction emoji support (#1213)

* feat(dingtalk): add reaction emoji support

* fix(dingtalk): satisfy emotion lint

* feat(slack,tmux): per-thread session scope and per-session tmux windows (#1179)

Add `session_scope` to the Slack platform ("user" | "channel" | "thread").
"thread" keys each Slack thread to its own cc-connect session, so a new
top-level message starts a fresh conversation while replies in the same
thread continue it. Backward compatible: when unset, behaviour is
unchanged (share_session_in_channel maps to "channel").

Add `window_per_session` to the tmux agent: each cc-connect session gets
its own tmux window (and its own init_command/agent instance) instead of
sharing one pane. Required for real isolation when the platform splits
sessions per thread; otherwise concurrent threads interleave into a single
shared agent. The allocated window name doubles as the agent session ID so
resumes reuse the same window.

Both options default off. Includes unit tests and config.example.toml docs.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Reply to unauthorized IM senders (#1190)

* feat(relay): configure group visibility (#1209)

* feat(send): 支持 --at-users / --at-all 命令行参数,DingTalk @mention 通知 (#1188)

* feat(send): add --at-users and --at-all support for DingTalk @mention

- Add --at-users and --at-all flags to cc-connect send command
- Add AtMentionSender optional interface for platforms that support @mention
- Implement ReplyWithAt in DingTalk platform using text msgtype
- Add CheckLinger stub for macOS compatibility

* fix: update test callers for SendToSessionWithAttachments new signature

* feat(send): add --at-users and --at-all support for DingTalk @mention

- Add --at-users and --at-all flags to cc-connect send command
- Add AtMentionSender optional interface for platforms that support @mention
- Implement ReplyWithAt in DingTalk platform using text msgtype
- Add CheckLinger stub for macOS compatibility
- Update all test callers for new signature

* fix(dingtalk): check resp.Body.Close return value for errcheck

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

---------

Co-authored-by: wen_guoxing <wen_guoxing@itrus.com.cn>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>

* feat(cron): align manual trigger command with exec (#1201)

* feat: add manual cron run support

* feat(cron): align manual trigger command with exec

* fix(cron): guard shell manual triggers

* test(cron): accept manual run output before trigger ack

* test(release): align footer expectations without markdown italics

* fix(core): keep full reply footer paths

* test(core): normalize local dir path expectation

* fix(cron): check exec response body close

---------

Co-authored-by: aoko <aokodesuka@gmail.com>
Co-authored-by: 张彧 <aaron@mac.tail449498.ts.net>
Co-authored-by: 张彧 <aaron@Aaron-MacBook-Pro-14.local>

* feat(codex): support request_user_input app-server events (#1200)

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(feishu): refresh rich card rendering and panel handling (#1204)

Squash merge of PR #1204 (rebased onto main, minor conflicts resolved).
Adds structured per-turn reply footer, cardkit-v1 streaming, status footer
interface, claude context usage tracking, and rich card body improvements.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(feishu): send audio and video attachments as media (#1202)

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(opencode): pass --agent flag when agent config option is set (#1210)

Adds optional 'agent' config option under [projects.agent.options] for
opencode. When set, the value is passed as --agent to every 'opencode run'
invocation, enabling plugin-defined agents (e.g. oh-my-openagent's
'Sisyphus - Ultraworker') to work correctly without falling back to the
default agent.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(core): remove leftover conflict markers in engine_test.go from #1202 merge

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: three bug fixes for #1184, #1139, and #1176

fix(core): multiSelect AskUserQuestion on card platforms (closes #1184)
- For multiSelect questions, render options as a numbered text list
  instead of instant-resolve buttons (buttons resolved on first click,
  preventing multi-selection)
- Add new i18n key MsgAskQuestionNoteMulti with per-language hint to
  reply with comma-separated numbers (e.g. 1,3)
- Single-select questions keep the existing button UX unchanged

fix(config): allow cc-connect web with agent-only config (closes #1139)
- Add LoadPermissive() / validatePermissive() that skip the
  "at least one platform" requirement
- runWeb now uses LoadPermissive so the web admin UI starts even
  before any platforms are configured, enabling first-time setup

fix(weixin): fail fast on ret=-2 when context_token cannot be refreshed (closes #1176)
- When sendMessage returns ret=-2 and the stored token is the same as
  the current one (no inbound message has refreshed it), stop retrying
  immediately instead of burning 3 retries on the same stale token
- Return a clear actionable error: "user must send a new message to
  refresh the session token"
- Applied to both text (weixin.go) and media (media_outbound.go) paths

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(matrix): support MATRIX_CROSS_SIGNING_PASSWORD env var and document crypto DB paths

- Add environment variable fallback for cross_signing_password (env var
  takes precedence over config file, avoiding plaintext password in config)
- Document E2EE crypto data storage location (~/.cc-connect/) and file
  purpose (crypto DB + cross-signing seeds) in both English and Chinese
  setup guides
- Add data directory comment in config.example.toml

Addresses P3 items from PR #834 review.

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: m3 <Marvae@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: 7a3lv7 <hjkl931031@gmail.com>
Co-authored-by: JazzuLu <27945372+JazzuLu@users.noreply.github.com>
Co-authored-by: shellus <353358601@qq.com>
Co-authored-by: Han <hanzr.nju@outlook.com>
Co-authored-by: Marco <48539922+MMMarcinho@users.noreply.github.com>
Co-authored-by: masakasu <32926719+MASAKASUNO1@users.noreply.github.com>
Co-authored-by: Rael <realraelmail@gmail.com>
Co-authored-by: Yu Zhang <34849476+AaronZ345@users.noreply.github.com>
Co-authored-by: wgx521 <wgx521@139.com>
Co-authored-by: wen_guoxing <wen_guoxing@itrus.com.cn>
Co-authored-by: aoko <aokodesuka@gmail.com>
Co-authored-by: 张彧 <aaron@mac.tail449498.ts.net>
Co-authored-by: 张彧 <aaron@Aaron-MacBook-Pro-14.local>
2026-06-16 09:54:55 +08:00
Han
bc902085fd fix(core): resolve pending permission lookup for cron sessions with composite keys (#1067)
* fix: resolve pending permission lookup for cron sessions with composite keys

Cron new-per-run sessions use composite keys like "key#cron:sid", but
platform permission button callbacks use the plain sessionKey, causing
the lookup in handlePendingPermission to fail silently. Add a fallback
path that searches for matching cron-prefixed keys when the direct
lookup returns nothing.

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

* fix(core): add cron fallback test and multi-cron race comment for pending permission lookup

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

* fix: resolve pre-existing pr-1202 conflict marker in engine_test.go

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 09:54:01 +08:00
Claude
dc1c63b9eb release: v1.3.3 stable
Bump VERSION to v1.3.3 and npm/package.json to 1.3.3. Stabilizes the
v1.3.3-beta.1 -> v1.3.3-beta.5 line (~235 PRs since v1.3.2) plus 7
post-beta fixes (qoder streaming, weixin typing ticket, daemon
linger_other.go, wps-xiezuo newlines, /switch history loss, minimax TTS
trailer, provider-resume regression tests).

- Add changelogs/v1.3.3.md with themed summary (New Agents, Platform
  Capabilities, Core, Behavior Changes, Fixed)
- Prepend v1.3.3 section to CHANGELOG.md with highlights + post-beta
  delta + behavior-change checklist
- Refresh "What's New" in README.md and README.zh-CN.md to v1.3.3
- Fix CUJ test flake: bump TestCUJ_H2_TwoPlatformsConcurrentNoBleed
  deadline from 5s to 30s so it stays green under `-race -parallel=N`
  on constrained CI hosts (passes <2s in isolation)

Co-authored-by: Cursor <cursoragent@cursor.com>
v1.3.3
2026-06-15 23:02:36 +08:00
Yu Zhang
2a52fe3c9d fix(qoder): emit streaming text without dropping final result (#1290)
* fix(qoder): emit streaming text without dropping final result

* fix(qoder): bound streaming text dedup cache
2026-06-15 15:21:01 +08:00
肖佳权
b98efb322a fix(weixin): use ilink_user_id in getConfigReq for typing ticket (#1308)
The WeChat ilink API requires 'ilink_user_id' field in getconfig request,
but getConfigReq was using 'user_id' which caused typing ticket retrieval
to fail with 'ilink_user_id required' error.

Align with sendTypingReq which correctly uses 'ilink_user_id'.
2026-06-15 15:14:44 +08:00
Baocang Nie
cd8b442c18 fix(daemon): remove redundant linger_other.go that breaks non-linux builds (#1314)
daemon/linger_other.go (//go:build !linux) defines CheckLinger, but darwin
(launchd.go), windows (windows.go) and the catch-all (unsupported.go) each
already define it. CheckLinger is therefore redeclared on every non-linux
platform, so the build fails on macOS and Windows. linux uses systemd.go and
every platform is already covered, so the file is dead code — remove it.

Fixes #1311
2026-06-15 15:13:28 +08:00