diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bef69327..891e02ff3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-## 2026.1.14-1 (unreleased)
+## 2026.1.14-1
### Highlights
- Web search: `web_search`/`web_fetch` tools (Brave API) + first-time setup in onboarding/configure.
diff --git a/appcast.xml b/appcast.xml
index 9cada6d60..038abc8c4 100644
--- a/appcast.xml
+++ b/appcast.xml
@@ -2,6 +2,178 @@
Clawdbot
+ -
+ 2026.1.14-1
+ Thu, 15 Jan 2026 11:14:40 +0000
+ https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
+ 5825
+ 2026.1.14-1
+ 15.0
+ Clawdbot 2026.1.14-1
+
Highlights
+
+- Web search:
web_search/web_fetch tools (Brave API) + first-time setup in onboarding/configure.
+- Browser control: Chrome extension relay takeover mode + remote browser control via
clawdbot browser serve.
+- Plugins: channel plugins (gateway HTTP hooks) + Zalo plugin + onboarding install flow. (#854) — thanks @longmaba.
+- Security: expanded
clawdbot security audit (+ --fix), detect-secrets CI scan, and a SECURITY.md reporting policy.
+
+Changes
+Web Tools
+
+- Tools: add
web_search/web_fetch (Brave API), including helpful setup hints when the key is missing.
+- Tools: enable
web_fetch by default (unless explicitly disabled in config).
+- CLI/Docs: add
clawdbot configure --section web for storing Brave API keys and update onboarding tips.
+
+Browser / Control UI
+
+- Browser: add Chrome extension relay takeover mode (toolbar button) +
clawdbot browser serve remote control + browser.controlToken.
+- Browser: ship a built-in
chrome profile for extension relay and start the relay automatically when running locally.
+- Browser: default
browser.defaultProfile to chrome (existing Chrome takeover mode).
+- Browser: add
clawdbot browser extension install/path and copy extension path to clipboard.
+- Browser: add
snapshot refs=aria (Playwright aria-ref ids) for self-resolving refs across snapshot → act.
+- Browser:
profile="chrome" now defaults to host control and returns clearer “attach a tab” errors.
+- Browser: extension mode recovers when only one tab is attached (stale targetId fallback).
+- Control UI: show raw any-map entries in config views; move Docs link into the left nav.
+
+Plugins
+
+- Plugins: add plugin HTTP hooks + loader updates to support channel plugins. (#854) — thanks @longmaba.
+- Plugins: add onboarding plugin install flow. (#854) — thanks @longmaba.
+- Channels: add Matrix plugin (external) with docs + onboarding hooks.
+- Voice Call: add Plivo provider (no SDK dependency). (#846) — thanks @vrknetha.
+
+Security
+
+- Security: expand
clawdbot security audit checks and publish a SECURITY.md reporting policy.
+- Security: extend
clawdbot security audit --fix to tighten more sensitive state paths.
+- Security: add detect-secrets CI scan and baseline guidance. (#227) — thanks @Hyaxia.
+
+Onboarding / Daemon
+
+- Onboarding: add a security checkpoint prompt (docs link + sandboxing hint); require
--accept-risk for --non-interactive.
+- Daemon: support profile-aware service names for multi-gateway setups. (#671) — thanks @bjesuiter.
+
+Auth / Usage / Config
+
+- Usage: add MiniMax coding plan usage tracking.
+- Auth: label Claude Code CLI auth options. (#915) — thanks @SeanZoR.
+- Agents: add optional auth-profile copy prompt on
agents add and improve auth error messaging.
+- Auth: add dynamic template variables to
messages.responsePrefix. (#928) — thanks @sebslight.
+- Config: add
channels..configWrites gating for channel-initiated config writes; migrate Slack channel IDs.
+
+Channels
+
+- Telegram: add message delete action in the message tool. (#903) — thanks @sleontenko.
+- WhatsApp: add
channels.whatsapp.sendReadReceipts to disable auto read receipts. (#882) — thanks @chrisrodz.
+
+Docs
+
+- Docs: clarify per-agent auth stores, sandboxed skill binaries, and elevated semantics.
+- Docs: add FAQ entries for missing provider auth after adding agents and Gemini thinking signature errors.
+- Docs: expand gateway security hardening guidance and incident response checklist.
+- Docs: document DM history limits for channel DMs. (#883) — thanks @pkrmf.
+- Docs: standardize Claude Code CLI naming across docs and prompts. (follow-up to #915)
+- Docs: add per-command CLI doc pages and link them from
clawdbot --help.
+- Docs: add multi-gateway guide (sidebar + nav).
+
+Fixes
+Gateway / Daemon / Sessions
+
+- Gateway: forward termination signals to respawned CLI child processes to avoid orphaned systemd runs. (#933) — thanks @roshanasingh4.
+- Gateway/UI: ship session defaults in the hello snapshot so the Control UI canonicalizes main session keys (no bare
main alias).
+- Agents: skip thinking/final tag stripping inside Markdown code spans. (#939) — thanks @ngutman.
+- Browser: add tests for snapshot labels/efficient query params and labeled image responses.
+- Browser: persist role snapshot refs per CDP target so
snapshot → act clicks work even if Playwright returns a different Page instance.
+- macOS: ensure launchd log directory exists with a test-only override. (#909) — thanks @roshanasingh4.
+- macOS: format ConnectionsStore config to satisfy SwiftFormat lint. (#852) — thanks @mneves75.
+- Packaging: run
pnpm build on prepack so npm publishes include fresh dist/ output.
+- Telegram: register dock native commands with underscores to avoid
BOT_COMMAND_INVALID (#929, fixes #901) — thanks @grp06.
+- Google: downgrade unsigned thinking blocks before send to avoid missing signature errors.
+- Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams.
+- Agents: strip downgraded tool call text without eating adjacent replies and filter thinking-tag leaks. (#905) — thanks @erikpr1994.
+- Agents: cap tool call IDs for OpenAI/OpenRouter to avoid request rejections. (#875) — thanks @j1philli.
+- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06.
+- Agents: scrub tuple
items schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
+- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4.
+- Apps: use canonical main session keys from gateway defaults across macOS/iOS/Android to avoid creating bare
main sessions.
+- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
+- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight.
+- Daemon: clear persisted launchd disabled state before bootstrap (fixes
daemon install after uninstall). (#849) — thanks @ndraiman.
+- Sessions: return deep clones (
structuredClone) so cached session entries can't be mutated. (#934) — thanks @ronak-guliani.
+- Heartbeat: keep
updatedAt monotonic when restoring heartbeat sessions. (#934) — thanks @ronak-guliani.
+- Agent: clear run context after CLI runs (
clearAgentRunContext) to avoid runaway contexts. (#934) — thanks @ronak-guliani.
+- Gateway/Dev: ensure
pnpm gateway:dev always uses the dev profile config + state (~/.clawdbot-dev).
+
+CLI / Onboarding
+
+- Onboarding: show web search setup at the end (not the beginning).
+- Onboarding: show daemon install/restart progress (avoid “blinking cursor”) and fix daemon install output formatting.
+- Health: colorize “not configured” provider lines for easier scanning.
+
+Control UI / TUI
+
+- Control UI: load cron run history on job selection and clarify empty-state messaging. (#866)
+- UI: use application-defined WebSocket close code and fix dashboard auth query items. (#918) — thanks @rahthakor.
+- UI: always apply
?token= from URL (fixes unauthorized after re-onboard).
+- Browser: add tests for snapshot labels/efficient query params and labeled image responses.
+- TUI: render picker overlays via the overlay stack so /models and /settings display. (#921) — thanks @grizzdank.
+- TUI: add a bright spinner + elapsed time in the status line for send/stream/run states.
+- TUI: show LLM error messages (rate limits, auth, etc.) instead of
(no output).
+
+Agents / Auth / Tools / Sandbox
+
+- Agents: make user time zone and 24-hour time explicit in the system prompt. (#859) — thanks @CashWilliams.
+- Agents: strip downgraded tool call text without eating adjacent replies and filter thinking-tag leaks. (#905) — thanks @erikpr1994.
+- Agents: cap tool call IDs for OpenAI/OpenRouter to avoid request rejections. (#875) — thanks @j1philli.
+- Agents: scrub tuple
items schemas for Gemini tool calls. (#926, fixes #746) — thanks @grp06.
+- Agents: stabilize sub-agent announce status from runtime outcomes and normalize Result/Notes. (#835) — thanks @roshanasingh4.
+- Auth: normalize Claude Code CLI profile mode to oauth and auto-migrate config. (#855) — thanks @sebslight.
+- Embedded runner: suppress raw API error payloads from replies. (#924) — thanks @grp06.
+- Logging: tolerate
EIO from console writes to avoid gateway crashes. (#925, fixes #878) — thanks @grp06.
+- Sandbox: restore
docker.binds config validation and preserve configured PATH for docker exec. (#873) — thanks @akonyer.
+- Google: downgrade unsigned thinking blocks before send to avoid missing signature errors.
+
+macOS / Apps
+
+- macOS: ensure launchd log directory exists with a test-only override. (#909) — thanks @roshanasingh4.
+- macOS: format ConnectionsStore config to satisfy SwiftFormat lint. (#852) — thanks @mneves75.
+- macOS: pass auth token/password to dashboard URL for authenticated access. (#918) — thanks @rahthakor.
+- macOS: reuse launchd gateway auth and skip wizard when gateway config already exists. (#917)
+- Apps: use canonical main session keys from gateway defaults across macOS/iOS/Android to avoid creating bare
main sessions.
+- macOS: fix cron preview/testing payload to use
channel key. (#867) — thanks @wes-davis.
+- macOS: update cron testing channel arg. (#896) — thanks @ngutman.
+
+Channels / Messaging
+
+- Slack: isolate thread history and avoid inheriting channel transcripts for new threads by default. (#758)
+- Slack: respect
channels.slack.requireMention default when resolving channel mention gating. (#850) — thanks @evalexpr.
+- Slack: drop Socket Mode events with mismatched
api_app_id/team_id. (#889) — thanks @roshanasingh4.
+- Commands: add native command argument menus across Discord/Slack/Telegram. (#936) — thanks @thewilloftheshadow.
+- Discord: isolate autoThread thread context. (#856) — thanks @davidguttman.
+- Telegram: honor
channels.telegram.timeoutSeconds for grammY API requests. (#863) — thanks @Snaver.
+- Telegram: aggregate split inbound messages into one prompt (reduces “one reply per fragment”).
+- Telegram: let control commands bypass per-chat sequentialization; always allow abort triggers.
+- Telegram: split long captions into media + follow-up text messages. (#907) — thanks @jalehman.
+- Telegram: migrate group config when supergroups change chat IDs. (#906) — thanks @sleontenko.
+- Telegram: register dock native commands with underscores to avoid
BOT_COMMAND_INVALID (#929, fixes #901) — thanks @grp06.
+- Messaging: unify markdown formatting + format-first chunking for Slack/Telegram/Signal. (#920) — thanks @TheSethRose.
+- iMessage: prefer handle routing for direct-message replies; include imsg RPC error details. (#935)
+- WhatsApp: fix context isolation using wrong ID (was bot's number, now conversation ID). (#911) — thanks @tristanmanchester.
+- WhatsApp: normalize user JIDs with device suffix for allowlist checks in groups. (#838) — thanks @peschee.
+- WhatsApp: harden owner command auth.
+- Auto-reply: treat trailing
NO_REPLY tokens as silent replies.
+
+Config / Doctor / Packaging
+
+- Config: prevent partial config writes from clobbering unrelated settings (base hash guard + merge patch for connection saves).
+- Config/Doctor: remove legacy Clawdis env fallbacks and config/service migrations (Clawdbot-only).
+- Doctor: avoid re-adding WhatsApp config when only legacy ack reactions are set. (#927, fixes #900) — thanks @grp06.
+- Packaging: run
pnpm build on prepack so npm publishes include fresh dist/ output.
+
+View full changelog
+]]>
+
+
-
2026.1.12-2
Tue, 13 Jan 2026 10:05:25 +0000
@@ -35,22 +207,5 @@
]]>
- -
- 2026.1.11-2
- Mon, 12 Jan 2026 10:25:53 +0000
- https://raw.githubusercontent.com/clawdbot/clawdbot/main/appcast.xml
- 5210
- 2026.1.11-2
- 15.0
- Clawdbot 2026.1.11-2
-
Fixes
-
-- Installer: ensure the CLI entrypoint is executable after npm installs.
-- Packaging: include
dist/plugins/ in the npm package to avoid missing module errors.
-
-View full changelog
-]]>
-
-
\ No newline at end of file
diff --git a/apps/macos/Sources/Clawdbot/ConnectionsStore+Config.swift b/apps/macos/Sources/Clawdbot/ConnectionsStore+Config.swift
index faa6cfc16..e387c7ef2 100644
--- a/apps/macos/Sources/Clawdbot/ConnectionsStore+Config.swift
+++ b/apps/macos/Sources/Clawdbot/ConnectionsStore+Config.swift
@@ -2,6 +2,16 @@ import ClawdbotProtocol
import Foundation
extension ConnectionsStore {
+ var isTelegramTokenLocked: Bool {
+ self.snapshot?.decodeChannel("telegram", as: ChannelsStatusSnapshot.TelegramStatus.self)?
+ .tokenSource == "env"
+ }
+
+ var isDiscordTokenLocked: Bool {
+ self.snapshot?.decodeChannel("discord", as: ChannelsStatusSnapshot.DiscordStatus.self)?
+ .tokenSource == "env"
+ }
+
func loadConfig() async {
do {
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
diff --git a/apps/macos/Sources/Clawdbot/GatewayConnection.swift b/apps/macos/Sources/Clawdbot/GatewayConnection.swift
index 7e30cd3a0..13a8ddfee 100644
--- a/apps/macos/Sources/Clawdbot/GatewayConnection.swift
+++ b/apps/macos/Sources/Clawdbot/GatewayConnection.swift
@@ -233,14 +233,18 @@ actor GatewayConnection {
func canvasHostUrl() async -> String? {
guard let snapshot = self.lastSnapshot else { return nil }
- let trimmed = snapshot.canvashosturl?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+ let trimmed = snapshot.canvashosturl?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) ?? ""
return trimmed.isEmpty ? nil : trimmed
}
+ private func sessionDefaultString(_ defaults: [String: AnyCodable]?, key: String) -> String {
+ (defaults?[key]?.stringValue ?? "")
+ .trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
+ }
+
func cachedMainSessionKey() -> String? {
guard let snapshot = self.lastSnapshot else { return nil }
- let trimmed = snapshot.snapshot.sessiondefaults?.mainsessionkey
- .trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
+ let trimmed = self.sessionDefaultString(snapshot.snapshot.sessiondefaults, key: "mainSessionKey")
return trimmed.isEmpty ? nil : trimmed
}
@@ -287,13 +291,13 @@ actor GatewayConnection {
}
private func canonicalizeSessionKey(_ raw: String) -> String {
- let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
+ let trimmed = raw.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
guard !trimmed.isEmpty else { return trimmed }
guard let defaults = self.lastSnapshot?.snapshot.sessiondefaults else { return trimmed }
- let mainSessionKey = defaults.mainsessionkey.trimmingCharacters(in: .whitespacesAndNewlines)
+ let mainSessionKey = self.sessionDefaultString(defaults, key: "mainSessionKey")
guard !mainSessionKey.isEmpty else { return trimmed }
- let mainKey = defaults.mainkey.trimmingCharacters(in: .whitespacesAndNewlines)
- let defaultAgentId = defaults.defaultagentid.trimmingCharacters(in: .whitespacesAndNewlines)
+ let mainKey = self.sessionDefaultString(defaults, key: "mainKey")
+ let defaultAgentId = self.sessionDefaultString(defaults, key: "defaultAgentId")
let isMainAlias =
trimmed == "main" ||
(!mainKey.isEmpty && trimmed == mainKey) ||
diff --git a/scripts/changelog-to-html.sh b/scripts/changelog-to-html.sh
index cbfd1b823..15dfcb885 100755
--- a/scripts/changelog-to-html.sh
+++ b/scripts/changelog-to-html.sh
@@ -43,6 +43,8 @@ extract_version_section() {
markdown_to_html() {
local text=$1
+ text=$(echo "$text" | sed 's/^##### \(.*\)$/
\1<\/h5>/')
+ text=$(echo "$text" | sed 's/^#### \(.*\)$/\1<\/h4>/')
text=$(echo "$text" | sed 's/^### \(.*\)$/\1<\/h3>/')
text=$(echo "$text" | sed 's/^## \(.*\)$/\1<\/h2>/')
text=$(echo "$text" | sed 's/^- \*\*\([^*]*\)\*\*\(.*\)$/
\1<\/strong>\2<\/li>/')