diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c5321870..b6e8445bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,143 +2,61 @@ Docs: https://docs.molt.bot -## 2026.1.27-beta.1 +## 2026.1.29 Status: beta. +### Highlights +- Rebrand: rename the npm package/CLI to `moltbot`, keep a `moltbot` compatibility shim, move extensions to the `@moltbot/*` scope, and update bot.molt bundle IDs/labels/logging subsystems. Thanks @thewilloftheshadow. +- New channels/plugins: Twitch plugin; Google Chat (beta) with Workspace Add-on events + typing indicator. (#1612, #1635) Thanks @tyler6204, @iHildy. +- Security hardening: gateway auth defaults required, hook token query-param deprecation, Windows ACL audits, mDNS minimal discovery, and SSH target option injection fix. (#4001, #2016, #1957, #1882, #2200) +- WebChat: image paste + image-only sends; keep sub-agent announce replies visible. (#1925, #1977) +- Tooling: per-sender group tool policies + tools.alsoAllow additive allowlist. (#1757, #1762) +- Memory Search: allow extra paths for memory indexing. (#3600) Thanks @kira-ariaki. + ### Changes -- Rebrand: rename the npm package/CLI to `moltbot`, add a `moltbot` compatibility shim, and move extensions to the `@moltbot/*` scope. -- Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev. -- macOS: limit project-local `node_modules/.bin` PATH preference to debug builds (reduce PATH hijacking risk). -- macOS: finish Moltbot app rename for macOS sources, bundle identifiers, and shared kit paths. (#2844) Thanks @fal3. -- Branding: update launchd labels, mobile bundle IDs, and logging subsystems to bot.molt (legacy com.clawdbot migrations). Thanks @thewilloftheshadow. -- Tools: add per-sender group tool policies and fix precedence. (#1757) Thanks @adam91holt. -- Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47. -- Memory Search: allow extra paths for memory indexing (ignores symlinks). (#3600) Thanks @kira-ariaki. -- Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204. -- Agents: honor tools.exec.safeBins in exec allowlist checks. (#2281) -- Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk. -- Docs: tighten Fly private deployment steps. (#2289) Thanks @dguido. -- Docs: add migration guide for moving to a new machine. (#2381) -- Docs: add Northflank one-click deployment guide. (#2167) Thanks @AdeboyeDN. -- Gateway: warn on hook tokens via query params; document header auth preference. (#2200) Thanks @YuriNachos. -- Gateway: add dangerous Control UI device auth bypass flag + audit warnings. (#2248) +- Providers: add Venice AI integration; update Moonshot Kimi references to kimi-k2.5; update MiniMax API endpoint/format. (#2762, #3064) +- Telegram: quote replies, edit-message action, silent sends, sticker support + vision caching, linkPreview toggle, plugin sendPayload support. (#2900, #2394, #2382, #2548, #1700, #1917) +- Discord: configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro. +- Browser: route browser control via gateway/node; fallback URL matching for relay targets. (#1999) +- macOS: add direct gateway transport; preserve custom SSH usernames for remote control; bump Textual to 0.3.1. (#2033, #2046) +- Routing: add per-account DM session scope + guidance for multi-account setups. (#3095) Thanks @jarvis-sam. +- Hooks: make session-memory message count configurable. (#2681) +- Tools: honor tools.exec.safeBins in exec allowlist checks. (#2281) +- Security: add Control UI device auth bypass flag + audit warnings; warn on hook tokens via query params; add security audit CLI surface. (#2248, #2200) - Doctor: warn on gateway exposure without auth. (#2016) Thanks @Alex-Alaniz. -- Config: auto-migrate legacy state/config paths and keep config resolution consistent across legacy filenames. -- Discord: add configurable privileged gateway intents for presences/members. (#2266) Thanks @kentaro. -- 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. -- Docs: add Claude Max API Proxy guide. (#1875) Thanks @atalovesyou. -- Docs: add DigitalOcean deployment guide. (#1870) Thanks @0xJonHoldsCrypto. -- Docs: add Oracle Cloud (OCI) platform guide + cross-links. (#2333) Thanks @hirefrank. -- Docs: add Raspberry Pi install guide. (#1871) Thanks @0xJonHoldsCrypto. -- Docs: add GCP Compute Engine deployment guide. (#1848) Thanks @hougangdev. -- Docs: add LINE channel guide. Thanks @thewilloftheshadow. -- Docs: credit both contributors for Control UI refresh. (#1852) Thanks @EnzeD. -- Onboarding: add Venice API key to non-interactive flow. (#1893) Thanks @jonisjongithub. -- Onboarding: strengthen security warning copy for beta + access control expectations. -- Tlon: format thread reply IDs as @ud. (#1837) Thanks @wca4a. -- Gateway: prefer newest session metadata when combining stores. (#1823) Thanks @emanuelst. -- Web UI: keep sub-agent announce replies visible in WebChat. (#1977) Thanks @andrescardonas7. -- CI: increase Node heap size for macOS checks. (#1890) Thanks @realZachi. -- macOS: avoid crash when rendering code blocks by bumping Textual to 0.3.1. (#2033) Thanks @garricn. -- Browser: fall back to URL matching for extension relay target resolution. (#1999) Thanks @jonit-dev. -- Browser: route browser control via gateway/node; remove standalone browser control command and control URL config. -- Browser: route `browser.request` via node proxies when available; honor proxy timeouts; derive browser ports from `gateway.port`. -- Update: ignore dist/control-ui for dirty checks and restore after ui builds. (#1976) Thanks @Glucksberg. -- Build: bundle A2UI assets during build and stop tracking generated bundles. (#2455) Thanks @0oAstro. -- Telegram: allow caption param for media sends. (#1888) Thanks @mguellsegarra. -- Telegram: support plugin sendPayload channelData (media/buttons) and validate plugin commands. (#1917) Thanks @JoshuaLelon. -- Telegram: avoid block replies when streaming is disabled. (#1885) Thanks @ivancasco. -- Docs: keep docs header sticky so navbar stays visible while scrolling. (#2445) Thanks @chenyuan99. -- Docs: update exe.dev install instructions. (#https://github.com/moltbot/moltbot/pull/3047) Thanks @zackerthescar. -- Security: use Windows ACLs for permission audits and fixes on Windows. (#1957) -- Auth: show copyable Google auth URL after ASCII prompt. (#1787) Thanks @robbyczgw-cla. -- Routing: precompile session key regexes. (#1697) Thanks @Ray0907. -- TUI: avoid width overflow when rendering selection lists. (#1686) Thanks @mossein. -- Telegram: keep topic IDs in restart sentinel notifications. (#1807) Thanks @hsrvc. -- Telegram: add optional silent send flag (disable notifications). (#2382) Thanks @Suksham-sharma. -- Telegram: support editing sent messages via message(action="edit"). (#2394) Thanks @marcelomar21. -- Telegram: support quote replies for message tool and inbound context. (#2900) Thanks @aduk059. -- Telegram: add sticker receive/send with vision caching. (#2629) Thanks @longjos. -- Telegram: send sticker pixels to vision models. (#2650) -- Config: apply config.env before ${VAR} substitution. (#1813) Thanks @spanishflu-est1918. -- Slack: clear ack reaction after streamed replies. (#2044) Thanks @fancyboi999. -- macOS: keep custom SSH usernames in remote target. (#2046) Thanks @algal. -- CLI: use Node's module compile cache for faster startup. (#2808) Thanks @pi0. -- Routing: add per-account DM session scope and document multi-account isolation. (#3095) Thanks @jarvis-sam. +- Config: apply config.env before ${VAR} substitution. (#1813) +- Web search: add Brave freshness filter parameter. (#1688) Thanks @JonUleis. +- Control UI: improve chat session dropdown refresh, URL confirmation flow, config-save guardrails, and chat composer sizing. (#3682, #3578, #1707, #2950) +- Commands: group /help and /commands output with Telegram paging. (#2504) Thanks @hougangdev. +- CLI: use Node's compile cache for faster startup; recognize versioned node binaries (e.g., node-22). (#2808, #2490) Thanks @pi0, @David-Marsh-Photo. +- Agents: summarize dropped messages during compaction safeguard pruning. (#2509) Thanks @jogi47. +- Skills: add multi-image input support to Nano Banana Pro skill. (#1958) Thanks @tyler6204. +- Matrix: switch plugin SDK to @vector-im/matrix-bot-sdk. +- Docs: new deployment guides (Northflank, Render, Oracle, Raspberry Pi, GCP, DigitalOcean), Claude Max API Proxy, Vercel AI Gateway, migration guide, formal verification updates, and Fly private hardening. (#2167, #1975, #2333, #1871, #1848, #1870, #1875, #1901, #2381, #2289) +- Onboarding: add Venice API key to non-interactive flow; strengthen security warning copy. ### Breaking - **BREAKING:** Gateway auth mode "none" is removed; gateway now requires token/password (Tailscale Serve identity still allowed). ### Fixes -- Telegram: avoid silent empty replies by tracking normalization skips before fallback. (#3796) -- Mentions: honor mentionPatterns even when explicit mentions are present. (#3303) Thanks @HirokiKobayashi-R. -- Discord: restore username directory lookup in target resolution. (#3131) Thanks @bonald. -- Agents: align MiniMax base URL test expectation with default provider config. (#3131) Thanks @bonald. -- Agents: prevent retries on oversized image errors and surface size limits. (#2871) Thanks @Suksham-sharma. -- Agents: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94. -- Memory Search: keep auto provider model defaults and only include remote when configured. (#2576) Thanks @papago2355. -- Telegram: include AccountId in native command context for multi-agent routing. (#2942) Thanks @Chloe-VP. -- Telegram: handle video note attachments in media extraction. (#2905) Thanks @mylukin. -- TTS: read OPENAI_TTS_BASE_URL at runtime instead of module load to honor config.env. (#3341) Thanks @hclsys. -- macOS: auto-scroll to bottom when sending a new message while scrolled up. (#2471) Thanks @kennyklee. -- Web UI: auto-expand the chat compose textarea while typing (with sensible max height). (#2950) Thanks @shivamraut101. -- Gateway: prevent crashes on transient network errors (fetch failures, timeouts, DNS). Added fatal error detection to only exit on truly critical errors. Fixes #2895, #2879, #2873. (#2980) Thanks @elliotsecops. -- Agents: guard channel tool listActions to avoid plugin crashes. (#2859) Thanks @mbelinky. -- Discord: stop resolveDiscordTarget from passing directory params into messaging target parsers. Fixes #3167. Thanks @thewilloftheshadow. -- Discord: avoid resolving bare channel names to user DMs when a username matches. Thanks @thewilloftheshadow. -- Discord: fix directory config type import for target resolution. Thanks @thewilloftheshadow. -- Providers: update MiniMax API endpoint and compatibility mode. (#3064) Thanks @hlbbbbbbb. -- Telegram: treat more network errors as recoverable in polling. (#3013) Thanks @ryancontent. -- Discord: resolve usernames to user IDs for outbound messages. (#2649) Thanks @nonggialiang. -- Providers: update Moonshot Kimi model references to kimi-k2.5. (#2762) Thanks @MarvinCui. -- Gateway: suppress AbortError and transient network errors in unhandled rejections. (#2451) Thanks @Glucksberg. -- TTS: keep /tts status replies on text-only commands and avoid duplicate block-stream audio. (#2451) Thanks @Glucksberg. -- Security: pin npm overrides to keep tar@7.5.4 for install toolchains. -- Security: properly test Windows ACL audit for config includes. (#2403) Thanks @dominicnunez. -- CLI: recognize versioned Node executables when parsing argv. (#2490) Thanks @David-Marsh-Photo. -- CLI: avoid prompting for gateway runtime under the spinner. (#2874) -- BlueBubbles: coalesce inbound URL link preview messages. (#1981) Thanks @tyler6204. -- Cron: allow payloads containing "heartbeat" in event filter. (#2219) Thanks @dwfinkelstein. -- CLI: avoid loading config for global help/version while registering plugin commands. (#2212) Thanks @dial481. -- Agents: include memory.md when bootstrapping memory context. (#2318) Thanks @czekaj. -- Agents: release session locks on process termination and cover more signals. (#2483) Thanks @janeexai. -- Agents: skip cooldowned providers during model failover. (#2143) Thanks @YiWang24. -- Telegram: harden polling + retry behavior for transient network errors and Node 22 transport issues. (#2420) Thanks @techboss. -- Telegram: ignore non-forum group message_thread_id while preserving DM thread sessions. (#2731) Thanks @dylanneve1. -- Telegram: wrap reasoning italics per line to avoid raw underscores. (#2181) Thanks @YuriNachos. -- Telegram: centralize API error logging for delivery and bot calls. (#2492) Thanks @altryne. -- Voice Call: enforce Twilio webhook signature verification for ngrok URLs; disable ngrok free tier bypass by default. -- Security: harden Tailscale Serve auth by validating identity via local tailscaled before trusting headers. -- Media: fix text attachment MIME misclassification with CSV/TSV inference and UTF-16 detection; add XML attribute escaping for file output. (#3628) Thanks @frankekn. -- Build: align memory-core peer dependency with lockfile. -- Security: add mDNS discovery mode with minimal default to reduce information disclosure. (#1882) Thanks @orlyjamie. -- Security: harden URL fetches with DNS pinning to reduce rebinding risk. Thanks Chris Zheng. -- Web UI: improve WebChat image paste previews and allow image-only sends. (#1925) Thanks @smartprogrammer93. -- Security: wrap external hook content by default with a per-hook opt-out. (#1827) Thanks @mertcicekci0. -- Gateway: default auth now fail-closed (token/password required; Tailscale Serve identity remains allowed). -- Gateway: treat loopback + non-local Host connections as remote unless trusted proxy headers are present. -- Onboarding: remove unsupported gateway auth "off" choice from onboarding/configure flows and CLI flags. +- Security: harden SSH tunnel target parsing to prevent option injection/DoS. (#4001) Thanks @YLChen-007. +- Security: prevent PATH injection in exec sandbox; harden file serving; pin DNS in URL fetches; verify Twilio webhooks; fix LINE webhook timing-attack edge case; validate Tailscale Serve identity; flag loopback Control UI with auth disabled as critical. (#1616, #1795) +- Gateway: prevent crashes on transient network errors, suppress AbortError/unhandled rejections, sanitize error responses, clean session locks on exit, and harden reverse proxy handling for unauthenticated proxied connects. (#2980, #2451, #2483, #1795) +- Config: auto-migrate legacy state/config paths; honor state dir overrides. +- Packaging: include missing dist/shared and dist/link-understanding outputs in npm tarball installs. +- Telegram: avoid silent empty replies, improve polling/network recovery, handle video notes, keep DM thread sessions, ignore non-forum message_thread_id, centralize API error logging, include AccountId in native command context. (#3796, #3013, #2905, #2731, #2492, #2942) +- Discord: restore username resolution, resolve outbound usernames to IDs, honor threadId replies, guard forum thread access. (#3131, #2649) +- BlueBubbles: coalesce URL link previews, improve reaction handling, preserve reply-tag GUIDs. (#1981, #1641) +- Voice Call: prevent TTS overlap, validate env-var config, return TwiML for conversation calls. (#1713, #1634) +- Media: fix text attachment MIME classification + XML escaping on Windows. (#3628, #3750) +- Models: inherit provider baseUrl/api for inline models. (#2740) Thanks @lploc94. +- Web UI: auto-scroll on send; fix textarea sizing; improve chat session refresh. (#2471, #2950, #3682) +- CLI/TUI: resume sessions cleanly; guard width overflow; avoid spinner prompt race. (#1921, #1686, #2874) +- Slack: fix file downloads failing on redirects with missing auth header. (#1936) +- iMessage: normalize messaging targets. (#1708) +- Signal: fix reactions and add configurable startup timeout. (#1651, #1677) +- Matrix: decrypt E2EE media with size guard. (#1744) -## 2026.1.24-3 - -### Fixes -- Slack: fix image downloads failing due to missing Authorization header on cross-origin redirects. (#1936) Thanks @sanderhelgesen. -- Gateway: harden reverse proxy handling for local-client detection and unauthenticated proxied connects. (#1795) Thanks @orlyjamie. -- Security audit: flag loopback Control UI with auth disabled as critical. (#1795) Thanks @orlyjamie. -- CLI: resume claude-cli sessions and stream CLI replies to TUI clients. (#1921) Thanks @rmorse. - -## 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 diff --git a/apps/android/app/build.gradle.kts b/apps/android/app/build.gradle.kts index 3ddcb3b81..97f9a250e 100644 --- a/apps/android/app/build.gradle.kts +++ b/apps/android/app/build.gradle.kts @@ -21,8 +21,8 @@ android { applicationId = "bot.molt.android" minSdk = 31 targetSdk = 36 - versionCode = 202601260 - versionName = "2026.1.27-beta.1" + versionCode = 202601290 + versionName = "2026.1.29" } buildTypes { diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist index d3e398ab4..fe6a6154f 100644 --- a/apps/ios/Sources/Info.plist +++ b/apps/ios/Sources/Info.plist @@ -19,9 +19,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.1.27-beta.1 + 2026.1.29 CFBundleVersion - 20260126 + 20260129 NSAppTransportSecurity NSAllowsArbitraryLoadsInWebContent diff --git a/apps/ios/Tests/Info.plist b/apps/ios/Tests/Info.plist index a5336c6ad..847ca3b01 100644 --- a/apps/ios/Tests/Info.plist +++ b/apps/ios/Tests/Info.plist @@ -17,8 +17,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2026.1.27-beta.1 + 2026.1.29 CFBundleVersion - 20260126 + 20260129 diff --git a/apps/ios/project.yml b/apps/ios/project.yml index a6728cd98..1d31171b9 100644 --- a/apps/ios/project.yml +++ b/apps/ios/project.yml @@ -81,8 +81,8 @@ targets: properties: CFBundleDisplayName: Moltbot CFBundleIconName: AppIcon - CFBundleShortVersionString: "2026.1.27-beta.1" - CFBundleVersion: "20260126" + CFBundleShortVersionString: "2026.1.29" + CFBundleVersion: "20260129" UILaunchScreen: {} UIApplicationSceneManifest: UIApplicationSupportsMultipleScenes: false @@ -130,5 +130,5 @@ targets: path: Tests/Info.plist properties: CFBundleDisplayName: MoltbotTests - CFBundleShortVersionString: "2026.1.27-beta.1" - CFBundleVersion: "20260126" + CFBundleShortVersionString: "2026.1.29" + CFBundleVersion: "20260129" diff --git a/apps/macos/Sources/Moltbot/Resources/Info.plist b/apps/macos/Sources/Moltbot/Resources/Info.plist index 0c0de8b9e..623ae02b0 100644 --- a/apps/macos/Sources/Moltbot/Resources/Info.plist +++ b/apps/macos/Sources/Moltbot/Resources/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2026.1.27-beta.1 + 2026.1.29 CFBundleVersion - 202601260 + 202601290 CFBundleIconFile Moltbot CFBundleURLTypes diff --git a/docs/platforms/fly.md b/docs/platforms/fly.md index d8db124ac..02d24e84a 100644 --- a/docs/platforms/fly.md +++ b/docs/platforms/fly.md @@ -185,7 +185,7 @@ cat > /data/moltbot.json << 'EOF' "bind": "auto" }, "meta": { - "lastTouchedVersion": "2026.1.27-beta.1" + "lastTouchedVersion": "2026.1.29" } } EOF diff --git a/docs/platforms/mac/release.md b/docs/platforms/mac/release.md index 237eac616..5be621bba 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=bot.molt.mac \ -APP_VERSION=2026.1.27-beta.1 \ +APP_VERSION=2026.1.29 \ 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/Moltbot.app dist/Moltbot-2026.1.27-beta.1.zip +ditto -c -k --sequesterRsrc --keepParent dist/Moltbot.app dist/Moltbot-2026.1.29.zip # Optional: also build a styled DMG for humans (drag to /Applications) -scripts/create-dmg.sh dist/Moltbot.app dist/Moltbot-2026.1.27-beta.1.dmg +scripts/create-dmg.sh dist/Moltbot.app dist/Moltbot-2026.1.29.dmg # Recommended: build + notarize/staple zip + DMG # First, create a keychain profile once: @@ -48,26 +48,26 @@ scripts/create-dmg.sh dist/Moltbot.app dist/Moltbot-2026.1.27-beta.1.dmg # --apple-id "" --team-id "" --password "" NOTARIZE=1 NOTARYTOOL_PROFILE=moltbot-notary \ BUNDLE_ID=bot.molt.mac \ -APP_VERSION=2026.1.27-beta.1 \ +APP_VERSION=2026.1.29 \ 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/Moltbot.app.dSYM dist/Moltbot-2026.1.27-beta.1.dSYM.zip +ditto -c -k --keepParent apps/macos/.build/release/Moltbot.app.dSYM dist/Moltbot-2026.1.29.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/Moltbot-2026.1.27-beta.1.zip https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml +SPARKLE_PRIVATE_KEY_FILE=/path/to/ed25519-private-key scripts/make_appcast.sh dist/Moltbot-2026.1.29.zip https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml ``` Generates HTML release notes from `CHANGELOG.md` (via [`scripts/changelog-to-html.sh`](https://github.com/moltbot/moltbot/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 `Moltbot-2026.1.27-beta.1.zip` (and `Moltbot-2026.1.27-beta.1.dSYM.zip`) to the GitHub release for tag `v2026.1.27-beta.1`. +- Upload `Moltbot-2026.1.29.zip` (and `Moltbot-2026.1.29.dSYM.zip`) to the GitHub release for tag `v2026.1.29`. - Ensure the raw appcast URL matches the baked feed: `https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml`. - Sanity checks: - `curl -I https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml` returns 200. diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index e648fb33c..77a59df29 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.27-beta.1`). +- [ ] Bump `package.json` version (e.g., `2026.1.29`). - [ ] Run `pnpm plugins:sync` to align extension package versions + changelogs. - [ ] Update CLI/version strings: [`src/cli/program.ts`](https://github.com/moltbot/moltbot/blob/main/src/cli/program.ts) and the Baileys user agent in [`src/provider-web.ts`](https://github.com/moltbot/moltbot/blob/main/src/provider-web.ts). - [ ] Confirm package metadata (name, description, repository, keywords, license) and `bin` map points to [`moltbot.mjs`](https://github.com/moltbot/moltbot/blob/main/moltbot.mjs) for `moltbot`. diff --git a/extensions/bluebubbles/package.json b/extensions/bluebubbles/package.json index fc1ac34ae..7ffa39845 100644 --- a/extensions/bluebubbles/package.json +++ b/extensions/bluebubbles/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/bluebubbles", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot BlueBubbles channel plugin", "moltbot": { diff --git a/extensions/copilot-proxy/package.json b/extensions/copilot-proxy/package.json index 2d4753446..fd8cbcbbb 100644 --- a/extensions/copilot-proxy/package.json +++ b/extensions/copilot-proxy/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/copilot-proxy", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Copilot Proxy provider plugin", "moltbot": { diff --git a/extensions/diagnostics-otel/package.json b/extensions/diagnostics-otel/package.json index f6560702b..3c6fc084b 100644 --- a/extensions/diagnostics-otel/package.json +++ b/extensions/diagnostics-otel/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/diagnostics-otel", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot diagnostics OpenTelemetry exporter", "moltbot": { diff --git a/extensions/discord/package.json b/extensions/discord/package.json index 9921468b4..9e068ee36 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/discord", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Discord channel plugin", "moltbot": { diff --git a/extensions/google-antigravity-auth/package.json b/extensions/google-antigravity-auth/package.json index 8b13861ec..8c828cab0 100644 --- a/extensions/google-antigravity-auth/package.json +++ b/extensions/google-antigravity-auth/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/google-antigravity-auth", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Google Antigravity OAuth provider plugin", "moltbot": { diff --git a/extensions/google-gemini-cli-auth/package.json b/extensions/google-gemini-cli-auth/package.json index 59cbd52a9..452118707 100644 --- a/extensions/google-gemini-cli-auth/package.json +++ b/extensions/google-gemini-cli-auth/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/google-gemini-cli-auth", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Gemini CLI OAuth provider plugin", "moltbot": { diff --git a/extensions/googlechat/package.json b/extensions/googlechat/package.json index 0a01621e6..3893d452a 100644 --- a/extensions/googlechat/package.json +++ b/extensions/googlechat/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/googlechat", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Google Chat channel plugin", "moltbot": { diff --git a/extensions/imessage/package.json b/extensions/imessage/package.json index 29ceb0631..e103bbcd7 100644 --- a/extensions/imessage/package.json +++ b/extensions/imessage/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/imessage", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot iMessage channel plugin", "moltbot": { diff --git a/extensions/line/package.json b/extensions/line/package.json index bd336b158..a45438252 100644 --- a/extensions/line/package.json +++ b/extensions/line/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/line", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot LINE channel plugin", "moltbot": { diff --git a/extensions/llm-task/package.json b/extensions/llm-task/package.json index 247d126a9..94b6e72b0 100644 --- a/extensions/llm-task/package.json +++ b/extensions/llm-task/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/llm-task", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot JSON-only LLM task plugin", "moltbot": { diff --git a/extensions/lobster/package.json b/extensions/lobster/package.json index c95d7021a..ee9b21ac7 100644 --- a/extensions/lobster/package.json +++ b/extensions/lobster/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/lobster", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Lobster workflow tool plugin (typed pipelines + resumable approvals)", "moltbot": { diff --git a/extensions/matrix/CHANGELOG.md b/extensions/matrix/CHANGELOG.md index 8b7dcb62c..6972449d9 100644 --- a/extensions/matrix/CHANGELOG.md +++ b/extensions/matrix/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/matrix/package.json b/extensions/matrix/package.json index abc608b5b..49c6d9236 100644 --- a/extensions/matrix/package.json +++ b/extensions/matrix/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/matrix", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Matrix channel plugin", "moltbot": { diff --git a/extensions/mattermost/package.json b/extensions/mattermost/package.json index 6e7d3f1fc..0063726eb 100644 --- a/extensions/mattermost/package.json +++ b/extensions/mattermost/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/mattermost", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Mattermost channel plugin", "moltbot": { diff --git a/extensions/memory-core/package.json b/extensions/memory-core/package.json index e863adbd2..b97fd2c68 100644 --- a/extensions/memory-core/package.json +++ b/extensions/memory-core/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/memory-core", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot core memory search plugin", "moltbot": { diff --git a/extensions/memory-lancedb/package.json b/extensions/memory-lancedb/package.json index 0e79ce83a..e0c16ca44 100644 --- a/extensions/memory-lancedb/package.json +++ b/extensions/memory-lancedb/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/memory-lancedb", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot LanceDB-backed long-term memory plugin with auto-recall/capture", "dependencies": { diff --git a/extensions/msteams/CHANGELOG.md b/extensions/msteams/CHANGELOG.md index 09a9e92bd..3a56e1a1b 100644 --- a/extensions/msteams/CHANGELOG.md +++ b/extensions/msteams/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/msteams/package.json b/extensions/msteams/package.json index 29e615862..4b070d3e2 100644 --- a/extensions/msteams/package.json +++ b/extensions/msteams/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/msteams", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Microsoft Teams channel plugin", "moltbot": { diff --git a/extensions/nextcloud-talk/package.json b/extensions/nextcloud-talk/package.json index 5e98956da..0f28a5279 100644 --- a/extensions/nextcloud-talk/package.json +++ b/extensions/nextcloud-talk/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/nextcloud-talk", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Nextcloud Talk channel plugin", "moltbot": { diff --git a/extensions/nostr/CHANGELOG.md b/extensions/nostr/CHANGELOG.md index 65ac7f56e..a1a3fdf63 100644 --- a/extensions/nostr/CHANGELOG.md +++ b/extensions/nostr/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/nostr/package.json b/extensions/nostr/package.json index 8ba9a48d0..ccc4346cf 100644 --- a/extensions/nostr/package.json +++ b/extensions/nostr/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/nostr", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Nostr channel plugin for NIP-04 encrypted DMs", "moltbot": { diff --git a/extensions/open-prose/package.json b/extensions/open-prose/package.json index 89904fcca..a71bbd0d0 100644 --- a/extensions/open-prose/package.json +++ b/extensions/open-prose/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/open-prose", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "OpenProse VM skill pack plugin (slash command + telemetry).", "moltbot": { diff --git a/extensions/signal/package.json b/extensions/signal/package.json index 105a4fee8..986215e15 100644 --- a/extensions/signal/package.json +++ b/extensions/signal/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/signal", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Signal channel plugin", "moltbot": { diff --git a/extensions/slack/package.json b/extensions/slack/package.json index 8ada7de5f..9dc9c62a8 100644 --- a/extensions/slack/package.json +++ b/extensions/slack/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/slack", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Slack channel plugin", "moltbot": { diff --git a/extensions/telegram/package.json b/extensions/telegram/package.json index 0f485d029..a709e269f 100644 --- a/extensions/telegram/package.json +++ b/extensions/telegram/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/telegram", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Telegram channel plugin", "moltbot": { diff --git a/extensions/tlon/package.json b/extensions/tlon/package.json index 2df375b55..a8af17c9c 100644 --- a/extensions/tlon/package.json +++ b/extensions/tlon/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/tlon", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Tlon/Urbit channel plugin", "moltbot": { diff --git a/extensions/twitch/CHANGELOG.md b/extensions/twitch/CHANGELOG.md index 95b5ff2c7..25ff5fb6d 100644 --- a/extensions/twitch/CHANGELOG.md +++ b/extensions/twitch/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/twitch/package.json b/extensions/twitch/package.json index 6654f9bb7..d6a6b4474 100644 --- a/extensions/twitch/package.json +++ b/extensions/twitch/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/twitch", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "description": "Moltbot Twitch channel plugin", "type": "module", "dependencies": { diff --git a/extensions/voice-call/CHANGELOG.md b/extensions/voice-call/CHANGELOG.md index 312e95917..f3ec738ed 100644 --- a/extensions/voice-call/CHANGELOG.md +++ b/extensions/voice-call/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/voice-call/package.json b/extensions/voice-call/package.json index 72bfba03d..b99074aeb 100644 --- a/extensions/voice-call/package.json +++ b/extensions/voice-call/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/voice-call", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot voice-call plugin", "dependencies": { diff --git a/extensions/whatsapp/package.json b/extensions/whatsapp/package.json index d5139e18f..81bb39b71 100644 --- a/extensions/whatsapp/package.json +++ b/extensions/whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/whatsapp", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot WhatsApp channel plugin", "moltbot": { diff --git a/extensions/zalo/CHANGELOG.md b/extensions/zalo/CHANGELOG.md index 55766ea8e..6134b5275 100644 --- a/extensions/zalo/CHANGELOG.md +++ b/extensions/zalo/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/zalo/package.json b/extensions/zalo/package.json index 2a6cf9a5f..61350d86b 100644 --- a/extensions/zalo/package.json +++ b/extensions/zalo/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/zalo", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Zalo channel plugin", "moltbot": { diff --git a/extensions/zalouser/CHANGELOG.md b/extensions/zalouser/CHANGELOG.md index e189e2e45..079990a79 100644 --- a/extensions/zalouser/CHANGELOG.md +++ b/extensions/zalouser/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 2026.1.27-beta.1 +## 2026.1.29 ### Changes - Version alignment with core Moltbot release numbers. diff --git a/extensions/zalouser/package.json b/extensions/zalouser/package.json index 6bace36e8..eb5f19d25 100644 --- a/extensions/zalouser/package.json +++ b/extensions/zalouser/package.json @@ -1,6 +1,6 @@ { "name": "@moltbot/zalouser", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "type": "module", "description": "Moltbot Zalo Personal Account plugin via zca-cli", "dependencies": { diff --git a/package.json b/package.json index 04322f3af..4d38edf18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "moltbot", - "version": "2026.1.27-beta.1", + "version": "2026.1.29", "description": "WhatsApp gateway CLI (Baileys web) with Pi RPC agent", "type": "module", "main": "dist/index.js", diff --git a/src/commands/gateway-status.test.ts b/src/commands/gateway-status.test.ts index 09547a83d..5e816f581 100644 --- a/src/commands/gateway-status.test.ts +++ b/src/commands/gateway-status.test.ts @@ -192,6 +192,45 @@ describe("gateway-status command", () => { expect(targets.some((t) => t.kind === "sshTunnel")).toBe(true); }); + it("skips invalid ssh-auto discovery targets", async () => { + const runtimeLogs: string[] = []; + const runtime = { + log: (msg: string) => runtimeLogs.push(msg), + error: (_msg: string) => {}, + exit: (code: number) => { + throw new Error(`__exit__:${code}`); + }, + }; + + const originalUser = process.env.USER; + try { + process.env.USER = "steipete"; + loadConfig.mockReturnValueOnce({ + gateway: { + mode: "remote", + remote: {}, + }, + }); + discoverGatewayBeacons.mockResolvedValueOnce([ + { tailnetDns: "-V" }, + { tailnetDns: "goodhost" }, + ]); + + startSshPortForward.mockClear(); + const { gatewayStatusCommand } = await import("./gateway-status.js"); + await gatewayStatusCommand( + { timeout: "1000", json: true, sshAuto: true }, + runtime as unknown as import("../runtime.js").RuntimeEnv, + ); + + expect(startSshPortForward).toHaveBeenCalledTimes(1); + const call = startSshPortForward.mock.calls[0]?.[0] as { target: string }; + expect(call.target).toBe("steipete@goodhost"); + } finally { + process.env.USER = originalUser; + } + }); + it("infers SSH target from gateway.remote.url and ssh config", async () => { const runtimeLogs: string[] = []; const runtime = { diff --git a/src/commands/gateway-status.ts b/src/commands/gateway-status.ts index 3a51a7886..a5a34d6e4 100644 --- a/src/commands/gateway-status.ts +++ b/src/commands/gateway-status.ts @@ -107,7 +107,9 @@ export async function gatewayStatusCommand( const base = user ? `${user}@${host.trim()}` : host.trim(); return sshPort !== 22 ? `${base}:${sshPort}` : base; }) - .filter((x): x is string => Boolean(x)); + .filter((candidate): candidate is string => + Boolean(candidate && parseSshTarget(candidate)), + ); if (candidates.length > 0) sshTarget = candidates[0] ?? null; } diff --git a/src/gateway/tools-invoke-http.ts b/src/gateway/tools-invoke-http.ts index fa45bf3dc..bf05e2822 100644 --- a/src/gateway/tools-invoke-http.ts +++ b/src/gateway/tools-invoke-http.ts @@ -18,6 +18,7 @@ import { import { loadConfig } from "../config/config.js"; import { resolveMainSessionKey } from "../config/sessions.js"; import { logWarn } from "../logger.js"; +import { isTestDefaultMemorySlotDisabled } from "../plugins/config-state.js"; import { getPluginToolMeta } from "../plugins/tools.js"; import { isSubagentSessionKey } from "../routing/session-key.js"; import { normalizeMessageChannel } from "../utils/message-channel.js"; @@ -33,6 +34,7 @@ import { } from "./http-common.js"; const DEFAULT_BODY_BYTES = 2 * 1024 * 1024; +const MEMORY_TOOL_NAMES = new Set(["memory_search", "memory_get"]); type ToolsInvokeBody = { tool?: unknown; @@ -47,6 +49,26 @@ function resolveSessionKeyFromBody(body: ToolsInvokeBody): string | undefined { return undefined; } +function resolveMemoryToolDisableReasons(cfg: ReturnType): string[] { + if (!process.env.VITEST) return []; + const reasons: string[] = []; + const plugins = cfg.plugins; + const slotRaw = plugins?.slots?.memory; + const slotDisabled = + slotRaw === null || (typeof slotRaw === "string" && slotRaw.trim().toLowerCase() === "none"); + const pluginsDisabled = plugins?.enabled === false; + const defaultDisabled = isTestDefaultMemorySlotDisabled(cfg); + + if (pluginsDisabled) reasons.push("plugins.enabled=false"); + if (slotDisabled) { + reasons.push(slotRaw === null ? "plugins.slots.memory=null" : 'plugins.slots.memory="none"'); + } + if (!pluginsDisabled && !slotDisabled && defaultDisabled) { + reasons.push("memory plugin disabled by test default"); + } + return reasons; +} + function mergeActionIntoArgsIfSupported(params: { toolSchema: unknown; action: string | undefined; @@ -103,6 +125,23 @@ export async function handleToolsInvokeHttpRequest( return true; } + if (process.env.VITEST && MEMORY_TOOL_NAMES.has(toolName)) { + const reasons = resolveMemoryToolDisableReasons(cfg); + if (reasons.length > 0) { + const suffix = reasons.length > 0 ? ` (${reasons.join(", ")})` : ""; + sendJson(res, 400, { + ok: false, + error: { + type: "invalid_request", + message: + `memory tools are disabled in tests${suffix}. ` + + 'Enable by setting plugins.slots.memory="memory-core" (and ensure plugins.enabled is not false).', + }, + }); + return true; + } + } + const action = typeof body.action === "string" ? body.action.trim() : undefined; const argsRaw = body.args; diff --git a/src/infra/ssh-config.test.ts b/src/infra/ssh-config.test.ts index 8f3248e0c..48a8bf310 100644 --- a/src/infra/ssh-config.test.ts +++ b/src/infra/ssh-config.test.ts @@ -54,6 +54,8 @@ describe("ssh-config", () => { expect(config?.host).toBe("peters-mac-studio-1.sheep-coho.ts.net"); expect(config?.port).toBe(2222); expect(config?.identityFiles).toEqual(["/tmp/id_ed25519"]); + const args = spawnMock.mock.calls[0]?.[1] as string[] | undefined; + expect(args?.slice(-2)).toEqual(["--", "me@alias"]); }); it("returns null when ssh -G fails", async () => { diff --git a/src/infra/ssh-config.ts b/src/infra/ssh-config.ts index 037405e8c..0b0e95015 100644 --- a/src/infra/ssh-config.ts +++ b/src/infra/ssh-config.ts @@ -58,7 +58,8 @@ export async function resolveSshConfig( args.push("-i", opts.identity.trim()); } const userHost = target.user ? `${target.user}@${target.host}` : target.host; - args.push(userHost); + // Use "--" so userHost can't be parsed as an ssh option. + args.push("--", userHost); return await new Promise((resolve) => { const child = spawn(sshPath, args, { diff --git a/src/infra/ssh-tunnel.test.ts b/src/infra/ssh-tunnel.test.ts new file mode 100644 index 000000000..d31f25d1a --- /dev/null +++ b/src/infra/ssh-tunnel.test.ts @@ -0,0 +1,27 @@ +import { describe, expect, it } from "vitest"; + +import { parseSshTarget } from "./ssh-tunnel.js"; + +describe("parseSshTarget", () => { + it("parses user@host:port targets", () => { + expect(parseSshTarget("me@example.com:2222")).toEqual({ + user: "me", + host: "example.com", + port: 2222, + }); + }); + + it("parses host-only targets with default port", () => { + expect(parseSshTarget("example.com")).toEqual({ + user: undefined, + host: "example.com", + port: 22, + }); + }); + + it("rejects hostnames that start with '-'", () => { + expect(parseSshTarget("-V")).toBeNull(); + expect(parseSshTarget("me@-badhost")).toBeNull(); + expect(parseSshTarget("-oProxyCommand=echo")).toBeNull(); + }); +}); diff --git a/src/infra/ssh-tunnel.ts b/src/infra/ssh-tunnel.ts index 8b3c7693b..399dc22e3 100644 --- a/src/infra/ssh-tunnel.ts +++ b/src/infra/ssh-tunnel.ts @@ -41,10 +41,14 @@ export function parseSshTarget(raw: string): SshParsedTarget | null { const portRaw = hostPart.slice(colonIdx + 1).trim(); const port = Number.parseInt(portRaw, 10); if (!host || !Number.isFinite(port) || port <= 0) return null; + // Security: Reject hostnames starting with '-' to prevent argument injection + if (host.startsWith("-")) return null; return { user: userPart, host, port }; } if (!hostPart) return null; + // Security: Reject hostnames starting with '-' to prevent argument injection + if (hostPart.startsWith("-")) return null; return { user: userPart, host: hostPart, port: 22 }; } @@ -134,7 +138,8 @@ export async function startSshPortForward(opts: { if (opts.identity?.trim()) { args.push("-i", opts.identity.trim()); } - args.push(userHost); + // Security: Use '--' to prevent userHost from being interpreted as an option + args.push("--", userHost); const stderr: string[] = []; const child = spawn("/usr/bin/ssh", args, { diff --git a/src/plugins/config-state.ts b/src/plugins/config-state.ts index bf44b5fe4..5bfff7dbc 100644 --- a/src/plugins/config-state.ts +++ b/src/plugins/config-state.ts @@ -64,6 +64,72 @@ export const normalizePluginsConfig = ( }; }; +const hasExplicitMemorySlot = (plugins?: MoltbotConfig["plugins"]) => + Boolean(plugins?.slots && Object.prototype.hasOwnProperty.call(plugins.slots, "memory")); + +const hasExplicitMemoryEntry = (plugins?: MoltbotConfig["plugins"]) => + Boolean(plugins?.entries && Object.prototype.hasOwnProperty.call(plugins.entries, "memory-core")); + +const hasExplicitPluginConfig = (plugins?: MoltbotConfig["plugins"]) => { + if (!plugins) return false; + if (typeof plugins.enabled === "boolean") return true; + if (Array.isArray(plugins.allow) && plugins.allow.length > 0) return true; + if (Array.isArray(plugins.deny) && plugins.deny.length > 0) return true; + if (plugins.load?.paths && Array.isArray(plugins.load.paths) && plugins.load.paths.length > 0) + return true; + if (plugins.slots && Object.keys(plugins.slots).length > 0) return true; + if (plugins.entries && Object.keys(plugins.entries).length > 0) return true; + return false; +}; + +export function applyTestPluginDefaults( + cfg: MoltbotConfig, + env: NodeJS.ProcessEnv = process.env, +): MoltbotConfig { + if (!env.VITEST) return cfg; + const plugins = cfg.plugins; + const explicitConfig = hasExplicitPluginConfig(plugins); + if (explicitConfig) { + if (hasExplicitMemorySlot(plugins) || hasExplicitMemoryEntry(plugins)) { + return cfg; + } + return { + ...cfg, + plugins: { + ...plugins, + slots: { + ...plugins?.slots, + memory: "none", + }, + }, + }; + } + + return { + ...cfg, + plugins: { + ...plugins, + enabled: false, + slots: { + ...plugins?.slots, + memory: "none", + }, + }, + }; +} + +export function isTestDefaultMemorySlotDisabled( + cfg: MoltbotConfig, + env: NodeJS.ProcessEnv = process.env, +): boolean { + if (!env.VITEST) return false; + const plugins = cfg.plugins; + if (hasExplicitMemorySlot(plugins) || hasExplicitMemoryEntry(plugins)) { + return false; + } + return true; +} + export function resolveEnableState( id: string, origin: PluginRecord["origin"], diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 174441bfc..79c785f27 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -10,6 +10,7 @@ import { resolveUserPath } from "../utils.js"; import { discoverMoltbotPlugins } from "./discovery.js"; import { loadPluginManifestRegistry } from "./manifest-registry.js"; import { + applyTestPluginDefaults, normalizePluginsConfig, resolveEnableState, resolveMemorySlotDecision, @@ -162,7 +163,7 @@ function pushDiagnostics(diagnostics: PluginDiagnostic[], append: PluginDiagnost } export function loadMoltbotPlugins(options: PluginLoadOptions = {}): PluginRegistry { - const cfg = options.config ?? {}; + const cfg = applyTestPluginDefaults(options.config ?? {}); const logger = options.logger ?? defaultLogger(); const validateOnly = options.mode === "validate"; const normalized = normalizePluginsConfig(cfg.plugins);