20 Commits
Author SHA1 Message Date
7b93b085cb feat(googlechat): add Google Chat platform adapter (#1424)
* feat(googlechat): scaffold platform skeleton

Add a Google Chat platform adapter registered as "googlechat", wired
into selective compilation via plugin_platform_googlechat.go (build tag
no_googlechat) and the Makefile ALL_PLATFORMS list.

This commit lands the skeleton only: option parsing/validation in New
(gws_path, project, target, subscription, allow_from, trigger,
session_scope, credentials_file) plus stub Start/Reply/Send/Stop.
Receive, send and gating follow in subsequent commits.

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

* feat(googlechat): receive messages via gws events subscribe

Start() now supervises a `gws events +subscribe` subprocess: stdout is
scanned as NDJSON (one Workspace Events CloudEvent per line, buffer
enlarged for the full message resource), stderr is logged, and the
process is restarted with a small backoff if it exits while the context
is alive. Stop() cancels the context to kill it.

parseEvent decodes chat.message.v1.created envelopes, ignores non-human
senders (so the app never replies to its own posts), and dispatches a
core.Message carrying the space/thread reply context. Trigger-word,
allow_from gating and session scoping follow in a later commit.

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

* feat(googlechat): send replies via gws chat messages create

Reply and Send post content through `gws chat spaces messages create`,
using the generic raw method (--params/--json) rather than the +send
helper so replies can be threaded: when the reply context carries a
thread name, the request sets messageReplyOption and thread.name so the
answer lands in the originating thread.

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

* feat(googlechat): add trigger-word, allow_from and session scoping

parseEvent now gates and routes incoming messages:

- trigger word: when configured, only messages starting with the prefix
  are handled (prefix stripped) for user-OAuth mode without a Chat App;
  otherwise argumentText (mention markup already removed by Google) is
  used, falling back to text.
- allow_from via core.AllowList, and a core.IsOldMessage guard on
  createTime so a restart does not replay backlog.
- session_scope space|thread|user via buildSessionKey, plus
  ReconstructReplyCtx so proactive sends reach the right space/thread.

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

* docs(googlechat): add config example

Document the googlechat platform options in config.example.toml,
including the gws prerequisites and the two operating modes (@mention
with a Chat App service account, vs trigger-word with user OAuth).

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

* test(googlechat): add event parsing and reply-arg unit tests

Cover parseEvent (mention vs trigger mode, argumentText fallback,
non-human and wrong-type skipping, allow_from, old-message guard),
buildSessionKey for each scope, ReconstructReplyCtx round-trips, and
buildCreateArgs threaded vs top-level argument shapes.

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

* feat(googlechat): switch to Chat app Pub/Sub receive + service-account send

Pivot the platform from the Workspace Events API to a registered Google
Chat app's native Cloud Pub/Sub connection. The subscription is fixed, so
there is no Workspace Events subscription expiry or per-restart resource
leak (the two operational problems of the previous approach).

- receive: parse the Chat-app event format — gws wraps each Pub/Sub
  message as {"data":{"type":"MESSAGE","message":{...}}} — instead of
  Workspace Events CloudEvents; pull a fixed subscription via
  `gws events +subscribe --subscription`.
- send: post via the Chat REST API authenticated as the app's service
  account (chat.bot scope) so replies appear as the bot. gws has no
  service-account auth, so sending is native Go via
  golang.org/x/oauth2/google; receiving still uses gws (its own OAuth).
- config: require `subscription`; `credentials_file` is the SA key used
  for replies. Drop the Workspace Events target/project auto-create.

Verified end-to-end against a real Chat app: the receive format matches
gws output and a service-account reply posts as the bot.

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

* docs(googlechat): add GCP/Chat app setup guide

Add docs/googlechat.md documenting the end-to-end Google Chat setup:
enabling the Chat + Pub/Sub APIs, creating the Pub/Sub topic/subscription
and granting the Chat push service account publisher, creating a service
account key for bot replies, configuring the Chat app (LIVE, classic
model, Cloud Pub/Sub connection, visibility), cc-connect config, and an
FAQ covering the issues hit during bring-up (app must be LIVE to send,
Workspace-only, add-on vs classic). Link it from the README platform
table.

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

* refactor(googlechat): receive via native Pub/Sub client instead of gws

Replace the `gws events +subscribe` subprocess with a native streaming pull
using cloud.google.com/go/pubsub. Sending was already native Go, so this
removes the gws binary dependency entirely — the platform is now fully native.

Both directions now authenticate with the same service-account key, so
`credentials_file` is required (it also needs roles/pubsub.subscriber on the
subscription for the receive path). The `gws_path` option is removed.

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

* refactor(googlechat): drop trigger, centralize session-key constants, harden session_scope (#5)

- Remove the `trigger` field and its prefix-matching branch from extractContent;
  Google Chat already scopes delivery so a prefix filter is redundant and subtly
  broken for @mention spaces (m.Text still contains the markup).
- Extract sessionKeyPrefix and threadSep constants used by both buildSessionKey
  and ReconstructReplyCtx so the encode/decode pair stays in sync automatically.
- Add a slog.Warn in the normalizeSessionScope default branch so typos like
  "thraed" surface at startup instead of silently falling back to "space".
- Update tests, docs/googlechat.md, and config.example.toml accordingly.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(googlechat): implement FormattingInstructions for Google Chat text formatting (#7)

* feat(googlechat): implement FormattingInstructions for Google Chat text formatting

Adds FormattingInstructions() to *Platform so the engine injects
Google Chat-specific formatting guidance into the agent system prompt,
steering agents away from unsupported ## headings and [text](url) links.

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

* fix(googlechat): add block quote and display-text link to FormattingInstructions

Add missing formatting rules per reviewer feedback:
- Block quote syntax (>text)
- Display-text hyperlink syntax (<url|text>) alongside raw URL auto-linking

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

* fix(googlechat): fix test call after base-branch refactor of newTestPlatform

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(googlechat): implement streaming preview (PreviewStarter + MessageUpdater) (#6)

* feat(googlechat): implement streaming preview via PreviewStarter + MessageUpdater

Adds SendPreviewStart (POST a threaded message, return resource-name handle)
and UpdateMessage (PATCH spaces.messages with updateMask=text) so the engine's
real-time streaming-preview path is activated for Google Chat.

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

* refactor(googlechat): extract doRequest helper + drain response bodies

Extract a `doRequest` helper that centralizes HTTP execution and non-2xx
error handling. Both `post()` and `SendPreviewStart` become thin wrappers,
eliminating the duplicated logic that would otherwise diverge on future
changes (retries, auth header tweaks, etc.).

Drain response bodies on success paths in `post()`, `SendPreviewStart`,
and `UpdateMessage` so the transport can return TCP connections to the
pool. Addresses the performance issue where each streaming PATCH would
open a new TLS+TCP handshake.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(googlechat): send images and files via attachment upload (#8)

* feat(googlechat): implement SendImage and SendFile via attachment upload

Adds media-upload support to the Google Chat platform so cc-connect can
send images and files (e.g. /diff HTML output) instead of returning
ErrNotSupported.

Two-step flow for each send:
1. Upload raw bytes to the Chat media endpoint (multipart/related POST
   to https://chat.googleapis.com/upload/v1/{space}/attachments:upload)
   and retrieve the attachmentDataRef resource name.
2. Create a Chat message that references the data ref, respecting thread
   context the same way as the existing text send path.

Shared helper postAttachment handles both steps; SendImage and SendFile
delegate to it with appropriate filename/MIME defaults. Compile-time
interface assertions ensure *Platform satisfies core.ImageSender and
core.FileSender.

Adds TestBuildAttachmentRequest mirroring TestBuildSendRequest to cover
the message body shape (attachment data ref + thread, reply option URL).

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

* fix(googlechat): address code-review feedback on SendImage/SendFile

- Add rc.space == "" guard to postAttachment (bug fix: prevents malformed
  upload URL when ReconstructReplyCtx("googlechat:") yields empty space)
- Pre-size bytes.Buffer in uploadAttachment to avoid a full double-copy of
  attachment data on large files
- Extract httpErrorBody helper to deduplicate the 3 error-drain sites
- Extract messageURL/applyThread helpers to deduplicate threading URL logic
  shared between buildSendRequest and buildAttachmentRequest
- Extract coalesce helper and simplify SendImage/SendFile
- Use doRequest in postAttachment for the create-message step
- Add HTTP-level unit tests: uploadAttachment (success, 403 error,
  empty resourceName), postAttachment (missing space, two-step flow),
  SendImage/SendFile (default filename/MIME fallbacks)

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

* fix(googlechat): use doRequest in uploadAttachment and drain response body

Two follow-up fixes from code review:
- Route upload request through doRequest so it benefits from any future
  centralized logging/retry/tracing alongside post() and postAttachment().
- Drain remaining bytes from resp.Body after json.Decode so the HTTP
  transport can return the connection to the pool (partial reads prevent
  connection reuse, causing a new TLS handshake per SendImage/SendFile call).

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

* fix(googlechat): drain resp.Body on decode error in uploadAttachment

Deferred drain ensures the HTTP connection is returned to the pool
even when JSON decoding fails, not just on success.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(googlechat): handle Stop error, remove redundant drain defer, add compile-time assertion (#12)

- Return psClient.Close() error from Stop() instead of silently discarding it
- Remove redundant drain defer in uploadAttachment; the json.Decoder already
  consumes the body, and defer resp.Body.Close() handles cleanup
- Add compile-time assertion var _ core.ReplyContextReconstructor = (*Platform)(nil)
  alongside the existing ImageSender and FileSender guards

Closes #10

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(googlechat): use doRequest in uploadAttachment and drain response body

Two follow-up fixes from code review:
- Route upload request through doRequest so it benefits from any future
  centralized logging/retry/tracing alongside post() and postAttachment().
- Drain remaining bytes from resp.Body after json.Decode so the HTTP
  transport can return the connection to the pool (partial reads prevent
  connection reuse, causing a new TLS handshake per SendImage/SendFile call).

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

* fix(googlechat): drain resp.Body on decode error in uploadAttachment

Deferred drain ensures the HTTP connection is returned to the pool
even when JSON decoding fails, not just on success.

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

* fix(googlechat): clear lint issues in googlechat package (#13)

* fix(googlechat): resolve package lint errors

Handle all body/multipart close errors, remove deprecated auth option usage, and tighten HTTP test writers so golangci-lint passes for platform/googlechat.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(googlechat): ack Pub/Sub message after handler, nack on panic

Previously m.Ack() was called before p.handler(), so a handler panic or
failure would silently drop the message without redelivery. Move ack to
after handler completion and nack on panic so Pub/Sub can redeliver.

Introduce the ackable interface and dispatchMessage to make the timing
testable without a real Pub/Sub client, and add three regression tests
covering the success, panic, and parse-failure paths.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(googlechat): add unit tests for New() error cases (#14)

Closes #11

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-08-13 16:49:41 +08:00
72d6fb617d Integrate WPS Agentspace platform with auto-login and token encryption (#1439)
* feat: add WPS Agentspace platform integration

Add wps-agentspace platform that connects to WPS Digital Employee (数字员工)
via WebSocket, enabling users to interact with Claude Code through WPS Xiezuo
without creating a WPS application.

Features:
- WebSocket connection to agentspace.wps.cn
- Token decryption (AES-256-GCM) for OpenClaw-encrypted tokens
- Message deduplication
- Auto-reconnection with exponential backoff
- Heartbeat keep-alive
- Typing indicator support

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* chore: add wps-agentspace to ALL_PLATFORMS in Makefile

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* feat: add auto-login for wps-agentspace platform

When wps_sid is empty, automatically open browser for WPS OAuth login
and poll for the token. This provides a smoother setup experience.

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* feat: encrypt wps_sid after auto-login

After OAuth login, encrypt the token using AES-256-GCM and display
the encrypted value for safe storage in config.toml.

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* feat: auto-set environment variable after login

After OAuth login, automatically display encrypted token and instructions
for setting WPS_SID environment variable. Also add Windows browser support.

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* feat: auto-save token to config file after login

After OAuth login, automatically save encrypted token to config.toml.
Falls back to displaying token for manual environment variable setup.

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* chore: remove sensitive data from test file

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix: resolve lint errors (govet, errcheck)

- Remove redundant newline in fmt.Println
- Check error return values with _ = for non-critical calls
- Fix return and defer statements

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix: remove unused statFile function

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix: check error return in defer statement

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* feat: support file/media attachments in incoming messages

Parse media_urls and media_url fields from WPS messages and append
file URLs to the message content so Claude can access attachments.

Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix(wps-agentspace): address PR review feedback

- Re-enable token decryption in Start(); remove token prefix from logs
- Rewrite saveToConfig with TOML validation + surgical line replacement
- Fix autoLogin connection leak (http.Client + deferred Body.Close)
- Fix openBrowser Windows path (cmd /c start  url)
- findConfigFile: prefer config.ConfigPath (honours --config flag)
- decryptWpsSid: fallback to defaultKeySource when appID key fails
- generateUUID: use google/uuid v4 instead of custom implementation
- expandPath: support ~user/... via os/user.Lookup
- gofmt: fix struct alignment, trailing blank lines
- Add 6 new tests (11 total): Start decrypt, handleFrame dispatch,
  fatal errors, saveToConfig surgical update, autoLogin HTTP flow,
  decrypt fallback to defaultKeySource

Generated with Kscc
Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix(wps-agentspace): resolve lint errors

- Remove unused userInfoAPI variable
- Check errcheck: json.NewEncoder.Encode and p.Stop() return values

Generated with Kscc
Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

* fix(wps-agentspace): fix race condition on Platform.stopped field

Use atomic.Bool for stopped flag to avoid data race between
connectLoop goroutine reading stopped and Stop() writing it.
Detected by go test -race.

Generated with Kscc
Co-Authored-By: fengjianxiong <fengjianxing@kingsoft.com>

---------

Co-authored-by: fengjianxiong <fengjianxiong@kingsoft.com>
Co-authored-by: fengjianxiong <fengjianxing@kingsoft.com>
2026-07-14 06:45:46 +08:00
+7 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
Vahid JoziandClaude Opus 4.7 f166ee494d feat(slack): support Slack Assistant API (Agent toggle) (#844)
When a Slack app has the Agent/Assistant toggle on, users get a 'Chat' tab
in the app sidebar (a dedicated assistant_thread). Messages typed there
arrive as message.im events with ThreadTimeStamp set to the assistant
thread's root ts.

Before this patch, cc-connect's Reply() and Send() ignore that thread_ts
(MsgOptionTS is a no-op on chat.postMessage; Send didn't pass it at all).
Replies thus went to the DM root, surfacing in the 'History' tab feed
rather than the Chat tab — breaking the conversational UX for any bot
configured as an Agent/Assistant.

Changes:
- New helper assistantOrThreadTS() returns ev.ThreadTimeStamp if present
- MessageEvent handler uses assistantOrThreadTS() for replyContext
- Reply() switched from MsgOptionTS (chat.update only) to
  MsgOptionPostMessageParameters{ThreadTimestamp} (real thread reply)
- Send() now also includes ThreadTimestamp when present (engine often
  falls back to Send when streaming preview is unavailable)
- New AssistantThreadStartedEvent handler logs the event + sets initial
  status (informational; Reply path does the actual routing)

Tested against a live Donna Slack app with Agent enabled — replies now
land in Chat tab as expected.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 10:01:31 +08:00
梦典andmengdian 35a9aa56aa feat(claudecode): support project-level environment variables (#812)
* feat(claudecode): support project-level environment variables

Add parsing of [projects.agent.options.env] in config.toml to allow
per-project environment variable overrides (e.g. ANTHROPIC_BASE_URL,
ANTHROPIC_AUTH_TOKEN, ANTHROPIC_MODEL) instead of requiring a single
global configuration in ~/.claude/settings.json.

- Parse env map from opts["env"] in New(), supporting both
  map[string]string and map[string]any TOML type variants
- Merge sessionEnv into the existing three-layer env resolution chain
  (provider < sessionEnv < router) via core.MergeEnv()
- Add example config with Chinese comments in config.example.toml
- Add 4 unit tests in project_env_test.go

This enables different projects to use different LLM providers
(e.g. Kimi, DeepSeek) without conflicting with each other.

Made-with: Claude Code

* fix: bypass LookPath in tests for CI

---------

Co-authored-by: mengdian <wy@lucidata>
2026-05-05 09:54:11 +08:00
Claude c4279d142e feat: import providers from cc-switch in web UI
Add a "Import from CC-Switch" button to the web provider management
page. The backend reads the cc-switch SQLite database directly using
modernc.org/sqlite (pure Go, no CGO/sqlite3 CLI dependency), and the
frontend shows a modal with checkboxes to select which providers to
import as global providers.

- Add GET/POST /api/v1/providers/cc-switch management API endpoints
- Replace sqlite3 CLI usage with database/sql + modernc.org/sqlite
- Add CCSwitchImportModal component with i18n support (5 languages)

Made-with: Cursor
2026-04-18 17:31:40 +08:00
Buqian Zheng 1104739c90 feat(telegram): migrate to go-telegram/bot and add forum topic support (#321)
* feat(telegram): migrate to go-telegram/bot and add forum topic support

Migrate the Telegram platform from the unmaintained go-telegram-bot-api/v5
(last release 2021) to go-telegram/bot v1.20.0, which is actively maintained
and supports the full Telegram Bot API including forum topics.

Key changes:

- **Library migration**: Replace go-telegram-bot-api/v5 with go-telegram/bot.
  Redesign the telegramBot interface around the new library's typed API methods
  (SendMessage, SendPhoto, EditMessageText, etc.) instead of the old generic
  Send/Request pattern. The *bot.Bot type satisfies the interface directly.

- **Forum topic support**: Add threadID to replyContext and session key.
  Messages in forum groups (IsForum=true) now include the MessageThreadID
  in the session key (telegram:{chatID}:{threadID}:{userID}), giving each
  topic its own independent agent session. All send methods propagate
  MessageThreadID so replies land in the correct topic. Non-forum groups
  and private chats are unaffected.

- **Per-topic workspace binding**: Add ChannelKey field to core.Message so
  platforms can provide an explicit channel identifier for workspace binding.
  Telegram sets this to "chatID:threadID" for forum topics, enabling
  multi-workspace mode to bind different topics to different project
  directories. Add effectiveChannelID/effectiveWorkspaceChannelKey helpers
  and update all workspace resolution call sites in the engine.

- **Skill command registration fix**: GetAllCommands now respects
  disabled_commands for skills, matching the existing behavior for
  built-in commands. This prevents disabled skills from being registered
  as Telegram bot menu commands.

- **Connection management**: Simplify the reconnect architecture.
  bot.New() handles initial validation, bot.Start(ctx) manages long
  polling internally. Remove the stale bot detection (generation
  comparison in handlers) since the new library's blocking Start()
  ensures handlers are never called from a superseded bot instance.

Signed-off-by: Buqian Zheng <zhengbuqian@gmail.com>

* fix: resolve lint errors exposed by engine.go and telegram_test.go changes

- Remove unused assignment to `s` in cmdNew (SA4006)
- Remove unused stubBotFactory and failingBotFactory helpers in telegram tests

Signed-off-by: Buqian Zheng <zhengbuqian@gmail.com>

---------

Signed-off-by: Buqian Zheng <zhengbuqian@gmail.com>
2026-03-27 23:01:33 +08:00
Claude cb2371c15a feat: /dir cards, context indicator toggle, Feishu flat send option
- core: interactive /dir card (nav/act) on CardSender platforms; dirApply refactor
- core+i18n: card strings for dir history UI
- config: projects.show_context_indicator (hide [ctx: ~N%] suffix)
- feishu/lark: reply_to_trigger=false posts via Create instead of Reply
- codex: skip flush when ctx done; sync.Once on session Close
- dingtalk: stream runner reconnect loop; cancel on Stop
- go.mod: mark rsc.io/qr as direct require
2026-03-23 16:49:42 +08:00
Claude ee985bf026 feat(tests): add testing infrastructure foundation
- Add testify and httpmock dependencies for testing
- Create tests/ directory structure with mocks and fakes
- Add mock_platform.go: mock for core.Platform interface
- Add mock_agent.go: mock for core.Agent/AgentSession interfaces
- Add fake/message.go: test message and event fixtures
- Add fake/session.go: fake agent session implementation
- Add fake/response.go: test helpers and fixtures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 10:18:30 +08:00
q107580018 2f01442f75 feat(feishu): add setup/new/bind onboarding with config-safe writeback (#151)
* feat(feishu): add setup/new/bind onboarding and config-safe writeback

* test(codex): stabilize args-file polling under coverage
2026-03-16 06:43:38 +08:00
Sean c79d8e9077 feat: add 'sessions' subcommand with TUI viewer (#143)
* feat: add 'sessions' subcommand with TUI viewer (WIP)

* feat: add 'sessions' subcommand with TUI viewer for browsing session history

Three modes: interactive TUI browser (default), non-interactive list,
and non-interactive show. Reads session JSON files directly from
~/.cc-connect/sessions/ without requiring the daemon to be running.

- TUI: bubbles/table overview + bubbles/viewport detail with lipgloss styling
- `sessions list`: pipe-friendly tabwriter output
- `sessions show <id> [-n N]`: view specific session by composite ID or index

* feat: persist user/group names for sessions viewer

- Add UserMeta (UserName/ChatName) to SessionManager with persistence
- Capture metadata in engine.handleMessage() on every incoming message
- Fix feishu userName bug: was using sender.SenderType ("user"), now
  calls resolveUserName() for actual display names
- Populate ChatName with Chat.Title for Telegram group chats
- Display User and Group/Chat columns in sessions list/show/TUI
- Return copy from GetUserMeta() for thread safety
- Use rune-based truncation to avoid corrupting multi-byte UTF-8

* feat: add user/chat name resolution for all platforms

- Slack: resolve UserName via GetUserInfo API, ChatName via channel name
- Discord: resolve ChatName via Channel API with sync.Map cache
- LINE: resolve UserName via GetProfile API with sync.Map cache
- DingTalk: populate ChatName from ConversationTitle (SDK provides it)
- QQ (OneBot): resolve ChatName via get_group_info API with cache
- WeChat Work (HTTP): resolve UserName via user/get API with cache

Platforms with API limitations (left as raw ID):
- QQ Bot: official API does not provide user nicknames
- WeChat Work (WS): no REST API access from WebSocket mode

* fix: consistent fallback to raw ID when name resolution fails

- Slack: resolveChannelNameForMsg returns channelID (not "") on error
- Discord: resolveChannelName returns channelID (not "") on error,
  skip caching empty names
- QQ: resolveGroupName returns groupID string (not "") on error
- WeChat Work WS: add comment documenting why raw userID is used

* feat: add chat/group name resolution for feishu and LINE

- Feishu: resolve chat name via Im.Chat.Get API with sync.Map cache,
  populate ChatName for all message types
- LINE: resolve group name via GetGroupSummary API with sync.Map cache,
  populate ChatName for group messages

* fix: populate UserName/ChatName in all remaining message paths

- Feishu: add UserName + ChatName to onCardAction handler (perm/askq/cmd)
- QQ Bot: add GroupOpenID as ChatName fallback in group messages
- WeChat Work WS: add ChatID as ChatName for group chats
2026-03-15 21:41:20 +08:00
Leigh StillardandClaude Opus 4.6 6ec4199e70 chore: update golang.org/x/crypto and golang.org/x/sys to latest (#77)
Update outdated standard library dependencies:
- golang.org/x/crypto: v0.0.0-20210421170649 → v0.48.0
- golang.org/x/sys: v0.0.0-20201119102817 → v0.42.0
- Go minimum version: 1.24.2 → 1.25.0 (required by x/sys v0.42.0)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 07:34:51 +08:00
xinyuan0801 e623b0ebe8 build: restore go directive to 1.24.2 2026-03-07 14:19:07 +08:00
xinyuan0801 64df463e3b iflow: stabilize interactive tool-call turns 2026-03-07 14:16:33 +08:00
chenhg5andClaude Opus 4.6 2160090ec6 feat(streaming): add per-platform streaming preview control
Add disabled_platforms config option to disable streaming preview
for specific platforms (e.g. feishu) where it causes formatting issues.

- Add DisabledPlatforms field to StreamPreviewConfig and StreamPreviewCfg
- Check platform name against disabled list in canPreview()
- Allows pure text messages to use correct message type instead of cards

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:08:43 +08:00
chenhg5andClaude c33f90cb34 feat(cron): add cron scheduling system with CLI and API support
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-02 14:35:05 +08:00
chenhg5andClaude c88d46a973 feat(qq): add QQ platform support via OneBot v11/NapCat
generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-02 11:50:53 +08:00
chenhg5andClaude 2b1152c1c7 feat(platform): add support for Telegram, Slack, Discord and LINE
The changes introduce support for four new messaging platforms:

Includes implementation code for LINE platform adapter, documentation updates in READMEs, and configuration examples. Also adds required dependencies to go.mod.

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-28 15:17:17 +08:00
chenhg5andClaude eaec71f8a5 feat: add Telegram, Slack and Discord platform support
Add new platform integrations with configuration examples and dependencies:
- Telegram long polling bot support
- Slack Socket Mode WebSocket integration
- Discord Gateway WebSocket connection
- Include required dependencies in go.mod

generated by llmgit

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-28 14:56:29 +08:00
chenhg5andClaude 3b097a80cb init
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-28 10:38:24 +08:00