v0.9.0 — Claude Agent SDK runtime uplift, Lark / Feishu messaging, Telegram supergroup topics
Features
-
Claude Agent SDK uplifted to the native-binary distribution (0.2.123) — SDK 0.2.113 split the package into a thin core (
@anthropic-ai/claude-agent-sdk) plus a per-platform optional dependency carrying the nativeclaudebinary. Craft Agent now resolves the binary via the build-script alias@anthropic-ai/claude-agent-sdk-binary/claude(with a per-arch package fallback in dev mode), and ripgrep moves from the deprecated SDK-bundled vendor copy to a top-level@vscode/ripgrepdep.electron-builder.ymlnow bundles the thin core, the binary alias, and@vscode/ripgrepas extraResources on macOS, Linux, and Windows; the build scripts cross-fetch the target arch's binary package vianpm packwhen the host arch differs. Bundle size grows ~210 MB per platform — intrinsic to the new SDK packaging model. (9ae87dc1) -
Lark / Feishu messaging adapter (Phase 1 + Phase 2) — A third messaging platform alongside Telegram and WhatsApp. Connect a Lark Custom App via the Open Platform —
open.larksuite.com(international) oropen.feishu.cn(China) — through the same/pairflow as Telegram, with no public webhook URL required (long-connection WebSocket transport). Phase 1 covers App ID + App Secret + region selector in Settings → Messaging → Lark / Feishu, text in/out for DMs, @mentions in groups, and session pairing. Phase 2 adds live message editing (single message that streams), interactive cards for plan accept / Allow / Deny buttons (schema 2.0, max 10 buttons, 30-char labels), bidirectional image and file attachments, and Markdown rendering via Lark'spostmessage type for bold/italic/strikethrough/links/code blocks (plain replies stay on the lightertexttype). 24 new i18n keys per locale across all 7 supported locales. Closes #583, partially addresses #639 and #584. (5443aa41,e56862e4) -
Telegram supergroup pairing and per-automation topic routing — Pair a Telegram supergroup to a workspace from Settings → Messaging → Telegram → Pair Supergroup; the bot captures the supergroup's
chat.idafter you type/pair <code>in any topic. Pairing requireschat.type === 'supergroup'ANDis_forum === trueso DMs, basic groups, and regular supergroups can't be registered as the workspace supergroup. Bound bindings render asGroup › Topic #Nin Settings andlist_messaging_channels. Add the optionaltelegramTopicfield to any automation matcher inautomations.jsonand the spawned session is bound to a forum topic of that name (created on first use, reused thereafter; matchers sharing a value share one topic). Env-var expansion is supported on the topic value (e.g."Reports: $LABEL"). (77e1f5f1,dd22996e){ "matcher": "^urgent$", "telegramTopic": "Urgent Alerts", "actions": [ { "type": "prompt", "prompt": "Investigate the urgent issue: $LABEL" } ] } -
Group by Unread mode in All Sessions view — A third grouping option alongside Group by Date and Group by Status splits the session list into two fixed buckets: Unread (N) on top, Read (N) below, sorted by
lastMessageAtwithin each. Both buckets always render even when empty, so the active mode is always visible; empty groups suppress the collapse caret. The mode is disabled in state sub-views (Inbox, Flagged, etc.) where status filtering already constrains the list — those fall back to Group by Date. Collapse state is per-mode, so collapsing the Read group in Unread mode doesn't bleed into other modes. (59b826a2) -
Telegram messaging settings rework promoted to production — The Settings → Messaging → Telegram card now splits bindings into a Direct session row (DM-paired) and a Supergroup section (collapsible chevron when paired, "Pair Supergroup" CTA when not), mirroring the
WorkspaceOverrideCardpattern from AI Settings. Topic-bound bindings render asTitle / # TopicName · Topic #Nrows. WhatsApp and Lark cards keep the existing flat layout — they have no supergroup/topic concept. (312016a3,7e235f8e)
Improvements
- Resilient session message loading with a retry control — Session message loading had two failure modes that left users staring at infinite spinners: stale
loadedflags after metadata refresh, and lost transport replies. A newreplaceLoadedSessionAtomperforms authoritative full-session updates that mark the session as loaded and update metadata in one transaction, eliminating the previousupdateSessionDirect + sessionMetaMapAtom.setdivergence risk. A newderiveSessionMessagesLoadStatehelper distinguishes in-memory ready vs stale loaded-flag, known-empty vs not-yet-loaded, and load failures vs still loading — used byChatDisplayto surface a Retry control instead of spinning forever when a transport hiccup loses the load reply. The loadingAnimatePresenceswitches tomode="sync"so a stale loading exit can't briefly mask ready content during recovery. (d8262fab) bunfig.tomllinker pinned tohoisted— Bun 1.3 changed the default toisolated, but Vite + esbuild bundling in this monorepo expects the hoisted layout (i18next, croner, pdfjs-dist, @tiptap/* must resolve at top-levelnode_modules). The Windows build script was already using--linker=hoistedfor the same reason; codifying it inbunfig.tomlremoves the platform-specific divergence. (9ae87dc1)- Docker image now copies
bunfig.toml— Without it the container was using Bun's defaultisolatedlinker, which broke the Vite build. (cbed3061,1e85b5a2) webuiruntime npm deps and workspace deps now declared explicitly —apps/webuiwas implicitly inheriting deps via the hoistednode_moduleslayout. Declaring@craft-agent/{shared,ui}as workspace deps and listing every runtime npm package the source actually imports keeps the package self-contained and Docker builds reproducible. (7fd8177f,9c9eb073)
Bug Fixes
- Loopback custom-endpoint API keys are now sent on requests — Custom OpenAI-compatible endpoints configured against a loopback host (
localhost,127.0.0.1) had the API key silently stripped from outgoing requests, returning 401 from any local proxy that required auth. The key now flows through unchanged. Fixes #636. (62c65169) - Vision overrides on custom endpoints are preserved end-to-end — Custom endpoint model capabilities can now keep explicit per-model
supportsImages: true/supportsImages: falseoverrides through scrolling and prompt changes. In particular,supportsImages: falseremains available to override a global endpoint image default. (81259d0b) - Mermaid validation aligned with the renderer —
mermaid_validateand the in-app diagram renderer use the same parser configuration, so a diagram that validates clean now actually renders, and validation errors mirror the renderer's error reporting. (8b39c84c) - Stale reconnect no longer leaves the session list unrefreshed — After a reconnect, the session list could remain bound to pre-disconnect state until the user manually refreshed. The list now refreshes authoritatively on reconnect. (
3a2f7b6b) - Lark interactive cards render reliably — A series of fixes to the new Lark adapter: the card builder's schema-2.0 wrapper (
elementsunderbodyinstead of top-level), button schema (behaviors[]instead of legacyactionwrapper), error extraction from axiosresponse.data(so Lark's error code/message surface in logs instead of opaque axios fields), and WS lifecycle + event-arrival diagnostic logging. The flattened-event-payload code path from the SDK dispatcher is now handled. Plan submission, Allow/Deny buttons, and Accept Plan buttons all work end-to-end. (d47b6648,8cc5fb3f,23576f15,d7126b80,1273a1d0,b958ae4f) - Lark connect dialog auto-dismisses on bind — After a successful pair, the connect dialog now auto-dismisses and shows confirmation copy, matching the Telegram flow. The session-menu connect submenu now lists Lark / Feishu alongside Telegram and WhatsApp. (
ef8884f5,6e3455f7) - WebUI OAuth popups work on iOS Safari — iOS Safari's strict popup-blocker semantics required the OAuth popup to be opened synchronously inside the user-gesture handler. The flow now reuses a pre-opened window and navigates it after the auth URL is computed, instead of opening from inside an async callback. (
5e0e7d1d)
Breaking Changes
- None for users. The Claude SDK runtime change is internal — no config changes required. Custom endpoint configs, automations, and messaging bindings continue to work unchanged.
Notes
- Workspaces upgrading from 0.8.13 will see the bundled app size grow by ~210 MB per platform on next install — this is intrinsic to the new Claude Agent SDK packaging (the native
claudebinary now ships per-platform instead of acli.jsscript). No action required. - The full Lark setup walkthrough lives at docs.craft.do/messaging/lark. When configuring the Custom App, leave the Encrypt Key field blank — long-connection mode doesn't use webhook payload encryption, and filling it in silently breaks inbound events.