diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 000000000..0c3d863cf
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,109 @@
+"channel: bluebubbles":
+ - "extensions/bluebubbles/**"
+ - "docs/channels/bluebubbles.md"
+"channel: discord":
+ - "src/discord/**"
+ - "extensions/discord/**"
+ - "docs/channels/discord.md"
+"channel: googlechat":
+ - "extensions/googlechat/**"
+ - "docs/channels/googlechat.md"
+"channel: imessage":
+ - "src/imessage/**"
+ - "extensions/imessage/**"
+ - "docs/channels/imessage.md"
+"channel: line":
+ - "extensions/line/**"
+"channel: matrix":
+ - "extensions/matrix/**"
+ - "docs/channels/matrix.md"
+"channel: mattermost":
+ - "extensions/mattermost/**"
+ - "docs/channels/mattermost.md"
+"channel: msteams":
+ - "extensions/msteams/**"
+ - "docs/channels/msteams.md"
+"channel: nextcloud-talk":
+ - "extensions/nextcloud-talk/**"
+ - "docs/channels/nextcloud-talk.md"
+"channel: nostr":
+ - "extensions/nostr/**"
+ - "docs/channels/nostr.md"
+"channel: signal":
+ - "src/signal/**"
+ - "extensions/signal/**"
+ - "docs/channels/signal.md"
+"channel: slack":
+ - "src/slack/**"
+ - "extensions/slack/**"
+ - "docs/channels/slack.md"
+"channel: telegram":
+ - "src/telegram/**"
+ - "extensions/telegram/**"
+ - "docs/channels/telegram.md"
+"channel: tlon":
+ - "extensions/tlon/**"
+ - "docs/channels/tlon.md"
+"channel: voice-call":
+ - "extensions/voice-call/**"
+"channel: whatsapp-web":
+ - "src/web/**"
+ - "extensions/whatsapp/**"
+ - "docs/channels/whatsapp.md"
+"channel: zalo":
+ - "extensions/zalo/**"
+ - "docs/channels/zalo.md"
+"channel: zalouser":
+ - "extensions/zalouser/**"
+ - "docs/channels/zalouser.md"
+
+"app: android":
+ - "apps/android/**"
+ - "docs/platforms/android.md"
+"app: ios":
+ - "apps/ios/**"
+ - "docs/platforms/ios.md"
+"app: macos":
+ - "apps/macos/**"
+ - "docs/platforms/macos.md"
+ - "docs/platforms/mac/**"
+"app: web-ui":
+ - "ui/**"
+ - "src/gateway/control-ui.ts"
+ - "src/gateway/control-ui-shared.ts"
+ - "src/infra/control-ui-assets.ts"
+
+"cli":
+ - "src/cli/**"
+ - "src/commands/**"
+ - "src/tui/**"
+
+"gateway":
+ - "src/gateway/**"
+ - "src/daemon/**"
+ - "docs/gateway/**"
+
+"docs":
+ - "docs/**"
+ - "docs.acp.md"
+
+"extensions: copilot-proxy":
+ - "extensions/copilot-proxy/**"
+"extensions: diagnostics-otel":
+ - "extensions/diagnostics-otel/**"
+"extensions: google-antigravity-auth":
+ - "extensions/google-antigravity-auth/**"
+"extensions: google-gemini-cli-auth":
+ - "extensions/google-gemini-cli-auth/**"
+"extensions: llm-task":
+ - "extensions/llm-task/**"
+"extensions: lobster":
+ - "extensions/lobster/**"
+"extensions: memory-core":
+ - "extensions/memory-core/**"
+"extensions: memory-lancedb":
+ - "extensions/memory-lancedb/**"
+"extensions: open-prose":
+ - "extensions/open-prose/**"
+"extensions: qwen-portal-auth":
+ - "extensions/qwen-portal-auth/**"
diff --git a/.github/workflows/auto-response.yml b/.github/workflows/auto-response.yml
new file mode 100644
index 000000000..7f242a094
--- /dev/null
+++ b/.github/workflows/auto-response.yml
@@ -0,0 +1,59 @@
+name: Auto response
+
+on:
+ issues:
+ types: [labeled]
+ pull_request:
+ types: [labeled]
+
+permissions:
+ issues: write
+ pull-requests: write
+
+jobs:
+ auto-response:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Handle labeled items
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const rules = [
+ {
+ label: "skill-clawdhub",
+ close: true,
+ message:
+ "Thanks for the contribution! New skills should be published to Clawdhub for everyone to use. We’re keeping the core lean on skills, so I’m closing this out.",
+ },
+ ];
+
+ const labelName = context.payload.label?.name;
+ if (!labelName) {
+ return;
+ }
+
+ const rule = rules.find((item) => item.label === labelName);
+ if (!rule) {
+ return;
+ }
+
+ const issueNumber = context.payload.issue?.number ?? context.payload.pull_request?.number;
+ if (!issueNumber) {
+ return;
+ }
+
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issueNumber,
+ body: rule.message,
+ });
+
+ if (rule.close) {
+ await github.rest.issues.update({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: issueNumber,
+ state: "closed",
+ });
+ }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fcd8e457c..8cc86bd63 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -342,6 +342,8 @@ jobs:
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
- name: Run ${{ matrix.task }}
+ env:
+ NODE_OPTIONS: --max-old-space-size=4096
run: ${{ matrix.command }}
macos-app:
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 000000000..6ec73a1a3
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,17 @@
+name: Labeler
+
+on:
+ pull_request_target:
+ types: [opened, synchronize, reopened]
+
+permissions:
+ contents: read
+ pull-requests: write
+
+jobs:
+ label:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/labeler@v5
+ with:
+ configuration-path: .github/labeler.yml
diff --git a/AGENTS.md b/AGENTS.md
index deed6d9bd..ac85a00d8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -13,6 +13,7 @@
- Core channel docs: `docs/channels/`
- Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
- Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
+- When adding channels/extensions/apps/docs, review `.github/labeler.yml` for label coverage.
## Docs Linking (Mintlify)
- Docs are hosted on Mintlify (docs.clawd.bot).
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee138f13e..93b171b38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,9 +3,15 @@
Docs: https://docs.clawd.bot
## 2026.1.25
+Status: unreleased.
### Changes
-- TBD.
+- Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz.
+- Docs: add Vercel AI Gateway to providers sidebar. (#1901) Thanks @jerilynzheng.
+- Agents: expand cron tool description with full schema docs. (#1988) Thanks @tomascupr.
+- Skills: add missing dependency metadata for GitHub, Notion, Slack, Discord. (#1995) Thanks @jackheuberger.
+- Docs: add Render deployment guide. (#1975) Thanks @anurag.
+- CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi.
## 2026.1.24-3
diff --git a/README.md b/README.md
index ebbdc43d5..47f3a9090 100644
--- a/README.md
+++ b/README.md
@@ -479,31 +479,32 @@ Thanks to all clawtributors:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts
index d8d77ebe1..a015c0e36 100644
--- a/apps/android/app/build.gradle.kts
+++ b/apps/android/app/build.gradle.kts
@@ -21,8 +21,8 @@ android {
applicationId = "com.clawdbot.android"
minSdk = 31
targetSdk = 36
- versionCode = 202601240
- versionName = "2026.1.24"
+ versionCode = 202601250
+ versionName = "2026.1.25"
}
buildTypes {
diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist
index 9dd7a0315..e1cf2b71d 100644
--- a/apps/ios/Sources/Info.plist
+++ b/apps/ios/Sources/Info.plist
@@ -19,9 +19,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 2026.1.24
+ 2026.1.25
CFBundleVersion
- 20260124
+ 20260125
NSAppTransportSecurity
NSAllowsArbitraryLoadsInWebContent
diff --git a/apps/ios/Tests/Info.plist b/apps/ios/Tests/Info.plist
index 798a77421..6ff977b05 100644
--- a/apps/ios/Tests/Info.plist
+++ b/apps/ios/Tests/Info.plist
@@ -17,8 +17,8 @@
CFBundlePackageType
BNDL
CFBundleShortVersionString
- 2026.1.24
+ 2026.1.25
CFBundleVersion
- 20260124
+ 20260125
diff --git a/apps/ios/project.yml b/apps/ios/project.yml
index 52faeb9d0..0073b4ef9 100644
--- a/apps/ios/project.yml
+++ b/apps/ios/project.yml
@@ -81,8 +81,8 @@ targets:
properties:
CFBundleDisplayName: Clawdbot
CFBundleIconName: AppIcon
- CFBundleShortVersionString: "2026.1.24"
- CFBundleVersion: "20260124"
+ CFBundleShortVersionString: "2026.1.25"
+ CFBundleVersion: "20260125"
UILaunchScreen: {}
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: false
@@ -130,5 +130,5 @@ targets:
path: Tests/Info.plist
properties:
CFBundleDisplayName: ClawdbotTests
- CFBundleShortVersionString: "2026.1.24"
- CFBundleVersion: "20260124"
+ CFBundleShortVersionString: "2026.1.25"
+ CFBundleVersion: "20260125"
diff --git a/apps/macos/Sources/Clawdbot/Resources/Info.plist b/apps/macos/Sources/Clawdbot/Resources/Info.plist
index 1c7d9619f..ee9e3113d 100644
--- a/apps/macos/Sources/Clawdbot/Resources/Info.plist
+++ b/apps/macos/Sources/Clawdbot/Resources/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 2026.1.24
+ 2026.1.25
CFBundleVersion
- 202601240
+ 202601250
CFBundleIconFile
Clawdbot
CFBundleURLTypes
diff --git a/docs/docs.json b/docs/docs.json
index 27ea82d23..983585bff 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -984,6 +984,7 @@
"bedrock",
"providers/moonshot",
"providers/minimax",
+ "providers/vercel-ai-gateway",
"providers/openrouter",
"providers/synthetic",
"providers/opencode",
diff --git a/docs/platforms/fly.md b/docs/platforms/fly.md
index d43b83ed7..0fdf176ae 100644
--- a/docs/platforms/fly.md
+++ b/docs/platforms/fly.md
@@ -182,7 +182,7 @@ cat > /data/clawdbot.json << 'EOF'
"bind": "auto"
},
"meta": {
- "lastTouchedVersion": "2026.1.24"
+ "lastTouchedVersion": "2026.1.25"
}
}
EOF
diff --git a/docs/platforms/mac/release.md b/docs/platforms/mac/release.md
index d2d267661..d3bfd02c3 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-3 \
+APP_VERSION=2026.1.25 \
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-3.zip
+ditto -c -k --sequesterRsrc --keepParent dist/Clawdbot.app dist/Clawdbot-2026.1.25.zip
# Optional: also build a styled DMG for humans (drag to /Applications)
-scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.24-3.dmg
+scripts/create-dmg.sh dist/Clawdbot.app dist/Clawdbot-2026.1.25.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-3.dmg
# --apple-id "" --team-id "" --password ""
NOTARIZE=1 NOTARYTOOL_PROFILE=clawdbot-notary \
BUNDLE_ID=com.clawdbot.mac \
-APP_VERSION=2026.1.24-3 \
+APP_VERSION=2026.1.25 \
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-3.dSYM.zip
+ditto -c -k --keepParent apps/macos/.build/release/Clawdbot.app.dSYM dist/Clawdbot-2026.1.25.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-3.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.25.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-3.zip` (and `Clawdbot-2026.1.24-3.dSYM.zip`) to the GitHub release for tag `v2026.1.24-3`.
+- Upload `Clawdbot-2026.1.25.zip` (and `Clawdbot-2026.1.25.dSYM.zip`) to the GitHub release for tag `v2026.1.25`.
- 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/providers/vercel-ai-gateway.md b/docs/providers/vercel-ai-gateway.md
index bd31f0a87..36cf51cda 100644
--- a/docs/providers/vercel-ai-gateway.md
+++ b/docs/providers/vercel-ai-gateway.md
@@ -1,4 +1,5 @@
---
+title: "Vercel AI Gateway"
summary: "Vercel AI Gateway setup (auth + model selection)"
read_when:
- You want to use Vercel AI Gateway with Clawdbot
diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md
index 6492bd469..244757a48 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., `2026.1.24`).
+- [ ] Bump `package.json` version (e.g., `2026.1.25`).
- [ ] 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 925b05bc1..7d82036a0 100644
--- a/extensions/bluebubbles/package.json
+++ b/extensions/bluebubbles/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/bluebubbles",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot BlueBubbles channel plugin",
"clawdbot": {
diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json
index 792a94225..2a9a63c71 100644
--- a/extensions/copilot-proxy/package.json
+++ b/extensions/copilot-proxy/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/copilot-proxy",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Copilot Proxy provider plugin",
"clawdbot": {
diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json
index 2afc99e2e..65a6bf0cd 100644
--- a/extensions/diagnostics-otel/package.json
+++ b/extensions/diagnostics-otel/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/diagnostics-otel",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot diagnostics OpenTelemetry exporter",
"clawdbot": {
diff --git a/extensions/discord/package.json b/extensions/discord/package.json
index dae5fe1f1..90a99d4d3 100644
--- a/extensions/discord/package.json
+++ b/extensions/discord/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/discord",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"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 96bffde7c..f1d8f86bd 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.24",
+ "version": "2026.1.25",
"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 dc8a894d7..7e3fef15b 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.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Gemini CLI OAuth provider plugin",
"clawdbot": {
diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json
index 056bdedb6..af1ccf8e1 100644
--- a/extensions/googlechat/package.json
+++ b/extensions/googlechat/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/googlechat",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Google Chat channel plugin",
"clawdbot": {
@@ -34,6 +34,6 @@
"clawdbot": "workspace:*"
},
"peerDependencies": {
- "clawdbot": ">=2026.1.24"
+ "clawdbot": ">=2026.1.25"
}
}
diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json
index 79aa7890d..944ad06bf 100644
--- a/extensions/imessage/package.json
+++ b/extensions/imessage/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/imessage",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot iMessage channel plugin",
"clawdbot": {
diff --git a/extensions/line/package.json b/extensions/line/package.json
index b518f5ca5..346d66415 100644
--- a/extensions/line/package.json
+++ b/extensions/line/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/line",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot LINE channel plugin",
"clawdbot": {
diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json
index a03344d1a..d6bfbb31d 100644
--- a/extensions/llm-task/package.json
+++ b/extensions/llm-task/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/llm-task",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot JSON-only LLM task plugin",
"clawdbot": {
diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json
index 3926b553b..b73dbac69 100644
--- a/extensions/lobster/package.json
+++ b/extensions/lobster/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/lobster",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"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 24529ee97..7fa12bc74 100644
--- a/extensions/matrix/package.json
+++ b/extensions/matrix/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/matrix",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Matrix channel plugin",
"clawdbot": {
diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json
index 77d799c34..60c02d50f 100644
--- a/extensions/mattermost/package.json
+++ b/extensions/mattermost/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/mattermost",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Mattermost channel plugin",
"clawdbot": {
diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json
index c70c2a63f..c70da1395 100644
--- a/extensions/memory-core/package.json
+++ b/extensions/memory-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-core",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot core memory search plugin",
"clawdbot": {
@@ -9,6 +9,6 @@
]
},
"peerDependencies": {
- "clawdbot": ">=2026.1.24"
+ "clawdbot": ">=2026.1.25"
}
}
diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json
index 80018044f..e003f5890 100644
--- a/extensions/memory-lancedb/package.json
+++ b/extensions/memory-lancedb/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/memory-lancedb",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"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 b336b80e6..b94f8e76a 100644
--- a/extensions/msteams/package.json
+++ b/extensions/msteams/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/msteams",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Microsoft Teams channel plugin",
"clawdbot": {
diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json
index bf5e443e5..2da3f3b2a 100644
--- a/extensions/nextcloud-talk/package.json
+++ b/extensions/nextcloud-talk/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/nextcloud-talk",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Nextcloud Talk channel plugin",
"clawdbot": {
diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json
index 3a3e5ac56..b2fb4b799 100644
--- a/extensions/nostr/package.json
+++ b/extensions/nostr/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/nostr",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"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 873f3458a..052201205 100644
--- a/extensions/open-prose/package.json
+++ b/extensions/open-prose/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/open-prose",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"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 034c65dea..65948eb7b 100644
--- a/extensions/signal/package.json
+++ b/extensions/signal/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/signal",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Signal channel plugin",
"clawdbot": {
diff --git a/extensions/slack/package.json b/extensions/slack/package.json
index 73f2f6ecd..5bd452d2e 100644
--- a/extensions/slack/package.json
+++ b/extensions/slack/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/slack",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Slack channel plugin",
"clawdbot": {
diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json
index 81b378df2..64d3d7dea 100644
--- a/extensions/telegram/package.json
+++ b/extensions/telegram/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/telegram",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Telegram channel plugin",
"clawdbot": {
diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json
index dca4f914d..06750126d 100644
--- a/extensions/tlon/package.json
+++ b/extensions/tlon/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/tlon",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Tlon/Urbit channel plugin",
"clawdbot": {
diff --git a/extensions/voice-call/CHANGELOG.md b/extensions/voice-call/CHANGELOG.md
index 6123a7315..a8721d47d 100644
--- a/extensions/voice-call/CHANGELOG.md
+++ b/extensions/voice-call/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-## 2026.1.24
+## 2026.1.25
### Changes
- Breaking: voice-call TTS now uses core `messages.tts` (plugin TTS config deep‑merges with core).
diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json
index 840776c19..31b171f76 100644
--- a/extensions/voice-call/package.json
+++ b/extensions/voice-call/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/voice-call",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot voice-call plugin",
"dependencies": {
diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json
index 8e18af842..b7b57eb51 100644
--- a/extensions/whatsapp/package.json
+++ b/extensions/whatsapp/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/whatsapp",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot WhatsApp channel plugin",
"clawdbot": {
diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json
index a3a87a878..8f077a6b3 100644
--- a/extensions/zalo/package.json
+++ b/extensions/zalo/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalo",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Zalo channel plugin",
"clawdbot": {
diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json
index 513295b46..0ab93d1ce 100644
--- a/extensions/zalouser/package.json
+++ b/extensions/zalouser/package.json
@@ -1,6 +1,6 @@
{
"name": "@clawdbot/zalouser",
- "version": "2026.1.24",
+ "version": "2026.1.25",
"type": "module",
"description": "Clawdbot Zalo Personal Account plugin via zca-cli",
"dependencies": {
diff --git a/package.json b/package.json
index 5d77e25d0..0c63d5d69 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "clawdbot",
- "version": "2026.1.24-3",
+ "version": "2026.1.25",
"description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent",
"type": "module",
"main": "dist/index.js",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 781a461a9..14bef9f5c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -357,7 +357,7 @@ importers:
extensions/memory-core:
dependencies:
clawdbot:
- specifier: '>=2026.1.24'
+ specifier: '>=2026.1.25'
version: link:../..
extensions/memory-lancedb:
diff --git a/scripts/sync-labels.ts b/scripts/sync-labels.ts
new file mode 100644
index 000000000..297644c1e
--- /dev/null
+++ b/scripts/sync-labels.ts
@@ -0,0 +1,107 @@
+import { execFileSync } from "node:child_process";
+import { readFileSync } from "node:fs";
+import { resolve } from "node:path";
+
+type RepoLabel = {
+ name: string;
+ color?: string;
+};
+
+const COLOR_BY_PREFIX = new Map([
+ ["channel", "1d76db"],
+ ["app", "6f42c1"],
+ ["extensions", "0e8a16"],
+ ["docs", "0075ca"],
+ ["cli", "f9d0c4"],
+ ["gateway", "d4c5f9"],
+]);
+
+const configPath = resolve(".github/labeler.yml");
+const labelNames = extractLabelNames(readFileSync(configPath, "utf8"));
+
+if (!labelNames.length) {
+ throw new Error("labeler.yml must declare at least one label.");
+}
+
+const repo = resolveRepo();
+const existing = fetchExistingLabels(repo);
+
+const missing = labelNames.filter((label) => !existing.has(label));
+if (!missing.length) {
+ console.log("All labeler labels already exist.");
+ process.exit(0);
+}
+
+for (const label of missing) {
+ const color = pickColor(label);
+ execFileSync(
+ "gh",
+ [
+ "api",
+ "-X",
+ "POST",
+ `repos/${repo}/labels`,
+ "-f",
+ `name=${label}`,
+ "-f",
+ `color=${color}`,
+ ],
+ { stdio: "inherit" },
+ );
+ console.log(`Created label: ${label}`);
+}
+
+function extractLabelNames(contents: string): string[] {
+ const labels: string[] = [];
+ for (const line of contents.split("\n")) {
+ if (!line.trim() || line.trimStart().startsWith("#")) {
+ continue;
+ }
+ if (/^\s/.test(line)) {
+ continue;
+ }
+ const match = line.match(/^(["'])(.+)\1\s*:/) ?? line.match(/^([^:]+):/);
+ if (match) {
+ const name = (match[2] ?? match[1] ?? "").trim();
+ if (name) {
+ labels.push(name);
+ }
+ }
+ }
+ return labels;
+}
+
+function pickColor(label: string): string {
+ const prefix = label.includes(":") ? label.split(":", 1)[0].trim() : label.trim();
+ return COLOR_BY_PREFIX.get(prefix) ?? "ededed";
+}
+
+function resolveRepo(): string {
+ const remote = execFileSync("git", ["config", "--get", "remote.origin.url"], {
+ encoding: "utf8",
+ }).trim();
+
+ if (!remote) {
+ throw new Error("Unable to determine repository from git remote.");
+ }
+
+ if (remote.startsWith("git@github.com:")) {
+ return remote.replace("git@github.com:", "").replace(/\.git$/, "");
+ }
+
+ if (remote.startsWith("https://github.com/")) {
+ return remote.replace("https://github.com/", "").replace(/\.git$/, "");
+ }
+
+ throw new Error(`Unsupported GitHub remote: ${remote}`);
+}
+
+function fetchExistingLabels(repo: string): Map {
+ const raw = execFileSync(
+ "gh",
+ ["api", `repos/${repo}/labels?per_page=100`, "--paginate"],
+ { encoding: "utf8" },
+ );
+ const labels = JSON.parse(raw) as RepoLabel[];
+ return new Map(labels.map((label) => [label.name, label]));
+}
diff --git a/skills/discord/SKILL.md b/skills/discord/SKILL.md
index 0b64f14e1..5525a3bf5 100644
--- a/skills/discord/SKILL.md
+++ b/skills/discord/SKILL.md
@@ -1,6 +1,7 @@
---
name: discord
description: Use when you need to control Discord from Clawdbot via the discord tool: send messages, react, post or upload stickers, upload emojis, run polls, manage threads/pins/search, create/edit/delete channels and categories, fetch permissions or member/role/channel info, or handle moderation actions in Discord DMs or channels.
+metadata: {"clawdbot":{"emoji":"🎮","requires":{"config":["channels.discord"]}}}
---
# Discord Actions
diff --git a/skills/github/SKILL.md b/skills/github/SKILL.md
index 03b2a0033..e7c89f7ba 100644
--- a/skills/github/SKILL.md
+++ b/skills/github/SKILL.md
@@ -1,6 +1,7 @@
---
name: github
description: "Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries."
+metadata: {"clawdbot":{"emoji":"🐙","requires":{"bins":["gh"]},"install":[{"id":"brew","kind":"brew","formula":"gh","bins":["gh"],"label":"Install GitHub CLI (brew)"},{"id":"apt","kind":"apt","package":"gh","bins":["gh"],"label":"Install GitHub CLI (apt)"}]}}
---
# GitHub Skill
diff --git a/skills/notion/SKILL.md b/skills/notion/SKILL.md
index 869871b3c..04921e250 100644
--- a/skills/notion/SKILL.md
+++ b/skills/notion/SKILL.md
@@ -2,7 +2,7 @@
name: notion
description: Notion API for creating and managing pages, databases, and blocks.
homepage: https://developers.notion.com
-metadata: {"clawdbot":{"emoji":"📝"}}
+metadata: {"clawdbot":{"emoji":"📝","requires":{"env":["NOTION_API_KEY"]},"primaryEnv":"NOTION_API_KEY"}}
---
# notion
diff --git a/skills/slack/SKILL.md b/skills/slack/SKILL.md
index df04f858f..b72bab1f3 100644
--- a/skills/slack/SKILL.md
+++ b/skills/slack/SKILL.md
@@ -1,6 +1,7 @@
---
name: slack
description: Use when you need to control Slack from Clawdbot via the slack tool, including reacting to messages or pinning/unpinning items in Slack channels or DMs.
+metadata: {"clawdbot":{"emoji":"💬","requires":{"config":["channels.slack"]}}}
---
# Slack Actions
diff --git a/src/agents/tools/cron-tool.ts b/src/agents/tools/cron-tool.ts
index a1d218dd7..739b3ada3 100644
--- a/src/agents/tools/cron-tool.ts
+++ b/src/agents/tools/cron-tool.ts
@@ -133,8 +133,50 @@ export function createCronTool(opts?: CronToolOptions): AnyAgentTool {
return {
label: "Cron",
name: "cron",
- description:
- "Manage Gateway cron jobs (status/list/add/update/remove/run/runs) and send wake events. Use `jobId` as the canonical identifier; `id` is accepted for compatibility. Use `contextMessages` (0-10) to add previous messages as context to the job text.",
+ description: `Manage Gateway cron jobs (status/list/add/update/remove/run/runs) and send wake events.
+
+ACTIONS:
+- status: Check cron scheduler status
+- list: List jobs (use includeDisabled:true to include disabled)
+- add: Create job (requires job object, see schema below)
+- update: Modify job (requires jobId + patch object)
+- remove: Delete job (requires jobId)
+- run: Trigger job immediately (requires jobId)
+- runs: Get job run history (requires jobId)
+- wake: Send wake event (requires text, optional mode)
+
+JOB SCHEMA (for add action):
+{
+ "name": "string (optional)",
+ "schedule": { ... }, // Required: when to run
+ "payload": { ... }, // Required: what to execute
+ "sessionTarget": "main" | "isolated", // Required
+ "enabled": true | false // Optional, default true
+}
+
+SCHEDULE TYPES (schedule.kind):
+- "at": One-shot at absolute time
+ { "kind": "at", "atMs": }
+- "every": Recurring interval
+ { "kind": "every", "everyMs": , "anchorMs": }
+- "cron": Cron expression
+ { "kind": "cron", "expr": "", "tz": "" }
+
+PAYLOAD TYPES (payload.kind):
+- "systemEvent": Injects text as system event into session
+ { "kind": "systemEvent", "text": "" }
+- "agentTurn": Runs agent with message (isolated sessions only)
+ { "kind": "agentTurn", "message": "", "model": "", "thinking": "", "timeoutSeconds": , "deliver": , "channel": "", "to": "", "bestEffortDeliver": }
+
+CRITICAL CONSTRAINTS:
+- sessionTarget="main" REQUIRES payload.kind="systemEvent"
+- sessionTarget="isolated" REQUIRES payload.kind="agentTurn"
+
+WAKE MODES (for wake action):
+- "next-heartbeat" (default): Wake on next heartbeat
+- "now": Wake immediately
+
+Use jobId as the canonical identifier; id is accepted for compatibility. Use contextMessages (0-10) to add previous messages as context to the job text.`,
parameters: CronToolSchema,
execute: async (_toolCallId, args) => {
const params = args as Record;
diff --git a/src/commands/doctor-security.ts b/src/commands/doctor-security.ts
index b3d82247f..483917faa 100644
--- a/src/commands/doctor-security.ts
+++ b/src/commands/doctor-security.ts
@@ -10,6 +10,61 @@ export async function noteSecurityWarnings(cfg: ClawdbotConfig) {
const warnings: string[] = [];
const auditHint = `- Run: ${formatCliCommand("clawdbot security audit --deep")}`;
+ // ===========================================
+ // GATEWAY NETWORK EXPOSURE CHECK
+ // ===========================================
+ // Check for dangerous gateway binding configurations
+ // that expose the gateway to network without proper auth
+
+ const gatewayBind = cfg.gateway?.bind ?? "loopback";
+ const customBindHost = cfg.gateway?.customBindHost?.trim();
+ const authMode = cfg.gateway?.auth?.mode ?? "off";
+ const authToken = cfg.gateway?.auth?.token;
+ const authPassword = cfg.gateway?.auth?.password;
+
+ const isLoopbackBindHost = (host: string) => {
+ const normalized = host.trim().toLowerCase();
+ return (
+ normalized === "localhost" ||
+ normalized === "::1" ||
+ normalized === "[::1]" ||
+ normalized.startsWith("127.")
+ );
+ };
+
+ // Bindings that expose gateway beyond localhost
+ const exposedBindings = ["all", "lan", "0.0.0.0"];
+ const isExposed =
+ exposedBindings.includes(gatewayBind) ||
+ (gatewayBind === "custom" && (!customBindHost || !isLoopbackBindHost(customBindHost)));
+
+ if (isExposed) {
+ if (authMode === "off") {
+ warnings.push(
+ `- CRITICAL: Gateway bound to "${gatewayBind}" with NO authentication.`,
+ ` Anyone on your network (or internet if port-forwarded) can fully control your agent.`,
+ ` Fix: ${formatCliCommand("clawdbot config set gateway.bind loopback")}`,
+ ` Or enable auth: ${formatCliCommand("clawdbot config set gateway.auth.mode token")}`,
+ );
+ } else if (authMode === "token" && !authToken) {
+ warnings.push(
+ `- CRITICAL: Gateway bound to "${gatewayBind}" with empty auth token.`,
+ ` Fix: ${formatCliCommand("clawdbot doctor --fix")} to generate a token`,
+ );
+ } else if (authMode === "password" && !authPassword) {
+ warnings.push(
+ `- CRITICAL: Gateway bound to "${gatewayBind}" with empty password.`,
+ ` Fix: ${formatCliCommand("clawdbot configure")} to set a password`,
+ );
+ } else {
+ // Auth is configured, but still warn about network exposure
+ warnings.push(
+ `- WARNING: Gateway bound to "${gatewayBind}" (network-accessible).`,
+ ` Ensure your auth credentials are strong and not exposed.`,
+ );
+ }
+ }
+
const warnDmPolicy = async (params: {
label: string;
provider: ChannelId;