44 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
6031c2e2a5 Prevent IM stream cards from blocking completion
Feishu CardKit and DingTalk AI Card updates run inside the serialized adapter message path. A hung platform update can prevent the later message_complete event from being handled, leaving mobile IM users stuck on a partial streaming card.

This bounds platform card API calls and gives Feishu a final patch fallback when CardKit finalization fails, so a bad intermediate frame cannot permanently block the chat turn.

Constraint: Feishu and DingTalk card APIs are external network calls without reliable SDK-level deadline guarantees

Rejected: Disable streaming cards entirely | loses the live IM experience for normal fast responses

Confidence: high

Scope-risk: narrow

Directive: Keep IM card platform calls bounded because adapter server messages are serialized per chat

Tested: bun test adapters/feishu/__tests__/streaming-card.test.ts

Tested: bun test adapters/dingtalk/__tests__/ai-card.test.ts

Tested: bun run check:adapters

Tested: bunx tsc -p adapters/tsconfig.json --noEmit

Not-tested: Full bun run verify was interrupted during the coverage lane after dependency setup
2026-05-13 12:44:52 +08:00
程序员阿江(Relakkes)
1f62bcd919 Prevent IM chats from reusing deleted desktop sessions
Deleting a desktop session removed the transcript but left IM adapter
chat mappings and live WebSocket state able to point at the old session.
The server now closes the active session socket and removes adapter
mappings after deletion, while adapters refresh the shared session store
before reads so a running process cannot reuse stale in-memory data.

Constraint: Adapter session mappings are shared through adapter-sessions.json across long-running IM processes
Rejected: Patch each adapter ensureSession path separately | shared store refresh fixes all current adapters and avoids drift
Confidence: high
Scope-risk: moderate
Directive: Do not cache adapter session mappings without invalidating after server-side session deletion
Tested: bun test common/__tests__/session-store.test.ts common/__tests__/ws-bridge.test.ts
Tested: bun test src/server/__tests__/websocket-handler.test.ts src/server/__tests__/sessions.test.ts --timeout 30000
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Tested: bun run check:server
Not-tested: bun run verify fails on pre-existing agent-utils coverage ratchet; changed-line coverage is 95.92% and affected lanes pass
Related: https://github.com/NanmiCoder/cc-haha/issues/305
2026-05-08 09:58:24 +08:00
程序员阿江(Relakkes)
54c975bc09 fix: preserve DingTalk permission reply order
DingTalk keeps AI card placement tied to the original card delivery time, so a card created before a permission request can later be updated with the final answer above the permission card. Finish and drop the active stream when permission interrupts a turn so authorized output creates a new card below the prompt.

Constraint: DingTalk does not reorder an existing delivered AI card when its streaming content is updated
Rejected: Reuse the pre-permission AI card after approval | final answers can remain visually above the permission request
Confidence: high
Scope-risk: narrow
Directive: Keep permission_request as a lifecycle boundary for DingTalk AI card streams
Tested: cd adapters && bun test dingtalk/__tests__/stream-state.test.ts dingtalk/__tests__/ai-card.test.ts
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Tested: git diff --check
Not-tested: Live DingTalk account end-to-end permission approval
2026-05-05 21:49:39 +08:00
程序员阿江(Relakkes)
ca62d1b24f docs: clarify IM setup and command discovery
The WeChat and DingTalk IM docs were behind the actual desktop adapter flow, especially around QR binding versus user pairing and how users discover slash commands when no platform menu exists. This records the setup path with screenshots and nudges paired users toward /help from the bot itself.

Constraint: DingTalk does not expose the same menu setup path documented for Feishu, so command discovery must work through chat text.
Rejected: Document commands only in VitePress | users may be on mobile when they need the commands.
Confidence: high
Scope-risk: narrow
Directive: Keep IM command docs aligned with adapters/common/format.ts when adding commands.
Tested: cd adapters && bun test common/ dingtalk/ wechat/
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:docs
Tested: bun run check:adapters
Not-tested: Live WeChat or DingTalk message delivery with real platform accounts
2026-05-05 21:08:55 +08:00
程序员阿江(Relakkes)
7ca25687fd feat: make IM permission approval mobile-friendly
Text-only IM channels forced users to copy long permission request IDs from mobile chat, which made approval slow and error-prone. This keeps the requestId-based authorization protocol intact while adding short replies for the single-pending-request case and preserving full command fallbacks for ambiguous cases.

Constraint: IM authorization must still resolve through the existing requestId permission_response path
Rejected: Replace request IDs with global numeric IDs | concurrent permission prompts would make numeric IDs ambiguous across chats
Confidence: high
Scope-risk: moderate
Directive: Do not allow short numeric replies when more than one permission request is pending in the chat
Tested: cd adapters && bun test common/ telegram/ feishu/ dingtalk/ wechat/
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Not-tested: Real WeChat or Telegram account end-to-end message delivery
2026-05-05 21:06:58 +08:00
程序员阿江(Relakkes)
622b94011f fix: keep WeChat agent replies visible
WeChat development turns were reaching the local CLI, but the adapter treated iLink business failures as successful sends and only emitted a short-lived typing indicator. The adapter now validates sendmessage/sendtyping ret codes, retries text replies without stale context tokens, keeps typing alive during long-running tool work, and surfaces queue failures back to the chat.

Constraint: WeChat uses separate sendmessage and sendtyping APIs with business-level ret codes inside HTTP 200 responses.
Rejected: Only fixing the typing indicator | the transcript showed the agent completed the task, so the reply delivery path also needed hardening.
Confidence: high
Scope-risk: narrow
Directive: Do not treat WeChat HTTP 200 responses as successful until the iLink ret/errcode body has been checked.
Tested: bun test adapters/wechat/__tests__/protocol.test.ts adapters/wechat/__tests__/typing.test.ts
Tested: cd adapters && bunx tsc --noEmit
Tested: bun run check:adapters
Tested: real bound WeChat getconfig, typing, text send, cancel typing smoke
Not-tested: Full inbound WeChat user-message loop without a fresh user-triggered message
2026-05-05 19:58:53 +08:00
程序员阿江(Relakkes)
2459488703 Harden provider auth, adapter paths, and notification retries
This captures the pending worktree fixes before applying them to the
current local main. The changes tighten IM adapter path and credential
handling, preserve retry behavior for failed desktop notifications, and
make Azure/OpenAI provider auth and stop reasons reflect actual runtime
state.

Constraint: Worktree was detached from an older local main with pending uncommitted fixes
Rejected: Merge the detached HEAD directly | would also replay unrelated stale history
Rejected: Leave notification dedupe as fire-and-forget | failed sends consumed retry keys
Confidence: high
Scope-risk: broad
Directive: Keep adapter absolute-path matching constrained to configured work roots
Tested: git diff --check
Not-tested: full quality gate before local main integration
2026-05-04 21:37:30 +08:00
程序员阿江(Relakkes)
f1c5f86b80 feat: keep IM permission approvals consistent
DingTalk can receive interactive card callbacks, but the card UI still depends on a published template, so the adapter now supports a template-backed card path with text commands as the reliable fallback. Telegram and WeChat use the same allow-once, allow-always, and deny semantics so manual authorization behaves the same across platforms.

Constraint: DingTalk button rendering requires an operator-provided interactive card template id
Rejected: Treat the existing AI streaming card template as a permission card | it cannot guarantee visible action buttons
Confidence: high
Scope-risk: moderate
Directive: Do not remove the text approval fallback unless DingTalk card template provisioning is guaranteed
Tested: bun test adapters/common/__tests__/permission.test.ts adapters/dingtalk/__tests__/permission-card.test.ts adapters/telegram/__tests__/telegram.test.ts adapters/common/__tests__/config.test.ts src/server/__tests__/adapters.test.ts
Tested: bunx tsc -p adapters/tsconfig.json --noEmit
Tested: bun run check:adapters
Tested: cd desktop && bun run build
Tested: bun run check:server
Tested: bun run check:docs
Tested: git diff --check
Not-tested: bun run quality:pr is blocked by existing CLI core approval policy; see artifacts/quality-runs/2026-05-03T13-19-56-232Z/report.md
2026-05-04 19:03:36 +08:00
程序员阿江(Relakkes)
2ca5228171 feat: make IM adapter behavior consistent
WeChat and DingTalk were using different pairing, attachment, and response
state paths, which made the new IM channels behave differently from Feishu
and Telegram. Align the shared pairing model, wire inbound media into the
existing attachment bridge, and map platform response capabilities to their
real APIs: WeChat block streaming plus typing, DingTalk AI Card streaming.

Constraint: WeChat iLink exposes typing and block streaming, but no editable message/card streaming API
Constraint: DingTalk streaming depends on the AI Card create/deliver/stream/finalize lifecycle
Rejected: Fake DingTalk typing with standalone markdown | it would add chat noise instead of platform state
Rejected: Auto-pair WeChat after QR login | it bypasses the shared IM pairing model
Confidence: high
Scope-risk: moderate
Directive: Keep WeChat streaming as block-send unless iLink adds editable messages; keep DingTalk streaming on AI Card APIs
Tested: bun run check:adapters; bun run check:server; cd desktop && bun run test src/stores/adapterStore.test.ts; cd desktop && bun run build; bunx tsc -p adapters/tsconfig.json --noEmit; git diff --check
Not-tested: Live WeChat and DingTalk platform smoke with real production credentials
2026-05-03 21:08:12 +08:00
程序员阿江(Relakkes)
6d9aa98022 Support DingTalk IM without blocking on project history
DingTalk uses QR registration to store client credentials, then reuses the existing IM pairing and session bridge. The merged main implementation keeps the existing WeChat QR binding path intact while adding DingTalk as a peer IM platform. The default IM workdir now falls back to the local user working directory so a newly bound chat can start immediately even when recent-project history is empty.

Constraint: Local main already carries WeChat IM, so the merge keeps WeChat config, QR binding, sidecar args, and unbind behavior intact while adding DingTalk.
Rejected: Keep empty defaultProjectDir as project-picker-only | newly bound IM users can hit a dead end with no recent projects.
Confidence: high
Scope-risk: moderate
Directive: Keep IM platform unions synchronized across config, pairing, sidecar args, desktop settings, and docs.
Tested: bun test common/ dingtalk/ wechat/; bun test src/server/__tests__/adapters.test.ts; cd adapters && bunx tsc --noEmit; bun run check:policy; bun run check:server; bun run check:desktop; bun run check:adapters; bun run check:native; bun run check:docs
Not-tested: quality:pr full gate was blocked by existing local main CLI-core diff requiring allow-cli-core-change maintainer approval; live post-fix DingTalk second-message delivery was not repeated after the merge.
2026-05-03 17:56:22 +08:00
程序员阿江(Relakkes)
a627bb19f2 feat: support WeChat as a first-class IM channel
WeChat needs a QR-paired path instead of bot-token setup, so the adapter layer now includes the iLink protocol calls, desktop pairing UI, server-side bind/unbind APIs, and shared IM command behavior. Empty project history falls back to the user's default work directory so mobile /new works without pre-opening a desktop project.

Constraint: Tencent iLink login returns a URL that the desktop UI must render as a QR image locally
Constraint: IM adapters should keep /new, /projects, status, permission, and default workdir behavior consistent across WeChat, Feishu, and Telegram
Rejected: Require users to paste absolute project paths for first WeChat sessions | mobile onboarding should work from the default user working directory
Confidence: high
Scope-risk: moderate
Directive: Do not change WeChat polling back to overlapping intervals; getupdates is a long-poll endpoint and must remain serialized
Tested: Real WeChat QR scan, inbound /status, outbound reply, and unbind E2E
Tested: bun run check:adapters
Tested: bun run quality:pr
Not-tested: Re-scan live WeChat after the default-workdir fallback tweak; covered by adapter config tests and PR gate
2026-05-03 17:38:08 +08:00
Ziang Xie
9698307f27 fix(adapters): harden credential file storage in pairing.ts
Three security improvements for adapters.json storage:

- Use crypto.randomBytes for temp filename instead of predictable
  Date.now(), preventing TOCTOU symlink race during atomic write.
- Enforce 0o600 permissions on adapters.json via writeFileSync mode
  option to prevent credential leakage via permissive umask.
- Enforce 0o700 permissions on ~/.claude/ config directory via
  mkdirSync mode option.

adapters.json contains sensitive credentials including Feishu app
secrets and Telegram bot tokens.
2026-05-02 00:40:44 +08:00
lvjunjie-byte
6578916061 fix: 为飞书适配器的 HTTP 请求添加超时机制
问题:AdapterHttpClient 中的 HTTP 请求没有设置超时时间,
当服务器响应缓慢时,请求会无限期挂起,阻塞聊天队列,
导致飞书机器人看起来卡住了(电脑在运行,但飞书不回复)。

修复:
- 为所有 HTTP 方法添加 30 秒超时(AbortController)
- createSession、listRecentProjects、getGitInfo、getTasksForSession
- 为图片下载的 fetch 调用添加超时

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 15:10:14 +08:00
程序员阿江(Relakkes)
23b31b397a Prevent local team sessions from dropping members and stalling adapters
This bundles the pending desktop/server team-session fixes with the local adapter recovery changes already in the worktree. The team path now keeps teammate membership stable under concurrent spawns, surfaces real teammate identities in the desktop UI, and allows direct interaction with member transcripts. The adapter changes recover automatically when stale thinking signatures invalidate an existing session.

Constraint: Team config writes can happen concurrently while multiple reviewers spawn in parallel

Constraint: Desktop member views must follow mailbox/transcript semantics rather than hijacking teammate runtime sessions

Rejected: Keep relying on config.json alone for member discovery | in-process teammates can be lost after concurrent writes

Rejected: Open teammate sessionIds as normal desktop sessions | would attach a second CLI instead of the running teammate

Confidence: medium

Scope-risk: moderate

Reversibility: clean

Directive: Preserve locked team-file mutation for any future teammate registration path and keep teammate labels sourced from member names before agent types

Tested: bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts

Tested: cd desktop && bun run test --run src/stores/chatStore.test.ts src/pages/ActiveSession.test.tsx

Tested: cd desktop && bun run lint

Tested: cd desktop && bun run build

Not-tested: Manual end-to-end validation against a live Agent Teams run in the desktop app
2026-04-14 17:27:07 +08:00
程序员阿江(Relakkes)
bfafd26fb6 fix: batch resolve 5 issues from Feishu bug tracker
- fix(desktop): Settings Agents/Skills page layout overflow at narrow widths
  Shift header grid breakpoint md→xl, summary cards sm→3col, add min-w-0/truncate

- fix(desktop): TabBar tab style — remove border-r separators, use bg-only active state
  Drop ::after indicator and inter-tab borders; active tab distinguished by surface bg

- feat(desktop): IM Adapters page — horizontal tabs with Feishu first, Telegram second
  Replace vertical stack with tablist; add ImTabButton with aria-selected + focus-visible

- fix(server): bundled IM session cwd resolves to / instead of selected project
  Root cause: preload.ts chdir(CALLER_DIR) inherits '/' from Tauri-spawned sidecar.
  Fix: pin CALLER_DIR and PWD to session workDir in conversationService spawn env.
  Add diagnostic logs at sessionService, ws/handler, and conversationService.

- fix(feishu): StreamingCard shows duplicate "thinking" indicators
  Merge static loading element into streaming_content; renderedText() controls state.

- test(adapters): add 5 ImageBlockWatcher edge-case unit tests (reset, relative path,
  multi-image chunk, malformed data URI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:36:14 +08:00
程序员阿江(Relakkes)
e338b2f7a3 fix(feishu): stream reasoning + tool steps live, drop them on finalize
Feishu cards used to sit on "正在思考中..." for the entire turn and
then dump everything at message_complete, while Telegram showed the
reasoning and tool calls progressively. Two underlying causes:

1. handleServerMessage ignored `thinking`, `content_start{tool_use}`
   and `tool_use_complete` entirely, so nothing was fed into the card
   between turns of text output.
2. StreamingCard.performFlush silently set `cardKitStreamActive=false`
   on any non-rate-limit error, after which all middle frames were
   skipped and only finalize touched the card — exactly the
   "long wait → all at once" symptom.

StreamingCard now tracks accumulatedReasoningText and a toolSteps
list, with appendReasoning / startTool / completeTool methods that
trigger throttled flushes like appendText. renderedText composes
tools → reasoning → answer in plain markdown (no blockquotes / lists
that historically tripped Feishu's parser). consecutiveStreamFailures
threshold (3) keeps a single transient error from killing the stream.

terminalText() is used by finalize so the final card holds only the
answer text, matching the Desktop UI's clean post-completion view.

handleServerMessage wires the new events to the existing per-chat
StreamingCard via the streamingCards map (read-only — never
auto-creates cards from thinking/tool events to avoid empty cards
for /clear-style commands).

Test coverage: realistic event-stream regression (thinking → tool_use
→ text → finalize), first-frame-failure recovery, finalize drops
intermediate state, plus per-method tests for the new APIs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 22:37:39 +08:00
程序员阿江(Relakkes)
207da0ad9b feat(feishu): integrate inbound/outbound attachments into StreamingCard architecture
Re-implements the inbound and outbound attachment paths on top of the
StreamingCard rewrite (ae586b0) instead of the legacy MessageBuffer
pipeline. Replaces the dangling extractText reference and ports the
behavior originally drafted on feat/im-attachments to the new structure:

Inbound (user → Claude):
- handleMessage now uses extractInboundPayload to detect text and any
  PendingDownloads (image/file/file_archive/post-embedded media)
- safeMessageId captured outside enqueue to keep TS narrowing happy
- All command branches gated on `!hasAttachments` so attachment-bearing
  messages bypass /new, /clear, /stop, /projects, etc.
- Per-message Promise.allSettled download via FeishuMediaService;
  per-attachment checkAttachmentLimit; partial-failure user hint
- AttachmentRef[] forwarded through bridge.sendUserMessage(...)
- effectiveText guard prevents empty content from being sent to Claude
  when all attachments are rejected and the user supplied no text

Outbound (Claude → user):
- content_delta handler feeds the streaming text into per-chat
  ImageBlockWatcher and dispatches each new PendingUpload via
  dispatchOutboundImage (fire-and-forget)
- dispatchOutboundImage handles all three source kinds (base64/path/url),
  applies the same checkAttachmentLimit, dedupes by fingerprint, and
  publishes the image as an independent im.message.create({msg_type:'image'})
  alongside the streaming card text
- Card-embedded `{tag:'img', img_key}` rendering is intentionally deferred
  to a follow-up PR — the MVP keeps streaming text and outbound images as
  separate messages

Lifecycle: clearTransientChatState and startNewSession now also delete
imageWatchers and uploadedImageKeys for the chat so a new session never
inherits stale fingerprints.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:17:09 +08:00
程序员阿江(Relakkes)
80eaa8cc1e fix(adapters): tsconfig-strict types for Dirent and Lark image upload
- attachment-store.ts: pin Dirent<string>[] and pass `encoding: 'utf8'`
  so newer @types/node doesn't infer Dirent<NonSharedBuffer>
- feishu/media.ts: drop Readable.from(buffer) and pass Buffer directly
  to im.image.create / im.file.create — Lark SDK already accepts Buffer
  and the stream wrapper trips the stricter Buffer | ReadStream union
  used on the main branch's tsconfig

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:14:28 +08:00
程序员阿江(Relakkes)
6bcbbc547f fix(adapters): drop HEIC mime, align Telegram error text, inline extractText 2026-04-11 21:09:00 +08:00
程序员阿江(Relakkes)
30b9370a43 docs(adapters): document IM attachment support
Lists inbound/outbound attachment coverage, local staging directory,
size limits, and the file/module additions from the im-attachments
feature branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:08:42 +08:00
程序员阿江(Relakkes)
b71538f53e feat(telegram): send agent-produced images via sendPhoto 2026-04-11 21:08:42 +08:00
程序员阿江(Relakkes)
eda97e5ad9 feat(telegram): accept photo/document/video/audio/voice and forward attachments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:08:42 +08:00
程序员阿江(Relakkes)
2e78d3f885 feat(telegram): add TelegramMediaService for download/upload
Implements TDD Task 9: wraps grammY bot.api.getFile/sendPhoto/sendDocument
with local attachment staging via AttachmentStore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:08:42 +08:00
程序员阿江(Relakkes)
61657d46d1 feat(feishu): extractInboundPayload recognizes image/file/post attachments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:08:36 +08:00
程序员阿江(Relakkes)
766e1e63ab feat(feishu): add FeishuMediaService for download/upload/send
Implements FeishuMediaService wrapping @larksuiteoapi/node-sdk im.messageResource,
im.image, im.file, and im.message APIs. SDK call signatures verified against
OpenClaw lark plugin reference implementation. 5 new tests, 172 total passing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:08:30 +08:00
程序员阿江(Relakkes)
ac89ec618c feat(adapters): add ImageBlockWatcher for streaming markdown image extraction 2026-04-11 21:08:30 +08:00
程序员阿江(Relakkes)
bf9b561cc7 fix(adapters): eliminate AttachmentStore collision race and orphan .part cleanup
Address two production issues in the AttachmentStore:
1. TOCTOU race in resolvePath() — concurrent downloads with same filename in
   the same millisecond could collide. Fixed by appending a 6-char random suffix
   to the timestamp-based path, ensuring millisecond-level collisions never occur.
2. Orphan .part files from crashed writes never cleaned up. Fixed by extending
   gc() to recognize .part files and clean them with a shorter grace period
   (10 minutes by default) rather than the normal retention window.

Added two test cases covering heavy collision pressure and .part cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:08:30 +08:00
程序员阿江(Relakkes)
ae586b0d9b feat(feishu): rewrite streaming to CardKit Schema 2.0 with proper markdown
Replace legacy im.message.patch streaming path with a Feishu CardKit
Schema 2.0 pipeline mirroring openclaw-lark's implementation. Desktop
now renders real formatted markdown (headings, tables, code blocks)
instead of literal text, and streaming output updates per chat without
cross-talk.

New modules:
- card-errors.ts: structured Lark SDK error parsing for code 230020
  (rate limit -> skip frame) and 230099/11310 (table limit -> disable
  CardKit streaming and fall back to settings+update at finalize)
- flush-controller.ts: mutex + needsReflush + long-gap batching
  throttler for per-chat flush coordination
- cardkit.ts: thin wrapper over client.cardkit.v1.* (card.create +
  im.message.{create,reply} + cardElement.content + card.settings +
  card.update), with monotonic sequence and CardKitApiError
- streaming-card.ts: per-chat StreamingCard state machine covering
  idle -> creating -> streaming -> finalizing -> completed/aborted,
  including patch fallback when CardKit create/send fails

index.ts rewrite:
- replaces MessageBuffer/chatStates/buffers maps with a single
  streamingCards Map<chatId, StreamingCard>
- content_start{text}/content_delta create or reuse the card; finalize
  runs on message_complete; abort renders a red error card
- /clear and other silent commands stay out of the streaming-card path
  so they don't leave empty cards
- all command handlers + normal chat are wrapped in the per-chat
  enqueue() serial queue, preventing reply reordering when commands
  are fired rapidly
- createSessionForChat() now first resets any stale WS session before
  calling connectSession, fixing /projects pick_project leaving the
  old session bound to the previous workDir
- normal messages pre-create the card before sendUserMessage so users
  see a "☁️ 正在思考中..." loading indicator immediately
- content_start{tool_use} no longer finalizes the current card, letting
  one user turn's full text stay in a single card

markdown-style.ts:
- default cardVersion changed from 1 to 2 (Schema 2.0)
- headings H2~H6 demoted to H5, H1 to H4, only when source has H1~H3
- Schema 2.0 <br> padding around headings, tables, code blocks
- stripInvalidImageKeys() drops non-img_* image references
- FEISHU_CARD_TABLE_LIMIT=3 + sanitizeTextForCard() wraps 4th+ table
  in a fenced code block to avoid 230099/11310

Tests: 182 pass, 0 fail, 383 expect() calls across 6 files. tsc clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:04:22 +08:00
程序员阿江(Relakkes)
413437ced0 feat(adapters): add AttachmentStore for IM download staging 2026-04-11 15:33:56 +08:00
程序员阿江(Relakkes)
e2ea346754 feat(adapters): add attachment type and limit check module 2026-04-11 15:29:25 +08:00
程序员阿江(Relakkes)
63e5fb3a41 feat(adapters): extend WsBridge.sendUserMessage to carry attachments 2026-04-11 15:25:57 +08:00
程序员阿江(Relakkes)
4ccda92cfe chore(adapters): snapshot pre-attachment WIP (permission card v2, handler serialization, markdown style)
Carries forward ~800 lines of in-flight work that existed in the working
tree before the IM-attachment branch was cut. Kept as a single isolated
commit so later attachment work has a clean diff baseline and so this
WIP can be cherry-picked back to main on its own merits.

Contents:
- ws-bridge: per-chat handler chain serialization; sendPermissionResponse
  now accepts optional `rule: 'always'` for persistent permits
- feishu/index.ts: permission card rewritten to Schema 2.0 with icon,
  cross-dir warning, and ♾️ 永久允许 button; optimizeMarkdownForFeishu
  applied to streaming card
- feishu/markdown-style.ts: new Feishu-specific markdown downgrader
- tests: corresponding bun:test coverage

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:24:10 +08:00
程序员阿江(Relakkes)
77974f7052 feat(feishu): add interactive project picker and fix bot flows
- Resolve bot open_id via /open-apis/bot/v3/info — the SDK's
  contact.user.get({user_id: 'me'}) path is invalid on Feishu and
  returned 99992351 on every startup.
- Stream replies via interactive cards instead of post messages:
  im.message.patch only accepts msg_type=interactive, so the old
  path-rich-text placeholder caused "This message is NOT a card"
  (230001) on every flush. Added buildStreamingCard helper.
- Add project picker card: one column_set row per project with
  [info (name + branch + path) | select button], Schema 2.0,
  mobile-friendly path display (~ substitution + middle-truncation),
  header subtitle with project count. pick_project actions route
  through the existing handleCardAction.

Card patterns verified against openclaw-lark's Schema 2.0 usage in
src/tools/oauth-cards.ts and src/card/builder.ts. 34 unit tests
cover the new card structure and action routing.
2026-04-11 14:25:55 +08:00
程序员阿江(Relakkes)
635a966c3e fix(desktop): unblock rollout with reliable session and IM flows
This folds together the desktop-side fixes needed before broader rollout.
Session resume no longer deadlocks waiting on init, Mermaid and inline image
output render inside chat, task and sub-agent state stay visible during
execution, local build/release paths are safer, and Feishu/Telegram now expose
lightweight mobile commands (/help, /status, /clear) without adding a new
adapter-specific protocol.

Constraint: Desktop releases must publish updater artifacts from non-draft GitHub releases
Constraint: IM commands need short, phone-friendly responses and low operational complexity
Rejected: Add a dedicated IM command API surface | re-used existing slash commands and session/task REST endpoints to keep adapters thin
Rejected: Wait for task_update push events in WebUI | added low-risk polling because the current frontend ignores that event path
Confidence: medium
Scope-risk: broad
Reversibility: clean
Directive: Keep IM command replies terse and mobile-first, and merge local fallback slash commands when server-provided lists are partial
Tested: cd desktop && bun x vitest run src/components/chat/MermaidRenderer.test.tsx src/components/markdown/MarkdownRenderer.test.tsx
Tested: cd desktop && bun x vitest run src/components/chat/composerUtils.test.ts src/pages/ActiveSession.test.tsx src/stores/chatStore.test.ts
Tested: cd desktop && bun run lint
Tested: bun test src/server/__tests__/conversations.test.ts --test-name-pattern "SDK init arrives only after the first user turn" --timeout 60000
Tested: cd adapters && bun test common/ feishu/ telegram/
Tested: cd adapters && bunx tsc --noEmit
Not-tested: Full GitHub Actions release run on all three desktop platforms
Not-tested: Local DMG packaging end-to-end on Apple Silicon
Not-tested: Real Feishu/Telegram device sessions against a live adapter process
2026-04-10 16:41:59 +08:00
程序员阿江(Relakkes)
41fc58d73a feat(adapters): add /new <project> quick switch and unify project selection
- Add matchProject() to http-client for fuzzy project matching by
  index or name
- Extract startNewSession() in both adapters to share reset + match +
  create logic
- Project selection replies now go through startNewSession(), creating
  a clean new session instead of continuing the old one
- Support: /new 2 (by index), /new backend (by name), /new (default)
- Add usage hint in /projects list output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:25:23 +08:00
程序员阿江(Relakkes)
b4ad6f15de fix(adapters): ensure placeholder cleanup on message_complete
When all buffered text has already been flushed via periodic timer,
buf.complete() calls flush(true) which returns early on empty buffer,
skipping the onFlush callback that cleans up placeholder state.
This causes subsequent messages to keep editing the old completed
message instead of creating a new one.

Add explicit placeholder cleanup in message_complete handler as
a safety net, matching the same pattern used for tool_use finalization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:52:00 +08:00
程序员阿江(Relakkes)
824b1967ff fix(adapters): fix IM message delivery issues and improve UX
- Fix WebSocket race condition: wait for connection to open before
  sending first message, preventing silent message loss after pairing
  or project selection
- Fix response text appearing above tool calls: finalize placeholder
  before tool_use blocks so post-tool text gets a new message
- Remove noisy tool_use and tool_result messages from IM output;
  thinking indicator is preserved, details visible in Desktop
- Fix /new command using default project dir instead of always showing
  project picker
- Fix duplicate projects in list by deduping on realPath instead of
  projectPath
- Improve formatToolUse with human-readable summaries for common tools
- Add send failure feedback to users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:40:07 +08:00
程序员阿江(Relakkes)
f2fa6fc56d chore: gitignore .claude and docs/superpowers, clean up temp files
Remove .claude/ and docs/superpowers/ from git tracking as they are
local-only config and planning files. Also remove stale screenshots
and update docs/channel content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:50:43 +08:00
程序员阿江(Relakkes)
158f2de13e fix(security): add rate limiting, default-closed mode, extract isAllowedUser
- Rate limit: max 5 failed pairing attempts per user per 5 minutes
- Default closed: reject all users when no allowedUsers/pairedUsers configured
- Extract isAllowedUser() to common/pairing.ts to eliminate duplication

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:51:30 +08:00
程序员阿江(Relakkes)
d9905c78fd feat(pairing): integrate pairing check into telegram adapter - private chat only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:09:11 +08:00
程序员阿江(Relakkes)
74cf078ed3 feat(pairing): integrate pairing check into feishu adapter - DM only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:08:52 +08:00
程序员阿江(Relakkes)
4bcac9d25f feat(pairing): implement core pairing logic module
Add adapters/common/pairing.ts with generatePairingCode(), isPaired(),
and tryPair() — one-time-use 6-char code flow with atomic config writes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 20:07:44 +08:00
程序员阿江(Relakkes)
88e7e14598 feat(pairing): extend type definitions with pairing and pairedUsers fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:06:37 +08:00
程序员阿江(Relakkes)
82e6e27687 feat: add IM adapter integration (Telegram + Feishu) with web settings UI
Implement IM adapters allowing users to chat with Claude Code from Telegram
and Feishu/Lark. Includes persistent session management (chatId→sessionId
mapping), project selection via /projects command, and a web UI settings page
for configuring bot tokens, allowed users, and default project directory.

Key changes:
- adapters/: Telegram and Feishu adapter scripts with shared common modules
  (WsBridge, MessageBuffer, SessionStore, HttpClient, config, formatting)
- Backend: adapterService + REST API (GET/PUT /api/adapters) with secret masking
- Frontend: AdapterSettings page in Settings tab with i18n support
- DirectoryPicker: use React Portal for dropdown to fix overflow clipping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:38:51 +08:00