Commit Graph

14133 Commits

Author SHA1 Message Date
Devin Robison b7d70ade3b
Fix/telegram writeback admin scope gate (#54561)
* fix(telegram): require operator.admin for legacy target writeback persistence

* Address claude feedback

* Update extensions/telegram/src/target-writeback.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Remove stray brace

* Add updated docs

* Add missing test file, address codex concerns

* Fix test formatting error

* Address comments, fix tests

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-25 12:12:09 -06:00
Andrii Furmanets 89c4c674d1
fix(compaction): surface safeguard cancel reasons and clarify /compact skips (#51072)
Merged via squash.

Prepared head SHA: f1dbef0443
Co-authored-by: afurm <6375192+afurm@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 11:03:22 -07:00
M1a0 7847e67f8a
plugin-runtime: expose runHeartbeatOnce in system API (#40299)
* plugin-runtime: expose runHeartbeatOnce in system API

Plugins that enqueue system events and need the agent to deliver
responses to the originating channel currently have no way to
override the default `heartbeat.target: "none"` behaviour.

Expose `runHeartbeatOnce` in the plugin runtime `system` namespace
so plugins can trigger a single heartbeat cycle with an explicit
`heartbeat: { target: "last" }` override — the same pattern the
cron service already uses (see #28508).

Changes:
- Add `RunHeartbeatOnceOptions` type and `runHeartbeatOnce` to
  `PluginRuntimeCore.system` (types-core.ts)
- Wire the function through a thin wrapper in runtime-system.ts
- Update the test-utils plugin-runtime mock

Made-with: Cursor

* feat(plugins): expose runHeartbeatOnce in system API (#40299) (thanks @loveyana)

---------

Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 10:47:01 -07:00
chenxingzhen 4ae4d1fabe
fix: mid-turn 429 rate limit silent no-reply and context engine registration failure (#50930)
Merged via squash.

Prepared head SHA: eea7800df3
Co-authored-by: infichen <13826604+infichen@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 10:43:08 -07:00
Matt Van Horn e0972db7a2
fix: stop leaking reply tags in iMessage outbound text (#39512) (thanks @mvanhorn)
* fix: stop leaking reply tags in iMessage outbound text (#39512) (thanks @mvanhorn)

* fix: preserve iMessage outbound whitespace without directive tags (#39512) (thanks @mvanhorn)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 23:00:16 +05:30
Jackal Xin 2de32fbf14
fix: reconcile session compaction count after late compaction success (#45493)
Merged via squash.

Prepared head SHA: d0715a5555
Co-authored-by: jackal092927 <3854860+jackal092927@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-25 10:00:41 -07:00
Peter Steinberger 97a7e93db4
build: prepare 2026.3.24 release 2026-03-25 09:31:05 -07:00
liyuan97 e2e9f979ca
feat(minimax): add image generation provider and trim model catalog to M2.7 (#54487)
* feat(minimax): add image generation and TTS providers, trim TUI model list

Register MiniMax image-01 and speech-2.8 models as plugin providers for
the image_generate and TTS tools. Both resolve CN/global base URLs from
the configured model endpoint origin.

- Image generation: base64 response, aspect-ratio support, image-to-image
  via subject_reference, registered for minimax and minimax-portal
- TTS: speech-2.8-turbo (default) and speech-2.8-hd, hex-encoded audio,
  voice listing via get_voice API, telephony PCM support
- Add MiniMax to TTS auto-detection cascade (after ElevenLabs, before
  Microsoft) and TTS config section
- Remove MiniMax-VL-01, M2, M2.1, M2.5 and variants from TUI picker;
  keep M2.7 and M2.7-highspeed only (backend routing unchanged)

* feat(minimax): trim legacy model catalog to M2.7 only

Cherry-picked from temp/feat/minimax-trim-legacy-models (949ed28).
Removes MiniMax-VL-01, M2, M2.1, M2.5 and variants from the model
catalog, model order, modern model matchers, OAuth config, docs, and
tests. Keeps only M2.7 and M2.7-highspeed.

Conflicts resolved:
- provider-catalog.ts: removed MINIMAX_TUI_MODELS filter (no longer
  needed since source array is now M2.7-only)
- index.ts: kept image generation + speech provider registrations
  (added by this branch), moved media understanding registrations
  earlier (as intended by the cherry-picked commit)

* fix(minimax): update discovery contract test to reflect M2.7-only catalog

Cherry-picked from temp/feat/minimax-trim-legacy-models (2c750cb).

* feat(minimax): add web search provider and register in plugin entry

* fix(minimax): resolve OAuth credentials for TTS speech provider

* MiniMax: remove web search and TTS providers

* fix(minimax): throw on empty images array after generation failure

* feat(minimax): add image generation provider and trim catalog to M2.7 (#54487) (thanks @liyuan97)

---------

Co-authored-by: tars90percent <tars@minimaxi.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 09:29:35 -07:00
xieyongliang 7cc86e9685
fix(release): add plugin-sdk:check-exports to release:check (#54283)
* fix(plugins): resolve sdk alias from import.meta.url for external plugins

When a plugin is installed outside the openclaw package (e.g.
~/.openclaw/extensions/), resolveLoaderPluginSdkPackageRoot() fails to
locate the openclaw root via cwd or argv1 hints, resulting in an empty
alias map. Jiti then cannot resolve openclaw/plugin-sdk/* imports and
the plugin fails to load with "Cannot find module".

Since sdk-alias.ts is always compiled into the openclaw package itself,
import.meta.url reliably points inside the installation directory. Add it
as an unconditional fallback in resolveLoaderPluginSdkPackageRoot() so
external plugins can always resolve the plugin SDK.

Fixes: Error: Cannot find module 'openclaw/plugin-sdk/plugin-entry'

* fix(plugins): pass loader moduleUrl to resolve sdk alias for external plugins

The previous approach of adding import.meta.url as an unconditional
fallback inside resolveLoaderPluginSdkPackageRoot() broke test isolation:
tests that expected null from untrusted fixtures started finding the real
openclaw root. Revert that and instead thread an optional moduleUrl through
buildPluginLoaderAliasMap → resolvePluginSdkScopedAliasMap →
listPluginSdkExportedSubpaths → resolveLoaderPluginSdkPackageRoot.

loader.ts passes its own import.meta.url as the hint, which is always
inside the openclaw installation. This guarantees the sdk alias map is
built correctly even when argv1 does not resolve to the openclaw root
(e.g. single-binary distributions, custom launchers, or Docker images
where the binary wrapper is not a standard npm symlink).

Tests that call sdk-alias helpers directly without moduleUrl are
unaffected and continue to enforce the existing isolation semantics.
A new test covers the moduleUrl resolution path explicitly.

* fix(plugins): use existing fixture file for moduleUrl hint in test

The previous test pointed loaderModuleUrl to dist/plugins/loader.js
which is not created by createPluginSdkAliasFixture, causing resolution
to fall back to the real openclaw root instead of the fixture root.
Use fixture.root/openclaw.mjs (created by the bin+marker fixture) so
the moduleUrl hint reliably resolves to the fixture package root.

* fix(test): use fixture.root as cwd in external plugin alias test

When process.cwd() is mocked to the external plugin dir, the
findNearestPluginSdkPackageRoot(process.cwd()) fallback resolves to
the real openclaw repo root in the CI test runner, making the test
resolve the wrong aliases. Using fixture.root as cwd ensures all
resolution paths consistently point to the fixture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(release): add plugin-sdk:check-exports to release:check

plugin-sdk subpath exports (e.g. openclaw/plugin-sdk/plugin-entry,
openclaw/plugin-sdk/provider-auth) were missing from the published
package.json, causing external plugins to fail at load time with
'Cannot find module openclaw/plugin-sdk/plugin-entry'.

Root cause: sync-plugin-sdk-exports.mjs syncs plugin-sdk-entrypoints.json
into package.json exports, but this sync was never validated in the
release:check pipeline. As a result, any drift between
plugin-sdk-entrypoints.json and the published package.json goes
undetected until users hit the runtime error.

Fix: add plugin-sdk:check-exports to release:check so the CI gate
fails loudly if the exports are out of sync before publishing.

* fix(test): isolate moduleUrl hint test from process.cwd() fallback

Use externalPluginRoot as cwd instead of fixture.root, so only the
moduleUrl hint can resolve the openclaw package root. Previously,
withCwd(fixture.root) allowed the process.cwd() fallback to also
resolve the fixture root, making the moduleUrl path untested.

Spotted by greptile-apps review on #54283.

* fix(test): use empty string to disable argv1 in moduleUrl hint test

Passing undefined for argv1 in buildPluginLoaderAliasMap triggers the
STARTUP_ARGV1 default (process.argv[1], the vitest runner binary inside
the openclaw repo). resolveTrustedOpenClawRootFromArgvHint then resolves
to the real openclaw root before the moduleUrl hint is checked, making
the test resolve wrong aliases.

Pass "" instead: falsy so the hint is skipped, but does not trigger the
default parameter value. Only the moduleUrl can bridge the gap.

Made-with: Cursor

* fix(plugins): thread moduleUrl through SDK alias resolution for external plugins (#54283) Thanks @xieyongliang

---------

Co-authored-by: bojsun <bojie.sun@bytedance.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Jerry <jerry@JerrydeMacBook-Air-2.local>
Co-authored-by: yongliang.xie <yongliang.xie@bytedance.com>
Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-25 09:11:17 -07:00
Devin Robison c2a2edb329
Fix local copied package installs honoring staged project .npmrc (#54543) 2026-03-25 09:59:33 -06:00
Nimrod Gutman edb5123f26
fix(sandbox): honor sandbox alsoAllow and explicit re-allows (#54492)
* fix(sandbox): honor effective sandbox alsoAllow policy

* fix(sandbox): prefer resolved sandbox context policy

* fix: honor sandbox alsoAllow policy (#54492) (thanks @ngutman)
2026-03-25 16:51:13 +02:00
Peter Steinberger e9ac2860c1
docs: prepare 2026.3.24-beta.2 release 2026-03-25 06:58:39 -07:00
Harold Hunt da60aff17a
Tests: isolate security audit home skill resolution (#54473)
Merged via squash.

Prepared head SHA: 82181e15fb
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-25 09:43:19 -04:00
Peter Steinberger ee714f5a42
test(media): make local roots fixture windows-safe 2026-03-25 06:24:39 -07:00
Peter Steinberger ea08f2eb8c
fix(runtime): support Node 22.14 installs 2026-03-25 06:22:18 -07:00
Peter Steinberger 66c88b4c77
fix(update): preflight npm target node engine 2026-03-25 06:01:20 -07:00
Peter Steinberger c92002e1de
fix(media): align outbound media access with fs policy 2026-03-25 05:50:21 -07:00
Peter Steinberger 9e95125f06
fix(config): ignore same-base correction publish warnings 2026-03-25 04:58:44 -07:00
Peter Steinberger d874f3970a
build: prepare 2026.3.24-beta.1 2026-03-25 04:41:26 -07:00
Peter Steinberger c3d1dbc696
refactor(openai): extract codex auth identity helper 2026-03-25 04:24:46 -07:00
Peter Steinberger d363af8c13
refactor(auth): separate profile ids from email metadata 2026-03-25 04:24:46 -07:00
Peter Steinberger b7f2b0d7b9
refactor: align pairing replies, daemon hints, and feishu mention policy 2026-03-25 04:22:53 -07:00
Ayaan Zaidi b497f3cda0
fix: normalize before_dispatch conversation id 2026-03-25 16:28:31 +05:30
ZhangXuan a10d587b41
fix: preserve before_dispatch delivery semantics (#50444) (thanks @gfzhx)
* Plugins: add before_dispatch hook

* Tests: fix before_dispatch hook mock typing

* Rebase: adapt before_dispatch hook to routeReplyRuntime refactor

* fix: preserve before_dispatch delivery semantics (#50444) (thanks @gfzhx)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 16:16:08 +05:30
Ayaan Zaidi 765182dcc6
fix: skip session:patch hook clone without listeners 2026-03-25 16:12:39 +05:30
Ayaan Zaidi ee0dcaa7b0 fix: unify log timestamp offsets (#38904) (thanks @sahilsatralkar) 2026-03-25 16:06:33 +05:30
Gracie Gould 3e2e9bc238
fix: isolate session:patch hook payload (#53880) (thanks @graciegould)
* gateway: make session:patch hook typed and non-blocking

* gateway(test): add session:patch hook coverage

* docs(gateway): clarify session:patch security note

* fix: address review feedback on session:patch hook

Remove unused createInternalHookEvent import and fix doc example
to use inline event.type check matching existing hook examples.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: isolate hook payload to prevent mutation leaking into response

Shallow-copy sessionEntry and patch in the session:patch hook event
so fire-and-forget handlers cannot mutate objects used by the
response path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: isolate session:patch hook payload (#53880) (thanks @graciegould)

---------

Co-authored-by: “graciegould” <“graciegould5@gmail.com”>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 15:59:38 +05:30
Liu Yuan 419824729a
fix: fail loud when PTY cursor mode is unknown (#51490) (thanks @liuy)
* fix(process): auto-detect PTY cursor key mode for send-keys

When a PTY session sends smkx (\x1b[?1h) or rmkx (\x1b[?1l) to switch
cursor key mode, send-keys now detects this and encodes cursor keys
accordingly.

- smkx/rmkx detection in handleStdout before sanitizeBinaryOutput
- cursorKeyMode stored in ProcessSession
- encodeKeySequence accepts cursorKeyMode parameter
- DECCKM_SS3_KEYS for application mode (arrows + home/end)
- CSI sequences for normal mode
- Modified keys (including alt) always use xterm modifier scheme
- Extract detectCursorKeyMode for unit testing
- Use lastIndexOf to find last toggle in chunk (later one wins)

Fixes #51488

* fix: fail loud when PTY cursor mode is unknown (#51490) (thanks @liuy)

* style: format process send-keys guard (#51490) (thanks @liuy)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 15:51:27 +05:30
Ayaan Zaidi 717ff0d667 fix: cover macOS Edge osascript fallback path (#48561) (thanks @zoherghadyali) 2026-03-25 15:47:04 +05:30
Zoher Ghadyali 2fe38b0201 fix(browser): add Edge LaunchServices bundle IDs for macOS default browser detection
macOS registers Edge as 'com.microsoft.edgemac' in LaunchServices, which
differs from the CFBundleIdentifier 'com.microsoft.Edge' in the app's own
Info.plist. Without recognising the LaunchServices IDs, Edge users who set
Edge as their default browser are not detected as having a Chromium browser.

Add the four com.microsoft.edgemac* variants to CHROMIUM_BUNDLE_IDS and a
corresponding test that mocks the LaunchServices → osascript resolution
path for Edge.
2026-03-25 15:47:04 +05:30
Sparkyrider 55dc6a8bb2 cron: queue isolated delivery awareness 2026-03-25 15:21:14 +05:30
Ayaan Zaidi 2a40612058 fix: make telegram thread create use topic payload (#54336) (thanks @andyliu) 2026-03-25 13:43:09 +05:30
Andy e1cd90db6e fix(cli): route telegram thread create to topic-create 2026-03-25 13:43:09 +05:30
ToToKr 4140100807
fix: clarify cron best-effort partial delivery status (#42535) (thanks @MoerAI)
* fix(cron): track and log bestEffort delivery failures, mark not delivered on partial failure

* fix(cron): cache successful results on partial failure to preserve replay idempotency

When a best-effort send partially fails, we now still cache the successful delivery results via rememberCompletedDirectCronDelivery. This prevents duplicate sends on same-process replay while still correctly marking the job as not fully delivered.

* fix(cron): preserve partial-failure state on replay (#27069)

* fix(cron): restore test infrastructure and fix formatting

* fix: clarify cron best-effort partial delivery status (#42535) (thanks @MoerAI)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 12:49:32 +05:30
Peter Steinberger b9857a2b79 test: allow daemon start hints to grow on linux (#54058) (thanks @byungsker) 2026-03-24 23:09:04 -07:00
Peter Steinberger f5408d82d2
refactor: unify gateway handshake timeout wiring 2026-03-24 22:53:55 -07:00
Peter Steinberger 258a214bcb
refactor: centralize daemon service start state flow 2026-03-24 22:49:34 -07:00
Liren Pan 773427470a test(auth): cover codex jwt fallback branches 2026-03-24 22:49:06 -07:00
Liren Pan b6e70a5cdd auth: derive codex oauth profile ids from jwt claims 2026-03-24 22:49:06 -07:00
dobbylorenzbot 717ee2fa59 fix(gateway): raise default connect challenge timeout 2026-03-24 22:38:17 -07:00
HCL db35f30005 fix: validate config before restart + derive loaded from real state
Address Codex P1 + Greptile P2:
- Move config validation before the restart attempt so invalid config
  is caught in the stop→start path (not just the already-loaded path)
- Derive service.loaded from actual isLoaded() after restart instead
  of hardcoded true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
2026-03-24 22:35:09 -07:00
HCL d2248534d8 fix(daemon): bootstrap stopped service on `gateway start`
After `gateway stop` (which runs `launchctl bootout`), `gateway start`
checks `isLoaded` → false → prints "not loaded" hints and exits.
The service is never re-bootstrapped, so `start` cannot recover from
`stop` — only `gateway install` works.

Root cause: src/cli/daemon-cli/lifecycle-core.ts:208-217 — runServiceStart
calls handleServiceNotLoaded which only prints hints, never attempts
service.restart() (which already handles bootstrap via
bootstrapLaunchAgentOrThrow at launchd.ts:598).

Fix: when service is not loaded, attempt service.restart() first (which
handles re-bootstrapping on all platforms). If restart fails (e.g. plist
was deleted, not just booted out), fall back to the existing hints.

The restart path is already proven: restartLaunchAgent (launchd.ts:556)
handles "not loaded" via bootstrapLaunchAgentOrThrow. This fix routes
the start command through the same recovery path.

Closes #53878

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
2026-03-24 22:35:09 -07:00
SUMUKH 149c4683a3
fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219)
* Telegram: format pairing challenge for easier copy

* test: restore Telegram pairing chatId assertion

* fix: scope Telegram pairing code blocks (#52784) (thanks @sumukhj1219)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 11:03:33 +05:30
w-sss 247f82119c
fix: improve Telegram 403 membership delivery errors (#53635) (thanks @w-sss)
* fix(telegram): improve error messages for 403 bot not member errors

- Detect 403 'bot is not a member' errors specifically
- Provide actionable guidance for users to fix the issue
- Fixes #48273 where outbound sendMessage fails with 403

Root cause:
When a Telegram bot tries to send a message to a channel/group it's not
a member of, the API returns 403 'bot is not a member of the channel chat'.
The error message was not clear about how to fix this.

Fix:
1. Detect 403 errors in wrapTelegramChatNotFoundError
2. Provide clear error message explaining the issue
3. Suggest adding the bot to the channel/group

* fix(telegram): fix regex precedence for 403 error detection

- Group alternatives correctly: /403.*(bot.*not.*member|bot was blocked)/i
- Require 403 for both alternatives (previously bot.*blocked matched any error)
- Update error message to cover both scenarios
- Fixes Greptile review feedback

* fix(telegram): correct regex alternation precedence for 403 errors

- Fix: /403.*(bot.*not.*member|bot was blocked)/ → /403.*(bot.*not.*member|bot.*blocked)/
- Ensures 403 requirement applies to both alternatives
- Fixes Greptile review comment on PR #48650

* fix(telegram): add 'bot was kicked' to 403 error regex and message

* fix(telegram): preserve membership delivery errors

* fix: improve Telegram 403 membership delivery errors (#53635) (thanks @w-sss)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:59:29 +05:30
Jealous 2c3cf4f387 chore(tts): rename VOICE_BUBBLE identifiers to OPUS and update docs 2026-03-25 10:49:21 +05:30
Peter Steinberger 46d3617d25
refactor: split gateway plugin bootstrap and registry surfaces 2026-03-24 22:16:26 -07:00
Josh Avant 10161c2d79
Plugins: enforce terminal hook decision semantics for tool/message guards (#54241)
* Plugins: enforce terminal hook decision policies

* Tests: assert terminal hook behavior in integration paths

* Docs: clarify terminal hook decision semantics

* Docs: add hook guard semantics to plugin guides

* Tests: isolate outbound format label expectations

* changelog

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>

---------

Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-03-25 00:11:13 -05:00
dongdong 5a5c5d4cde
fix: refresh DeepSeek pricing to current V3.2 rates (#54143) (thanks @arkyu2077)
* fix: add actual DeepSeek API pricing to model catalog

Replace zero-cost placeholder with real pricing from DeepSeek docs:
- deepseek-chat (V3): /bin/bash.27/1M input, .10/1M output, /bin/bash.07 cache read
- deepseek-reasoner (R1): /bin/bash.55/1M input, .19/1M output, /bin/bash.14 cache read

Fixes #54134

* fix: refresh DeepSeek pricing to current V3.2 rates

* fix: refresh DeepSeek pricing to current V3.2 rates (#54143) (thanks @arkyu2077)

---------

Co-authored-by: Jasmine Zhang <jasminezhang@192.168.1.75>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:34:03 +05:30
Quinn H. d43dda465d
fix: note marketplace streaming and ClawHub URL (#54160) (thanks @QuinnH496)
* fix: correct ClawHub URL in system prompt and use streaming download in marketplace

- Fix #54154: Change clawhub.com to clawhub.ai in system prompt
- Fix #54156: Replace arrayBuffer() with streaming pipeline for marketplace
  plugin downloads to avoid OOM on memory-constrained devices

* fix: guard marketplace archive stream body

* fix: note marketplace streaming and ClawHub URL (#54160) (thanks @QuinnH496)

---------

Co-authored-by: Li Enying <li.enying@openclaw.ai>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-25 10:29:21 +05:30
Peter Steinberger 61dd61e917
refactor: tighten split-runtime live-state guardrails 2026-03-24 21:58:50 -07:00