diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2bc3ebae3..4edf42b68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,21 @@
Docs: https://docs.clawd.bot
+## 2026.1.25
+
+### Changes
+- TBD.
+
+## 2026.1.24-2
+
+### Fixes
+- Packaging: include dist/link-understanding output in npm tarball (fixes missing apply.js import on install).
+
+## 2026.1.24-1
+
+### Fixes
+- Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).
+
## 2026.1.24
### Highlights
@@ -38,6 +53,7 @@ Docs: https://docs.clawd.bot
- Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.
- BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.clawd.bot/channels/bluebubbles
- BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
+- iMessage: normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively and keep service-prefixed handles stable. (#1708) Thanks @aaronn.
- Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev.
- Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.clawd.bot/channels/signal
- Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338.
diff --git a/README.md b/README.md
index 1329c5e2b..ebbdc43d5 100644
--- a/README.md
+++ b/README.md
@@ -477,32 +477,33 @@ Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and
Thanks to all clawtributors:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/appcast.xml b/appcast.xml
index a589863ae..8158ac244 100644
--- a/appcast.xml
+++ b/appcast.xml
@@ -2,6 +2,101 @@
Clawdbot
+ -
+
2026.1.24-1
+ Sun, 25 Jan 2026 14:05:25 +0000
+ https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
+ 7952
+ 2026.1.24-1
+ 15.0
+ Clawdbot 2026.1.24-1
+Fixes
+
+Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).
+
+View full changelog
+]]>
+
+
+ -
+
2026.1.24
+ Sun, 25 Jan 2026 13:31:05 +0000
+ https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
+ 7944
+ 2026.1.24
+ 15.0
+ Clawdbot 2026.1.24
+Highlights
+
+Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.clawd.bot/providers/ollama https://docs.clawd.bot/providers/venice
+Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg.
+TTS: Edge fallback (keyless) + /tts auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.clawd.bot/tts
+Exec approvals: approve in-chat via /approve across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands
+Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.clawd.bot/channels/telegram
+
+Changes
+
+Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg.
+TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.clawd.bot/tts
+TTS: add auto mode enum (off/always/inbound/tagged) with per-session /tts override. (#1667) Thanks @sebslight. https://docs.clawd.bot/tts
+Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal.
+Telegram: add channels.telegram.linkPreview to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.clawd.bot/channels/telegram
+Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.clawd.bot/tools/web
+UI: refresh Control UI dashboard design system (typography, colors, spacing). (#1786) Thanks @mousberg.
+Exec approvals: forward approval prompts to chat with /approve for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/slash-commands
+Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg.
+Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.clawd.bot/diagnostics/flags
+Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround).
+Docs: add verbose installer troubleshooting guidance.
+Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua.
+Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.clawd.bot/bedrock
+Docs: update Fly.io guide notes.
+Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido.
+
+Fixes
+
+Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589.
+Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.
+Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.
+Web UI: hide internal message_id hints in chat bubbles.
+Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (gateway.controlUi.allowInsecureAuth). (#1679) Thanks @steipete.
+Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.
+BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.clawd.bot/channels/bluebubbles
+BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.
+Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev.
+Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.clawd.bot/channels/signal
+Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338.
+Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)
+Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt.
+Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken.
+Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634)
+Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido.
+Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy.
+Google Chat: normalize space targets without double spaces/ prefix.
+Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz.
+Agents: use the active auth profile for auto-compaction recovery.
+Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204.
+Models: default missing custom provider fields so minimal configs are accepted.
+Messaging: keep newline chunking safe for fenced markdown blocks across channels.
+TUI: reload history after gateway reconnect to restore session state. (#1663)
+Heartbeat: normalize target identifiers for consistent routing.
+Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco.
+Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19.
+Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep.
+Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671)
+Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b.
+Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690)
+Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work.
+Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676)
+macOS: default direct-transport ws:// URLs to port 18789; document gateway.remote.transport. (#1603) Thanks @ngutman.
+Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal.
+Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal.
+Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal.
+
+View full changelog
+]]>
+
+
-
2026.1.23
Sat, 24 Jan 2026 13:02:18 +0000
@@ -89,127 +184,5 @@
]]>
- -
-
2026.1.22
- Fri, 23 Jan 2026 08:58:14 +0000
- https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
- 7530
- 2026.1.22
- 15.0
- Clawdbot 2026.1.22
-Changes
-
-Highlight: Compaction safeguard now uses adaptive chunking, progressive fallback, and UI status + retries. (#1466) Thanks @dlauer.
-Providers: add Antigravity usage tracking to status output. (#1490) Thanks @patelhiren.
-Slack: add chat-type reply threading overrides via replyToModeByChatType. (#1442) Thanks @stefangalescu.
-BlueBubbles: add asVoice support for MP3/CAF voice memos in sendAttachment. (#1477, #1482) Thanks @Nicell.
-Onboarding: add hatch choice (TUI/Web/Later), token explainer, background dashboard seed on macOS, and showcase link.
-
-Fixes
-
-BlueBubbles: stop typing indicator on idle/no-reply. (#1439) Thanks @Nicell.
-Message tool: keep path/filePath as-is for send; hydrate buffers only for sendAttachment. (#1444) Thanks @hopyky.
-Auto-reply: only report a model switch when session state is available. (#1465) Thanks @robbyczgw-cla.
-Control UI: resolve local avatar URLs with basePath across injection + identity RPC. (#1457) Thanks @dlauer.
-Agents: sanitize assistant history text to strip tool-call markers. (#1456) Thanks @zerone0x.
-Discord: clarify Message Content Intent onboarding hint. (#1487) Thanks @kyleok.
-Gateway: stop the service before uninstalling and fail if it remains loaded.
-Agents: surface concrete API error details instead of generic AI service errors.
-Exec: fall back to non-PTY when PTY spawn fails (EBADF). (#1484)
-Exec approvals: allow per-segment allowlists for chained shell commands on gateway + node hosts. (#1458) Thanks @czekaj.
-Agents: make OpenAI sessions image-sanitize-only; gate tool-id/repair sanitization by provider.
-Doctor: honor CLAWDBOT_GATEWAY_TOKEN for auth checks and security audit token reuse. (#1448) Thanks @azade-c.
-Agents: make tool summaries more readable and only show optional params when set.
-Agents: honor SOUL.md guidance even when the file is nested or path-qualified. (#1434) Thanks @neooriginal.
-Matrix (plugin): persist m.direct for resolved DMs and harden room fallback. (#1436, #1486) Thanks @sibbl.
-CLI: prefer ~ for home paths in output.
-Mattermost (plugin): enforce pairing/allowlist gating, keep @username targets, and clarify plugin-only docs. (#1428) Thanks @damoahdominic.
-Agents: centralize transcript sanitization in the runner; keep tags and error turns intact.
-Auth: skip auth profiles in cooldown during initial selection and rotation. (#1316) Thanks @odrobnik.
-Agents/TUI: honor user-pinned auth profiles during cooldown and preserve search picker ranking. (#1432) Thanks @tobiasbischoff.
-Docs: fix gog auth services example to include docs scope. (#1454) Thanks @zerone0x.
-Slack: reduce WebClient retries to avoid duplicate sends. (#1481)
-Slack: read thread replies for message reads when threadId is provided (replies-only). (#1450) Thanks @rodrigouroz.
-macOS: prefer linked channels in gateway summary to avoid false “not linked” status.
-macOS/tests: fix gateway summary lookup after guard unwrap; prevent browser opens during tests. (ECID-1483)
-
-View full changelog
-]]>
-
-
- -
-
2026.1.21
- Thu, 22 Jan 2026 12:22:35 +0000
- https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
- 7374
- 2026.1.21
- 15.0
- Clawdbot 2026.1.21
-Highlights
-
-Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster
-Custom assistant identity + avatars in the Control UI. https://docs.clawd.bot/cli/agents https://docs.clawd.bot/web/control-ui
-Cache optimizations: cache-ttl pruning + defaults reduce token spend on cold requests. https://docs.clawd.bot/concepts/session-pruning
-Exec approvals + elevated ask/full modes. https://docs.clawd.bot/tools/exec-approvals https://docs.clawd.bot/tools/elevated
-Signal typing/read receipts + MSTeams attachments. https://docs.clawd.bot/channels/signal https://docs.clawd.bot/channels/msteams
-/models UX refresh + clawdbot update wizard. https://docs.clawd.bot/cli/models https://docs.clawd.bot/cli/update
-
-Changes
-
-Highlight: Lobster optional plugin tool for typed workflows + approval gates. https://docs.clawd.bot/tools/lobster (#1152) Thanks @vignesh07.
-Agents/UI: add identity avatar config support and Control UI avatar rendering. (#1329, #1424) Thanks @dlauer. https://docs.clawd.bot/gateway/configuration https://docs.clawd.bot/cli/agents
-Control UI: add custom assistant identity support and per-session identity display. (#1420) Thanks @robbyczgw-cla. https://docs.clawd.bot/web/control-ui
-CLI: add clawdbot update wizard with interactive channel selection + restart prompts, plus preflight checks before rebasing. https://docs.clawd.bot/cli/update
-Models/Commands: add /models, improve /model listing UX, and expand clawdbot models paging. (#1398) Thanks @vignesh07. https://docs.clawd.bot/cli/models
-CLI: move gateway service commands under clawdbot gateway, flatten node service commands under clawdbot node, and add gateway probe for reachability. https://docs.clawd.bot/cli/gateway https://docs.clawd.bot/cli/node
-Exec: add elevated ask/full modes, tighten allowlist gating, and render approvals tables on write. https://docs.clawd.bot/tools/elevated https://docs.clawd.bot/tools/exec-approvals
-Exec approvals: default to local host, add gateway/node targeting + target details, support wildcard agent allowlists, and tighten allowlist parsing/safe bins. https://docs.clawd.bot/cli/approvals https://docs.clawd.bot/tools/exec-approvals
-Heartbeat: allow explicit session keys and active hours. (#1256) Thanks @zknicker. https://docs.clawd.bot/gateway/heartbeat
-Sessions: add per-channel idle durations via sessions.channelIdleMinutes. (#1353) Thanks @cash-echo-bot.
-Nodes: run exec-style, expose PATH in status/describe, and bootstrap PATH for node-host execution. https://docs.clawd.bot/cli/node
-Cache: add cache.ttlPrune mode and auth-aware defaults for cache TTL behavior.
-Queue: add per-channel debounce overrides for auto-reply. https://docs.clawd.bot/concepts/queue
-Discord: add wildcard channel config support. (#1334) Thanks @pvoo. https://docs.clawd.bot/channels/discord
-Signal: add typing indicators and DM read receipts via signal-cli. https://docs.clawd.bot/channels/signal
-MSTeams: add file uploads, adaptive cards, and attachment handling improvements. (#1410) Thanks @Evizero. https://docs.clawd.bot/channels/msteams
-Onboarding: remove the run setup-token auth option (paste setup-token or reuse CLI creds instead).
-macOS: refresh Settings (location access in Permissions, connection mode in menu, remove CLI install UI).
-Diagnostics: add cache trace config for debugging. (#1370) Thanks @parubets.
-Docs: Lobster guides + org URL updates, /model allowlist troubleshooting, Gmail message search examples, gateway.mode troubleshooting, prompt injection guidance, npm prefix/node CLI notes, control UI dev gatewayUrl note, tool_use FAQ, showcase video, and sharp/node-gyp workaround. (#1427, #1220, #1405) Thanks @vignesh07, @mbelinky.
-
-Breaking
-
-BREAKING: Control UI now rejects insecure HTTP without device identity by default. Use HTTPS (Tailscale Serve) or set gateway.controlUi.allowInsecureAuth: true to allow token-only auth. https://docs.clawd.bot/web/control-ui#insecure-http
-BREAKING: Envelope and system event timestamps now default to host-local time (was UTC) so agents don’t have to constantly convert.
-
-Fixes
-
-Streaming/Typing/Media: keep reply tags across streamed chunks, start typing indicators at run start, and accept MEDIA paths with spaces/tilde while preferring the message tool hint for image replies.
-Agents/Providers: drop unsigned thinking blocks for Claude models (Google Antigravity) and enforce alphanumeric tool call ids for strict providers (Mistral/OpenRouter). (#1372) Thanks @zerone0x.
-Exec approvals: treat main as the default agent, align node/gateway allowlist prechecks, validate resolved paths, avoid allowlist resolve races, and avoid null optional params. (#1417, #1414, #1425) Thanks @czekaj.
-Exec/Windows: resolve Windows exec paths with extensions and handle safe-bin exe names.
-Nodes/macOS: prompt on allowlist miss for node exec approvals, persist allowlist decisions, and flatten node invoke errors. (#1394) Thanks @ngutman.
-Gateway: prevent multiple gateways from sharing the same config/state (singleton lock), keep auto bind loopback-first with explicit tailnet binding, and improve SSH auth handling. (#1380)
-Control UI: remove the chat stop button, keep the composer aligned to the bottom edge, stabilize session previews, and refresh the debug panel on route-driven tab changes. (#1373) Thanks @yazinsai.
-UI/config: export SECTION_META for config form modules. (#1418) Thanks @MaudeBot.
-macOS: keep chat pinned during streaming replies, include Textual resources, respect wildcard exec approvals, allow SSH agent auth, and default distribution builds to universal binaries. (#1279, #1362, #1384, #1396) Thanks @ameno-, @JustYannicc.
-BlueBubbles: resolve short message IDs safely, expose full IDs in templates, and harden short-id fetch wrappers. (#1369, #1387) Thanks @tyler6204.
-Models/Configure: inherit session model overrides in threads/topics, map OpenCode Zen models to the correct APIs, narrow Anthropic OAuth allowlist handling, seed allowlist fallbacks, list the full catalog when no allowlist is set, and limit /model list output. (#1376, #1416)
-Memory: prevent CLI hangs by deferring vector probes, add sqlite-vec/embedding timeouts, and make session memory indexing async.
-Cron: cap reminder context history to 10 messages and honor contextMessages. (#1103) Thanks @mkbehr.
-Cache: restore the 1h cache TTL option and reset the pruning window.
-Zalo Personal: tolerate ANSI/log-prefixed JSON output from zca. (#1379) Thanks @ptn1411.
-Browser: suppress Chrome restore prompts for managed profiles. (#1419) Thanks @jamesgroat.
-Infra: preserve fetch helper methods/preconnect when wrapping abort signals and normalize Telegram fetch aborts.
-Config/Doctor: avoid stack traces for invalid configs, log the config path, avoid WhatsApp config resurrection, and warn when gateway.mode is unset. (#900)
-CLI: read Codex CLI account_id for workspace billing. (#1422) Thanks @aj47.
-Logs/Status: align rolling log filenames with local time and report sandboxed runtime in clawdbot status. (#1343)
-Embedded runner: persist injected history images so attachments aren’t reloaded each turn. (#1374) Thanks @Nicell.
-Nodes/Subagents: include agent/node/gateway context in tool failure logs and ensure subagent list uses the command session.
-
-View full changelog
-]]>
-
-
-
+
\ No newline at end of file
diff --git a/docs/platforms/mac/release.md b/docs/platforms/mac/release.md
index 8015ffe2e..7f37951cb 100644
--- a/docs/platforms/mac/release.md
+++ b/docs/platforms/mac/release.md
@@ -30,17 +30,17 @@ Notes:
# From repo root; set release IDs so Sparkle feed is enabled.
# APP_BUILD must be numeric + monotonic for Sparkle compare.
BUNDLE_ID=com.clawdbot.mac \
-APP_VERSION=2026.1.24 \
+APP_VERSION=2026.1.24-1 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: ()" \
scripts/package-mac-app.sh
# Zip for distribution (includes resource forks for Sparkle delta support)
-ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.24.zip
+ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.24-1.zip
# Optional: also build a styled DMG for humans (drag to /Applications)
-scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24.dmg
+scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24-1.dmg
# Recommended: build + notarize/staple zip + DMG
# First, create a keychain profile once:
@@ -48,26 +48,26 @@ scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24.dmg
# --apple-id "" --team-id "" --password ""
NOTARIZE=1 NOTARYTOOL_PROFILE=clawdbot-notary \
BUNDLE_ID=com.clawdbot.mac \
-APP_VERSION=2026.1.24 \
+APP_VERSION=2026.1.24-1 \
APP_BUILD="$(git rev-list --count HEAD)" \
BUILD_CONFIG=release \
SIGN_IDENTITY="Developer ID Application: ()" \
scripts/package-mac-dist.sh
# Optional: ship dSYM alongside the release
-ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.24.dSYM.zip
+ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.24-1.dSYM.zip
```
## Appcast entry
Use the release note generator so Sparkle renders formatted HTML notes:
```bash
-SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.24.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
+SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Clawdbot-2026.1.24-1.zip https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
```
Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/clawdbot/clawdbot/blob/main/scripts/changelog-to-html.sh)) and embeds them in the appcast entry.
Commit the updated `appcast.xml` alongside the release assets (zip + dSYM) when publishing.
## Publish & verify
-- Upload `Clawdbot-2026.1.24.zip` (and `Clawdbot-2026.1.24.dSYM.zip`) to the GitHub release for tag `v2026.1.24`.
+- Upload `Clawdbot-2026.1.24-1.zip` (and `Clawdbot-2026.1.24-1.dSYM.zip`) to the GitHub release for tag `v2026.1.24-1`.
- Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml`.
- Sanity checks:
- `curl -I https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml` returns 200.
diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md
index 070abb1c3..6492bd469 100644
--- a/docs/reference/RELEASING.md
+++ b/docs/reference/RELEASING.md
@@ -17,7 +17,7 @@ When the operator says “release”, immediately do this preflight (no extra qu
- Use Sparkle keys from `~/Library/CloudStorage/Dropbox/Backup/Sparkle` if needed.
1) **Version & metadata**
-- [ ] Bump `package.json` version (e.g., `1.1.0`).
+- [ ] Bump `package.json` version (e.g., `2026.1.24`).
- [ ] Run `pnpm plugins:sync` to align extension package versions + changelogs.
- [ ] Update CLI/version strings: [`src/cli/program.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/cli/program.ts) and the Baileys user agent in [`src/provider-web.ts`](https://github.com/clawdbot/clawdbot/blob/main/src/provider-web.ts).
- [ ] Confirm package metadata (name, description, repository, keywords, license) and `bin` map points to [`dist/entry.js`](https://github.com/clawdbot/clawdbot/blob/main/dist/entry.js) for `clawdbot`.
diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json
index 4385272be..925b05bc1 100644
--- a/extensions/bluebubbles/package.json
+++ b/extensions/bluebubbles/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/bluebubbles",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot BlueBubbles channel plugin",
"clawdbot": {
diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json
index 02d1cdbdd..792a94225 100644
--- a/extensions/copilot-proxy/package.json
+++ b/extensions/copilot-proxy/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/copilot-proxy",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Copilot Proxy provider plugin",
"clawdbot": {
diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json
index 407ce60d1..2afc99e2e 100644
--- a/extensions/diagnostics-otel/package.json
+++ b/extensions/diagnostics-otel/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/diagnostics-otel",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot diagnostics OpenTelemetry exporter",
"clawdbot": {
diff --git a/extensions/discord/package.json b/extensions/discord/package.json
index 0a645718b..dae5fe1f1 100644
--- a/extensions/discord/package.json
+++ b/extensions/discord/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/discord",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Discord channel plugin",
"clawdbot": {
diff --git a/extensions/google-antigravity-auth/package.json b/extensions/google-antigravity-auth/package.json
index ff3c485f2..96bffde7c 100644
--- a/extensions/google-antigravity-auth/package.json
+++ b/extensions/google-antigravity-auth/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/google-antigravity-auth",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Google Antigravity OAuth provider plugin",
"clawdbot": {
diff --git a/extensions/google-gemini-cli-auth/package.json b/extensions/google-gemini-cli-auth/package.json
index f4b666ab0..dc8a894d7 100644
--- a/extensions/google-gemini-cli-auth/package.json
+++ b/extensions/google-gemini-cli-auth/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/google-gemini-cli-auth",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Gemini CLI OAuth provider plugin",
"clawdbot": {
diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json
index cf73b6795..056bdedb6 100644
--- a/extensions/googlechat/package.json
+++ b/extensions/googlechat/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/googlechat",
- "version": "2026.1.22",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Google Chat channel plugin",
"clawdbot": {
@@ -34,6 +34,6 @@
"clawdbot": "workspace:*"
},
"peerDependencies": {
- "clawdbot": ">=2026.1.24-0"
+ "clawdbot": ">=2026.1.24"
}
}
diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json
index a3ac1c642..79aa7890d 100644
--- a/extensions/imessage/package.json
+++ b/extensions/imessage/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/imessage",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot iMessage channel plugin",
"clawdbot": {
diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts
index 50615cd22..556c2970a 100644
--- a/extensions/imessage/src/channel.ts
+++ b/extensions/imessage/src/channel.ts
@@ -8,8 +8,10 @@ import {
imessageOnboardingAdapter,
IMessageConfigSchema,
listIMessageAccountIds,
+ looksLikeIMessageTargetId,
migrateBaseNameToDefaultAccount,
normalizeAccountId,
+ normalizeIMessageMessagingTarget,
PAIRING_APPROVED_MESSAGE,
resolveChannelMediaMaxBytes,
resolveDefaultIMessageAccountId,
@@ -110,14 +112,9 @@ export const imessagePlugin: ChannelPlugin = {
resolveToolPolicy: resolveIMessageGroupToolPolicy,
},
messaging: {
+ normalizeTarget: normalizeIMessageMessagingTarget,
targetResolver: {
- looksLikeId: (raw) => {
- const trimmed = raw.trim();
- if (!trimmed) return false;
- if (/^(imessage:|chat_id:)/i.test(trimmed)) return true;
- if (trimmed.includes("@")) return true;
- return /^\+?\d{3,}$/.test(trimmed);
- },
+ looksLikeId: looksLikeIMessageTargetId,
hint: "",
},
},
diff --git a/extensions/line/package.json b/extensions/line/package.json
index b58b2eb4d..b518f5ca5 100644
--- a/extensions/line/package.json
+++ b/extensions/line/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/line",
- "version": "2026.1.22",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot LINE channel plugin",
"clawdbot": {
diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json
index e27384d9e..a03344d1a 100644
--- a/extensions/llm-task/package.json
+++ b/extensions/llm-task/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/llm-task",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot JSON-only LLM task plugin",
"clawdbot": {
diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json
index ea774ecba..3926b553b 100644
--- a/extensions/lobster/package.json
+++ b/extensions/lobster/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/lobster",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)",
"clawdbot": {
diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json
index edf64c999..24529ee97 100644
--- a/extensions/matrix/package.json
+++ b/extensions/matrix/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/matrix",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Matrix channel plugin",
"clawdbot": {
diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json
index 251fe7b0b..77d799c34 100644
--- a/extensions/mattermost/package.json
+++ b/extensions/mattermost/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/mattermost",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Mattermost channel plugin",
"clawdbot": {
diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json
index 2dd09751b..c70c2a63f 100644
--- a/extensions/memory-core/package.json
+++ b/extensions/memory-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-core",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot core memory search plugin",
"clawdbot": {
@@ -9,6 +9,6 @@
]
},
"peerDependencies": {
- "clawdbot": ">=2026.1.23-1"
+ "clawdbot": ">=2026.1.24"
}
}
diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json
index 4f0e97377..80018044f 100644
--- a/extensions/memory-lancedb/package.json
+++ b/extensions/memory-lancedb/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-lancedb",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot LanceDB-backed long-term memory plugin with auto-recall/capture",
"dependencies": {
diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json
index 80d566e7c..b336b80e6 100644
--- a/extensions/msteams/package.json
+++ b/extensions/msteams/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/msteams",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Microsoft Teams channel plugin",
"clawdbot": {
diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json
index 5c6f5e243..bf5e443e5 100644
--- a/extensions/nextcloud-talk/package.json
+++ b/extensions/nextcloud-talk/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/nextcloud-talk",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Nextcloud Talk channel plugin",
"clawdbot": {
diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json
index b415ffe83..3a3e5ac56 100644
--- a/extensions/nostr/package.json
+++ b/extensions/nostr/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/nostr",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Nostr channel plugin for NIP-04 encrypted DMs",
"clawdbot": {
diff --git a/extensions/open-prose/package.json b/extensions/open-prose/package.json
index 3fa6e8b17..873f3458a 100644
--- a/extensions/open-prose/package.json
+++ b/extensions/open-prose/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/open-prose",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "OpenProse VM skill pack plugin (slash command + telemetry).",
"clawdbot": {
diff --git a/extensions/signal/package.json b/extensions/signal/package.json
index 89de33544..034c65dea 100644
--- a/extensions/signal/package.json
+++ b/extensions/signal/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/signal",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Signal channel plugin",
"clawdbot": {
diff --git a/extensions/slack/package.json b/extensions/slack/package.json
index f129515f5..73f2f6ecd 100644
--- a/extensions/slack/package.json
+++ b/extensions/slack/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/slack",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Slack channel plugin",
"clawdbot": {
diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json
index e4005c739..81b378df2 100644
--- a/extensions/telegram/package.json
+++ b/extensions/telegram/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/telegram",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Telegram channel plugin",
"clawdbot": {
diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json
index 6fd64d03f..dca4f914d 100644
--- a/extensions/tlon/package.json
+++ b/extensions/tlon/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/tlon",
- "version": "2026.1.22",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Tlon/Urbit channel plugin",
"clawdbot": {
diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json
index 248b0cb8b..840776c19 100644
--- a/extensions/voice-call/package.json
+++ b/extensions/voice-call/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/voice-call",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot voice-call plugin",
"dependencies": {
diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json
index 3dcc4cf6b..8e18af842 100644
--- a/extensions/whatsapp/package.json
+++ b/extensions/whatsapp/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/whatsapp",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot WhatsApp channel plugin",
"clawdbot": {
diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json
index 7ced3106a..a3a87a878 100644
--- a/extensions/zalo/package.json
+++ b/extensions/zalo/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalo",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Zalo channel plugin",
"clawdbot": {
diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json
index 9f406c56c..513295b46 100644
--- a/extensions/zalouser/package.json
+++ b/extensions/zalouser/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalouser",
- "version": "2026.1.23",
+ "version": "2026.1.24",
"type": "module",
"description": "Clawdbot Zalo Personal Account plugin via zca-cli",
"dependencies": {
diff --git a/package.json b/package.json
index 3e908b3e1..39e7b31d2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "clawdbot",
- "version": "2026.1.24-0",
+ "version": "2026.1.24-2",
"description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
"type": "module",
"main": "dist/index.js",
@@ -33,6 +33,7 @@
"dist/macos/**",
"dist/media/**",
"dist/media-understanding/**",
+ "dist/link-understanding/**",
"dist/process/**",
"dist/plugins/**",
"dist/plugin-sdk/**",
@@ -64,6 +65,7 @@
"git-hooks/**",
"dist/terminal/**",
"dist/routing/**",
+ "dist/shared/**",
"dist/utils/**",
"dist/logging/**",
"dist/memory/**",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0ee59170b..d49866b74 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -357,8 +357,8 @@ importers:
extensions/memory-core:
dependencies:
clawdbot:
- specifier: '>=2026.1.23-1'
- version: 2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3)
+ specifier: '>=2026.1.24'
+ version: link:../..
extensions/memory-lancedb:
dependencies:
@@ -3194,11 +3194,6 @@ packages:
class-variance-authority@0.7.1:
resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
- clawdbot@2026.1.23-1:
- resolution: {integrity: sha512-t51ks5bnTRQNCzoTunUJaoeMjamvP3zP5EyyadmI34kXYGIbWcCx242w5XMr5h4sLSw59nBw3lJ74vErWDsz9w==}
- engines: {node: '>=22.12.0'}
- hasBin: true
-
cli-cursor@5.0.0:
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
engines: {node: '>=18'}
@@ -9043,82 +9038,6 @@ snapshots:
dependencies:
clsx: 2.1.1
- clawdbot@2026.1.23-1(@types/express@5.0.6)(audio-decode@2.2.3)(devtools-protocol@0.0.1561482)(typescript@5.9.3):
- dependencies:
- '@agentclientprotocol/sdk': 0.13.1(zod@4.3.6)
- '@aws-sdk/client-bedrock': 3.975.0
- '@buape/carbon': 0.14.0(hono@4.11.4)
- '@clack/prompts': 0.11.0
- '@grammyjs/runner': 2.0.3(grammy@1.39.3)
- '@grammyjs/transformer-throttler': 1.2.1(grammy@1.39.3)
- '@homebridge/ciao': 1.3.4
- '@lydell/node-pty': 1.2.0-beta.3
- '@mariozechner/pi-agent-core': 0.49.3(ws@8.19.0)(zod@4.3.6)
- '@mariozechner/pi-ai': 0.49.3(ws@8.19.0)(zod@4.3.6)
- '@mariozechner/pi-coding-agent': 0.49.3(ws@8.19.0)(zod@4.3.6)
- '@mariozechner/pi-tui': 0.49.3
- '@mozilla/readability': 0.6.0
- '@sinclair/typebox': 0.34.47
- '@slack/bolt': 4.6.0(@types/express@5.0.6)
- '@slack/web-api': 7.13.0
- '@whiskeysockets/baileys': 7.0.0-rc.9(audio-decode@2.2.3)(sharp@0.34.5)
- ajv: 8.17.1
- body-parser: 2.2.2
- chalk: 5.6.2
- chokidar: 5.0.0
- chromium-bidi: 13.0.1(devtools-protocol@0.0.1561482)
- cli-highlight: 2.1.11
- commander: 14.0.2
- croner: 9.1.0
- detect-libc: 2.1.2
- discord-api-types: 0.38.37
- dotenv: 17.2.3
- express: 5.2.1
- file-type: 21.3.0
- grammy: 1.39.3
- hono: 4.11.4
- jiti: 2.6.1
- json5: 2.2.3
- jszip: 3.10.1
- linkedom: 0.18.12
- long: 5.3.2
- markdown-it: 14.1.0
- osc-progress: 0.3.0
- pdfjs-dist: 5.4.530
- playwright-core: 1.58.0
- proper-lockfile: 4.1.2
- qrcode-terminal: 0.12.0
- sharp: 0.34.5
- sqlite-vec: 0.1.7-alpha.2
- tar: 7.5.4
- tslog: 4.10.2
- undici: 7.19.0
- ws: 8.19.0
- yaml: 2.8.2
- zod: 4.3.6
- optionalDependencies:
- '@napi-rs/canvas': 0.1.88
- node-llama-cpp: 3.15.0(typescript@5.9.3)
- transitivePeerDependencies:
- - '@discordjs/opus'
- - '@modelcontextprotocol/sdk'
- - '@types/express'
- - audio-decode
- - aws-crt
- - bufferutil
- - canvas
- - debug
- - devtools-protocol
- - encoding
- - ffmpeg-static
- - jimp
- - link-preview-js
- - node-opus
- - opusscript
- - supports-color
- - typescript
- - utf-8-validate
-
cli-cursor@5.0.0:
dependencies:
restore-cursor: 5.1.0
diff --git a/scripts/clawtributors-map.json b/scripts/clawtributors-map.json
index 7ad1f926c..8899afc93 100644
--- a/scripts/clawtributors-map.json
+++ b/scripts/clawtributors-map.json
@@ -2,6 +2,7 @@
"ensureLogins": [
"odrobnik",
"alphonse-arianee",
+ "aaronn",
"ronak-guliani",
"cpojer",
"carlulsoe",
diff --git a/src/channels/plugins/normalize/imessage.test.ts b/src/channels/plugins/normalize/imessage.test.ts
new file mode 100644
index 000000000..afb2ec358
--- /dev/null
+++ b/src/channels/plugins/normalize/imessage.test.ts
@@ -0,0 +1,15 @@
+import { describe, expect, it } from "vitest";
+
+import { normalizeIMessageMessagingTarget } from "./imessage.js";
+
+describe("imessage target normalization", () => {
+ it("preserves service prefixes for handles", () => {
+ expect(normalizeIMessageMessagingTarget("sms:+1 (555) 222-3333")).toBe("sms:+15552223333");
+ });
+
+ it("drops service prefixes for chat targets", () => {
+ expect(normalizeIMessageMessagingTarget("sms:chat_id:123")).toBe("chat_id:123");
+ expect(normalizeIMessageMessagingTarget("imessage:CHAT_GUID:abc")).toBe("chat_guid:abc");
+ expect(normalizeIMessageMessagingTarget("auto:ChatIdentifier:foo")).toBe("chat_identifier:foo");
+ });
+});
diff --git a/src/channels/plugins/normalize/imessage.ts b/src/channels/plugins/normalize/imessage.ts
new file mode 100644
index 000000000..ec04d6557
--- /dev/null
+++ b/src/channels/plugins/normalize/imessage.ts
@@ -0,0 +1,35 @@
+import { normalizeIMessageHandle } from "../../../imessage/targets.js";
+
+// Service prefixes that indicate explicit delivery method; must be preserved during normalization
+const SERVICE_PREFIXES = ["imessage:", "sms:", "auto:"] as const;
+const CHAT_TARGET_PREFIX_RE =
+ /^(chat_id:|chatid:|chat:|chat_guid:|chatguid:|guid:|chat_identifier:|chatidentifier:|chatident:)/i;
+
+export function normalizeIMessageMessagingTarget(raw: string): string | undefined {
+ const trimmed = raw.trim();
+ if (!trimmed) return undefined;
+
+ // Preserve service prefix if present (e.g., "sms:+1555" → "sms:+15551234567")
+ const lower = trimmed.toLowerCase();
+ for (const prefix of SERVICE_PREFIXES) {
+ if (lower.startsWith(prefix)) {
+ const remainder = trimmed.slice(prefix.length).trim();
+ const normalizedHandle = normalizeIMessageHandle(remainder);
+ if (!normalizedHandle) return undefined;
+ if (CHAT_TARGET_PREFIX_RE.test(normalizedHandle)) return normalizedHandle;
+ return `${prefix}${normalizedHandle}`;
+ }
+ }
+
+ const normalized = normalizeIMessageHandle(trimmed);
+ return normalized || undefined;
+}
+
+export function looksLikeIMessageTargetId(raw: string): boolean {
+ const trimmed = raw.trim();
+ if (!trimmed) return false;
+ if (/^(imessage:|sms:|auto:)/i.test(trimmed)) return true;
+ if (CHAT_TARGET_PREFIX_RE.test(trimmed)) return true;
+ if (trimmed.includes("@")) return true;
+ return /^\+?\d{3,}$/.test(trimmed);
+}
diff --git a/src/imessage/targets.test.ts b/src/imessage/targets.test.ts
index 956dfa321..6350167a3 100644
--- a/src/imessage/targets.test.ts
+++ b/src/imessage/targets.test.ts
@@ -28,6 +28,27 @@ describe("imessage targets", () => {
expect(normalizeIMessageHandle(" +1 (555) 222-3333 ")).toBe("+15552223333");
});
+ it("normalizes chat_id prefixes case-insensitively", () => {
+ expect(normalizeIMessageHandle("CHAT_ID:123")).toBe("chat_id:123");
+ expect(normalizeIMessageHandle("Chat_Id:456")).toBe("chat_id:456");
+ expect(normalizeIMessageHandle("chatid:789")).toBe("chat_id:789");
+ expect(normalizeIMessageHandle("CHAT:42")).toBe("chat_id:42");
+ });
+
+ it("normalizes chat_guid prefixes case-insensitively", () => {
+ expect(normalizeIMessageHandle("CHAT_GUID:abc-def")).toBe("chat_guid:abc-def");
+ expect(normalizeIMessageHandle("ChatGuid:XYZ")).toBe("chat_guid:XYZ");
+ expect(normalizeIMessageHandle("GUID:test-guid")).toBe("chat_guid:test-guid");
+ });
+
+ it("normalizes chat_identifier prefixes case-insensitively", () => {
+ expect(normalizeIMessageHandle("CHAT_IDENTIFIER:iMessage;-;chat123")).toBe(
+ "chat_identifier:iMessage;-;chat123",
+ );
+ expect(normalizeIMessageHandle("ChatIdentifier:test")).toBe("chat_identifier:test");
+ expect(normalizeIMessageHandle("CHATIDENT:foo")).toBe("chat_identifier:foo");
+ });
+
it("checks allowFrom against chat_id", () => {
const ok = isAllowedIMessageSender({
allowFrom: ["chat_id:9"],
diff --git a/src/imessage/targets.ts b/src/imessage/targets.ts
index befb3f6d6..03fdcf306 100644
--- a/src/imessage/targets.ts
+++ b/src/imessage/targets.ts
@@ -34,6 +34,27 @@ export function normalizeIMessageHandle(raw: string): string {
if (lowered.startsWith("imessage:")) return normalizeIMessageHandle(trimmed.slice(9));
if (lowered.startsWith("sms:")) return normalizeIMessageHandle(trimmed.slice(4));
if (lowered.startsWith("auto:")) return normalizeIMessageHandle(trimmed.slice(5));
+
+ // Normalize chat_id/chat_guid/chat_identifier prefixes case-insensitively
+ for (const prefix of CHAT_ID_PREFIXES) {
+ if (lowered.startsWith(prefix)) {
+ const value = trimmed.slice(prefix.length).trim();
+ return `chat_id:${value}`;
+ }
+ }
+ for (const prefix of CHAT_GUID_PREFIXES) {
+ if (lowered.startsWith(prefix)) {
+ const value = trimmed.slice(prefix.length).trim();
+ return `chat_guid:${value}`;
+ }
+ }
+ for (const prefix of CHAT_IDENTIFIER_PREFIXES) {
+ if (lowered.startsWith(prefix)) {
+ const value = trimmed.slice(prefix.length).trim();
+ return `chat_identifier:${value}`;
+ }
+ }
+
if (trimmed.includes("@")) return trimmed.toLowerCase();
const normalized = normalizeE164(trimmed);
if (normalized) return normalized;
diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts
index 3e213746f..60782ff6d 100644
--- a/src/plugin-sdk/index.ts
+++ b/src/plugin-sdk/index.ts
@@ -197,12 +197,6 @@ export {
} from "../channels/plugins/setup-helpers.js";
export { formatPairingApproveHint } from "../channels/plugins/helpers.js";
export { PAIRING_APPROVED_MESSAGE } from "../channels/plugins/pairing-message.js";
-export {
- listIMessageAccountIds,
- resolveDefaultIMessageAccountId,
- resolveIMessageAccount,
- type ResolvedIMessageAccount,
-} from "../imessage/accounts.js";
export type {
ChannelOnboardingAdapter,
@@ -210,7 +204,6 @@ export type {
} from "../channels/plugins/onboarding-types.js";
export { addWildcardAllowFrom, promptAccountId } from "../channels/plugins/onboarding/helpers.js";
export { promptChannelAccessConfig } from "../channels/plugins/onboarding/channel-access.js";
-export { imessageOnboardingAdapter } from "../channels/plugins/onboarding/imessage.js";
export {
createActionGate,
@@ -264,6 +257,19 @@ export {
} from "../channels/plugins/normalize/discord.js";
export { collectDiscordStatusIssues } from "../channels/plugins/status-issues/discord.js";
+// Channel: iMessage
+export {
+ listIMessageAccountIds,
+ resolveDefaultIMessageAccountId,
+ resolveIMessageAccount,
+ type ResolvedIMessageAccount,
+} from "../imessage/accounts.js";
+export { imessageOnboardingAdapter } from "../channels/plugins/onboarding/imessage.js";
+export {
+ looksLikeIMessageTargetId,
+ normalizeIMessageMessagingTarget,
+} from "../channels/plugins/normalize/imessage.js";
+
// Channel: Slack
export {
listEnabledSlackAccounts,