Commit Graph
192 Commits
Author SHA1 Message Date
Claude a74d49e2ae fix(feishu): slash commands not recognized in thread/reply context
Quoted message prefix was prepended directly to Content, making
the first character not '/' and breaking command parsing. Move the
quoted prefix to ExtraContent which is merged after command detection.

Made-with: Cursor
2026-04-17 13:37:34 +08:00
Claude e055bc8cf5 feat(weibo): add Weibo DM platform support
Implement Weibo private message platform via the Open IM WebSocket
protocol (open-im.api.weibo.com). Supports app_id/app_secret auth,
automatic token refresh, heartbeat keep-alive, reconnect with
exponential backoff, message dedup, and chunked sending for long
messages (2000 char limit).

Includes plugin registration, build tag (no_weibo), unit tests,
setup documentation, and cross-references in all platform docs.

Made-with: Cursor
2026-04-17 13:18:33 +08:00
yzlu0917 e2b8dc80fa feat(feishu): default lark to websocket (#522) 2026-04-17 07:53:26 +08:00
de0534329b feat(feishu): support multi-level reply chain recognition (#634)
Trace parent_id links up to 5 levels deep when a user replies to a message,
providing the full conversation chain as context for the agent.

- Add fetchSingleMessage() to retrieve a single message with parent_id
- Add fetchReplyChain() for iterative chain traversal with cycle detection
- Add formatReplyChain() with backward-compatible single-message format
- Refactor fetchQuotedMessage() as a thin wrapper
- Show 'Bot' for app senders, 'User' for unresolved user names

Co-authored-by: MeiShaoqing <meishaoqing@xiaomi.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:45:08 +08:00
HanandClaude Opus 4.6 3d641eb316 feat(feishu): add done_emoji reaction after streaming preview updates (#580)
When the agent's response is delivered via in-place card update (Feishu
streaming preview), the user gets no push notification. This adds a
configurable done_emoji option (e.g. "Done") that adds a reaction after
the "OnIt" reaction is removed, so the user knows the agent finished.

Only activates in quiet mode when the preview card was updated in-place
at least once (via UpdateMessage). Non-quiet mode is unaffected as
intermediate messages already trigger push notifications. Single-round
responses that only use SendPreviewStart are also unaffected, as the
initial send already triggers a push notification.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 14:44:23 +08:00
q107580018 b8d75f73af fix(feishu): improve delete and /model card refresh flows (#645) 2026-04-16 14:43:53 +08:00
f852c8fcd1 fix(feishu): prefer card over post for markdown rendering (#643)
buildReplyContent previously used a three-tier strategy: card only for
"complex" markdown (code blocks/tables), post for everything else. This
caused most markdown content (headings, blockquotes, links, dividers,
lists) to be rendered via post format, which has poor markdown support —
links aren't clickable, blockquotes don't render, dividers are ignored.

Simplify to two paths:
- Has markdown → card (best rendering, supports all markdown features)
- No markdown → text
- Fallback to post md tag only when table count exceeds card limit (>5)

Removes the hasComplexMarkdown gate and the buildPostJSON path from
the rendering decision. hasComplexMarkdown and buildPostJSON are kept
as functions for backward compatibility but are no longer called by
buildReplyContent.

Co-authored-by: leoliang <leoliang@futunn.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 14:43:48 +08:00
Zeren Chen 41e7470ef5 (#597)
* fix(telegram): omit skill menu commands on overflow

* fix(telegram): clamp bot command descriptions
2026-04-16 14:41:24 +08:00
Claude bd9dc515f4 fix: address high/medium priority issues from PR review (batch 3)
H1: Prevent workspace path traversal via ../
    Add resolveLocalDirPath() that validates resolved path stays under
    baseDir, expands ~/... via os.UserHomeDir, rejects escaping paths.

M2: Sanitize sender_name in inject_sender header
    Replace quotes with single quotes, strip newlines/CR to prevent
    prompt injection via display names.

M3: Warn on unset env var placeholders in config
    resolveEnvPlaceholders now logs slog.Warn when ${VAR} references
    an environment variable that is not set.

M4: Validate WeCom api_base_url
    Reject non-http(s) or malformed URLs at New() to prevent credential
    exfiltration via misconfigured base URL.

M5: Quote systemd Environment= values
    Wrap values in double quotes to handle spaces, #, and special chars
    in proxy URLs and paths.

M7: Discord table detection now requires separator row
    Only wrap pipe-delimited blocks that contain a markdown separator
    row (| --- | --- |), reducing false positives on decorative pipes.
Made-with: Cursor
2026-04-16 09:21:18 +08:00
cg33 7e6b97df71 Merge pull request #622 from MeteorSkyOne/pr/discord-table-format
feat(discord): preserve table formatting
2026-04-16 08:06:54 +08:00
cg33 3124d2535e Merge pull request #614 from bugwz/wecom
feat(wecom): support configurable api_base_url for private deployments
2026-04-16 08:01:11 +08:00
Claude 0d92d808a3 fix: address high/medium priority issues from PR review
H1: Prevent double-Close on concurrent cleanupInteractiveState calls
    by nil'ing state.agentSession under the lock before closing.

H2: Weixin StartTyping sends typingStatusStop on ctx.Done() and on
    repeat-failure exit, preventing stuck "typing" indicator.

M3: Feishu @mention resolution: downgrade PII log to Debug, HTML-escape
    display names in at tags, skip ambiguous (duplicate) names.

M4: Narrow segmentStart==0 condition in EventResult to only apply when
    tool messages are hidden (!e.display.ToolMessages).

M5: Log warning when cron workspace agent creation fails instead of
    silently falling back to global agent.

M7: Discord file download capped at 50 MiB via io.LimitReader.

M8: Weixin getConfig now checks ret/errcode fields and returns error
    on non-zero values.
Made-with: Cursor
2026-04-15 20:41:32 +08:00
MeteorSky 877d8b88ec feat(discord): preserve table formatting 2026-04-15 10:46:47 +08:00
cg33 6bb2450261 Merge pull request #507 from chenhg5/fix/discord-slash-command-limit-100
fix(discord): correct slash command limit from 200 to 100
2026-04-15 09:44:54 +08:00
cg33 8601aaadbc Merge pull request #570 from leoliang1997/feat/feishu-mention-resolve
feat(feishu): auto-resolve @name mentions to Feishu at tags
2026-04-15 09:34:40 +08:00
cg33 96a1dc261c Merge pull request #558 from WEIFENG2333/feat/weixin-typing-indicator
feat(weixin): add typing indicator support via ilink sendtyping API
2026-04-15 09:29:54 +08:00
cg33 b7380cb389 Merge pull request #552 from xfromcnZL/feat/discord-inbound-file-attachments
feat(discord): support inbound file attachments
2026-04-15 09:23:57 +08:00
bugwz 52775ea508 feat(wecom): support configurable api_base_url for private deployments 2026-04-14 19:50:36 +08:00
Leo Liang 3231cdbf8d Merge branch 'chenhg5:main' into feat/feishu-mention-resolve 2026-04-13 18:40:28 +08:00
leoliangandClaude Opus 4.6 5ad4e5b5d4 feat(feishu): auto-resolve @name mentions to Feishu at tags
When resolve_mentions is enabled in feishu platform options, outgoing
messages are scanned for @displayName and replaced with native Feishu
at tags by reverse-matching against the group member list.

- New config option: resolve_mentions (default false)
- Lazy-load group members via chat members API with 1h cache TTL
- Match names longest-first to avoid partial matches
- Auto-select at syntax based on message type (text vs card)
- No special markup needed — just @name in natural text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 16:51:02 +08:00
Claude b7fbc78dd1 feat(weixin): implement SendAudio for TTS voice messages
Weixin platform now implements core.AudioSender interface, enabling TTS
audio output for WeChat personal chat via ilink API.

- Added ConvertAudioToAMR in core/speech.go for audio conversion
- Implemented SendAudio in platform/weixin/media_outbound.go
- Converts WAV/MP3/TTS output to AMR format (Weixin voice codec)
- Uploads via CDN and sends as voice message type

Fixes #587: TTS audio was generated but silently dropped because
weixin did not implement AudioSender interface.

Test plan:
- go build ./... passes
- go test ./... passes
- Unit tests for SendAudio edge cases (empty audio, invalid context)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 22:17:04 +00:00
cg33andClaude df12ba8a9d fix(feishu): resolve user/chat names in async goroutine (#582)
Synchronous resolveUserName/resolveChatName calls in the SDK dispatcher
callback blocked the event loop for 300-700ms on cache misses, causing
intermittent message drops in WebSocket mode.

The fix moves name resolution into the dispatchMessage goroutine, ensuring
the SDK dispatcher can process events without blocking. The dedup and
old-message checks remain synchronous for correctness.

Fixes #542

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-12 20:31:04 +08:00
8dd5868d72 feat(observe): mirror terminal Claude Code sessions to a chat channel (#559)
* feat(observe): add --observe CLI flag and engine config

Add observe fields to the Engine struct and a SetObserveConfig setter
to enable terminal session observation in a later task. Define --observe
and --observe-channel CLI flags (wiring deferred).

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

* feat(observe): add ObserverTarget interface, implement for Slack

Define ObserverTarget optional interface in core/ for platforms that can
receive terminal observation messages. Implement SendObservation on Slack
platform using PostMessageContext with link unfurl disabled.

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

* feat(observe): implement JSONL session file watcher and parser

Adds sessionObserver to core/observer.go which tails ~/.claude/projects/{projectKey}/*.jsonl
files, parses user/assistant events, filters out sdk-cli sessions (cc-connect's own),
and forwards new messages to ObserverTarget. Includes full test coverage.

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

* feat(observe): wire observer into engine startup and shutdown

Add findObserverTarget to locate the first ObserverTarget platform,
startObserver to launch the JSONL watcher goroutine when configured,
wired into Engine.Start() and cancelled in Engine.Stop().

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

* feat(observe): wire --observe and --observe-channel flags in CLI

Removes the TODO suppressor and connects the --observe/--observe-channel
CLI flags to engine.SetObserveConfig(), resolving the Claude Code project
directory from the configured work_dir via the new resolveClaudeProjectDir helper.

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

* feat(observe): add config.toml support for observe settings

Add ObserveConfig struct to ProjectConfig so users can configure terminal
session observation via [projects.observe] in config.toml, with CLI flags
(--observe, --observe-channel) taking precedence over config file values.

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

* fix(observe): rune-safe truncation, robust offset tracking, interface assertion

- Use file size for offset tracking instead of line-length calculation
  to avoid drift from \r\n line endings or partial final lines
- Truncate messages at valid rune boundaries to prevent garbled UTF-8
- Add compile-time ObserverTarget interface assertion for Slack

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

---------

Co-authored-by: Leigh Stillard <leigh@stillard.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 19:45:05 +08:00
53f6177231 feat(weixin): add typing indicator support via ilink sendtyping API
Implement the core.TypingIndicator interface for the Weixin (personal
WeChat) platform, showing a "typing..." bubble while the agent is
processing.

- Add getConfig API call to fetch typing_ticket from ilink
- Add sendTyping API call (start/stop) to ilink/bot/sendtyping
- Cache typing tickets per peer with 10-minute TTL
- Proactively refresh ticket on inbound message receipt
- Repeat typing status every 5 seconds (matching Telegram behavior)
- Send best-effort stop signal when processing completes

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-04-11 15:35:04 +08:00
xfromcnandClaude Opus 4.6 a4c93f96a0 feat(discord): support inbound file attachments
Previously the Discord platform only handled audio and image attachments
from incoming messages. Non-image, non-audio attachments (PDFs, documents,
code files, etc.) were silently dropped. This adds an else branch in the
attachment loop to capture them as core.FileAttachment, matching the
behavior already present in the Telegram platform.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 09:53:16 +08:00
cg33andClaude Opus 4.6 ad8b847a33 feat(feishu): format TodoWrite tool input as readable list (#426)
* feat(feishu): format TodoWrite tool input as readable list

Parse TodoWrite JSON input and display as a formatted list with status
icons (✅ completed, 🔄 in_progress, ⏳ pending) instead of raw JSON.

- Added isTodoWriteToolName() to detect TodoWrite tool
- Added formatTodoWriteInput() to parse and format todos
- Shows content, status icon, and activeForm for each todo
- Falls back to default formatting if JSON parsing fails

Fixes task t-20260401-3c8no7

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

* fix(feishu): TodoWrite fallback uses text block instead of inline code

When formatTodoWriteInput returns empty (invalid JSON or empty todos),
return a ```text block directly instead of falling through to the
generic formatter which uses inline backticks for short strings.
Fixes CI test failures for TestFormatProgressToolInput.

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-09 16:50:54 +08:00
cg33andClaude d11a151302 fix(weixin): add retry mechanism for sendMessage ret=-2 errors (#371)
When ilink API returns ret=-2, it typically indicates:
- context_token expired or invalid
- temporary rate limiting

This fix adds:
- Retry mechanism (3 attempts) for sendMessage when ret=-2 is returned
- context_token refresh between retries
- Delay between message chunks (100ms) to avoid rate limiting
- Delay between retries (500ms) for temporary failures
- More detailed logging including content_len for debugging

Fixes #365

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-09 16:50:29 +08:00
Claude 022b9ef55e fix(discord): correct slash command limit from 200 to 100
Discord allows at most 100 application commands per bulk overwrite
(guild or global). The previous limit of 200 caused silent HTTP 400
failures when users had >100 commands. Now truncates at 100 and logs
the count of dropped commands.

Closes #504

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:27:43 +00:00
ferocknew 6099f85c13 feat(telegram): add reply context and location message support (#420)
- Add reply enrichment: when user replies to a message, the original
  message content (text, image, voice, file, location) is included
  as context for the AI agent
- Add location message support: convert Telegram location attachments
  to text content that AI agents can understand
- Add ExtraContent field to core.Message for platform-enriched context
- Add LocationAttachment type to core.Message
- Refactor dispatchMessage to support platform-specific enrichment
2026-04-09 06:10:22 +08:00
xxb 1528d149ed feat(discord): support card-style progress updates (#427) 2026-04-08 23:31:22 +08:00
ahahahaandClaude Opus 4.6 8e116540de fix(feishu): add transient network retry for message send operations (#428)
When Feishu WebSocket connections experience rapid resets, HTTP API calls
for sending messages also fail with transient network errors (EOF,
connection reset, timeout). Previously these errors were not retried,
causing messages to be silently lost.

Add withTransientRetry wrapper with exponential backoff + jitter
(500ms→1s→2s, max 3 retries, +25% jitter) around all Feishu API calls:
reply, create, patch, delete, upload image/file/audio, and preview.
Transient detection uses typed syscall checks (ECONNRESET, EPIPE),
net.Error timeout, io.EOF, and string matching as fallback.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:31:17 +08:00
e00d0a660d fix(weixin): handle upload_full_url in getUploadUrl response (#435)
* fix(weixin): handle upload_full_url in getUploadUrl response

WeChat iLink API changed the getUploadUrl response format: it now
returns `upload_full_url` (a complete CDN URL with `encrypted_query_param`
as a query parameter) instead of the old `upload_param` field.

This caused all file/image uploads to fail with "empty upload_param"
because cc-connect only checked the old field name.

Fix: fallback to parsing `encrypted_query_param` from `upload_full_url`
when `upload_param` is empty.

Fixes #369

Ref: wong2/weixin-agent-sdk#21, hao-ji-xing/openclaw-weixin#11

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

* fix(weixin): fix CDN upload with three issues

1. Handle upload_full_url in getUploadUrl response
   WeChat iLink API returns upload_full_url instead of upload_param.
   Extract encrypted_query_param as fallback, and use upload_full_url
   directly for CDN uploads to avoid URL re-encoding issues.

2. Use dedicated no-proxy HTTP client for CDN uploads
   WeChat CDN (novac2c.cdn.weixin.qq.com) is a China-domestic service
   that times out when accessed through a proxy. Create a separate
   HTTP client with Proxy: nil for CDN operations.

3. Fix AES key encoding in sendMessage
   The aes_key field must be base64(hex_string), not base64(raw_bytes).
   WeChat client expects base64-encoded hex representation of the key,
   matching the official openclaw-weixin plugin behavior.

Fixes #369

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

* fix: address adversarial review findings

1. Make upload_full_url a standalone success path in getUploadURL.
   No longer require extracting encrypted_query_param from
   upload_full_url — if the server returns an opaque/signed URL,
   it is accepted as-is. Only fail when both upload_param and
   upload_full_url are empty.

2. Respect configured proxy for non-WeChat CDN hosts.
   Only bypass proxy when upload_full_url points to a known WeChat
   domestic CDN domain (*.weixin.qq.com, *.wechat.com). For custom
   cdn_base_url or other hosts, the configured httpClient (with
   proxy settings) is preserved.

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

* refactor: simplify CDN upload — remove duplicate function, extract helper

- Remove uploadBufferToCDNDirect (50-line duplicate of uploadBufferToCDN)
- Unify uploadBufferToCDN to accept a pre-built URL (caller handles URL construction)
- Extract formatAesKeyForAPI() helper for the base64(hex(key)) encoding
- Add cdnHttpClient comment clarifying why env proxy bypass is needed

Net -43 lines.

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

* test(weixin): add unit tests for CDN upload fixes

- TestFormatAesKeyForAPI: verify base64(hex) output matches Python SDK
- TestFormatAesKeyForAPI_NotRawBase64: regression guard against old bug
- TestFormatAesKeyForAPI (round-trip): verify formatAesKeyForAPI ↔ parseAesKey
- TestIsWeixinCDNHost: table-driven test for WeChat CDN domain detection
- TestGetUploadURLResponse_Validation: table-driven test for upload_param/upload_full_url acceptance logic

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

* test(weixin): add unit tests for CDN upload fixes

- TestFormatAesKeyForAPI: verify base64(hex(key)) output matches Python SDK
- TestFormatAesKeyForAPI_NotRawBase64: ensure old bug format is rejected
- TestIsWeixinCDNHost: table-driven test for CDN host detection
- TestGetUploadURLResponse_Validation: verify both-empty-fails, either-present-passes

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

---------

Co-authored-by: jiangbowen <robin@jiangbowendeMacBook-Air.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:31:07 +08:00
Claude d1d93f60b0 fix: address high/medium priority issues from PR review
- PR #445: auto-compress skips when agent has no CompressCommand;
  --max-context-tokens is now configurable via agent options instead
  of being hardcoded to 900000
- PR #448: codex app-server RPC timeout cleans pending map entry;
  emit() uses non-blocking send to prevent readLoop stall;
  readLoop uses scanner with 10MB max line size
- PR #462: restore backward-compatible quiet config — maps to
  display.thinking_messages/tool_messages when [display] not set
- PR #479: Gemini STT sends API key via x-goog-api-key header
  instead of URL query param; model name is URL-escaped
- PR #489: feishu fetchQuotedMessage uses caller context instead
  of context.Background; extractPostPlainText includes code_block
- PR #447/#474: SendToSessionWithAttachments uses workspace-prefix
  stripping fallback matching cron/heartbeat logic
- PR #446: PATH prepend for custom commands limited to Windows only

All changes are backward-compatible; new behavior gated by config.
Tests added for quiet config, workspace prefix, Gemini header, and
feishu code_block extraction.

Made-with: Cursor
2026-04-08 23:11:39 +08:00
Li Yan a0a7cc50f7 feat: split thinking/tool visibility and preserve QQ Bot references (#462) 2026-04-08 22:26:48 +08:00
Jim TangandClaude 6dd2155f87 feat(slack): implement FileSender for direct file uploads (#475)
Adds SendFile to the Slack platform, mirroring SendImage. Both use
Slack's UploadFileV2Context, which already handles arbitrary file
types. The engine routes attachments from `cc-connect send --file`
(invoked by agents via the system-prompt-documented CLI) to this
method via the core.FileSender interface assertion.

https://claude.ai/code/session_01WyqXEst7Z8tiMbzeh4rTfX

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-08 22:26:25 +08:00
q107580018 67b4ada900 fix(feishu): avoid model card callback timeouts (#477) 2026-04-08 22:26:19 +08:00
Entrenador d621cff2a4 fix(feishu): handle code_block elements in post message parsing (#480) 2026-04-08 22:26:10 +08:00
890cfb7427 feat(feishu): extract full interactive card content in quoted messages (#489)
* feat(feishu): include replied-to message content in agent context

When a user replies to a specific message in a Lark/Feishu chat,
the bot now fetches the parent message content via Im.Message.Get
and prepends it as a quoted context block, so the agent can see
what the user is referring to.

- Add fetchQuotedMessage() that retrieves and formats the parent
  message content for text, post (rich text), and other message types
- Add extractPostPlainText() helper for parsing Lark post JSON
  (supports both flat and locale-wrapped formats)
- Graceful degradation: on any API failure the quote is silently
  skipped and the user's message is delivered as before
- Only triggers when ParentId is non-empty (reply scenario)
- Uses the same Im.Message.Get API already used by parseMergeForward

Closes #470

* feat(feishu): extract full interactive card content in quoted messages

Use raw API call with card_msg_content_type=raw_card_content to retrieve
the complete card JSON instead of the simplified final state. Add
extractInteractiveCardText and extractCardElements to recursively parse
schema 2.0 card structures including code blocks, markdown, divs,
text tags, and nested elements. Falls back to legacy format (direct
title + flat elements) for older cards.

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

* fix(feishu): handle table, list and simplified body format in card extraction

The Feishu server transforms markdown card content into structured
schema 2.0 elements. Tables become "table" elements with columns/rows,
lists become "list" elements with items, and the body may use direct
"elements" instead of "property.elements". Also support top-level
"content" field on elements (simplified card format from buildCardJSON).

- Add extractCardTable: reconstructs markdown table from columns/rows
- Add extractCardListItems: extracts list items with bullet points
- Handle body.elements (simplified) in addition to body.property.elements
- Read elem.Content as fallback when elem.Property.Content is empty

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

---------

Co-authored-by: Cigarrr <cigarrr@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:25:59 +08:00
Claude 6cd7faeb9f fix: address high/medium priority issues from merged PR review
- fix(relay): process current event before timeout check, close session
  on channel drain (#405)
- fix(claudecode): prevent reader goroutine leak in usage probe, add
  terminal memory bounds (#380)
- fix(cron): validate mode/session_mode on UpdateJob, only restore live
  mode when override succeeds (#367)
- fix(core): simplify idle rotation recovery — remove inconsistent
  fallback to old session (#400)
- fix(feishu): use cache-disabled client for tenant token refresh (#401)
- fix(setup): revert --set-allow-from-empty default to false to avoid
  unintended CLI behavior change (#376)
- feat(telegram): gate message reactions behind enable_reactions config
  flag (#398)

Made-with: Cursor
2026-04-03 06:06:19 +08:00
yzlu0917 68dcb10906 fix: avoid web embed build failures (#391) 2026-04-02 09:28:38 +08:00
Abel e26773ccdf feat(telegram): add message reaction on incoming messages (#398)
When a user sends a message, the bot immediately reacts with ⚡ to
signal that the message has been received and is being processed.

Changes:
- Add SetMessageReaction to telegramBot interface
- Add reactToMessage helper that calls setMessageReaction asynchronously
- Call reactToMessage in handleMessage after building reply context
- Add setReactionCalls counter to stubTelegramBot in tests
2026-04-02 09:26:57 +08:00
Jerry a869ac21d2 fix(feishu): retry sends with a fresh tenant token (#401) 2026-04-02 09:25:45 +08:00
a0bc3f9e28 fix(relay): prevent agent session corruption on timeout (#405)
* fix(feishu): respond to @all messages in group chats

Feishu @all sends {"text":"@_all"} with 0 mentions, which was being
ignored when group_reply_all=false. Add respond_to_at_everyone_and_here
option to allow bots to respond to @all messages.

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

* fix(relay): prevent agent session corruption on timeout

Three issues caused agents to become unresponsive after a relay timeout:

1. StartSession received the relay timeout context -- when the deadline
   fired, exec.CommandContext killed the Claude process mid-turn,
   corrupting the session state. Now uses the engine context instead.

2. defer agentSession.Close() killed the process immediately on return.
   Now the session drains in a background goroutine (drainRelaySession)
   so the agent can finish its turn and save state cleanly.

3. No fallback when resuming a corrupted session. Added retry logic that
   clears the stale session ID and starts fresh, matching the existing
   interactive message flow.

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

---------

Co-authored-by: pinxi_mei <pinxi_mei@lebo.cn>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 09:24:19 +08:00
Jerry 59098aaa27 feat(feishu): support runtime domain override (#404) 2026-04-02 09:23:44 +08:00
Claude 6255fd5127 feat(discord): add proxy support for Discord platform
Discord Gateway and API connections can now be routed through an HTTP
proxy, configured via `proxy`, `proxy_username`, and `proxy_password`
options — matching the existing Telegram proxy support.

Made-with: Cursor
2026-03-30 21:17:05 +08:00
Jerry b56865fb06 feat(feishu): add configurable progress styles (compact/card) to reduce message spam
* feat(feishu): add compact progress style for thinking/tool updates

* feat(feishu): add card-style streaming progress updates
2026-03-28 00:16:13 +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
cg33andClaude 9ca371e1f7 feat(wecom): 企业微信 HTTP 回调支持接收文件并交给 Agent (#330)
* feat(wecom): handle inbound file messages over HTTP callback

Parse MsgType=file (FileName + MediaId), download via media/get API,
and forward as core.FileAttachment for agents. Add MIME inference helper
and unit tests. Document inbound capabilities in config.example.toml.

WebSocket AI bot mode unchanged; file receive there depends on WeChat policy.

* chore(wecom): add Info/Warn logs for callback diagnostics

Log every POST (size, query flags), parsed msg_type/msg_id/from_user,
duplicate/old/allow_from drops, unsupported types at Warn, and Debug
preview of decrypted XML (truncated). Helps trace missing file pushes.

* feat(wecom-ws): receive image, file, mixed and quote media over AI bot WebSocket

Parse msgtype image/file/mixed and quote (text, voice, image, file, mixed)
per WeCom smart-bot protocol. Download encrypted media from signed URLs
and decrypt with AES-256-CBC (IV = first 16 bytes of base64 key), matching
@wecom/aibot-node-sdk. Voice accepts both content and legacy text fields.

Adds wsCollectInboundParts, deliverWSMediaInbound, unit tests for collect,
Content-Disposition parsing, and round-trip decryption.

* fix(wecom-ws): handle file in mixed payloads and normalize aeskey decoding

- Collect file items inside mixed.msg_item and merge top-level file/image
  when msgtype is file/image but a non-empty mixed block is also present.
- decodeWeComAESKey: trim whitespace, URL-safe base64, padding, optional hex.
- Add regression tests for mixed+file and aeskey variants.

* fix: satisfy golangci-lint (gosimple, staticcheck)

- wecom: use := for walkMixed/walkQuote closures (S1021).
- engine: drop unused assignment from sessions.NewSession in cmdNew (SA4006).

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-26 14:51:28 +08:00
xxb 6d8bf93e4c fix: harden Discord session routing and remove implicit continue bridge (#322)
* fix(discord): dedup duplicate interaction events

* fix(core): remove implicit continue bridge

* fix(discord): avoid empty button reply references
2026-03-25 14:31:27 +08:00
ConverSun 3a0ab9dd51 fix(feishu): fall back to post format when markdown has >5 tables (#290)
Feishu interactive cards support at most 5 table components.
When the agent response contains more than 5 markdown tables,
buildReplyContent now falls back to post (md tag) format instead
of card format, preventing API error 11310 (card table number
over limit).

Add countMarkdownTables helper and corresponding tests.
2026-03-25 06:01:16 +08:00