Compare commits

..

102 Commits

Author SHA1 Message Date
Kevin Lin
fc26444f6d fix: preserve runtime alias with compat wrappers 2026-05-07 15:11:29 -07:00
Marcus Castro
5df08201ff refactor(runtime): add prepared runtime foundation (#78248)
* docs(runtime): document prepared runtime guidance

* refactor(provider-runtime): thread prepared provider handles

* refactor(runtime-plan): add prepared runtime foundation

* refactor(outbound): add prepared channel runtime facts

* refactor(models): add scoped model reference helpers

* refactor(plugin-sdk): expose prepared runtime helper surfaces
2026-05-07 18:49:42 -03:00
Shakker
70eabd3b08 fix: satisfy cron model selection checks 2026-05-07 22:47:04 +01:00
Shakker
ab3a3d14f0 fix: satisfy chat pending switch lint 2026-05-07 22:40:31 +01:00
Peter Steinberger
6f4272bd04 fix(providers): preserve streaming error bodies 2026-05-07 22:36:31 +01:00
Peter Steinberger
830a72d2ee fix(chat): reset model override with default 2026-05-07 22:36:31 +01:00
Peter Steinberger
139122f655 fix(cron): show rejected model allowlist 2026-05-07 22:36:31 +01:00
Peter Steinberger
e74347bbe7 fix(agents): retry overloaded subagent announces 2026-05-07 22:36:31 +01:00
Peter Steinberger
a95d7ab1c8 fix(providers): honor cidr no_proxy entries 2026-05-07 22:36:31 +01:00
Peter Steinberger
36835592df feat: log discord voice transcripts 2026-05-07 22:17:42 +01:00
Peter Steinberger
6785633d13 fix(ui): wait for pending model switches before send 2026-05-07 22:05:46 +01:00
Peter Steinberger
70717c50fc fix(agents): clamp compaction reserve tokens 2026-05-07 22:05:45 +01:00
Peter Steinberger
5a4676bd64 fix(byteplus): align Kimi catalog metadata 2026-05-07 22:05:45 +01:00
Peter Steinberger
fa8a85586c ci(release): create GitHub release during publish 2026-05-07 22:03:46 +01:00
sallyom
56fe64e8e3 fix: print resolved installer follow-up command
Signed-off-by: sallyom <somalley@redhat.com>
2026-05-07 16:31:21 -04:00
Alex Knight
6a8b4e422e Implement ACP bridge lifecycle handlers (#78880)
* Implement ACP bridge lifecycle handlers

* docs: add acp smoke evidence example

* docs: trim acp smoke example

* docs: remove acp pr plan file

* fix: tighten acp session list filters

---------

Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>
2026-05-08 06:26:36 +10:00
Vincent Koc
0fca665497 docs(imessage): document bluebubbles deprecation 2026-05-07 13:12:00 -07:00
Vincent Koc
2597723dfc fix(test): align main channel assumptions 2026-05-07 13:03:49 -07:00
Vincent Koc
7f4c0b3192 chore(config): refresh bundled channel metadata 2026-05-07 12:53:02 -07:00
Vincent Koc
91ed1604b0 docs(imessage): make imsg the supported setup path 2026-05-07 12:53:01 -07:00
Vincent Koc
84638bfbb0 fix(imessage): report non-mac default imsg hosts 2026-05-07 12:53:01 -07:00
Vincent Koc
4ad4be9aff test(channels): drop bluebubbles fixture assumptions 2026-05-07 12:53:01 -07:00
Vincent Koc
07bf572f35 chore(channels): delete bluebubbles plugin package 2026-05-07 12:53:00 -07:00
Vincent Koc
c97998ce21 chore(channels): remove bluebubbles bundled surface 2026-05-07 12:52:48 -07:00
Vincent Koc
f482e4d335 fix(channels): surface missing external plugin repairs
## Summary
- Add catalog-backed repair hints for official external channel plugins.
- Show configured Feishu/WhatsApp-style external channels as missing-plugin warning rows in status surfaces.
- Keep installed-but-unconfigured, disabled, allowlist-denied, and untrusted plugins on their real activation/configuration error paths.

Fixes #78702
Fixes #78593
2026-05-07 12:49:17 -07:00
sallyom
484a289be3 docs: document docker cli dns override
Signed-off-by: sallyom <somalley@redhat.com>
2026-05-07 15:18:25 -04:00
jesse-merhi
95a1c91531 ci: make network CodeQL shard additive 2026-05-08 01:18:04 +10:00
jesse-merhi
b6c9ed66c3 chore: preserve opengrep generated timestamp 2026-05-08 01:18:04 +10:00
jesse-merhi
cf9e9cd119 lint: move managed proxy guard to codeql 2026-05-08 01:18:04 +10:00
jesse-merhi
dd0a9bf869 lint: replace raw socket guard with codeql 2026-05-08 01:18:04 +10:00
jesse-merhi
9cc5e49e65 lint: replace proxy mutation guard with opengrep 2026-05-08 01:18:04 +10:00
jesse-merhi
f05e2222f3 lint: allow managed proxy mutation scopes 2026-05-08 01:18:04 +10:00
jesse-merhi
9eaadcdf29 chore: add proxy guardrail changelog 2026-05-08 01:18:04 +10:00
jesse-merhi
f4797921ac lint: classify raw socket callsites 2026-05-08 01:18:04 +10:00
Vincent Koc
8e88c7b297 test(plugins): align canvas startup metadata 2026-05-07 07:16:21 -07:00
Vincent Koc
fcb9dcc886 test(openai): align codex default auth contract 2026-05-07 07:16:20 -07:00
Vincent Koc
237fcbcbf1 test(llm-task): use real typebox schemas 2026-05-07 07:16:20 -07:00
Vincent Koc
9b279ef173 fix(agents): reclaim reported stale session locks 2026-05-07 07:16:20 -07:00
Vincent Koc
11a038207b fix(infra): support non-durable text writes 2026-05-07 07:16:20 -07:00
Vincent Koc
3a89e20b7b fix(infra): support hardlink-safe package moves 2026-05-07 07:16:20 -07:00
Peter Steinberger
a68ad39877 ci(release): speed up beta publish path 2026-05-07 15:02:24 +01:00
Vincent Koc
c41a73b828 docs(providers/arcee): note Trinity Large Thinking has tools disabled 2026-05-07 06:49:14 -07:00
Ayaan Zaidi
238e72d74d docs(changelog): note telegram poll cap (#78762) (thanks @obviyus) 2026-05-07 19:08:43 +05:30
Ayaan Zaidi
11d6a3f892 fix(telegram): keep dm allow separate from group auth 2026-05-07 19:08:43 +05:30
Ayaan Zaidi
c967628816 fix(telegram): restore outbound poll cap 2026-05-07 19:08:43 +05:30
Ayaan Zaidi
923ea990fd refactor(telegram): use grammY native helpers 2026-05-07 19:08:43 +05:30
Ayaan Zaidi
53efb6747d refactor(telegram): centralize access authorization 2026-05-07 19:08:43 +05:30
Ayaan Zaidi
6554e85ad6 refactor(telegram): unify outbound delivery adapter 2026-05-07 19:08:43 +05:30
Peter Steinberger
a966303216 build(canvas): refresh A2UI bundle hash 2026-05-07 14:28:38 +01:00
Peter Steinberger
dd09e6fe40 fix(arcee): disable tools for Trinity thinking 2026-05-07 14:28:33 +01:00
Peter Steinberger
a85261932e fix(cli): fall back to sips for HEIC infer inputs 2026-05-07 14:28:27 +01:00
Peter Steinberger
6ce1c98b61 fix: normalize auth profile inline secrets 2026-05-07 13:46:46 +01:00
Peter Steinberger
347b51be4b fix: sanitize existing prompt images 2026-05-07 13:46:46 +01:00
Peter Steinberger
548b55676f fix: strip unsupported Fireworks tool schema keywords 2026-05-07 13:46:46 +01:00
Peter Steinberger
772034d741 fix: strip tools for no-tool completions models 2026-05-07 13:46:46 +01:00
Pavan Kumar Gondhi
c65f3bc70e Compute plugin callback authorization dynamically [AI] (#78866)
* fix: compute plugin callback command authorization

* addressing codex review

* addressing ci

* addressing ci

* docs: add changelog entry for PR merge
2026-05-07 18:05:21 +05:30
Peter Steinberger
be33b68fd4 test: expand native sqlite Kysely coverage
Expand the native node:sqlite Kysely dialect tests for connection setup, insert metadata, transaction/savepoint behavior, and streaming.
2026-05-07 13:18:29 +01:00
Peter Steinberger
955b025697 feat: add native sqlite Kysely dialect
Add an owned Kysely dialect for native node:sqlite, raise the Node 22 floor to 22.16+ for StatementSync.columns(), and cover select/returning/stale insert id behavior.
2026-05-07 13:07:03 +01:00
Peter Steinberger
037174141e docs(changelog): clarify session-store fsync scope 2026-05-07 12:32:02 +01:00
Peter Steinberger
897bac5b8c fix(sessions): skip durable fsync for session store 2026-05-07 12:31:18 +01:00
Shakker
01dd593cfd test: stabilize prompt snapshot plugin tools 2026-05-07 12:25:08 +01:00
Peter Steinberger
64514a6548 test: remove unused canvas temp helper 2026-05-07 12:15:51 +01:00
Peter Steinberger
e867ab7e16 test: import os in canvas a2ui test 2026-05-07 12:15:51 +01:00
Peter Steinberger
f2bf925a38 fix: guard sandbox move cleanup identity 2026-05-07 12:15:51 +01:00
Peter Steinberger
530e4f93de refactor: use fs-safe for staged package swaps 2026-05-07 12:15:51 +01:00
Peter Steinberger
113761ab57 build: update fs-safe dependency 2026-05-07 12:15:51 +01:00
Peter Steinberger
2f69c40a62 fix: preserve late sandbox rename writes 2026-05-07 12:15:51 +01:00
Peter Steinberger
55a8f56a15 fix: harden sandbox runtime cleanup 2026-05-07 12:15:51 +01:00
Peter Steinberger
56636dfe57 fix(ci): restore main validation 2026-05-07 12:00:29 +01:00
Shakker
6ef7fa08af test: keep bluebubbles schema tests extension-local 2026-05-07 12:00:18 +01:00
Shakker
2c0f8a0beb fix: restore canvas ci checks 2026-05-07 11:55:52 +01:00
Shakker
0fd6607d56 fix: await control ui chat startup refresh 2026-05-07 11:50:48 +01:00
Vincent Koc
7ad53cefee fix(ci): account for canvas a2ui deps 2026-05-07 03:47:13 -07:00
pashpashpash
1c33990108 Route OpenAI agents through Codex by default (#78899)
* route openai agent runs through codex

* fix: load codex plugin for implicit openai runtime

* fix: preserve explicit OpenAI PI Codex auth routing

* fix: show codex auth for openai model listing

* fix: map codex auth into configured openai list rows

* fix: preserve explicit openai pi auth routes

* docs: keep openai model route examples canonical

* fix: clean openai codex test fixtures

* fix: scope codex auth status fallback

* fix: repair current ci boundary drift
2026-05-07 19:46:49 +09:00
Peter Steinberger
8b701ce1c7 fix: repair ci regressions 2026-05-07 11:46:21 +01:00
Vincent Koc
a6159bb60d docs(skills): clarify Crabbox broker auth 2026-05-07 03:43:49 -07:00
Vincent Koc
b165c0d10a fix(ci): restore main validation 2026-05-07 03:39:26 -07:00
Vincent Koc
c676cd4dcf docs(skills): keep broad OpenClaw gates remote 2026-05-07 03:35:12 -07:00
CaptainTimon
e1fec3c892 fix(config): remove core BlueBubbles schema (#78612)
* fix(config): remove core BlueBubbles schema

* fix(config): preserve BlueBubbles dmPolicy validation

* fix(config): type BlueBubbles account refinement

* chore(plugin-sdk): refresh API baseline

* chore(plugin-sdk): refresh API baseline

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-05-07 03:16:42 -07:00
Alex Knight
bf3b994378 fix(compaction): ignore metadata bytes in preflight pressure
Fix stale preflight compaction pressure estimation so metadata bytes before the latest usage record do not count as model-context tokens, while preserving post-usage tail pressure and the active transcript byte guard.

Fixes #78604.
2026-05-07 20:10:20 +10:00
Vincent Koc
f2b01bb7b1 feat(openai): add chat-latest model override
Add openai/chat-latest as an explicit direct API-key OpenAI model override, document the moving alias, and normalize unsupported Responses text verbosity for that model.
2026-05-07 03:09:16 -07:00
Pavan Kumar Gondhi
5852f5d15c fix(active-memory): require admin scope for global toggles [AI] (#78863)
* fix: gate active-memory global writes by admin scope

* addressing claude review

* docs: add changelog entry for PR merge
2026-05-07 15:35:30 +05:30
Vincent Koc
f4b2a08c85 test(gateway): use core node command in pairing authz 2026-05-07 03:00:34 -07:00
Vincent Koc
b5d434db61 docs(providers/anthropic): correct media-understanding default model to claude-opus-4-7 2026-05-07 02:57:16 -07:00
Pavan Kumar Gondhi
758051322d Honor owner enforcement for native commands [AI] (#78864)
* fix: honor owner enforcement for native commands

* addressing codex review

* addressing codex review

* docs: add changelog entry for PR merge
2026-05-07 15:26:49 +05:30
Vincent Koc
55bff24973 fix(plugins): share npm script shell env (#78887) 2026-05-07 02:56:32 -07:00
Vincent Koc
283c957fdc changelog: credit @sliverp for channels list channel-only rework 2026-05-07 02:50:31 -07:00
VACInc
8de5a55317 Fix Tavily tool SecretRef runtime config
Resolve Tavily dedicated tool credential lookup against the active runtime config snapshot.

PR: https://github.com/openclaw/openclaw/pull/78610
2026-05-07 02:40:22 -07:00
Peter Steinberger
129b9dad9e ci: use explicit channel contract runner 2026-05-07 10:28:34 +01:00
Sliverp
9170243f92 Feat/channels list show all and drop auth (#78456)
* feat(channels list): drop auth providers, add --all, surface installed/configured/enabled

`openclaw channels list` used to conflate two very different surfaces: chat
channels and OAuth/API-key auth providers for model routing. The auth
section was the first and most visible block in the output even for
operators who only cared about chat channels, and its JSON `auth` key
leaked model-provider identities into a command whose top-level help
describes it as channel management. Worse, the command silently hid
every channel that had no configured account, so users could not tell
from `channels list` which bundled or catalog channels were even
available to configure.

Split the surface cleanly around channels only:

1. Remove the `Auth providers (OAuth + API keys)` text section and the
   `auth` field from the JSON payload. Model-provider auth profiles
   remain reachable via `openclaw models auth list`, which is where
   they conceptually belong.

2. Add a `--all` flag to surface every channel an operator could
   configure: bundled channel plugins that have no account yet and
   catalog-listed external channels whose plugin package is not even
   installed on disk. Without `--all` the output still shows only
   channels with at least one configured account, matching the
   previous default behavior so existing scripts keep working. The
   "empty" default path now prints a hint pointing at `--all`.

3. Render three explicit status tags per row — `installed` /
   `not installed`, `configured` / `not configured`, `enabled` /
   `disabled` — so bundled-but-unconfigured plugins and installable
   catalog channels both render with accurate state instead of being
   invisible. Installed state comes from the same
   `isCatalogChannelInstalled` probe the setup flow uses, so it stays
   consistent with `openclaw onboard` and `channels add`.

4. JSON payload now carries an `origin` per channel (`configured`,
   `available`, `installable`) alongside `installed: boolean`, which
   lets tooling distinguish "user has set this up" from "user could
   set this up" without second-guessing.

Register `--all` on both the Commander CLI and the fast-path route-arg
parser so the flag works in both code paths, update the one routes
test that asserted the parsed args shape, and rewrite the old auth
profiles surface test as a broader `channels list` behavior spec
covering default output, `--all` output, JSON shape (no `auth`), and
the bundled-unconfigured + catalog-not-installed cases.

Docs: call out that `channels list` is chat-channel only now, mention
`--all`, and point at `openclaw models auth list` for what used to be
the auth providers block.

* fix(channels list): surface catalog channels that are installed on disk but not yet configured

The previous `--all` path filtered catalog entries with
`!installedByChannelId.get(entry.id)` before rendering them as
catalog-only rows. That assumed "catalog entry not already rendered
as a plugin row" implied "not installed", which is wrong: an external
channel plugin package can be installed on disk (`isCatalogChannelInstalled`
returns true) while the read-only channel loader still declines to
surface a plugin object for it — the loader only activates channels
that appear in user config, so a plugin that is installed but never
configured ended up in neither bucket and silently dropped out of
`channels list --all`.

Operator-facing symptom: `pnpm openclaw channels list --all` omitted
WeCom (and any other catalog channel in the same state) even though
its npm package was present on disk and its catalog entry existed,
while rendering every other uninstalled catalog channel as expected.

Fix: drop the `installed` filter from `catalogOnlyLines` so every
catalog entry that is not already represented by a plugin row is
rendered, and let the row itself carry the real installed/not-installed
tag. Two renderings now land in the catalog-only bucket:

- Not installed — rendered as `not installed, not configured, disabled`
  (installable row).
- Installed but unconfigured — rendered as `installed, not configured,
  disabled` (ready-to-configure row). The JSON `origin` for this case
  becomes `available`, matching the existing origin for bundled
  plugins that are installed but unconfigured, so downstream tooling
  sees a consistent "you could configure this now" signal regardless
  of whether the plugin came from bundled sources or from the catalog.

Regression test added under the WeCom scenario.

* refactor(channels list): drop model-provider usage surface, make the command channel-only

`openclaw channels list` used to append a model-provider usage/quota
snapshot (Anthropic, OpenRouter, OpenAI Codex, Gemini, Zai, Minimax,
etc.) under every invocation. That was a leftover from the days when
`channels list` was the only "operator overview" command; the same
data is now owned by `openclaw status` (overview) and
`openclaw models list` (per-provider), which handle timeouts, probe
errors, and output shape consistently for that class of data. Keeping
the snapshot wired into `channels list` meant:

- Every default invocation made one blocking `loadProviderUsageSummary`
  call that fanned out to every configured provider billing/auth
  endpoint, adding seconds of latency to a command that otherwise
  just reads local config.
- `channels list --no-usage` was the escape hatch, but the flag was
  itself a self-sustaining bug: it only existed because the command
  did work that did not belong to it.
- JSON consumers had an optional `usage` key whose shape was owned by
  the provider-usage module, not by the channels module, so any
  change upstream silently reshaped `channels list --json` output.
- Failed provider fetches printed provider-side errors on a command
  that never advertised itself as a provider-health surface.

Scope this PR tightens, in one move:

1. Remove `loadProviderUsageSummary` / `formatUsageReportLines` usage
   from `src/commands/channels/list.ts`. The command now only reads
   config, the read-only channel plugin registry, and the trusted
   catalog — matching its name.
2. Drop `--no-usage` from the Commander CLI registration, from the
   fast-path route-arg parser (`parseChannelsListRouteArgs`), and
   from `ChannelsListOptions`. The flag is gone, not silently
   ignored, so anyone depending on it will get a clear
   "unknown option" from Commander and from the fast-path router.
3. Drop the `usage` key from `channels list --json` payloads. Shape
   of the `chat` record and the new `origin` / `installed` tags
   introduced earlier in this branch are unchanged.
4. Print a single-line migration pointer at the bottom of the text
   output so operators who expected usage know where it went
   (`openclaw status` / `openclaw models list`). This replaces what
   used to be a block of fetched provider data with one static line,
   so it cannot fail or add latency.
5. Update `docs/cli/channels.md` troubleshooting to remove the
   `--no-usage` mention and point at the two new entry points.
6. Update tests: drop the `loadProviderUsageSummary` mock and the
   `"keeps JSON output valid when usage loading fails"` case,
   replace it with a positive assertion that `payload.usage` is
   undefined (locking in the narrower contract), and remove `usage`
   from every `channelsListCommand(...)` call to match the narrowed
   `ChannelsListOptions` type. The route-args test is updated to
   expect `{ json, all }` without `usage`.

No other command changes. `openclaw status` and `openclaw models list`
already render usage; they are the documented replacements.

Breaking-ish surface:

- CLI: `channels list --no-usage` now fails with "unknown option".
  Tooling should drop the flag — there is nothing left to opt out of.
- JSON: `channels list --json` no longer carries a top-level `usage`
  key. Tooling that read it must migrate to
  `openclaw status --json` or `openclaw models list --json`.

* fix(channels.list.test): widen isCatalogChannelInstalled mock signature to accept entry param

CI typecheck failed because the mock was declared with a zero-arg signature while one test called mockImplementation(({ entry }) => …). Tighten the generic so vitest's mock accepts the same params the real helper does.

* changelog: record channels list channel-only rework (#78456)
2026-05-07 17:28:03 +08:00
Vincent Koc
45778c66f4 docs(cli): document cron list/show --json status field 2026-05-07 02:25:09 -07:00
Peter Steinberger
8e17910191 fix: treat aws sdk auth profiles as config metadata 2026-05-07 10:24:19 +01:00
Aaron Weiker
8974a78f47 feat(cron): add computed status field to --json output (#78701)
* feat(cron): add computed status field to --json output

`openclaw cron list --json` and `openclaw cron show <id> --json` now
include a top-level `status` field on each job object, computed from
enabled + state.runningAtMs + state.lastRunStatus.

Values: "disabled" | "running" | "ok" | "error" | "skipped" | "idle"

This matches the human-readable status column already shown by
`cron list` and `cron show` (without --json), making it easier for
external tooling (dashboards, ops gateways) to determine job state
without re-implementing the derivation logic.

The raw state object is preserved unchanged for backward compatibility.

* fix: preserve lastStatus fallback + add changelog entry

Address ClawSweeper review findings:
- P2: Fall back to deprecated state.lastStatus when lastRunStatus is
  absent, matching the existing formatStatus behavior for legacy jobs.
- P3: Add CHANGELOG.md entry under Unreleased for this user-facing
  CLI feature.

* fix: address lint errors - add braces and avoid spread-in-map

---------

Co-authored-by: Rodin <rodin@forgedthought.ai>
Co-authored-by: claw <claw@weiker.me>
2026-05-07 02:19:18 -07:00
Christof
afdf03b563 fix: clear reset skills snapshot (#78873) 2026-05-07 11:18:39 +02:00
pashpashpash
3a901b5e95 Revert "Install Codex plugin on OpenAI model selection (#78799)" (#78878)
This reverts commit c8f3fecad6.
2026-05-07 18:13:59 +09:00
Vincent Koc
61386055b1 fix(test): use current gateway protocol in docker network smoke 2026-05-07 02:11:41 -07:00
Vincent Koc
34ca9adbf5 test(status): keep pi status expectation for openai routes 2026-05-07 02:02:59 -07:00
pashpashpash
c8f3fecad6 Install Codex plugin on OpenAI model selection (#78799)
* route openai agent runs through codex

* fix: load codex plugin for implicit openai runtime

* docs: credit openai codex auth fix

* fix(agents): respect custom openai runtime routing

* fix(agents): install codex plugin on openai selection

* fix(agents): preserve OpenAI Codex auth switching

* fix(ci): restore channel contract runner expression

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-05-07 01:57:34 -07:00
Vincent Koc
1831e124b2 fix(lint): clean up main lint regressions 2026-05-07 01:39:46 -07:00
Rajvardhan Patil
c25f319d49 fix(btw): keep usage placeholder visible
Fixes #62877.\n\nThanks @RajvardhanPatil07.
2026-05-07 01:36:11 -07:00
Pincer
8a66694c5e docs(lobster): clarify embedded openclaw.invoke limitation 2026-05-07 01:27:29 -07:00
Val Alexander
6b4ff8be81 fix(ui): bound sessions to configured agents
Fixes #41685.\n\nSummary:\n- Adds an additive sessions.list configuredAgentsOnly option for Control UI.\n- Filters default Control UI session listing to configured agents while preserving broad Gateway discovery for explicit callers.\n- Falls back restored unconfigured agent session keys before chat refresh.\n\nValidation:\n- pnpm protocol:check\n- pnpm test ui/src/ui/controllers/sessions.test.ts ui/src/ui/app-gateway.node.test.ts src/gateway/server.sessions.store-rpc.test.ts -- --reporter=verbose\n- pnpm format:docs:check\n- pnpm lint:swift\n- pnpm check:no-conflict-markers\n- git diff --check
2026-05-07 03:26:47 -05:00
592 changed files with 11886 additions and 31229 deletions

View File

@@ -32,6 +32,14 @@ pnpm crabbox:run -- --help | sed -n '1,120p'
Even if config still says AWS, maintainer validation should normally pass
`--provider blacksmith-testbox`.
- Prefer local targeted tests for tight edit loops. Broad gates belong remote.
- Do not treat inherited shell env as operator intent. In particular,
`OPENCLAW_LOCAL_CHECK_MODE=throttled` from the local shell is not permission
to move broad `pnpm check:changed`, `pnpm test:changed`, full `pnpm test`, or
lint/typecheck fan-out onto the laptop.
- Only use `OPENCLAW_LOCAL_CHECK_MODE=throttled|full` when the user explicitly
asks for local proof in the current task. If Testbox is queued or capacity is
constrained, report the blocker and keep only targeted local edit-loop checks
running.
## macOS And Windows Targets
@@ -198,6 +206,10 @@ Common Crabbox-only failures:
printed Actions URL.
- Cleanup uncertainty: run `blacksmith testbox list` and stop only boxes you
created.
- Testbox queued/capacity pressure: do not convert a broad changed gate or full
suite into local `OPENCLAW_LOCAL_CHECK_MODE=throttled pnpm ...`. Leave the
remote lane queued, switch to a narrower targeted local check, or stop and
report the capacity blocker.
If Crabbox cannot dispatch, sync, attach, or stop but Blacksmith itself works,
use direct Blacksmith from the repo root:
@@ -284,9 +296,27 @@ Install/auth for owned Crabbox if needed:
```sh
brew install openclaw/tap/crabbox
printf '%s' "$CRABBOX_COORDINATOR_TOKEN" | crabbox login --url https://crabbox.openclaw.ai --provider aws --token-stdin
crabbox login --url https://crabbox.openclaw.ai --provider aws
```
New users should self-resolve broker auth before anyone asks for AWS keys:
```sh
crabbox config show
crabbox doctor
crabbox whoami
```
- If broker auth is missing, run `crabbox login --url https://crabbox.openclaw.ai --provider aws`.
- If the CLI asks for `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, or AWS
profile setup during normal OpenClaw validation, assume the agent selected
the wrong path. Use brokered `crabbox login`, `--provider blacksmith-testbox`,
or an existing brokered lease before asking the user for cloud credentials.
- Ask for AWS keys only for explicit direct-provider/account administration,
not for normal brokered OpenClaw proof.
- Trusted automation may still use
`printf '%s' "$CRABBOX_COORDINATOR_TOKEN" | crabbox login --url https://crabbox.openclaw.ai --provider aws --token-stdin`.
macOS config lives at:
```text

View File

@@ -14,7 +14,6 @@ query-filters:
- security
paths:
- extensions/bluebubbles/src
- extensions/discord/src
- extensions/feishu/src
- extensions/googlechat/src

View File

@@ -0,0 +1,28 @@
name: openclaw-codeql-network-runtime-boundary-critical-quality
disable-default-queries: true
queries:
- uses: ./.github/codeql/openclaw-boundary/queries/raw-socket-callsite-classification.ql
- uses: ./.github/codeql/openclaw-boundary/queries/managed-proxy-runtime-mutation.ql
paths:
- src
- extensions
paths-ignore:
- "**/node_modules"
- "**/coverage"
- "**/*.generated.ts"
- "**/*.bundle.js"
- "**/*-runtime.js"
- "**/*.test.ts"
- "**/*.test.tsx"
- "**/*.e2e.test.ts"
- "**/*.e2e.test.tsx"
- "**/*test-support*"
- "**/*test-helper*"
- "**/*mock*"
- "**/*fixture*"
- "**/*bench*"
- "extensions/diffs/assets/**"

View File

@@ -0,0 +1,30 @@
---
lockVersion: 1.0.0
dependencies:
codeql/concepts:
version: 0.0.22
codeql/controlflow:
version: 2.0.32
codeql/dataflow:
version: 2.1.4
codeql/javascript-all:
version: 2.6.28
codeql/mad:
version: 1.0.48
codeql/regex:
version: 1.0.48
codeql/ssa:
version: 2.0.24
codeql/threat-models:
version: 1.0.48
codeql/tutorial:
version: 1.0.48
codeql/typetracking:
version: 2.0.32
codeql/util:
version: 2.0.35
codeql/xml:
version: 1.0.48
codeql/yaml:
version: 1.0.48
compiled: false

View File

@@ -0,0 +1,6 @@
name: openclaw/codeql-boundary-queries
version: 0.0.0
library: false
dependencies:
codeql/javascript-all: 2.6.28
extractor: javascript

View File

@@ -0,0 +1,325 @@
/**
* @name Managed proxy runtime mutation
* @description Proxy-related process.env and GLOBAL_AGENT runtime mutations must stay in managed proxy owner scopes.
* @kind problem
* @problem.severity error
* @precision high
* @id js/openclaw/managed-proxy-runtime-mutation
* @tags maintainability
* security
* external/cwe/cwe-441
*/
import javascript
predicate forbiddenEnvKey(string key) {
key =
[
"HTTP_PROXY",
"HTTPS_PROXY",
"http_proxy",
"https_proxy",
"NO_PROXY",
"no_proxy",
"GLOBAL_AGENT_HTTP_PROXY",
"GLOBAL_AGENT_HTTPS_PROXY",
"GLOBAL_AGENT_NO_PROXY",
"GLOBAL_AGENT_FORCE_GLOBAL_AGENT",
"OPENCLAW_PROXY_ACTIVE",
"OPENCLAW_PROXY_LOOPBACK_MODE"
]
}
predicate forbiddenGlobalAgentKey(string key) { key = ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"] }
predicate relevantSourceFile(File file) {
exists(string path |
path = file.getRelativePath() and
path.regexpMatch("^(src|extensions)/.*\\.(ts|mts|js|mjs)$") and
not path.regexpMatch(".*\\.(test|spec)\\.(ts|mts|js|mjs)$") and
not path.regexpMatch(".*\\.(test-utils|test-harness|e2e-harness)\\.ts$") and
not path.regexpMatch(".*/test-support/.*") and
not path.regexpMatch(".*/vendor/.*") and
not path.regexpMatch(".*\\.min\\.js$") and
not path.regexpMatch("^extensions/diffs/assets/.*")
)
}
predicate namedExpr(Expr expr, string name) {
expr.getUnderlyingValue().(Identifier).getName() = name
}
predicate directProcessEnvExpr(Expr expr) {
exists(PropAccess access |
expr.getUnderlyingValue() = access and
access.getPropertyName() = "env" and
namedExpr(access.getBase(), "process")
)
}
predicate envAlias(Variable variable) {
exists(VariableDeclarator decl |
decl.getBindingPattern().getAVariable() = variable and
directProcessEnvExpr(decl.getInit())
)
or
exists(VariableDeclarator decl, ObjectPattern pattern, PropertyPattern property |
decl.getBindingPattern() = pattern and
namedExpr(decl.getInit(), "process") and
property = pattern.getAPropertyPattern() and
property.getName() = "env" and
property.getValuePattern().(BindingPattern).getAVariable() = variable
)
}
predicate processEnvExpr(Expr expr) {
directProcessEnvExpr(expr)
or
exists(VarAccess access |
expr.getUnderlyingValue() = access and
envAlias(access.getVariable())
)
}
predicate stringConst(Variable variable, string value) {
exists(VariableDeclarator decl |
decl.getBindingPattern().getAVariable() = variable and
value = decl.getInit().getStringValue()
)
}
predicate stringArrayContains(Variable variable, string value) {
exists(VariableDeclarator decl, ArrayExpr array, Expr element |
decl.getBindingPattern().getAVariable() = variable and
decl.getInit().getUnderlyingValue() = array and
element = array.getAnElement().getUnderlyingValue() and
value = element.getStringValue()
)
or
exists(VariableDeclarator decl, ArrayExpr array, SpreadElement spread, VarAccess access |
decl.getBindingPattern().getAVariable() = variable and
decl.getInit().getUnderlyingValue() = array and
spread = array.getAnElement().getUnderlyingValue() and
spread.getOperand().getUnderlyingValue() = access and
stringArrayContains(access.getVariable(), value)
)
}
predicate forbiddenEnvLoopVariable(Variable variable) {
exists(ForOfStmt loop, VarAccess domain, string key |
variable = loop.getAnIterationVariable() and
loop.getIterationDomain().getUnderlyingValue() = domain and
stringArrayContains(domain.getVariable(), key) and
forbiddenEnvKey(key)
)
}
predicate envKeyExprForbidden(Expr keyExpr) {
forbiddenEnvKey(keyExpr.getStringValue())
or
exists(VarAccess access, string key |
keyExpr.getUnderlyingValue() = access and
stringConst(access.getVariable(), key) and
forbiddenEnvKey(key)
)
or
exists(VarAccess access |
keyExpr.getUnderlyingValue() = access and
forbiddenEnvLoopVariable(access.getVariable())
)
}
predicate globalAgentKeyExprForbidden(Expr keyExpr) {
forbiddenGlobalAgentKey(keyExpr.getStringValue())
or
exists(VarAccess access, string key |
keyExpr.getUnderlyingValue() = access and
stringConst(access.getVariable(), key) and
forbiddenGlobalAgentKey(key)
)
}
predicate directGlobalExpr(Expr expr) {
namedExpr(expr, "global")
or
namedExpr(expr, "globalThis")
}
predicate globalAlias(Variable variable) {
exists(VariableDeclarator decl |
decl.getBindingPattern().getAVariable() = variable and
directGlobalExpr(decl.getInit())
)
}
predicate globalExpr(Expr expr) {
directGlobalExpr(expr)
or
exists(VarAccess access |
expr.getUnderlyingValue() = access and
globalAlias(access.getVariable())
)
}
predicate directGlobalAgentExpr(Expr expr) {
exists(PropAccess access |
expr.getUnderlyingValue() = access and
access.getPropertyName() = "GLOBAL_AGENT" and
globalExpr(access.getBase())
)
}
predicate globalAgentAlias(Variable variable) {
exists(VariableDeclarator decl |
decl.getBindingPattern().getAVariable() = variable and
directGlobalAgentExpr(decl.getInit())
)
}
predicate globalAgentExpr(Expr expr) {
directGlobalAgentExpr(expr)
or
exists(VarAccess access |
expr.getUnderlyingValue() = access and
globalAgentAlias(access.getVariable())
)
}
predicate envMutationTarget(Expr target) {
exists(PropAccess access |
target.getUnderlyingReference() = access and
processEnvExpr(access.getBase()) and
(
forbiddenEnvKey(access.getPropertyName())
or
envKeyExprForbidden(access.getPropertyNameExpr())
)
)
}
predicate globalAgentMutationTarget(Expr target) {
globalAgentExpr(target)
or
exists(PropAccess access |
target.getUnderlyingReference() = access and
globalAgentExpr(access.getBase()) and
(
forbiddenGlobalAgentKey(access.getPropertyName())
or
globalAgentKeyExprForbidden(access.getPropertyNameExpr())
)
)
}
predicate objectPropertyWithKey(Expr expr, string key) {
exists(ObjectExpr object, Property property |
expr.getUnderlyingValue() = object and
property = object.getAProperty() and
property.getName() = key
)
}
Expr managedProxyRuntimeMutation() {
exists(Assignment assignment |
result = assignment and
(
envMutationTarget(assignment.getTarget())
or
globalAgentMutationTarget(assignment.getTarget())
)
)
or
exists(DeleteExpr delete |
result = delete and
(
envMutationTarget(delete.getOperand())
or
globalAgentMutationTarget(delete.getOperand())
)
)
or
exists(MethodCallExpr call |
result = call and
namedExpr(call.getReceiver(), "Object") and
call.getMethodName() = "assign" and
(
processEnvExpr(call.getArgument(0)) and
exists(string key |
forbiddenEnvKey(key) and
objectPropertyWithKey(call.getArgument(1), key)
)
or
globalAgentExpr(call.getArgument(0)) and
exists(string key |
forbiddenGlobalAgentKey(key) and
objectPropertyWithKey(call.getArgument(1), key)
)
)
)
or
exists(MethodCallExpr call |
result = call and
namedExpr(call.getReceiver(), "Object") and
call.getMethodName() = "defineProperty" and
(
processEnvExpr(call.getArgument(0)) and
envKeyExprForbidden(call.getArgument(1))
or
globalAgentExpr(call.getArgument(0)) and
globalAgentKeyExprForbidden(call.getArgument(1))
)
)
}
predicate allowedFunctionOwnerScope(Expr mutation, string path, string functionName) {
exists(Function owner |
mutation.getFile().getRelativePath() = path and
owner.getFile() = mutation.getFile() and
owner.getName() = functionName and
mutation.getParent*() = owner.getBody()
)
}
predicate allowedMethodOwnerScope(Expr mutation, string path, string methodName) {
exists(MethodDeclaration method |
mutation.getFile().getRelativePath() = path and
method.getFile() = mutation.getFile() and
method.getDeclaringType().getName() + "." + method.getName() = methodName and
mutation.getParent*() = method.getBody().getBody()
)
}
predicate allowedManagedProxyRuntimeMutation(Expr mutation) {
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts", "applyProxyEnv")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts", "restoreProxyEnv")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
"restoreGlobalAgentRuntime")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
"restoreNodeHttpStack")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
"bootstrapNodeHttpStack")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
"writeGlobalAgentNoProxy")
or
allowedFunctionOwnerScope(mutation, "src/infra/net/proxy/proxy-lifecycle.ts",
"disableGlobalAgentProxyForIpv6GatewayLoopback")
or
allowedMethodOwnerScope(mutation, "extensions/browser/src/browser/cdp-proxy-bypass.ts",
"NoProxyLeaseManager.acquire")
or
allowedMethodOwnerScope(mutation, "extensions/browser/src/browser/cdp-proxy-bypass.ts",
"NoProxyLeaseManager.release")
}
from Expr mutation
where
managedProxyRuntimeMutation() = mutation and
relevantSourceFile(mutation.getFile()) and
not allowedManagedProxyRuntimeMutation(mutation)
select mutation,
"Only managed proxy owner scopes may mutate proxy-related process.env or GLOBAL_AGENT runtime state."

View File

@@ -0,0 +1,92 @@
/**
* @name Raw socket client callsite classification
* @description Raw net/tls/http2 client egress must be classified before landing.
* @kind problem
* @problem.severity error
* @precision high
* @id js/openclaw/raw-socket-callsite-classification
* @tags maintainability
* security
* external/cwe/cwe-441
*/
import javascript
predicate rawModule(string moduleName) {
moduleName = ["net", "node:net", "tls", "node:tls", "http2", "node:http2"]
}
predicate netModule(string moduleName) { moduleName = ["net", "node:net"] }
predicate rawConnectMember(string memberName) { memberName = ["connect", "createConnection"] }
predicate relevantSourceFile(File file) {
exists(string path |
path = file.getRelativePath() and
path.regexpMatch("^(src|extensions)/.*\\.ts$") and
not path.regexpMatch(".*\\.(test|spec|test-utils|test-harness|e2e-harness)\\.ts$") and
not path.regexpMatch(".*/test-support/.*") and
not path.regexpMatch("^extensions/diffs/assets/.*")
)
}
Expr rawSocketClientCall() {
exists(API::CallNode call, string moduleName, string memberName |
rawModule(moduleName) and
rawConnectMember(memberName) and
call = API::moduleImport(moduleName).getMember(memberName).getACall() and
result = call.asExpr()
)
or
exists(string moduleName |
netModule(moduleName) and
result =
DataFlow::moduleMember(moduleName, "Socket")
.getAnInstantiation()
.getAMethodCall("connect")
.asExpr()
)
}
predicate allowedOwnerScope(Expr call, string path, string functionName) {
exists(Function owner |
call.getFile().getRelativePath() = path and
owner.getFile() = call.getFile() and
owner.getName() = functionName and
call.getParent*() = owner.getBody()
)
}
predicate allowedRawSocketClientCall(Expr call) {
allowedOwnerScope(call, "src/cli/gateway-cli/run-loop.ts", "waitForGatewayPortReady")
or
allowedOwnerScope(call, "src/infra/ssh-tunnel.ts", "canConnectLocal")
or
allowedOwnerScope(call, "src/infra/gateway-lock.ts", "checkPortFree")
or
allowedOwnerScope(call, "src/infra/jsonl-socket.ts", "requestJsonlSocket")
or
allowedOwnerScope(call, "src/infra/net/http-connect-tunnel.ts", "connectToProxy")
or
allowedOwnerScope(call, "src/infra/net/http-connect-tunnel.ts", "startTargetTls")
or
allowedOwnerScope(call, "src/infra/push-apns-http2.ts", "openProxiedApnsHttp2Session")
or
allowedOwnerScope(call, "src/infra/push-apns-http2.ts", "connectApnsHttp2Session")
or
allowedOwnerScope(call, "src/proxy-capture/proxy-server.ts", "startDebugProxyServer")
or
allowedOwnerScope(call, "extensions/irc/src/client.ts", "connectIrcClient")
or
allowedOwnerScope(call, "extensions/qa-lab/src/lab-server-capture.ts", "probeTcpReachability")
or
allowedOwnerScope(call, "extensions/qa-lab/src/lab-server-ui.ts", "proxyUpgradeRequest")
}
from Expr call
where
rawSocketClientCall() = call and
relevantSourceFile(call.getFile()) and
not allowedRawSocketClientCall(call)
select call,
"Classify raw net/tls/http2 client egress as managed/proxied, local-only, diagnostic guarded, or documented unsupported before adding this callsite."

5
.github/labeler.yml vendored
View File

@@ -1,8 +1,3 @@
"channel: bluebubbles":
- changed-files:
- any-glob-to-any-file:
- "extensions/bluebubbles/**"
- "docs/channels/bluebubbles.md"
"plugin: azure-speech":
- changed-files:
- any-glob-to-any-file:

View File

@@ -854,7 +854,7 @@ jobs:
name: ${{ matrix.checkName }}
needs: [preflight]
if: needs.preflight.outputs.run_checks_fast == 'true'
runs-on: ${{ github.repository == 'openclaw/openclaw' && needs.preflight.outputs.runner_4vcpu_ubuntu || 'ubuntu-24.04' }}
runs-on: ${{ github.repository == 'openclaw/openclaw' && 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-24.04' }}
timeout-minutes: 60
strategy:
fail-fast: false

View File

@@ -21,17 +21,21 @@ on:
- plugin-sdk-package-contract
- plugin-sdk-reply-runtime
- provider-runtime-boundary
- network-runtime-boundary
- session-diagnostics-boundary
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- ".github/codeql/**"
- ".github/workflows/codeql-critical-quality.yml"
- "extensions/*.ts"
- "extensions/**/*.ts"
- "packages/plugin-package-contract/**"
- "packages/plugin-sdk/**"
- "packages/memory-host-sdk/**"
- "src/*.ts"
- "src/**/*.ts"
- "src/config/**"
- "extensions/bluebubbles/src/**"
- "extensions/discord/src/**"
- "extensions/feishu/src/**"
- "extensions/googlechat/src/**"
@@ -159,6 +163,7 @@ jobs:
plugin_sdk_package: ${{ steps.detect.outputs.plugin_sdk_package }}
plugin_sdk_reply: ${{ steps.detect.outputs.plugin_sdk_reply }}
provider: ${{ steps.detect.outputs.provider }}
network_runtime: ${{ steps.detect.outputs.network_runtime }}
session_diagnostics: ${{ steps.detect.outputs.session_diagnostics }}
steps:
- name: Detect PR shard paths
@@ -182,6 +187,7 @@ jobs:
plugin_sdk_package=false
plugin_sdk_reply=false
provider=false
network_runtime=false
session_diagnostics=false
if [[ "${EVENT_NAME}" != "pull_request" ]]; then
@@ -196,6 +202,7 @@ jobs:
plugin_sdk_package=true
plugin_sdk_reply=true
provider=true
network_runtime=true
session_diagnostics=true
else
while IFS= read -r file; do
@@ -212,6 +219,7 @@ jobs:
plugin_sdk_package=true
plugin_sdk_reply=true
provider=true
network_runtime=true
session_diagnostics=true
;;
src/acp/control-plane/*|src/agents/cli-runner/*|src/agents/command/*|src/agents/pi-embedded-runner/*|src/agents/tools/*|src/agents/*completion*.ts|src/agents/*transport*.ts|src/agents/model-*.ts|src/agents/openclaw-tools*.ts|src/agents/provider-*.ts|src/agents/session*.ts|src/agents/tool-call*.ts|src/auto-reply/reply/agent-runner*.ts|src/auto-reply/reply/commands*.ts|src/auto-reply/reply/directive-handling*.ts|src/auto-reply/reply/dispatch-*.ts|src/auto-reply/reply/get-reply-run*.ts|src/auto-reply/reply/provider-dispatcher*.ts|src/auto-reply/reply/queue*.ts|src/auto-reply/reply/reply-run-registry*.ts|src/auto-reply/reply/session*.ts)
@@ -220,7 +228,7 @@ jobs:
src/auto-reply/reply/post-compaction-context.ts|src/auto-reply/reply/queue/*|src/auto-reply/reply/startup-context.ts|src/commands/doctor-session-*.ts|src/commands/session-store-targets.ts|src/commands/sessions*.ts|src/infra/diagnostic-*.ts|src/infra/diagnostics-timeline.ts|src/infra/session-delivery-queue*.ts|src/logging/diagnostic*.ts)
session_diagnostics=true
;;
extensions/bluebubbles/src/*|extensions/discord/src/*|extensions/feishu/src/*|extensions/googlechat/src/*|extensions/imessage/src/*|extensions/irc/src/*|extensions/line/src/*|extensions/matrix/src/*|extensions/mattermost/src/*|extensions/msteams/src/*|extensions/nextcloud-talk/src/*|extensions/nostr/src/*|extensions/qa-channel/src/*|extensions/qqbot/src/*|extensions/signal/src/*|extensions/slack/src/*|extensions/synology-chat/src/*|extensions/telegram/src/*|extensions/tlon/src/*|extensions/twitch/src/*|extensions/whatsapp/src/*|extensions/zalo/src/*|extensions/zalouser/src/*|src/channels/*)
extensions/discord/src/*|extensions/feishu/src/*|extensions/googlechat/src/*|extensions/imessage/src/*|extensions/irc/src/*|extensions/line/src/*|extensions/matrix/src/*|extensions/mattermost/src/*|extensions/msteams/src/*|extensions/nextcloud-talk/src/*|extensions/nostr/src/*|extensions/qa-channel/src/*|extensions/qqbot/src/*|extensions/signal/src/*|extensions/slack/src/*|extensions/synology-chat/src/*|extensions/telegram/src/*|extensions/tlon/src/*|extensions/twitch/src/*|extensions/whatsapp/src/*|extensions/zalo/src/*|extensions/zalouser/src/*|src/channels/*)
channel=true
;;
src/config/*)
@@ -281,6 +289,12 @@ jobs:
plugin_sdk_package=true
;;
esac
case "${file}" in
src/*.ts|src/**/*.ts|extensions/*.ts|extensions/**/*.ts)
network_runtime=true
;;
esac
done < <(gh api --paginate "repos/${REPOSITORY}/pulls/${PR_NUMBER}/files" --jq '.[].filename')
fi
@@ -296,6 +310,7 @@ jobs:
echo "plugin_sdk_package=${plugin_sdk_package}"
echo "plugin_sdk_reply=${plugin_sdk_reply}"
echo "provider=${provider}"
echo "network_runtime=${network_runtime}"
echo "session_diagnostics=${session_diagnostics}"
} >> "${GITHUB_OUTPUT}"
@@ -391,6 +406,62 @@ jobs:
with:
category: "/codeql-critical-quality/channel-runtime-boundary"
network-runtime-boundary:
name: Critical Quality (network-runtime-boundary)
needs: quality-shards
if: ${{ needs.quality-shards.outputs.network_runtime == 'true' && (github.event_name != 'pull_request' || !github.event.pull_request.draft) && (github.event_name == 'pull_request' || github.event_name != 'workflow_dispatch' || inputs.profile == 'all' || inputs.profile == 'network-runtime-boundary') }}
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
submodules: false
- name: Initialize CodeQL
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:
languages: javascript-typescript
config-file: ./.github/codeql/codeql-network-runtime-boundary-critical-quality.yml
- name: Analyze
id: analyze
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4
with:
output: sarif-results
category: "/codeql-critical-quality/network-runtime-boundary"
- name: Fail on network runtime boundary findings
env:
SARIF_OUTPUT: sarif-results
run: |
set -euo pipefail
shopt -s nullglob
files=("$SARIF_OUTPUT"/*.sarif)
if [ "${#files[@]}" -eq 0 ]; then
echo "No SARIF files found in $SARIF_OUTPUT" >&2
exit 1
fi
findings="$(jq -s '[.[].runs[]?.results[]?] | length' "${files[@]}")"
if [ "$findings" = "0" ]; then
exit 0
fi
echo "Found ${findings} network runtime boundary finding(s):" >&2
jq -r '
.runs[]?.results[]?
| .locations[0].physicalLocation as $location
| "- "
+ ($location.artifactLocation.uri // "unknown")
+ ":"
+ (($location.region.startLine // 0) | tostring)
+ " "
+ (.message.text // .ruleId)
' "${files[@]}" >&2
exit 1
agent-runtime-boundary:
name: Critical Quality (agent-runtime-boundary)
needs: quality-shards

View File

@@ -37,10 +37,15 @@ on:
required: true
default: true
type: boolean
wait_for_clawhub:
description: Wait for ClawHub plugin publish before marking this workflow complete
required: true
default: false
type: boolean
permissions:
actions: write
contents: read
contents: write
concurrency:
group: openclaw-release-publish-${{ inputs.tag }}
@@ -166,6 +171,7 @@ jobs:
PLUGIN_PUBLISH_SCOPE: ${{ inputs.plugin_publish_scope }}
PLUGINS: ${{ inputs.plugins }}
PUBLISH_OPENCLAW_NPM: ${{ inputs.publish_openclaw_npm && 'true' || 'false' }}
WAIT_FOR_CLAWHUB: ${{ inputs.wait_for_clawhub && 'true' || 'false' }}
run: |
set -euo pipefail
@@ -203,19 +209,31 @@ jobs:
fi
echo "Dispatched ${workflow}: https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id}" >&2
{
echo "- ${workflow}: dispatched (https://github.com/${GITHUB_REPOSITORY}/actions/runs/${run_id})"
} >> "$GITHUB_STEP_SUMMARY"
printf '%s\n' "${run_id}"
}
wait_for_run() {
local workflow="$1"
local run_id="$2"
local status conclusion url
local status conclusion url updated_at last_state
last_state=""
while true; do
status="$(gh run view --repo "$GITHUB_REPOSITORY" "$run_id" --json status --jq '.status')"
run_json="$(gh run view --repo "$GITHUB_REPOSITORY" "$run_id" --json status,url,updatedAt)"
status="$(printf '%s' "$run_json" | jq -r '.status')"
if [[ "$status" == "completed" ]]; then
break
fi
url="$(printf '%s' "$run_json" | jq -r '.url')"
updated_at="$(printf '%s' "$run_json" | jq -r '.updatedAt')"
state="${status}:${updated_at}"
if [[ "$state" != "$last_state" ]]; then
echo "${workflow} still ${status} (updated ${updated_at}): ${url}"
last_state="$state"
fi
sleep 30
done
@@ -245,6 +263,53 @@ jobs:
wait_run_pid="$!"
}
create_or_update_github_release() {
local release_version notes_version title notes_file changelog_file latest_arg prerelease_args
release_version="${RELEASE_TAG#v}"
notes_version="${release_version}"
if [[ "${notes_version}" =~ ^([0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*)-(alpha|beta)\.[1-9][0-9]*$ ]]; then
notes_version="${BASH_REMATCH[1]}"
fi
title="openclaw ${release_version}"
changelog_file="${RUNNER_TEMP}/CHANGELOG.md"
notes_file="${RUNNER_TEMP}/release-notes.md"
gh api --repo "$GITHUB_REPOSITORY" "repos/${GITHUB_REPOSITORY}/contents/CHANGELOG.md?ref=${TARGET_SHA}" \
--jq '.content' | base64 --decode > "${changelog_file}"
awk -v version="${notes_version}" '
$0 == "## " version { in_section = 1; next }
/^## / && in_section { exit }
in_section { print }
' "${changelog_file}" > "${notes_file}"
if [[ ! -s "${notes_file}" ]]; then
echo "CHANGELOG.md does not contain release notes for ${notes_version}." >&2
exit 1
fi
prerelease_args=()
latest_arg="--latest=false"
if [[ "${RELEASE_TAG}" == *"-alpha."* || "${RELEASE_TAG}" == *"-beta."* ]]; then
prerelease_args=(--prerelease)
elif [[ "${RELEASE_NPM_DIST_TAG}" == "latest" ]]; then
latest_arg="--latest"
fi
if gh release view "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
gh release edit "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" \
--title "${title}" \
--notes-file "${notes_file}" \
"${prerelease_args[@]}"
else
gh release create "${RELEASE_TAG}" --repo "$GITHUB_REPOSITORY" \
--verify-tag \
--title "${title}" \
--notes-file "${notes_file}" \
"${prerelease_args[@]}" \
"${latest_arg}"
fi
echo "- GitHub release: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${RELEASE_TAG}" >> "$GITHUB_STEP_SUMMARY"
}
{
echo "### Publish sequence"
echo
@@ -257,6 +322,11 @@ jobs:
else
echo "- OpenClaw npm publish: skipped by input"
fi
if [[ "${WAIT_FOR_CLAWHUB}" == "true" ]]; then
echo "- Workflow completion waits for ClawHub"
else
echo "- Workflow completion does not wait for ClawHub; monitor the dispatched ClawHub run separately"
fi
} >> "$GITHUB_STEP_SUMMARY"
npm_args=(-f publish_scope="${PLUGIN_PUBLISH_SCOPE}" -f ref="${TARGET_SHA}")
@@ -286,10 +356,16 @@ jobs:
echo "- OpenClaw npm publish: skipped by input" >> "$GITHUB_STEP_SUMMARY"
fi
clawhub_result="$RUNNER_TEMP/clawhub-result.txt"
wait_run_pid=""
wait_for_run_background plugin-clawhub-release.yml "${plugin_clawhub_run_id}" "${clawhub_result}"
clawhub_pid="${wait_run_pid}"
clawhub_result=""
clawhub_pid=""
if [[ "${WAIT_FOR_CLAWHUB}" == "true" ]]; then
clawhub_result="$RUNNER_TEMP/clawhub-result.txt"
wait_run_pid=""
wait_for_run_background plugin-clawhub-release.yml "${plugin_clawhub_run_id}" "${clawhub_result}"
clawhub_pid="${wait_run_pid}"
else
echo "- plugin-clawhub-release.yml: not awaited (${plugin_clawhub_run_id})" >> "$GITHUB_STEP_SUMMARY"
fi
openclaw_result=""
openclaw_pid=""
@@ -301,7 +377,7 @@ jobs:
fi
failed=0
if ! wait "${clawhub_pid}"; then
if [[ -n "${clawhub_pid}" ]] && ! wait "${clawhub_pid}"; then
failed=1
fi
if [[ -n "${openclaw_pid}" ]] && ! wait "${openclaw_pid}"; then
@@ -316,3 +392,7 @@ jobs:
if [[ "${failed}" != "0" ]]; then
exit 1
fi
if [[ -n "${openclaw_npm_run_id}" ]]; then
create_or_update_github_release
fi

View File

@@ -37,6 +37,11 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
- New seams: backwards-compatible, documented, versioned. Third-party plugins exist.
- Channels: `src/channels/**` is implementation; plugin authors get SDK seams.
- Providers: core owns generic loop; provider plugins own auth/catalog/runtime hooks.
- Request-time runtime resolution: when a path already knows the provider id, model ref, channel id, outbound target, capability family, or attachment class, carry that as a prepared runtime fact instead of rediscovering it later.
- Prepared runtime facts should be small typed values produced once near startup, reply dispatch, model selection, tool planning, or channel resolution, then passed through context to consumers. Prefer `AgentRuntimePlan`, `ProviderRuntimePluginHandle`, scoped model/catalog helpers, active/runtime registries, manifest/public-artifact lookups, single-provider resolvers, and lazy registry construction.
- Avoid broad request-time rediscovery: hot reply/tool/outbound/media paths should not call broad plugin/provider/channel/capability loaders such as `loadOpenClawPlugins`, `resolveProviderPluginsForHooks`, `resolvePluginCapabilityProviders`, `resolvePluginDiscoveryProvidersRuntime`, `getChannelPlugin`, or broad model/tool/media registry builders just to answer a question the caller already knows. Do not build multimodal/provider registries for document-only or otherwise non-participating paths.
- Compatibility fallbacks are allowed only for startup/setup/admin/standalone/legacy callers that genuinely lack prepared facts. Keep them explicit, tested, and outside migrated hot reply/tool/outbound paths.
- Do not fix repeated request-time discovery by adding scattered cache layers. Move the canonical fact earlier, reuse the existing prepared-runtime object, and delete duplicate lookup branches when the last migrated caller stops needing them.
- Gateway protocol changes: additive first; incompatible needs versioning/docs/client follow-through.
- Config contract: exported types, schema/help, metadata, baselines, docs aligned. Retired public keys stay retired; compat in raw migration/doctor.
- Direction: manifest-first control plane; targeted runtime loaders; no hidden contract bypasses; broad mutable registries transitional.

View File

@@ -6,15 +6,22 @@ Docs: https://docs.openclaw.ai
### Changes
- Telegram: preserve the channel-specific 10-option poll cap in the unified outbound adapter so over-limit polls are rejected before send. (#78762) Thanks @obviyus.
- Runtime/install: raise the supported Node 22 floor to `22.16+` so native SQLite query handling can rely on the `node:sqlite` statement metadata API while continuing to recommend Node 24. (#78921)
- Discord/voice: include a bounded one-line STT transcript preview in verbose voice logs so live voice debugging shows what speakers said before the agent reply.
- Discord/voice: stream ElevenLabs TTS directly into Discord playback and send ElevenLabs latency optimization as the documented query parameter so spoken replies can start sooner.
- Discord/voice: keep TTS playback running when another user starts speaking, ignore new capture during playback to avoid feedback loops, and downgrade expected receive-stream aborts to verbose diagnostics.
- Telegram: treat successful same-chat `message` tool outbound sends during an inbound telegram turn as delivered when deciding whether to emit the rewritten silent reply fallback (#78685). Thanks @neeravmakwana.
- Gateway/tasks: reconcile stale CLI run-context tasks whose live run context disappeared even when a child session row remains, and apply the default bounded reload deferral timeout to channel hot reloads so stale task records cannot block Discord/Slack/Telegram reloads forever.
- Gateway/sessions: keep session-store index writes atomic while skipping durable fsync inside the writer lock, reducing cron and channel-turn starvation on slow filesystems and addressing the session-store strand of #73655. Thanks @mmartoccia.
- Discord/voice: make `openclaw channels capabilities --channel discord --target channel:<id>` and `channels status --probe` audit voice-channel permissions, including auto-join targets, so missing Connect/Speak/Read Message History permissions show up before `/vc join`.
- Docs/iMessage: deprecate BlueBubbles for new OpenClaw setups, document the upstream server-release rationale, and point new iMessage deployments toward the native `imsg` path while keeping BlueBubbles as a supported legacy fallback.
- Channels CLI: make `openclaw channels list` channel-only — drop the `Auth providers (OAuth + API keys)` block (use `openclaw models auth list`), drop the per-provider usage/quota fetch and the `--no-usage` flag (use `openclaw status` or `openclaw models list`), add `--all` to surface bundled-unconfigured, catalog-not-installed, and catalog-installed-but-unconfigured channels, and render explicit `installed` / `configured` / `enabled` tags per row plus an `origin` + `installed` field in JSON. Fixes WeCom-class catalog channels disappearing from `--all` when installed on disk but not yet configured. (#78456) Thanks @sliverp.
- CLI/cron: add computed `status` field to `cron list --json` and `cron show <id> --json` output, mirroring the human-readable status column (disabled/running/ok/error/skipped/idle) so external tooling can determine job state without re-deriving it from raw state fields. (#78701) Thanks @aweiker.
- Discord/voice: make voice capture less choppy by extending the default post-speech silence grace to 2.5s, add `voice.captureSilenceGraceMs` for noisy Discord sessions, and tighten the spoken-output prompt around live STT fragments. Thanks @vincentkoc.
- Discord/streaming: default Discord replies to progress draft previews so tool/work activity appears in one edited Discord message unless `channels.discord.streaming.mode` is set to `off`.
- OpenAI: support `openai/chat-latest` as an explicit direct API-key model override for trying the moving ChatGPT Instant API alias without changing the stable default model.
- Plugins/install: add `npm-pack:<path.tgz>` installs so local npm pack artifacts run through the same managed npm-root install, lockfile verification, dependency scan, and install-record path as registry npm plugins.
- Channels/plugins: show configured official external channels as missing-plugin status rows and send errors with exact install/doctor repair commands after raw package-manager upgrades leave Feishu or WhatsApp uninstalled. Fixes #78702 and #78593. Thanks @MarkMa84 and @mkupiainen.
- Codex app-server: disarm the short post-tool completion watchdog after current-turn activity, expose `appServer.turnCompletionIdleTimeoutMs`, and include raw assistant item context in idle-timeout diagnostics so status-only post-tool stalls stop failing as idle. Fixes #77984. Thanks @roseware-dev and @rubencu.
- Plugin skills/Windows: publish plugin-provided skill directories as junctions on Windows so standard users without Developer Mode can register plugin skills without symlink EPERM failures. Fixes #77958. (#77971) Thanks @hclsys and @jarro.
- MS Teams: surface blocked Bot Framework egress by logging JWKS fetch network failures and adding a Bot Connector send hint for transport-level reply failures. Fixes #77674. (#78081) Thanks @Beandon13.
@@ -25,6 +32,7 @@ Docs: https://docs.openclaw.ai
- Sessions CLI: show the selected agent runtime in the `openclaw sessions` table so terminal output matches the runtime visibility already present in JSON/status surfaces. Thanks @vincentkoc.
- ACPX/Codex: preserve trusted Codex project declarations when launching isolated Codex ACP sessions, avoiding interactive trust prompts in headless runs. Thanks @Stedyclaw.
- ACPX/Codex: reap stale OpenClaw-owned ACPX/Codex ACP process trees on startup and after ACP session close, preventing orphaned harness processes from slowing the Gateway. Thanks @91wan.
- ACP bridge: implement stable session list, resume, and close handlers so ACP clients can page Gateway sessions, rebind existing sessions without replay, and close bridge sessions cleanly. Thanks @amknight.
- ACP sessions: allow parent agents to inspect and message their own spawned cross-agent ACP sessions without enabling broad agent-to-agent visibility. Thanks @barronlroth.
- Talk/voice: unify realtime relay, transcription relay, managed-room handoff, Voice Call, Google Meet, VoiceClaw, and native clients around a shared Talk session controller and add the Gateway-managed `talk.session.*` RPC surface.
- Diagnostics/Talk: export bounded Talk lifecycle/audio metrics and session recovery metrics through OpenTelemetry and Prometheus without exposing transcripts, audio payloads, room ids, turn ids, or session ids.
@@ -49,11 +57,11 @@ Docs: https://docs.openclaw.ai
- Slack/streaming: add `streaming.progress.render: "rich"` for Block Kit progress drafts backed by structured progress line data.
- Slack/streaming: keep the newest rich progress lines when Block Kit limits trim long progress drafts. Thanks @vincentkoc.
- Slack/performance: reduce message preparation, stream recipient lookup, and thread-context allocation overhead on Slack reply hot paths. Thanks @vincentkoc.
- Agents/performance: trim OpenAI WebSocket stream queues, tool-call id queues, and agent/channel helper assembly hot paths while preserving event ordering, tool id ordering, and channel reply payloads. Thanks @vincentkoc.
- Channels/streaming: cap progress-draft tool lines by default so edited progress boxes avoid jumpy reflow from long wrapped lines.
- Control UI/chat: add an agent-first filter to the chat session picker, keep chat controls/composer responsive across phone/tablet/desktop widths, keep desktop chat controls on one row, avoid duplicate avatar refreshes during initial chat load, and hide that row while scrolling down the transcript. Thanks @BunsDev.
- Control UI/chat: collapse consecutive duplicate text messages into one bubble with a count so repeated text-only messages stay compact without hiding nearby context.
- Control UI/chat and Sessions: label inherited thinking defaults separately from explicit overrides while preserving provider-supplied option labels. Fixes #77581. Thanks @BunsDev and @Beandon13.
- Agents/runtime: add prepared runtime foundation contracts for carrying provider, model, tool, TTS, and outbound runtime facts through later reply-path migrations. Thanks @mcaxtr.
- Agents/subagents: preserve every grouped child result when direct completion fallback has to bypass the requester-agent announce turn. Thanks @vincentkoc.
- TTS/telephony: honor provider voice/model overrides in telephony synthesis providers so Google Meet agent speech logs match the backend that actually produced the audio. Thanks @vincentkoc.
- Voice Call/realtime: bound the paced Twilio audio queue and close overloaded realtime streams before provider audio can pile up behind the websocket backpressure guard. Thanks @vincentkoc.
@@ -90,6 +98,7 @@ Docs: https://docs.openclaw.ai
- Plugins/update: repair stale managed npm-root `openclaw` peer packages before plugin installs, so beta-channel official plugin updates are not downgraded by old core package-lock state. Thanks @vincentkoc.
- Plugins/install: run managed npm-root install, rollback, repair, and uninstall mutations with legacy peer resolution so removing one plugin cannot rehydrate a stale registry `openclaw` package into the shared root. Thanks @vincentkoc.
- Plugins/install: reassert managed npm plugin `openclaw` peer links after shared-root npm installs, updates, and uninstalls, so mutating one plugin does not leave previously installed SDK-using plugins unable to resolve `openclaw/plugin-sdk/*`.
- Plugins/install: use the same absolute POSIX npm lifecycle shell for managed plugin install, rollback, repair, and uninstall npm operations as staged package updates, preventing restricted PATH shells from breaking cleanup. Thanks @vincentkoc.
- Plugins/update: make package upgrades swap pnpm/npm-prefix installs cleanly, keep legacy plugin install runtime chunks working, and on the beta channel fall back default-line npm plugins to default/latest when plugin beta releases are missing or fail install validation. Thanks @vincentkoc and @joshavant.
- Plugins/active-memory: skip session-store channel entries that contain `:` when resolving the recall subagent's channel, so QQ c2c agent IDs (e.g. `c2c:10D4F7C2…`) and other scoped conversation IDs do not reach bundled-plugin `dirName` validation and crash the recall run. The same guard already applied to explicit `channelId` params (#76704); this extends it to store-derived channels. (#77396) Thanks @hclsys.
- Sandbox/Windows: accept drive-absolute Docker bind sources while keeping sandbox blocked-path and allowed-root policy comparisons Windows-case-insensitive. (#42174) Thanks @6607changchun.
@@ -140,41 +149,68 @@ Docs: https://docs.openclaw.ai
- Config/Nix: keep startup-derived plugin enablement, gateway auth tokens, control UI origins, and owner-display secrets runtime-only instead of rewriting `openclaw.json`; in Nix mode, config writers, mutating `openclaw update`, plugin lifecycle mutators, and doctor repair/token-generation now refuse with agent-first nix-openclaw guidance. (#78047) Thanks @joshp123.
- Agents/context engine: invalidate cached assembled context views when source history shrinks or assembly fails, preventing stale pre-reset history from being reused. Fixes #77968. (#78163) Thanks @brokemac79 and @ChrisBot2026.
### Breaking
- Channels/iMessage: remove the bundled BlueBubbles channel surface and deprecate BlueBubbles-backed iMessage setup in OpenClaw. Existing `channels.bluebubbles` configs must migrate to `channels.imessage` using `imsg` on a signed-in Mac or an SSH wrapper, and non-macOS default `imsg` configs now report remote-Mac wrapper guidance.
### Fixes
- TUI/local runs: keep stable runtime plugin aliases present when legacy compatibility wrappers already exist in dist, so sending a message no longer fails with a missing `runtime-plugins.runtime.js` module.
- Providers: preserve non-OK `text/event-stream` response bodies so provider HTTP errors keep their JSON detail instead of collapsing to generic streaming failures. Fixes #78180.
- Chat commands: make `/model default` reset the session model override instead of treating it as a literal model name. Fixes #78182.
- Cron: make rejected `payload.model` errors show the configured `agents.defaults.models` allowlist instead of echoing the rejected model twice. Fixes #79058.
- Agents/subagents: retry parent wake announces when the announce-summary model run fails with fallback cooldown exhaustion instead of dropping the wake on the first transient provider overload. Refs #78581.
- Providers/network: honor IPv4 CIDR and octet-wildcard `NO_PROXY` entries such as `100.64.0.0/10` and `100.64.*` before enabling trusted env-proxy mode for model-provider requests. Fixes #79030.
- Docs/Docker: document a local Compose override for Docker Desktop DNS failures in the shared-network `openclaw-cli` sidecar, keeping the default compose setup hardened while unblocking `openclaw plugins install` when users opt in. Fixes #79018. Thanks @Jason-Vaughan.
- Installer: when npm installs `openclaw` outside the parent shell PATH, print follow-up commands with the resolved binary path instead of telling users to run `openclaw` from a shell that will report `command not found`. Fixes #72382. Thanks @jbob762.
- Compute plugin callback authorization dynamically [AI]. (#78866) Thanks @pgondhi987.
- fix(active-memory): require admin scope for global toggles [AI]. (#78863) Thanks @pgondhi987.
- Honor owner enforcement for native commands [AI]. (#78864) Thanks @pgondhi987.
- Tavily: resolve dedicated `tavily_search` and `tavily_extract` tool credentials from the active runtime config snapshot, so `exec` SecretRef-backed API keys do not reach the tools unresolved. (#78610) Thanks @VACInc.
- Gateway/sessions: clear cached skills snapshots during `/new` and `sessions.reset` so long-lived channel sessions rebuild the visible skill list after skills change. (#78873) Thanks @Evizero.
- fix(auto-reply): gate inline skill tool dispatch [AI]. (#78517) Thanks @pgondhi987.
- Canvas plugin: keep legacy root `canvasHost` configs valid until `openclaw doctor --fix` migrates them into `plugins.entries.canvas.config.host`, move Canvas/A2UI clients to gateway protocol v4 plugin surfaces, and refresh the generated A2UI bundle hash so normal builds stay clean.
- feishu: honor config write policy for dynamic agents [AI]. (#78520) Thanks @pgondhi987.
- fix(skill-workshop): honor pending approval for tool suggestions [AI]. (#78516) Thanks @pgondhi987.
- BytePlus: mark Kimi K2.5 and Kimi K2 Thinking catalog entries as reasoning-capable, raise their output cap to 32k tokens, and fill Kimi cache-read pricing. Fixes #54149.
- Control UI/chat: wait for an in-flight model dropdown patch before sending the next chat message, so immediate sends use the selected session model instead of racing the previous override. Fixes #54240.
- Native chat: decode gateway-provided thinking metadata for the iOS/macOS picker so provider-specific levels such as `adaptive`, `xhigh`, and `max` appear without leaking unsupported default-model options. Thanks @BunsDev.
- Agents/compaction: cap summarization output reserve tokens to the selected model's `maxTokens` so 1M-context Anthropic compactions do not request more output than the API permits. Fixes #54383.
- Agents/tools: fail `exec host=node` before `system.run` when the selected node is known to be disconnected, with an actionable reconnect message instead of a raw node invoke failure. Thanks @BunsDev.
- Agents/models: accept legacy `anthropic-cli/*` model refs as Claude CLI runtime refs instead of failing model resolution with `Unknown model`. Thanks @BunsDev.
- Agents/tools: keep restrictive-profile tool-section warnings scoped to the configured sections whose tools are still missing from `alsoAllow`, so already re-allowed filesystem tools do not make exec-only fixes look broader than they are. Thanks @BunsDev.
- Agents/tools: avoid warning messaging-only agents about inherited global `tools.exec` or `tools.fs` sections when the agent profile did not configure those tool sections itself. Thanks @BunsDev.
- Codex dynamic tools: normalize runtime `toolsAllow` entries the same way as Pi tool policy, so aliases like `bash` and `apply-patch` still expose the intended OpenClaw tools. Thanks @BunsDev.
- Memory/dreaming: read OpenAI-style `output_text` assistant parts from narrative subagent transcripts, so light-phase Dream Diary entries are not dropped as empty. Thanks @BunsDev.
- OpenAI-compatible providers: honor `compat.supportsTools=false` by stripping tool payload fields before dispatch to chat-only endpoints. Fixes #74664.
- OpenAI-compatible providers: apply model-declared unsupported tool-schema keyword stripping to native OpenAI transport payloads and mark Fireworks Kimi K2.5 as rejecting `not` schemas. Fixes #75467.
- OpenAI-compatible gateway: sanitize images supplied through request content even when the prompt text contains no image file references, preventing oversized attachment payloads from bypassing the resize/drop pipeline. Fixes #59913.
- Auth profiles: normalize inline API keys and tokens loaded from `auth-profiles.json` so masked or rich-text credential artifacts fail as auth errors instead of crashing HTTP header construction. Fixes #77624.
- llm-task: resolve configured model aliases before embedded dispatch so `model="gemini-flash"` and other aliases route to the intended provider instead of the agent default. Fixes #54166.
- Media generation: resolve slash-containing model-only overrides like `fal-ai/flux/dev` through registered provider model metadata so FAL image/video models do not get misparsed as provider `fal-ai`. Fixes #77444.
- Commands/BTW: show the `/btw` missing-question usage placeholder with brackets so outbound channel sanitization keeps it visible. Fixes #62877. Thanks @RajvardhanPatil07.
- CLI backends: keep versioned OAuth identity matches reusable when auth profile ids rotate, so Claude CLI sessions do not reset and lose continuity during same-account OAuth refresh/profile alias changes. Fixes #78541.
- Model providers: normalize APNG sniffed PNG uploads, preserve Gemini 3 tool-call thought-signature replay with documented fallback signatures, accept legacy `__env__:VAR` custom-provider keys, and repair snake_case tool-call transcript sanitization. Fixes #51881, #48915, #77566, and #42858.
- Telegram/models: parse provider ids containing dots in `/models` callback buttons so `hf.co` model lists render as inline keyboard buttons. Fixes #38745.
- Auth profiles/Bedrock: accept persisted `type: "aws-sdk"` auth profiles so EC2/IMDS and shared AWS credential-chain Bedrock setups are not dropped as `invalid_type`. Fixes #69708.
- Amazon Bedrock: refresh shared AWS profile/config file credentials before Bedrock model, discovery, and embedding requests so long-running Gateway processes pick up renewed profile credentials without restart. Fixes #77551.
- Amazon Bedrock: treat named `aws-sdk` auth profiles as config routing metadata instead of stored credentials, and let `doctor --fix` move legacy markers out of `auth-profiles.json`. Fixes #69708.
- Anthropic: reject uppercase provider-prefixed forward-compat model ids locally instead of sending malformed dynamic ids upstream. Fixes #73715.
- OpenAI/embeddings: pass configured output dimensionality through single and batched embedding requests so memory embedding indexes can request smaller vectors. Fixes #55126.
- CLI/infer: normalize HEIC/HEIF image files to JPEG before model-run requests, avoiding providers that reject Apple image container formats. Fixes #50081.
- CLI/infer: fall back to macOS `sips` when optional image tooling cannot decode HEIC/HEIF input files before model-run requests. Refs #50081.
- OpenRouter: keep the default `openrouter/auto` model ref canonical while preventing TUI and Control UI catalog pickers from displaying or submitting `openrouter/openrouter/auto`. Fixes #62655.
- Status/Claude CLI: show `oauth (claude-cli)` for working Claude CLI OAuth runtime sessions instead of `unknown` when no local auth profile exists. Fixes #78632. Thanks @gorkem2020.
- Memory search: preserve keyword-only hybrid FTS matches when vector scoring is unavailable or below the configured minimum score, so exact lexical hits are not dropped by weighted min-score filtering.
- Exec approvals/node: let trusted backend node invokes complete no-device Control UI approvals after the original request connection changes, while keeping node, command, cwd, env, and allow-once replay bindings enforced. Fixes #78569. Thanks @naturedogdog.
- Agents/subagents: keep background completion delivery on the requester-agent handoff/queue-retry path instead of raw-sending child results directly, and strip child-result wrapper or OpenClaw runtime-context scaffolding from queued outbound retries. Fixes #78531. Thanks @EthanSK.
- Sandbox: recreate cached browser bridges when JavaScript-evaluation permission changes, keep failed prune removals tracked for retry, and make cross-device directory moves copy-then-commit without partially emptying the source on failure.
- CLI/completion: guard the shell-profile source line written by `openclaw completion --install` with a file existence check (`[ -f ... ] && source ...` for bash/zsh, `test -f ...; and source ...` for fish) so uninstalling OpenClaw no longer makes new login shells error on a missing completion cache. (#78659) Thanks @sjf.
- Cron/doctor: repair persisted cron jobs whose `payload.model` was stored as `"default"`, `"null"`, blank, or JSON `null` by removing the bad override during `openclaw doctor --fix` while keeping cron runtime model validation strict. Fixes #78549. Thanks @bizzle12368239.
- Telegram: honor `accessGroup:*` sender allowlists for DMs, groups, native commands, and callback authorization before applying Telegram's numeric sender-ID checks. Fixes #78660. Thanks @manugc.
- Agent delivery: report `deliverySucceeded=false` when outbound delivery returns no adapter result, so claimed/empty delivery paths no longer masquerade as successful sends. Fixes #78532. Thanks @joeyfrasier.
- Cron/isolated runs: fail implicit announce delivery before model execution when `delivery.channel=last` has no previous route, so recurring jobs do not spend tokens before hitting a permanent delivery-target error. Fixes #78608. Thanks @sallyom.
- Gateway/sessions: persist a new generated transcript file when daily gateway-agent session rollover changes the session id, while preserving custom transcript paths. Fixes #78607. Thanks @nailujac, @zerone0x, and @sallyom.
- Doctor/OpenAI Codex: revert the 2026.5.5 `doctor --fix` repair that rewrote valid `openai-codex/*` ChatGPT/Codex OAuth routes to `openai/*`, which could break OAuth-only GPT-5.5 setups or accidentally move users onto the OpenAI API-key route. If 2026.5.5 already changed your default model, run `openclaw models set openai-codex/gpt-5.5 && openclaw config validate` to switch the default agent back to the Codex OAuth PI route. Fixes #78407.
- Doctor/OpenAI Codex: repair legacy `openai-codex/*` agent model refs and stale OpenAI PI session pins to `openai/*` with the Codex runtime, preserving existing `openai-codex` auth profiles so ChatGPT/Codex OAuth users do not fall back to OpenAI API-key routing. Fixes #78407.
- Telegram: keep the polling watchdog tied to `getUpdates` liveness so unrelated outbound Bot API calls cannot mask a wedged inbound poller. Fixes #78422. Thanks @ai-hpc.
- Discord/groups: instruct group-chat agents to stay silent when a message is addressed to someone else, replying only when invited or correcting key facts. (#78615)
- Discord/groups: tell Discord-channel agents to wrap bare URLs as `<https://example.com>` so link previews do not expand into uninvited embeds. (#78614)
@@ -268,6 +304,7 @@ Docs: https://docs.openclaw.ai
- CLI/update: make dev-channel preflight lint opt-in and constrained when enabled, so `openclaw update --channel dev` no longer walks back otherwise-good main commits when Ubuntu hosts OOM-kill or fail parallel oxlint shards. Thanks @vincentkoc.
- Google Meet: fork the caller's current agent transcript into agent-mode meeting consultant sessions, so Meet replies inherit the context from the tool call that joined the meeting.
- Google Meet: log the concrete agent-mode TTS provider, model, voice, output format, and sample rate after speech synthesis, so Meet logs show which voice backend spoke each reply.
- Control UI/Sessions: hide disk-discovered unregistered-agent sessions by default and fall back from restored unconfigured agent session keys before chat refresh, preventing deleted-agent stores from reopening the wrong workspace. Fixes #41685. Thanks @BunsDev.
- Google Meet: log the resolved audio provider model when starting Chrome and paired-node Meet talk-back bridges, so agent-mode joins show the STT model and bidi joins show the realtime voice model.
- Google Meet: stop advertising legacy `mode: "realtime"` to agents and config UIs, while keeping it as a hidden compatibility alias for `mode: "agent"`, so new joins use the STT -> OpenClaw agent -> TTS path instead of selecting the direct realtime voice fallback.
- Google Meet: add `chrome.audioBufferBytes` for generated command-pair SoX audio commands and lower the default buffer from SoX's 8192 bytes to 4096 bytes to reduce Chrome talk-back latency.
@@ -530,6 +567,7 @@ Docs: https://docs.openclaw.ai
- WhatsApp: route proactive phone-number sends through Baileys LID forward mappings when available, so LID-addressed contacts receive agent messages instead of creating sender-only ghost chats. Fixes #67378. (#74925) Thanks @edenfunf.
- WhatsApp: send captioned `MEDIA:` directive auto-replies once instead of emitting an empty media message before the captioned media reply. (#78770) Thanks @ai-hpc.
- Hooks/cron: log returned `/hooks/agent` isolated-run errors and failed cron jobs with cron diagnostic summaries, so rejected `payload.model` values are visible instead of looking like accepted-but-missing runs. Fixes #78597. (#78655) Thanks @kevinslin.
- Managed proxy/security: classify raw socket callsites and proxy runtime mutations in boundary checks so new direct egress or unmanaged proxy-state changes cannot land without explicit review. (#77126) Thanks @jesse-merhi.
## 2026.5.3-1
@@ -555,6 +593,7 @@ Docs: https://docs.openclaw.ai
- Tools/BTW: add `/side` as a text and native slash-command alias for `/btw` side questions.
- Doctor/config: `doctor --fix` now commits safe legacy migrations even when unrelated validation issues (e.g. a missing plugin) prevent full validation from passing, so `agents.defaults.llm` and other known-legacy keys are always cleaned up by `doctor --fix` regardless of other config problems. Fixes #76798. (#76800) Thanks @hclsys.
- Agents/tools: skip optional media and PDF tool factories when the effective tool denylist already blocks them, avoiding unnecessary hot-path setup for tools that will be filtered out before model use. (#76773) Thanks @dorukardahan.
- Agents/compaction: ignore pre-usage transcript metadata bytes when stale token snapshots estimate preflight compaction pressure, while still counting post-usage transcript tail pressure. Fixes #78604. Thanks @amknight.
- Discord/status: let explicit reaction tool calls opt into tracking subsequent tool progress on the reacted message with `trackToolCalls: true`, and use the shared tool display emoji table for status reactions.
- Gateway/config: stop Gateway startup and hot reload from auto-restoring invalid config; invalid config now fails closed and `openclaw doctor --fix` owns last-known-good repair.
- Gateway/performance: lazy-load early runtime discovery and shutdown-hook helpers, defer maintenance timers until after readiness, and trim duplicate plugin auto-enable work during Gateway startup.

View File

@@ -29,7 +29,7 @@ Welcome to the lobster tank! 🦞
- **Ayaan Zaidi** - Telegram subsystem, Android app
- GitHub: [@obviyus](https://github.com/obviyus) · X: [@obviyus](https://x.com/obviyus)
- **Tyler Yust** - Agents/subagents, cron, BlueBubbles, macOS app
- **Tyler Yust** - Agents/subagents, cron, iMessage, macOS app
- GitHub: [@tyler6204](https://github.com/tyler6204) · X: [@tyleryust](https://x.com/tyleryust)
- **Mariano Belinky** - iOS app, Security

View File

@@ -23,7 +23,7 @@ It answers you on the channels you already use. It can speak and listen on macOS
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
Supported channels include: WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, QQ, WebChat.
Supported channels include: WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, QQ, WebChat.
[Website](https://openclaw.ai) · [Docs](https://docs.openclaw.ai) · [Vision](VISION.md) · [DeepWiki](https://deepwiki.com/openclaw/openclaw) · [Getting Started](https://docs.openclaw.ai/start/getting-started) · [Updating](https://docs.openclaw.ai/install/updating) · [Showcase](https://docs.openclaw.ai/start/showcase) · [FAQ](https://docs.openclaw.ai/help/faq) · [Onboarding](https://docs.openclaw.ai/start/wizard) · [Nix](https://github.com/openclaw/nix-openclaw) · [Docker](https://docs.openclaw.ai/install/docker) · [Discord](https://discord.gg/clawd)
@@ -96,7 +96,7 @@ Model note: while many providers and models are supported, prefer a current flag
## Install (recommended)
Runtime: **Node 24 (recommended) or Node 22.14+**.
Runtime: **Node 24 (recommended) or Node 22.16+**.
```bash
npm install -g openclaw@latest
@@ -109,7 +109,7 @@ OpenClaw Onboard installs the Gateway daemon (launchd/systemd user service) so i
## Quick start (TL;DR)
Runtime: **Node 24 (recommended) or Node 22.14+**.
Runtime: **Node 24 (recommended) or Node 22.16+**.
Full beginner guide (auth, pairing, channels): [Getting started](https://docs.openclaw.ai/start/getting-started)
@@ -121,7 +121,7 @@ openclaw gateway --port 18789 --verbose
# Send a message
openclaw message send --target +1234567890 --message "Hello from OpenClaw"
# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/IRC/Microsoft Teams/Matrix/Feishu/LINE/Mattermost/Nextcloud Talk/Nostr/Synology Chat/Tlon/Twitch/Zalo/Zalo Personal/WeChat/QQ/WebChat)
# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/IRC/Microsoft Teams/Matrix/Feishu/LINE/Mattermost/Nextcloud Talk/Nostr/Synology Chat/Tlon/Twitch/Zalo/Zalo Personal/WeChat/QQ/WebChat)
openclaw agent --message "Ship checklist" --thinking high
```
@@ -146,7 +146,7 @@ Run `openclaw doctor` to surface risky/misconfigured DM policies.
## Highlights
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, QQ, WebChat, macOS, iOS/Android.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, IRC, Microsoft Teams, Matrix, Feishu, LINE, Mattermost, Nextcloud Talk, Nostr, Synology Chat, Tlon, Twitch, Zalo, Zalo Personal, WeChat, QQ, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — wake words on macOS/iOS and continuous voice on Android (ElevenLabs + system TTS fallback).
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).

View File

@@ -312,7 +312,7 @@ OpenClaw's web interface (Gateway Control UI + HTTP endpoints) is intended for *
### Node.js Version
OpenClaw requires **Node.js 22.14.0 or later** (LTS). This version includes important security patches:
OpenClaw requires **Node.js 22.16.0 or later** (LTS). This version includes important security patches:
- CVE-2025-59466: async_hooks DoS vulnerability
- CVE-2026-21636: Permission model bypass vulnerability
@@ -320,7 +320,7 @@ OpenClaw requires **Node.js 22.14.0 or later** (LTS). This version includes impo
Verify your Node.js version:
```bash
node --version # Should be v22.14.0 or later
node --version # Should be v22.16.0 or later
```
### Docker Security

View File

@@ -1517,6 +1517,7 @@ public struct SessionsListParams: Codable, Sendable {
public let activeminutes: Int?
public let includeglobal: Bool?
public let includeunknown: Bool?
public let configuredagentsonly: Bool?
public let includederivedtitles: Bool?
public let includelastmessage: Bool?
public let label: String?
@@ -1529,6 +1530,7 @@ public struct SessionsListParams: Codable, Sendable {
activeminutes: Int?,
includeglobal: Bool?,
includeunknown: Bool?,
configuredagentsonly: Bool?,
includederivedtitles: Bool?,
includelastmessage: Bool?,
label: String?,
@@ -1540,6 +1542,7 @@ public struct SessionsListParams: Codable, Sendable {
self.activeminutes = activeminutes
self.includeglobal = includeglobal
self.includeunknown = includeunknown
self.configuredagentsonly = configuredagentsonly
self.includederivedtitles = includederivedtitles
self.includelastmessage = includelastmessage
self.label = label
@@ -1553,6 +1556,7 @@ public struct SessionsListParams: Codable, Sendable {
case activeminutes = "activeMinutes"
case includeglobal = "includeGlobal"
case includeunknown = "includeUnknown"
case configuredagentsonly = "configuredAgentsOnly"
case includederivedtitles = "includeDerivedTitles"
case includelastmessage = "includeLastMessage"
case label

View File

@@ -9,6 +9,7 @@ const rootEntries = [
"src/index.ts!",
"src/entry.ts!",
"src/cli/daemon-cli.ts!",
"src/infra/kysely-node-sqlite.ts!",
"src/infra/warning-filter.ts!",
"src/infra/command-explainer/index.ts!",
bundledPluginFile("telegram", "src/audit.ts", "!"),
@@ -30,10 +31,12 @@ const bundledPluginEntries = [
const bundledPluginIgnoredRuntimeDependencies = [
"@agentclientprotocol/claude-agent-acp",
"@a2ui/lit",
"@azure/identity",
"@clawdbot/lobster",
"@discordjs/opus",
"@homebridge/ciao",
"@lit/context",
"@matrix-org/matrix-sdk-crypto-wasm",
"@mozilla/readability",
"@openai/codex",
@@ -42,6 +45,7 @@ const bundledPluginIgnoredRuntimeDependencies = [
"@zed-industries/codex-acp",
"jiti",
"json5",
"lit",
"linkedom",
"openclaw",
"pdfjs-dist",
@@ -169,7 +173,7 @@ const config = {
// Bundled plugins often load their public surface via string specifiers in
// `index.ts` contracts, so Knip needs these convention-based entry files.
entry: bundledPluginEntries,
project: ["index.ts!", "src/**/*.ts!"],
project: ["index.ts!", "src/**/*.{js,mjs,ts}!"],
ignoreDependencies: bundledPluginIgnoredRuntimeDependencies,
},
},

View File

@@ -1,4 +1,4 @@
b14178c6945e0d9da9b35a12cc1fecd1406165bb440619c33bfec59fe5e0287d config-baseline.json
f860a7d43d3bd15379d8c3dfccbc6fcbf47b9bec8d8b67b29dd7313946905645 config-baseline.core.json
7238265b921affbb481198f603293c9b1c988025713c55ee19fdbf132a8339ab config-baseline.json
97579293de31bc607194bce3e22c16d140c08ab9e6f1e38298f3ce47fbc9d68b config-baseline.core.json
463c45a79d02598184caccbc6f316692df962fe6b0e84d1a3e3cc1809f862b15 config-baseline.channel.json
3094eba68b507a852a73952179e5f6decddfbb1ec377a2bc65409f92aff647a3 config-baseline.plugin.json
b6d36d17e554a2ec5a1a6c6d32107a9a1113c274a700100962d97b6afbdafb25 config-baseline.plugin.json

View File

@@ -1,2 +1,2 @@
a2a671522a9855594b011c86425911f2297e756c666f4ceb1cc453f613983725 plugin-sdk-api-baseline.json
b939b18ab3cbd21f338fe2a7cd8783b612c0956e79831d66dae4a49f9ba85014 plugin-sdk-api-baseline.jsonl
28e280d21693216c99cfa8da553589b41741d37c0ada956e316ee01d3d6c202c plugin-sdk-api-baseline.json
633dae33da97f6a073c5561709c57d5c0b7ff67af0512d0261f05455c24b38de plugin-sdk-api-baseline.jsonl

View File

@@ -76,7 +76,6 @@
{
"group": "消息平台",
"pages": [
"zh-CN/channels/bluebubbles",
"zh-CN/channels/discord",
"zh-CN/channels/feishu",
"zh-CN/channels/grammy",

View File

@@ -62,6 +62,18 @@ Explicit copy flows, such as `openclaw agents add`, use this portability policy:
Non-portable profiles remain available through read-through inheritance unless
the target agent signs in separately and creates its own local profile.
## Config-only auth routes
`auth.profiles` entries with `mode: "aws-sdk"` are routing metadata, not stored
credentials. They are valid when the target provider uses
`models.providers.<id>.auth: "aws-sdk"` or the built-in Amazon Bedrock default
AWS SDK route. These profile ids may appear in `auth.order` and session
overrides even when no matching entry exists in `auth-profiles.json`.
Do not write `type: "aws-sdk"` into `auth-profiles.json`. If a legacy install
has such a marker, `openclaw doctor --fix` moves it to `auth.profiles` and
removes the marker from the credential store.
## Explicit auth order filtering
- When `auth.order.<provider>` or the auth-store order override is set for a

View File

@@ -90,7 +90,7 @@ openclaw cron add \
--tz America/New_York \
--timeout-seconds 300 \
--announce \
--channel bluebubbles \
--channel imessage \
--to "+1XXXXXXXXXX" \
--message "Execute daily inbox triage per standing orders. Check mail for new alerts. Parse, categorize, and persist each item. Report summary to owner. Escalate unknowns."
```

View File

@@ -1,638 +0,0 @@
---
summary: "Legacy iMessage support via the BlueBubbles macOS server (REST send/receive, typing, reactions, pairing, advanced actions)."
read_when:
- Setting up BlueBubbles channel
- Troubleshooting webhook pairing
- Configuring iMessage on macOS
title: "BlueBubbles"
sidebarTitle: "BlueBubbles"
---
Status: bundled legacy plugin that talks to the BlueBubbles macOS server over HTTP. Existing BlueBubbles setups continue to work, but new OpenClaw iMessage deployments should prefer the native [iMessage](/channels/imessage) plugin when its requirements fit your host.
<Warning>
BlueBubbles is deprecated for new OpenClaw setups.
The upstream BlueBubbles ecosystem is still active, but OpenClaw depends on the BlueBubbles macOS server API. As of May 6, 2026, the official [`bluebubbles-server`](https://github.com/BlueBubblesApp/bluebubbles-server) development branch last changed on [January 22, 2026](https://github.com/BlueBubblesApp/bluebubbles-server/commit/88a4921bbd5a8111f1e9582b83715cf877171037), and the latest server release ([`v1.9.9`](https://github.com/BlueBubblesApp/bluebubbles-server/releases/tag/v1.9.9)) was published on May 16, 2025. The client app and helper repositories have newer activity, so this is not an abandonment claim; the deprecation is about reducing OpenClaw's dependency on an external HTTP server, webhooks, and private-API compatibility surface when the native `imsg` path keeps the integration on a local stdio contract.
</Warning>
<Note>
Current OpenClaw releases bundle BlueBubbles, so normal packaged builds do not need a separate `openclaw plugins install` step.
</Note>
## Overview
- Runs on macOS via the BlueBubbles helper app ([bluebubbles.app](https://bluebubbles.app)).
- Legacy fallback for installations that already rely on BlueBubbles channel IDs, webhook state, group targets, cron delivery, or workspace routing.
- Recommended/tested: macOS Sequoia (15). macOS Tahoe (26) works; edit is currently broken on Tahoe, and group icon updates may report success but not sync.
- OpenClaw talks to it through its REST API (`GET /api/v1/ping`, `POST /message/text`, `POST /chat/:id/*`).
- Incoming messages arrive via webhooks; outgoing replies, typing indicators, read receipts, and tapbacks are REST calls.
- Attachments and stickers are ingested as inbound media (and surfaced to the agent when possible).
- Auto-TTS replies that synthesize MP3 or CAF audio are delivered as iMessage voice memo bubbles instead of plain file attachments.
- Pairing/allowlist works the same way as other channels (`/channels/pairing` etc) with `channels.bluebubbles.allowFrom` + pairing codes.
- Reactions are surfaced as system events just like Slack/Telegram so agents can "mention" them before replying.
- Advanced features: edit, unsend, reply threading, message effects, group management.
## Quick start
<Steps>
<Step title="Install BlueBubbles">
Install the BlueBubbles server on your Mac (follow the instructions at [bluebubbles.app/install](https://bluebubbles.app/install)).
</Step>
<Step title="Enable the web API">
In the BlueBubbles config, enable the web API and set a password.
</Step>
<Step title="Configure OpenClaw">
Run `openclaw onboard` and select BlueBubbles, or configure manually:
```json5
{
channels: {
bluebubbles: {
enabled: true,
serverUrl: "http://192.168.1.100:1234",
password: "example-password",
webhookPath: "/bluebubbles-webhook",
},
},
}
```
</Step>
<Step title="Point webhooks at the gateway">
Point BlueBubbles webhooks to your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`).
</Step>
<Step title="Start the gateway">
Start the gateway; it will register the webhook handler and start pairing.
</Step>
</Steps>
<Warning>
**Security**
- Always set a webhook password.
- Webhook authentication is always required. OpenClaw rejects BlueBubbles webhook requests unless they include a password/guid that matches `channels.bluebubbles.password` (for example `?password=<password>` or `x-password`), regardless of loopback/proxy topology.
- Password authentication is checked before reading/parsing full webhook bodies.
</Warning>
## Keeping Messages.app alive (VM / headless setups)
Some macOS VM / always-on setups can end up with Messages.app going "idle" (incoming events stop until the app is opened/foregrounded). A simple workaround is to **poke Messages every 5 minutes** using an AppleScript + LaunchAgent.
<Steps>
<Step title="Save the AppleScript">
Save this as `~/Scripts/poke-messages.scpt`:
```applescript
try
tell application "Messages"
if not running then
launch
end if
-- Touch the scripting interface to keep the process responsive.
set _chatCount to (count of chats)
end tell
on error
-- Ignore transient failures (first-run prompts, locked session, etc).
end try
```
</Step>
<Step title="Install a LaunchAgent">
Save this as `~/Library/LaunchAgents/com.user.poke-messages.plist`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.user.poke-messages</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-lc</string>
<string>/usr/bin/osascript &quot;$HOME/Scripts/poke-messages.scpt&quot;</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StartInterval</key>
<integer>300</integer>
<key>StandardOutPath</key>
<string>/tmp/poke-messages.log</string>
<key>StandardErrorPath</key>
<string>/tmp/poke-messages.err</string>
</dict>
</plist>
```
This runs **every 300 seconds** and **on login**. The first run may trigger macOS **Automation** prompts (`osascript` → Messages). Approve them in the same user session that runs the LaunchAgent.
</Step>
<Step title="Load it">
```bash
launchctl unload ~/Library/LaunchAgents/com.user.poke-messages.plist 2>/dev/null || true
launchctl load ~/Library/LaunchAgents/com.user.poke-messages.plist
```
</Step>
</Steps>
## Onboarding
BlueBubbles is available in interactive onboarding:
```
openclaw onboard
```
The wizard prompts for:
<ParamField path="Server URL" type="string" required>
BlueBubbles server address (e.g., `http://192.168.1.100:1234`).
</ParamField>
<ParamField path="Password" type="string" required>
API password from BlueBubbles Server settings.
</ParamField>
<ParamField path="Webhook path" type="string" default="/bluebubbles-webhook">
Webhook endpoint path.
</ParamField>
<ParamField path="DM policy" type="string">
`pairing`, `allowlist`, `open`, or `disabled`.
</ParamField>
<ParamField path="Allow list" type="string[]">
Phone numbers, emails, or chat targets.
</ParamField>
You can also add BlueBubbles via CLI:
```
openclaw channels add bluebubbles --http-url http://192.168.1.100:1234 --password <password>
```
## Access control (DMs + groups)
<Tabs>
<Tab title="DMs">
- Default: `channels.bluebubbles.dmPolicy = "pairing"`.
- Unknown senders receive a pairing code; messages are ignored until approved (codes expire after 1 hour).
- Approve via:
- `openclaw pairing list bluebubbles`
- `openclaw pairing approve bluebubbles <CODE>`
- Pairing is the default token exchange. Details: [Pairing](/channels/pairing)
</Tab>
<Tab title="Groups">
- `channels.bluebubbles.groupPolicy = open | allowlist | disabled` (default: `allowlist`).
- `channels.bluebubbles.groupAllowFrom` controls who can trigger in groups when `allowlist` is set.
</Tab>
</Tabs>
### Contact name enrichment (macOS, optional)
BlueBubbles group webhooks often only include raw participant addresses. If you want `GroupMembers` context to show local contact names instead, you can opt in to local Contacts enrichment on macOS:
- `channels.bluebubbles.enrichGroupParticipantsFromContacts = true` enables the lookup. Default: `false`.
- Lookups run only after group access, command authorization, and mention gating have allowed the message through.
- Only unnamed phone participants are enriched.
- Raw phone numbers remain as the fallback when no local match is found.
```json5
{
channels: {
bluebubbles: {
enrichGroupParticipantsFromContacts: true,
},
},
}
```
### Mention gating (groups)
BlueBubbles supports mention gating for group chats, matching iMessage/WhatsApp behavior:
- Uses `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`) to detect mentions.
- When `requireMention` is enabled for a group, the agent only responds when mentioned.
- Control commands from authorized senders bypass mention gating.
Per-group configuration:
```json5
{
channels: {
bluebubbles: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15555550123"],
groups: {
"*": { requireMention: true }, // default for all groups
"iMessage;-;chat123": { requireMention: false }, // override for specific group
},
},
},
}
```
### Command gating
- Control commands (e.g., `/config`, `/model`) require authorization.
- Uses `allowFrom` and `groupAllowFrom` to determine command authorization.
- Authorized senders can run control commands even without mentioning in groups.
### Per-group system prompt
Each entry under `channels.bluebubbles.groups.*` accepts an optional `systemPrompt` string. The value is injected into the agent's system prompt on every turn that handles a message in that group, so you can set per-group persona or behavioral rules without editing agent prompts:
```json5
{
channels: {
bluebubbles: {
groups: {
"iMessage;-;chat123": {
systemPrompt: "Keep responses under 3 sentences. Mirror the group's casual tone.",
},
},
},
},
}
```
The key matches whatever BlueBubbles reports as `chatGuid` / `chatIdentifier` / numeric `chatId` for the group, and a `"*"` wildcard entry provides a default for every group without an exact match (same pattern used by `requireMention` and per-group tool policies). Exact matches always win over the wildcard. DMs ignore this field; use agent-level or account-level prompt customization instead.
#### Worked example: threaded replies and tapback reactions (Private API)
With the BlueBubbles Private API enabled, inbound messages arrive with short message IDs (for example `[[reply_to:5]]`) and the agent can call `action=reply` to thread into a specific message or `action=react` to drop a tapback. A per-group `systemPrompt` is a reliable way to keep the agent choosing the right tool:
```json5
{
channels: {
bluebubbles: {
groups: {
"iMessage;+;chat-family": {
systemPrompt: "When replying in this group, always call action=reply with the [[reply_to:N]] messageId from context so your response threads under the triggering message. Never send a new unlinked message. For short acknowledgements ('ok', 'got it', 'on it'), use action=react with an appropriate tapback emoji (❤️, 👍, 😂, ‼️, ❓) instead of sending a text reply.",
},
},
},
},
}
```
Tapback reactions and threaded replies both require the BlueBubbles Private API; see [Advanced actions](#advanced-actions) and [Message IDs](#message-ids-short-vs-full) for the underlying mechanics.
## ACP conversation bindings
BlueBubbles chats can be turned into durable ACP workspaces without changing the transport layer.
Fast operator flow:
- Run `/acp spawn codex --bind here` inside the DM or allowed group chat.
- Future messages in that same BlueBubbles conversation route to the spawned ACP session.
- `/new` and `/reset` reset the same bound ACP session in place.
- `/acp close` closes the ACP session and removes the binding.
Configured persistent bindings are also supported through top-level `bindings[]` entries with `type: "acp"` and `match.channel: "bluebubbles"`.
`match.peer.id` can use any supported BlueBubbles target form:
- normalized DM handle such as `+15555550123` or `user@example.com`
- `chat_id:<id>`
- `chat_guid:<guid>`
- `chat_identifier:<identifier>`
For stable group bindings, prefer `chat_id:*` or `chat_identifier:*`.
Example:
```json5
{
agents: {
list: [
{
id: "codex",
runtime: {
type: "acp",
acp: { agent: "codex", backend: "acpx", mode: "persistent" },
},
},
],
},
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "bluebubbles",
accountId: "default",
peer: { kind: "dm", id: "+15555550123" },
},
acp: { label: "codex-imessage" },
},
],
}
```
See [ACP Agents](/tools/acp-agents) for shared ACP binding behavior.
## Typing + read receipts
- **Typing indicators**: Sent automatically before and during response generation.
- **Read receipts**: Controlled by `channels.bluebubbles.sendReadReceipts` (default: `true`).
- **Typing indicators**: OpenClaw sends typing start events; BlueBubbles clears typing automatically on send or timeout (manual stop via DELETE is unreliable).
```json5
{
channels: {
bluebubbles: {
sendReadReceipts: false, // disable read receipts
},
},
}
```
## Advanced actions
BlueBubbles supports advanced message actions when enabled in config:
```json5
{
channels: {
bluebubbles: {
actions: {
reactions: true, // tapbacks (default: true)
edit: true, // edit sent messages (macOS 13+, broken on macOS 26 Tahoe)
unsend: true, // unsend messages (macOS 13+)
reply: true, // reply threading by message GUID
sendWithEffect: true, // message effects (slam, loud, etc.)
renameGroup: true, // rename group chats
setGroupIcon: true, // set group chat icon/photo (flaky on macOS 26 Tahoe)
addParticipant: true, // add participants to groups
removeParticipant: true, // remove participants from groups
leaveGroup: true, // leave group chats
sendAttachment: true, // send attachments/media
},
},
},
}
```
<AccordionGroup>
<Accordion title="Available actions">
- **react**: Add/remove tapback reactions (`messageId`, `emoji`, `remove`). iMessage's native tapback set is `love`, `like`, `dislike`, `laugh`, `emphasize`, and `question`. When an agent picks an emoji outside that set (for example `👀`), the reaction tool falls back to `love` so the tapback still renders instead of failing the whole request. Configured ack reactions still validate strictly and error on unknown values.
- **edit**: Edit a sent message (`messageId`, `text`).
- **unsend**: Unsend a message (`messageId`).
- **reply**: Reply to a specific message (`messageId`, `text`, `to`).
- **sendWithEffect**: Send with iMessage effect (`text`, `to`, `effectId`).
- **renameGroup**: Rename a group chat (`chatGuid`, `displayName`).
- **setGroupIcon**: Set a group chat's icon/photo (`chatGuid`, `media`) - flaky on macOS 26 Tahoe (API may return success but the icon does not sync).
- **addParticipant**: Add someone to a group (`chatGuid`, `address`).
- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`).
- **leaveGroup**: Leave a group chat (`chatGuid`).
- **upload-file**: Send media/files (`to`, `buffer`, `filename`, `asVoice`).
- Voice memos: set `asVoice: true` with **MP3** or **CAF** audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos.
- Legacy alias: `sendAttachment` still works, but `upload-file` is the canonical action name.
</Accordion>
</AccordionGroup>
### Message IDs (short vs full)
OpenClaw may surface _short_ message IDs (e.g., `1`, `2`) to save tokens.
- `MessageSid` / `ReplyToId` can be short IDs.
- `MessageSidFull` / `ReplyToIdFull` contain the provider full IDs.
- Short IDs are in-memory; they can expire on restart or cache eviction.
- Actions accept short or full `messageId`, but short IDs will error if no longer available.
Use full IDs for durable automations and storage:
- Templates: `{{MessageSidFull}}`, `{{ReplyToIdFull}}`
- Context: `MessageSidFull` / `ReplyToIdFull` in inbound payloads
See [Configuration](/gateway/configuration) for template variables.
<a id="coalescing-split-send-dms-command--url-in-one-composition"></a>
## Coalescing split-send DMs (command + URL in one composition)
When a user types a command and a URL together in iMessage - e.g. `Dump https://example.com/article` - Apple splits the send into **two separate webhook deliveries**:
1. A text message (`"Dump"`).
2. A URL-preview balloon (`"https://..."`) with OG-preview images as attachments.
The two webhooks arrive at OpenClaw ~0.8-2.0 s apart on most setups. Without coalescing, the agent receives the command alone on turn 1, replies (often "send me the URL"), and only sees the URL on turn 2 - at which point the command context is already lost.
`channels.bluebubbles.coalesceSameSenderDms` opts a DM into merging consecutive same-sender webhooks into a single agent turn. Group chats continue to key per-message so multi-user turn structure is preserved.
<Tabs>
<Tab title="When to enable">
Enable when:
- You ship skills that expect `command + payload` in one message (dump, paste, save, queue, etc.).
- Your users paste URLs, images, or long content alongside commands.
- You can accept the added DM turn latency (see below).
Leave disabled when:
- You need minimum command latency for single-word DM triggers.
- All your flows are one-shot commands without payload follow-ups.
</Tab>
<Tab title="Enabling">
```json5
{
channels: {
bluebubbles: {
coalesceSameSenderDms: true, // opt in (default: false)
},
},
}
```
With the flag on and no explicit `messages.inbound.byChannel.bluebubbles`, the debounce window widens to **2500 ms** (the default for non-coalescing is 500 ms). The wider window is required - Apple's split-send cadence of 0.8-2.0 s does not fit in the tighter default.
To tune the window yourself:
```json5
{
messages: {
inbound: {
byChannel: {
// 2500 ms works for most setups; raise to 4000 ms if your Mac is slow
// or under memory pressure (observed gap can stretch past 2 s then).
bluebubbles: 2500,
},
},
},
}
```
</Tab>
<Tab title="Trade-offs">
- **Added latency for DM control commands.** With the flag on, DM control-command messages (like `Dump`, `Save`, etc.) now wait up to the debounce window before dispatching, in case a payload webhook is coming. Group-chat commands keep instant dispatch.
- **Merged output is bounded** - merged text caps at 4000 chars with an explicit `…[truncated]` marker; attachments cap at 20; source entries cap at 10 (first-plus-latest retained beyond that). Every source `messageId` still reaches inbound-dedupe so a later MessagePoller replay of any individual event is recognized as a duplicate.
- **Opt-in, per-channel.** Other channels (Telegram, WhatsApp, Slack, …) are unaffected.
</Tab>
</Tabs>
### Scenarios and what the agent sees
| User composes | Apple delivers | Flag off (default) | Flag on + 2500 ms window |
| ------------------------------------------------------------------ | ------------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
| `Dump https://example.com` (one send) | 2 webhooks ~1 s apart | Two agent turns: "Dump" alone, then URL | One turn: merged text `Dump https://example.com` |
| `Save this 📎image.jpg caption` (attachment + text) | 2 webhooks | Two turns | One turn: text + image |
| `/status` (standalone command) | 1 webhook | Instant dispatch | **Wait up to window, then dispatch** |
| URL pasted alone | 1 webhook | Instant dispatch | Instant dispatch (only one entry in bucket) |
| Text + URL sent as two deliberate separate messages, minutes apart | 2 webhooks outside window | Two turns | Two turns (window expires between them) |
| Rapid flood (>10 small DMs inside window) | N webhooks | N turns | One turn, bounded output (first + latest, text/attachment caps applied) |
### Split-send coalescing troubleshooting
If the flag is on and split-sends still arrive as two turns, check each layer:
<AccordionGroup>
<Accordion title="Config actually loaded">
```
grep coalesceSameSenderDms ~/.openclaw/openclaw.json
```
Then `openclaw gateway restart` - the flag is read at debouncer-registry creation.
</Accordion>
<Accordion title="Debounce window wide enough for your setup">
Look at the BlueBubbles server log under `~/Library/Logs/bluebubbles-server/main.log`:
```
grep -E "Dispatching event to webhook" main.log | tail -20
```
Measure the gap between the `"Dump"`-style text dispatch and the `"https://..."; Attachments:` dispatch that follows. Raise `messages.inbound.byChannel.bluebubbles` to comfortably cover that gap.
</Accordion>
<Accordion title="Session JSONL timestamps ≠ webhook arrival">
Session event timestamps (`~/.openclaw/agents/<id>/sessions/*.jsonl`) reflect when the gateway hands a message to the agent, **not** when the webhook arrived. A queued-second message tagged `[Queued messages while agent was busy]` means the first turn was still running when the second webhook arrived - the coalesce bucket had already flushed. Tune the window against the BB server log, not the session log.
</Accordion>
<Accordion title="Memory pressure slowing reply dispatch">
On smaller machines (8 GB), agent turns can take long enough that the coalesce bucket flushes before the reply completes, and the URL lands as a queued second turn. Check `memory_pressure` and `ps -o rss -p $(pgrep openclaw-gateway)`; if the gateway is over ~500 MB RSS and the compressor is active, close other heavy processes or bump to a larger host.
</Accordion>
<Accordion title="Reply-quote sends are a different path">
If the user tapped `Dump` as a **reply** to an existing URL-balloon (iMessage shows a "1 Reply" badge on the Dump bubble), the URL lives in `replyToBody`, not in a second webhook. Coalescing does not apply - that's a skill/prompt concern, not a debouncer concern.
</Accordion>
</AccordionGroup>
## Block streaming
Control whether responses are sent as a single message or streamed in blocks:
```json5
{
channels: {
bluebubbles: {
blockStreaming: true, // enable block streaming (off by default)
},
},
}
```
## Media + limits
- Inbound attachments are downloaded and stored in the media cache.
- Media cap via `channels.bluebubbles.mediaMaxMb` for inbound and outbound media (default: 8 MB).
- Outbound text is chunked to `channels.bluebubbles.textChunkLimit` (default: 4000 chars).
## Configuration reference
Full configuration: [Configuration](/gateway/configuration)
<AccordionGroup>
<Accordion title="Connection and webhook">
- `channels.bluebubbles.enabled`: Enable/disable the channel.
- `channels.bluebubbles.serverUrl`: BlueBubbles REST API base URL.
- `channels.bluebubbles.password`: API password.
- `channels.bluebubbles.webhookPath`: Webhook endpoint path (default: `/bluebubbles-webhook`).
</Accordion>
<Accordion title="Access policy">
- `channels.bluebubbles.dmPolicy`: `pairing | allowlist | open | disabled` (default: `pairing`).
- `channels.bluebubbles.allowFrom`: DM allowlist (handles, emails, E.164 numbers, `chat_id:*`, `chat_guid:*`).
- `channels.bluebubbles.groupPolicy`: `open | allowlist | disabled` (default: `allowlist`).
- `channels.bluebubbles.groupAllowFrom`: Group sender allowlist.
- `channels.bluebubbles.enrichGroupParticipantsFromContacts`: On macOS, optionally enrich unnamed group participants from local Contacts after gating passes. Default: `false`.
- `channels.bluebubbles.groups`: Per-group config (`requireMention`, etc.).
</Accordion>
<Accordion title="Delivery and chunking">
- `channels.bluebubbles.sendReadReceipts`: Send read receipts (default: `true`).
- `channels.bluebubbles.blockStreaming`: Enable block streaming (default: `false`; required for streaming replies).
- `channels.bluebubbles.textChunkLimit`: Outbound chunk size in chars (default: 4000).
- `channels.bluebubbles.sendTimeoutMs`: Per-request timeout in ms for outbound text sends via `/api/v1/message/text` (default: 30000). Raise on macOS 26 setups where Private API iMessage sends can stall for 60+ seconds inside the iMessage framework; for example `45000` or `60000`. Probes, chat lookups, reactions, edits, and health checks currently keep the shorter 10s default; broadening coverage to reactions and edits is planned as a follow-up. Per-account override: `channels.bluebubbles.accounts.<accountId>.sendTimeoutMs`.
- `channels.bluebubbles.chunkMode`: `length` (default) splits only when exceeding `textChunkLimit`; `newline` splits on blank lines (paragraph boundaries) before length chunking.
</Accordion>
<Accordion title="Media and history">
- `channels.bluebubbles.mediaMaxMb`: Inbound/outbound media cap in MB (default: 8).
- `channels.bluebubbles.mediaLocalRoots`: Explicit allowlist of absolute local directories permitted for outbound local media paths. Local path sends are denied by default unless this is configured. Per-account override: `channels.bluebubbles.accounts.<accountId>.mediaLocalRoots`.
- `channels.bluebubbles.coalesceSameSenderDms`: Merge consecutive same-sender DM webhooks into one agent turn so Apple's text+URL split-send arrives as a single message (default: `false`). See [Coalescing split-send DMs](#coalescing-split-send-dms-command--url-in-one-composition) for scenarios, window tuning, and trade-offs. Widens the default inbound debounce window from 500 ms to 2500 ms when enabled without an explicit `messages.inbound.byChannel.bluebubbles`.
- `channels.bluebubbles.historyLimit`: Max group messages for context (0 disables).
- `channels.bluebubbles.dmHistoryLimit`: DM history limit.
- `channels.bluebubbles.replyContextApiFallback`: When an inbound reply lands without `replyToBody`/`replyToSender` and the in-memory reply-context cache misses, fetch the original message from the BlueBubbles HTTP API as a best-effort fallback (default: `false`). Useful for multi-instance deployments sharing one BlueBubbles account, after process restarts, or after long-lived TTL/LRU cache eviction. The fetch is SSRF-guarded by the same policy as every other BlueBubbles client request, never throws, and populates the cache so subsequent replies amortize. Per-account override: `channels.bluebubbles.accounts.<accountId>.replyContextApiFallback`. A channel-level setting propagates to accounts that omit the flag.
</Accordion>
<Accordion title="Actions and accounts">
- `channels.bluebubbles.actions`: Enable/disable specific actions.
- `channels.bluebubbles.accounts`: Multi-account configuration.
</Accordion>
</AccordionGroup>
Related global options:
- `agents.list[].groupChat.mentionPatterns` (or `messages.groupChat.mentionPatterns`).
- `messages.responsePrefix`.
## Addressing / delivery targets
Prefer `chat_guid` for stable routing:
- `chat_guid:iMessage;-;+15555550123` (preferred for groups)
- `chat_id:123`
- `chat_identifier:...`
- Direct handles: `+15555550123`, `user@example.com`
- If a direct handle does not have an existing DM chat, OpenClaw will create one via `POST /api/v1/chat/new`. This requires the BlueBubbles Private API to be enabled.
### iMessage vs SMS routing
When the same handle has both an iMessage and an SMS chat on the Mac (for example a phone number that is iMessage-registered but has also received green-bubble fallbacks), OpenClaw prefers the iMessage chat and never silently downgrades to SMS. To force the SMS chat, use an explicit `sms:` target prefix (for example `sms:+15555550123`). Handles without a matching iMessage chat still send through whatever chat BlueBubbles reports.
## Security
- Webhook requests are authenticated by comparing `guid`/`password` query params or headers against `channels.bluebubbles.password`.
- Keep the API password and webhook endpoint secret (treat them like credentials).
- There is no localhost bypass for BlueBubbles webhook auth. If you proxy webhook traffic, keep the BlueBubbles password on the request end-to-end. `gateway.trustedProxies` does not replace `channels.bluebubbles.password` here. See [Gateway security](/gateway/security#reverse-proxy-configuration).
- Enable HTTPS + firewall rules on the BlueBubbles server if exposing it outside your LAN.
## Troubleshooting
- If typing/read events stop working, check the BlueBubbles webhook logs and verify the gateway path matches `channels.bluebubbles.webhookPath`.
- Pairing codes expire after one hour; use `openclaw pairing list bluebubbles` and `openclaw pairing approve bluebubbles <code>`.
- Reactions require the BlueBubbles private API (`POST /api/v1/message/react`); ensure the server version exposes it.
- Edit/unsend require macOS 13+ and a compatible BlueBubbles server version. On macOS 26 (Tahoe), edit is currently broken due to private API changes.
- Group icon updates can be flaky on macOS 26 (Tahoe): the API may return success but the new icon does not sync.
- OpenClaw auto-hides known-broken actions based on the BlueBubbles server's macOS version. If edit still appears on macOS 26 (Tahoe), disable it manually with `channels.bluebubbles.actions.edit=false`.
- `coalesceSameSenderDms` enabled but split-sends (e.g. `Dump` + URL) still arrive as two turns: see the [split-send coalescing troubleshooting](#split-send-coalescing-troubleshooting) checklist - common causes are too-tight debounce window, session-log timestamps misread as webhook arrival, or a reply-quote send (which uses `replyToBody`, not a second webhook).
- For status/health info: `openclaw status --all` or `openclaw status --deep`.
For general channel workflow reference, see [Channels](/channels) and the [Plugins](/tools/plugin) guide.
## Related
- [Channel Routing](/channels/channel-routing) - session routing for messages
- [Channels Overview](/channels) - all supported channels
- [Groups](/channels/groups) - group chat behavior and mention gating
- [Pairing](/channels/pairing) - DM authentication and pairing flow
- [Security](/gateway/security) - access model and hardening

View File

@@ -1211,6 +1211,7 @@ Notes:
- OpenClaw also watches receive decrypt failures and auto-recovers by leaving/rejoining the voice channel after repeated failures in a short window.
- If receive logs repeatedly show `DecryptionFailed(UnencryptedWhenPassthroughDisabled)` after updating, collect a dependency report and logs. The bundled `@discordjs/voice` line includes the upstream padding fix from discord.js PR #11449, which closed discord.js issue #11419.
- `The operation was aborted` receive events are expected when OpenClaw finalizes a captured speaker segment; they are verbose diagnostics, not warnings.
- Verbose Discord voice logs include a bounded one-line STT transcript preview for each accepted speaker segment, so debugging shows both the user side and the agent reply side without dumping unbounded transcript text.
Voice channel pipeline:

View File

@@ -482,10 +482,6 @@ Group inbound payloads set:
- `WasMentioned` (mention gating result)
- Telegram forum topics also include `MessageThreadId` and `IsForum`.
Channel-specific notes:
- BlueBubbles can optionally enrich unnamed macOS group participants from the local Contacts database before populating `GroupMembers`. This is off by default and only runs after normal group gating passes.
The agent system prompt includes a group intro on the first turn of a new group session. It reminds the model to respond like a human, avoid Markdown tables, minimize empty lines and follow normal chat spacing, and avoid typing literal `\n` sequences. Channel-sourced group names and participant labels are rendered as fenced untrusted metadata, not inline system instructions.
## iMessage specifics

View File

@@ -7,18 +7,22 @@ title: "iMessage"
---
<Note>
For new OpenClaw iMessage deployments, start here when you can run `imsg` on a signed-in macOS Messages host. BlueBubbles remains available as a legacy fallback for existing setups that depend on its HTTP server, webhooks, or richer private-API actions.
For OpenClaw iMessage deployments, use `imsg` on a signed-in macOS Messages host. If your Gateway runs on Linux or Windows, point `channels.imessage.cliPath` at an SSH wrapper that runs `imsg` on the Mac.
</Note>
<Warning>
BlueBubbles is deprecated and no longer ships as a bundled OpenClaw channel. Migrate `channels.bluebubbles` configs to `channels.imessage`; OpenClaw now supports iMessage through `imsg` only. If you still need a BlueBubbles-backed bridge, publish or install it as a third-party plugin outside core.
</Warning>
Status: native external CLI integration. Gateway spawns `imsg rpc` and communicates over JSON-RPC on stdio (no separate daemon/port).
<CardGroup cols={3}>
<Card title="BlueBubbles (legacy fallback)" icon="message-circle" href="/channels/bluebubbles">
Keep using it for existing BlueBubbles-backed routing; avoid it for new setups when imsg fits.
</Card>
<Card title="Pairing" icon="link" href="/channels/pairing">
iMessage DMs default to pairing mode.
</Card>
<Card title="Remote Mac" icon="terminal" href="#remote-mac-over-ssh">
Use an SSH wrapper when the Gateway is not running on the Messages Mac.
</Card>
<Card title="Configuration reference" icon="settings" href="/gateway/config-channels#imessage">
Full iMessage field reference.
</Card>
@@ -362,7 +366,23 @@ imsg rpc --help
openclaw channels status --probe
```
If probe reports RPC unsupported, update `imsg`.
If probe reports RPC unsupported, update `imsg`. If the Gateway is not running on macOS, use the Remote Mac over SSH setup above instead of the default local `imsg` path.
</Accordion>
<Accordion title="Gateway is not running on macOS">
The default `cliPath: "imsg"` must run on the Mac signed into Messages. On Linux or Windows, set `channels.imessage.cliPath` to a wrapper script that SSHes to that Mac and runs `imsg "$@"`.
```bash
#!/usr/bin/env bash
exec ssh -T messages-mac imsg "$@"
```
Then run:
```bash
openclaw channels status --probe --channel imessage
```
</Accordion>
@@ -414,7 +434,6 @@ imsg send <handle> "test"
- [Configuration reference - iMessage](/gateway/config-channels#imessage)
- [Gateway configuration](/gateway/configuration)
- [Pairing](/channels/pairing)
- [BlueBubbles](/channels/bluebubbles)
## Related

View File

@@ -21,11 +21,10 @@ Text is supported everywhere; media and reactions vary by channel.
## Supported channels
- [BlueBubbles](/channels/bluebubbles) - Legacy iMessage bridge via the BlueBubbles macOS server REST API; deprecated for new OpenClaw setups but still supported for existing configs and richer private-API actions.
- [Discord](/channels/discord) - Discord Bot API + Gateway; supports servers, channels, and DMs.
- [Feishu](/channels/feishu) - Feishu/Lark bot via WebSocket (bundled plugin).
- [Google Chat](/channels/googlechat) - Google Chat API app via HTTP webhook (downloadable plugin).
- [iMessage](/channels/imessage) - Native macOS integration via the imsg CLI; preferred for new OpenClaw iMessage setups when host permissions and Messages access fit.
- [iMessage](/channels/imessage) - Native macOS integration via the `imsg` CLI on a signed-in Mac; use an SSH wrapper when the Gateway runs elsewhere.
- [IRC](/channels/irc) - Classic IRC servers; channels + DMs with pairing/allowlist controls.
- [LINE](/channels/line) - LINE Messaging API bot (downloadable plugin).
- [Matrix](/channels/matrix) - Matrix protocol (downloadable plugin).

View File

@@ -45,7 +45,7 @@ That gives first-time setups an explicit owner for privileged commands and exec
approval prompts. After an owner exists, later pairing approvals only grant DM
access; they do not add more owners.
Supported channels: `bluebubbles`, `discord`, `feishu`, `googlechat`, `imessage`, `irc`, `line`, `matrix`, `mattermost`, `msteams`, `nextcloud-talk`, `nostr`, `openclaw-weixin`, `signal`, `slack`, `synology-chat`, `telegram`, `twitch`, `whatsapp`, `zalo`, `zalouser`.
Supported channels: `discord`, `feishu`, `googlechat`, `imessage`, `irc`, `line`, `matrix`, `mattermost`, `msteams`, `nextcloud-talk`, `nostr`, `openclaw-weixin`, `signal`, `slack`, `synology-chat`, `telegram`, `twitch`, `whatsapp`, `zalo`, `zalouser`.
### Reusable sender groups
@@ -209,6 +209,5 @@ Stored under `~/.openclaw/devices/`:
- WhatsApp: [WhatsApp](/channels/whatsapp)
- Signal: [Signal](/channels/signal)
- iMessage: [iMessage](/channels/imessage)
- BlueBubbles (legacy iMessage bridge): [BlueBubbles](/channels/bluebubbles)
- Discord: [Discord](/channels/discord)
- Slack: [Slack](/channels/slack)

View File

@@ -82,20 +82,19 @@ Full troubleshooting: [Discord troubleshooting](/channels/discord#troubleshootin
Full troubleshooting: [Slack troubleshooting](/channels/slack#troubleshooting)
## iMessage and BlueBubbles
## iMessage
### iMessage and BlueBubbles failure signatures
### iMessage failure signatures
| Symptom | Fastest check | Fix |
| -------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------- |
| No inbound events | Verify webhook/server reachability and app permissions | Fix webhook URL or BlueBubbles server state. |
| Can send but no receive on macOS | Check macOS privacy permissions for Messages automation | Re-grant TCC permissions and restart channel process. |
| DM sender blocked | `openclaw pairing list imessage` or `openclaw pairing list bluebubbles` | Approve pairing or update allowlist. |
| Symptom | Fastest check | Fix |
| ------------------------------------ | ------------------------------------------------------- | --------------------------------------------------------------------- |
| `imsg` missing or fails on non-macOS | `openclaw channels status --probe --channel imessage` | Run OpenClaw on the Messages Mac or use an SSH wrapper for `cliPath`. |
| Can send but no receive on macOS | Check macOS privacy permissions for Messages automation | Re-grant TCC permissions and restart channel process. |
| DM sender blocked | `openclaw pairing list imessage` | Approve pairing or update allowlist. |
Full troubleshooting:
- [iMessage troubleshooting](/channels/imessage#troubleshooting)
- [BlueBubbles troubleshooting](/channels/bluebubbles#troubleshooting)
## Signal

View File

@@ -42,7 +42,8 @@ Quick rule:
| ACP area | Status | Notes |
| --------------------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `initialize`, `newSession`, `prompt`, `cancel` | Implemented | Core bridge flow over stdio to Gateway chat/send + abort. |
| `listSessions`, slash commands | Implemented | Session list works against Gateway session state; commands are advertised via `available_commands_update`. |
| `listSessions`, slash commands | Implemented | Session list works against Gateway session state with bounded cursor pagination and `cwd` filtering where Gateway session rows carry workspace metadata; commands are advertised via `available_commands_update`. |
| `resumeSession`, `closeSession` | Implemented | Resume rebinds an ACP session to an existing Gateway session without replaying history. Close cancels active bridge work, resolves pending prompts as cancelled, and releases bridge session state. |
| `loadSession` | Partial | Rebinds the ACP session to a Gateway session key and replays stored user/assistant text history. Tool/system history is not reconstructed yet. |
| Prompt content (`text`, embedded `resource`, images) | Partial | Text/resources are flattened into chat input; images become Gateway attachments. |
| Session modes | Partial | `session/set_mode` is supported and the bridge exposes initial Gateway-backed session controls for thought level, tool verbosity, reasoning, usage detail, and elevated actions. Broader ACP-native mode/config surfaces are still out of scope. |
@@ -120,6 +121,50 @@ Permission model (client debug mode):
- Server-provided `toolCall.kind` is treated as untrusted metadata (not an authorization source).
- This ACP bridge policy is separate from ACPX harness permissions. If you run OpenClaw through the `acpx` backend, `plugins.entries.acpx.config.permissionMode=approve-all` is the break-glass "yolo" switch for that harness session.
## Protocol smoke testing
For protocol-level debugging, start a Gateway with isolated state and drive
`openclaw acp` over stdio with an ACP JSON-RPC client. Cover `initialize`,
`session/new`, `session/list` with an absolute `cwd`, `session/resume`,
`session/close`, duplicate close, and missing resume.
The proof should include the advertised lifecycle capabilities, a Gateway-backed
session row, update notifications, and the Gateway `sessions.list` log:
```json
{
"initialize": {
"protocolVersion": 1,
"agentCapabilities": {
"sessionCapabilities": {
"list": {},
"resume": {},
"close": {}
}
}
},
"listSessions": {
"sessions": [
{
"sessionId": "agent:main:acp-smoke",
"cwd": "/path/to/workspace",
"_meta": {
"sessionKey": "agent:main:acp-smoke",
"kind": "direct"
}
}
],
"nextCursor": null
},
"notifications": ["session_info_update", "available_commands_update", "usage_update"],
"gatewayLogTail": ["[gateway] ready", "[ws] ⇄ res ✓ sessions.list 305ms"]
}
```
Avoid using `openclaw gateway call sessions.list` as the only ACP proof. That
CLI path may request a fresh-token operator scope upgrade; ACP bridge
correctness is proven by ACP stdio frames plus the Gateway `sessions.list` log.
## How to use this
Use ACP when an IDE (or other client) speaks Agent Client Protocol and you want

View File

@@ -19,6 +19,7 @@ Related docs:
```bash
openclaw channels list
openclaw channels list --all
openclaw channels status
openclaw channels capabilities
openclaw channels capabilities --channel discord --target channel:123
@@ -27,6 +28,8 @@ openclaw channels resolve --channel slack "#general" "@jane"
openclaw channels logs --channel all
```
`channels list` shows chat channels only: configured accounts by default, with `installed`, `configured`, and `enabled` status tags per account. Pass `--all` to also surface bundled channels that have no configured account yet and installable catalog channels that are not yet on disk. Auth providers (OAuth + API keys) and model-provider usage/quota snapshots are no longer printed here; use `openclaw models auth list` for provider auth profiles and `openclaw status` or `openclaw models list` for usage.
## Status / capabilities / resolve / logs
- `channels status`: `--probe`, `--timeout <ms>`, `--json`
@@ -109,7 +112,7 @@ openclaw channels logout --channel whatsapp
- Run `openclaw status --deep` for a broad probe.
- Use `openclaw doctor` for guided fixes.
- `openclaw channels list` prints `Claude: HTTP 403 ... user:profile` → usage snapshot needs the `user:profile` scope. Use `--no-usage`, or provide a claude.ai session key (`CLAUDE_WEB_SESSION_KEY` / `CLAUDE_WEB_COOKIE`), or re-auth via Claude CLI.
- `openclaw channels list` no longer prints model provider usage/quota snapshots. For those, use `openclaw status` (overview) or `openclaw models list` (per-provider).
- `openclaw channels status` falls back to config-only summaries when the gateway is unreachable. If a supported channel credential is configured via SecretRef but unavailable in the current command path, it reports that account as configured with degraded notes instead of showing it as not configured.
## Capabilities probe

View File

@@ -220,6 +220,8 @@ openclaw cron runs --id <job-id> --limit 50
`openclaw cron list` shows all matching jobs by default. Pass `--agent <id>` to show only jobs whose effective normalized agent id matches; jobs without a stored agent id count as the configured default agent.
`cron list --json` and `cron show <job-id> --json` include a top-level `status` field on each job, computed from `enabled`, `state.runningAtMs`, and `state.lastRunStatus`. Values: `disabled`, `running`, `ok`, `error`, `skipped`, or `idle`. This mirrors the human-readable status column so external tooling can read job state without re-deriving it.
`cron runs` entries include delivery diagnostics with the intended cron target, the resolved target, message-tool sends, fallback use, and delivered state.
Agent and session retargeting:

View File

@@ -43,8 +43,8 @@ Probe rows can come from auth profiles, env credentials, or `models.json`.
For Codex OAuth troubleshooting, `openclaw models status`,
`openclaw models auth list --provider openai-codex`, and
`openclaw config get agents.defaults.model --json` are the quickest way to
confirm whether an agent is using `openai-codex/*` through PI or `openai/*`
through the native Codex runtime. See [OpenAI provider setup](/providers/openai#check-and-recover-codex-oauth-routing).
confirm whether an agent has a usable `openai-codex` auth profile for
`openai/*` through the native Codex runtime. See [OpenAI provider setup](/providers/openai#check-and-recover-codex-oauth-routing).
Notes:

View File

@@ -23,6 +23,11 @@ event loop. The CLI returns the newest 100 sessions by default; pass
need the full store. JSON responses include `totalCount`, `limitApplied`, and
`hasMore` when callers need to show that more rows exist.
RPC clients can pass `configuredAgentsOnly: true` to keep the broad combined
discovery source but return only rows for agents currently present in config.
Control UI uses that mode by default so deleted or disk-only agent stores do
not reappear in the Sessions view.
```bash
openclaw sessions
openclaw sessions --agent work

View File

@@ -41,19 +41,19 @@ There are two runtime families:
Most confusion comes from several different surfaces sharing the Codex name:
| Surface | OpenClaw name/config | What it does |
| ---------------------------------------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| Native Codex app-server runtime | `openai/*` plus `agentRuntime.id: "codex"` | Runs the embedded agent turn through Codex app-server. This is the usual ChatGPT/Codex subscription setup. |
| Codex OAuth provider route | `openai-codex/*` model refs | Uses ChatGPT/Codex subscription OAuth through the normal OpenClaw PI runner. |
| Codex ACP adapter | `runtime: "acp"`, `agentId: "codex"` | Runs Codex through the external ACP/acpx control plane. Use only when ACP/acpx is explicitly asked. |
| Native Codex chat-control command set | `/codex ...` | Binds, resumes, steers, stops, and inspects Codex app-server threads from chat. |
| OpenAI Platform API route for GPT/Codex-style models | `openai/*` model refs | Uses OpenAI API-key auth unless a runtime override, such as `agentRuntime.id: "codex"`, runs the turn. |
| Surface | OpenClaw name/config | What it does |
| ------------------------------------------------ | ------------------------------------ | -------------------------------------------------------------------------------------------------------------- |
| Native Codex app-server runtime | `openai/*` model refs | Runs OpenAI embedded agent turns through Codex app-server. This is the usual ChatGPT/Codex subscription setup. |
| Codex OAuth auth profiles | `openai-codex` auth provider | Stores ChatGPT/Codex subscription auth that the Codex app-server harness consumes. |
| Codex ACP adapter | `runtime: "acp"`, `agentId: "codex"` | Runs Codex through the external ACP/acpx control plane. Use only when ACP/acpx is explicitly asked. |
| Native Codex chat-control command set | `/codex ...` | Binds, resumes, steers, stops, and inspects Codex app-server threads from chat. |
| OpenAI Platform API route for non-agent surfaces | `openai/*` plus API-key auth | Used for direct OpenAI APIs such as images, embeddings, speech, and realtime. |
Those surfaces are intentionally independent. Enabling the `codex` plugin makes
the native app-server features available; it does not rewrite
`openai-codex/*` into `openai/*`, does not change existing sessions, and does
not make ACP the Codex default. Selecting `openai-codex/*` means "use the Codex
OAuth provider route" unless you separately force a runtime.
the native app-server features available; `openclaw doctor --fix` owns legacy
`openai-codex/*` route repair and stale session pin cleanup. Selecting
`openai/*` for an agent model now means "run this through Codex" unless a
non-agent OpenAI API surface is being used.
The common ChatGPT/Codex subscription setup uses Codex OAuth for auth, but keeps
the model ref as `openai/*` and selects the `codex` runtime:
@@ -63,9 +63,6 @@ the model ref as `openai/*` and selects the `codex` runtime:
agents: {
defaults: {
model: "openai/gpt-5.5",
agentRuntime: {
id: "codex",
},
},
},
}
@@ -88,20 +85,23 @@ This is the agent-facing decision tree:
1. If the user asks for **Codex bind/control/thread/resume/steer/stop**, use the
native `/codex` command surface when the bundled `codex` plugin is enabled.
2. If the user asks for **Codex as the embedded runtime** or wants the normal
subscription-backed Codex agent experience, use
`openai/<model>` with `agentRuntime.id: "codex"`.
3. If the user asks for **Codex OAuth/subscription auth on the normal OpenClaw
runner**, use `openai-codex/<model>` and leave the runtime as PI.
4. If the user explicitly says **ACP**, **acpx**, or **Codex ACP adapter**, use
subscription-backed Codex agent experience, use `openai/<model>`.
3. If the user explicitly chooses **PI for an OpenAI model**, keep the model ref
as `openai/<model>` and set `agentRuntime.id: "pi"`. A selected
`openai-codex` auth profile is routed internally through PI's legacy
Codex-auth transport.
4. If legacy config still contains **`openai-codex/*` model refs**, repair it to
`openai/<model>` with `openclaw doctor --fix`.
5. If the user explicitly says **ACP**, **acpx**, or **Codex ACP adapter**, use
ACP with `runtime: "acp"` and `agentId: "codex"`.
5. If the request is for **Claude Code, Gemini CLI, OpenCode, Cursor, Droid, or
6. If the request is for **Claude Code, Gemini CLI, OpenCode, Cursor, Droid, or
another external harness**, use ACP/acpx, not the native sub-agent runtime.
| You mean... | Use... |
| --------------------------------------- | -------------------------------------------- |
| Codex app-server chat/thread control | `/codex ...` from the bundled `codex` plugin |
| Codex app-server embedded agent runtime | `agentRuntime.id: "codex"` |
| OpenAI Codex OAuth on the PI runner | `openai-codex/*` model refs |
| Codex app-server embedded agent runtime | `openai/*` agent model refs |
| OpenAI Codex OAuth | `openai-codex` auth profiles |
| Claude Code or other external harness | ACP/acpx |
For the OpenAI-family prefix split, see [OpenAI](/providers/openai) and
@@ -166,17 +166,17 @@ Legacy refs such as `claude-cli/claude-opus-4-7` remain supported for
compatibility, but new config should keep the provider/model canonical and put
the execution backend in `agentRuntime.id`.
`auto` mode is intentionally conservative. Plugin runtimes can claim
provider/model pairs they understand, but the Codex plugin does not claim the
`openai-codex` provider in `auto` mode. That keeps
`openai-codex/*` as the explicit PI Codex OAuth route and avoids silently
moving subscription-auth configs onto the native app-server harness.
`auto` mode is intentionally conservative for most providers. OpenAI agent
models are the exception: unset runtime and `auto` both resolve to the Codex
harness. Explicit PI runtime config remains an opt-in compatibility route for
`openai/*` agent turns; when paired with a selected `openai-codex` auth profile,
OpenClaw routes PI internally through the legacy Codex-auth transport while
keeping the public model ref as `openai/*`. Stale OpenAI PI session pins without
explicit config are repaired back to Codex.
If `openclaw doctor` warns that the `codex` plugin is enabled while
`openai-codex/*` still routes through PI, treat that as a diagnosis, not a
migration. Keep the config unchanged when PI Codex OAuth is what you want.
Switch to `openai/<model>` plus `agentRuntime.id: "codex"` only when you want native
Codex app-server execution.
`openai-codex/*` remains in config, treat that as legacy route state. Run
`openclaw doctor --fix` to rewrite it to `openai/*` with the Codex runtime.
## Compatibility contract

View File

@@ -33,7 +33,7 @@ title: "Features"
**Channels:**
- Built-in channels include Discord, Google Chat, iMessage, IRC, Signal, Slack, Telegram, WebChat, and WhatsApp
- Bundled plugin channels include BlueBubbles as a legacy iMessage bridge, Feishu, LINE, Matrix, Mattermost, Microsoft Teams, Nextcloud Talk, Nostr, QQ Bot, Synology Chat, Tlon, Twitch, Zalo, and Zalo Personal
- Bundled plugin channels include Feishu, LINE, Matrix, Mattermost, Microsoft Teams, Nextcloud Talk, Nostr, QQ Bot, Synology Chat, Tlon, Twitch, Zalo, and Zalo Personal
- Optional separately installed channel plugins include Voice Call and third-party packages such as WeChat
- Third-party channel plugins can extend the Gateway further, such as WeChat
- Group chat support with mention-based activation

View File

@@ -763,7 +763,7 @@ Concrete migration hazards to preserve:
- Telegram silent fallback delivery must deliver the full projected payload
array. A single-payload shortcut can drop additional fallback payloads after
projection.
- LINE, BlueBubbles, Zalo, Nostr, and other existing assembled/helper paths may
- LINE, Zalo, Nostr, and other existing assembled/helper paths may
have reply-token handling, media proxying, sent-message caches, loading/status
cleanup, or callback-only targets. They stay on channel-owned delivery until
those semantics are represented by the send adapter and verified by tests.
@@ -854,30 +854,30 @@ Core policy:
## Channel mapping
| Channel | Target migration |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Telegram | Receive ack policy plus durable final sends. Live adapter owns send plus edit preview, stale preview final send, topics, quote-reply preview skip, media fallback, and retry-after handling. |
| Discord | Send adapter wraps existing durable payload delivery. Live adapter owns draft edit, progress draft, media/error preview cancel, reply target preservation, and message id receipts. Audit bot-authored gateway-failure echoes in shared rooms; use an outbound registry or other native equivalent if Discord cannot carry origin metadata on normal messages. |
| Slack | Send adapter handles normal chat posts. Live adapter chooses native stream when thread shape supports it, otherwise draft preview. Receipts preserve thread timestamps. Origin adapter maps OpenClaw gateway failures to Slack `chat.postMessage.metadata` and drops tagged bot-room echoes before `allowBots` authorization. |
| WhatsApp | Send adapter owns text/media send with durable final intents. Receive adapter handles group mention and sender identity. Live can stay absent until WhatsApp has an editable transport. |
| Matrix | Live adapter owns draft event edits, finalization, redaction, encrypted media constraints, and reply-target mismatch fallback. Receive adapter owns encrypted event hydration and dedupe. Origin adapter should encode OpenClaw gateway-failure origin into Matrix event content and drop configured-bot room echoes before `allowBots` handling. |
| Mattermost | Live adapter owns one draft post, progress/tool folding, finalization in place, and fresh-send fallback. |
| Microsoft Teams | Live adapter owns native progress and block stream behavior. Send adapter owns activities and attachment/card receipts. |
| Feishu | Render adapter owns text/card/raw rendering. Live adapter owns streaming cards and duplicate final suppression. Send adapter owns comments, topic sessions, media, and voice suppression. |
| QQ Bot | Live adapter owns C2C streaming, accumulator timeout, and fallback final send. Render adapter owns media tags and text-as-voice. |
| Signal | Simple receive plus send adapter. No live adapter unless signal-cli adds reliable edit support. |
| iMessage and BlueBubbles | Simple receive plus send adapter. iMessage send must preserve monitor echo-cache population before durable finals can bypass monitor delivery. BlueBubbles-specific typing, reactions, and attachments remain adapter capabilities. |
| Google Chat | Simple receive plus send adapter with thread relation mapped to spaces and thread ids. Audit `allowBots=true` room behavior for tagged OpenClaw gateway-failure echoes. |
| LINE | Simple receive plus send adapter with reply-token constraints modeled as target/relation capability. |
| Nextcloud Talk | SDK receive bridge plus send adapter. |
| IRC | Simple receive plus send adapter, no durable edit receipts. |
| Nostr | Receive plus send adapter for encrypted DMs; receipts are event ids. |
| QA Channel | Contract-test adapter for receive, send, live, retry, and recovery behavior. |
| Synology Chat | Simple receive plus send adapter. |
| Tlon | Send adapter must preserve model-signature rendering and participated-thread tracking before generic durable final delivery is enabled. |
| Twitch | Simple receive plus send adapter with rate-limit classification. |
| Zalo | Simple receive plus send adapter. |
| Zalo Personal | Simple receive plus send adapter. |
| Channel | Target migration |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Telegram | Receive ack policy plus durable final sends. Live adapter owns send plus edit preview, stale preview final send, topics, quote-reply preview skip, media fallback, and retry-after handling. |
| Discord | Send adapter wraps existing durable payload delivery. Live adapter owns draft edit, progress draft, media/error preview cancel, reply target preservation, and message id receipts. Audit bot-authored gateway-failure echoes in shared rooms; use an outbound registry or other native equivalent if Discord cannot carry origin metadata on normal messages. |
| Slack | Send adapter handles normal chat posts. Live adapter chooses native stream when thread shape supports it, otherwise draft preview. Receipts preserve thread timestamps. Origin adapter maps OpenClaw gateway failures to Slack `chat.postMessage.metadata` and drops tagged bot-room echoes before `allowBots` authorization. |
| WhatsApp | Send adapter owns text/media send with durable final intents. Receive adapter handles group mention and sender identity. Live can stay absent until WhatsApp has an editable transport. |
| Matrix | Live adapter owns draft event edits, finalization, redaction, encrypted media constraints, and reply-target mismatch fallback. Receive adapter owns encrypted event hydration and dedupe. Origin adapter should encode OpenClaw gateway-failure origin into Matrix event content and drop configured-bot room echoes before `allowBots` handling. |
| Mattermost | Live adapter owns one draft post, progress/tool folding, finalization in place, and fresh-send fallback. |
| Microsoft Teams | Live adapter owns native progress and block stream behavior. Send adapter owns activities and attachment/card receipts. |
| Feishu | Render adapter owns text/card/raw rendering. Live adapter owns streaming cards and duplicate final suppression. Send adapter owns comments, topic sessions, media, and voice suppression. |
| QQ Bot | Live adapter owns C2C streaming, accumulator timeout, and fallback final send. Render adapter owns media tags and text-as-voice. |
| Signal | Simple receive plus send adapter. No live adapter unless signal-cli adds reliable edit support. |
| iMessage | Simple receive plus send adapter. iMessage send must preserve monitor echo-cache population before durable finals can bypass monitor delivery. |
| Google Chat | Simple receive plus send adapter with thread relation mapped to spaces and thread ids. Audit `allowBots=true` room behavior for tagged OpenClaw gateway-failure echoes. |
| LINE | Simple receive plus send adapter with reply-token constraints modeled as target/relation capability. |
| Nextcloud Talk | SDK receive bridge plus send adapter. |
| IRC | Simple receive plus send adapter, no durable edit receipts. |
| Nostr | Receive plus send adapter for encrypted DMs; receipts are event ids. |
| QA Channel | Contract-test adapter for receive, send, live, retry, and recovery behavior. |
| Synology Chat | Simple receive plus send adapter. |
| Tlon | Send adapter must preserve model-signature rendering and participated-thread tracking before generic durable final delivery is enabled. |
| Twitch | Simple receive plus send adapter with rate-limit classification. |
| Zalo | Simple receive plus send adapter. |
| Zalo Personal | Simple receive plus send adapter. |
## Migration plan
@@ -1035,7 +1035,7 @@ Channel tests:
- Discord prepared dispatcher finals route through the send context before docs
or changelog claim Discord final-reply durability.
- iMessage durable final sends populate the monitor sent-message echo cache.
- LINE, BlueBubbles, Zalo, and Nostr legacy delivery paths are not bypassed by
- LINE, Zalo, and Nostr legacy delivery paths are not bypassed by
generic durable send until their adapter parity tests exist.
- Direct-DM/Nostr callback delivery remains authoritative unless explicitly
migrated to a complete message target and replay-safe send adapter.

View File

@@ -59,7 +59,7 @@ Config (global default + per-channel overrides):
Notes:
- Debounce applies to **text-only** messages; media/attachments flush immediately.
- Control commands bypass debouncing so they remain standalone**except** when a channel explicitly opts in to same-sender DM coalescing (e.g. [BlueBubbles `coalesceSameSenderDms`](/channels/bluebubbles#coalescing-split-send-dms-command--url-in-one-composition)), where DM commands wait inside the debounce window so a split-send payload can join the same agent turn.
- Control commands bypass debouncing so they remain standalone. Channels that explicitly opt in to same-sender DM coalescing can keep DM commands inside the debounce window so a split-send payload can join the same agent turn.
## Sessions and devices

View File

@@ -262,7 +262,7 @@ Common channels supporting this pattern include:
- `whatsapp`, `telegram`, `discord`, `slack`, `signal`, `imessage`
- `irc`, `line`, `googlechat`, `mattermost`, `matrix`, `nextcloud-talk`
- `bluebubbles`, `zalo`, `zalouser`, `nostr`, `feishu`
- `zalo`, `zalouser`, `nostr`, `feishu`
## Concepts

View File

@@ -52,6 +52,10 @@
]
},
"redirects": [
{
"source": "/channels/bluebubbles",
"destination": "/channels/imessage"
},
{
"source": "/install/migrating-matrix",
"destination": "/channels/matrix-migration"
@@ -1059,7 +1063,6 @@
"channels/msteams",
"channels/googlechat",
"channels/imessage",
"channels/bluebubbles",
"channels/matrix",
"channels/matrix-migration",
"channels/matrix-push-rules"

View File

@@ -110,6 +110,8 @@ openclaw models auth paste-token --provider openrouter
OpenClaw expects the canonical `version` + `profiles` shape at runtime. If an older install still has a flat file such as `{ "openrouter": { "apiKey": "..." } }`, run `openclaw doctor --fix` to rewrite it as an `openrouter:default` API-key profile; doctor keeps a `.legacy-flat.*.bak` copy beside the original. Endpoint details such as `baseUrl`, `api`, model ids, headers, and timeouts belong under `models.providers.<id>` in `openclaw.json` or `models.json`, not in `auth-profiles.json`.
External auth routes such as Bedrock `auth: "aws-sdk"` are also not credentials. If you want a named Bedrock route, put `auth.profiles.<id>.mode: "aws-sdk"` in `openclaw.json`; do not write `type: "aws-sdk"` into `auth-profiles.json`. `openclaw doctor --fix` moves legacy AWS SDK markers from the credential store into config metadata.
Auth profile refs are also supported for static credentials:
- `api_key` credentials can use `keyRef: { source, provider, id }`

View File

@@ -387,7 +387,7 @@ Time format in system prompt. Default: `auto` (OS preference).
- `toolProgressDetail`: detail mode for `/verbose` tool summaries and progress-draft tool lines. Values: `"explain"` (default, compact human labels) or `"raw"` (append raw command/detail when available). Per-agent `agents.list[].toolProgressDetail` overrides this default.
- `reasoningDefault`: default reasoning visibility for agents. Values: `"off"`, `"on"`, `"stream"`. Per-agent `agents.list[].reasoningDefault` overrides this default. Configured reasoning defaults are only applied for owners, authorized senders, or operator-admin gateway contexts when no per-message or session reasoning override is set.
- `elevatedDefault`: default elevated-output level for agents. Values: `"off"`, `"on"`, `"ask"`, `"full"`. Default: `"on"`.
- `model.primary`: format `provider/model` (e.g. `openai/gpt-5.5` for API-key access or `openai-codex/gpt-5.5` for Codex OAuth). If you omit the provider, OpenClaw tries an alias first, then a unique configured-provider match for that exact model id, and only then falls back to the configured default provider (deprecated compatibility behavior, so prefer explicit `provider/model`). If that provider no longer exposes the configured default model, OpenClaw falls back to the first configured provider/model instead of surfacing a stale removed-provider default.
- `model.primary`: format `provider/model` (e.g. `openai/gpt-5.5` for OpenAI API-key or Codex OAuth access). If you omit the provider, OpenClaw tries an alias first, then a unique configured-provider match for that exact model id, and only then falls back to the configured default provider (deprecated compatibility behavior, so prefer explicit `provider/model`). If that provider no longer exposes the configured default model, OpenClaw falls back to the first configured provider/model instead of surfacing a stale removed-provider default.
- `models`: the configured model catalog and allowlist for `/model`. Each entry can include `alias` (shortcut) and `params` (provider-specific, for example `temperature`, `maxTokens`, `cacheRetention`, `context1m`, `responsesServerCompaction`, `responsesCompactThreshold`, `chat_template_kwargs`, `extra_body`/`extraBody`).
- Safe edits: use `openclaw config set agents.defaults.models '<json>' --strict-json --merge` to add entries. `config set` refuses replacements that would remove existing allowlist entries unless you pass `--replace`.
- Provider-scoped configure/onboarding flows merge selected provider models into this map and preserve unrelated providers already configured.
@@ -426,24 +426,24 @@ model, see [Agent runtimes](/concepts/agent-runtimes).
- `id`: `"auto"`, `"pi"`, a registered plugin harness id, or a supported CLI backend alias. The bundled Codex plugin registers `codex`; the bundled Anthropic plugin provides the `claude-cli` CLI backend.
- `id: "auto"` lets registered plugin harnesses claim supported turns and uses PI when no harness matches. An explicit plugin runtime such as `id: "codex"` requires that harness and fails closed if it is unavailable or fails.
- Environment override: `OPENCLAW_AGENT_RUNTIME=<id|auto|pi>` overrides `id` for that process.
- For Codex-only deployments, set `model: "openai/gpt-5.5"` and `agentRuntime.id: "codex"`.
- OpenAI agent models use the Codex harness by default; `agentRuntime.id: "codex"` remains valid when you want to make that explicit.
- For Claude CLI deployments, prefer `model: "anthropic/claude-opus-4-7"` plus `agentRuntime.id: "claude-cli"`. Legacy `claude-cli/claude-opus-4-7` model refs still work for compatibility, but new config should keep provider/model selection canonical and put the execution backend in `agentRuntime.id`.
- Older runtime-policy keys are rewritten to `agentRuntime` by `openclaw doctor --fix`.
- Harness choice is pinned per session id after the first embedded run. Config/env changes affect new or reset sessions, not an existing transcript. Legacy sessions with transcript history but no recorded pin are treated as PI-pinned. `/status` reports the effective runtime, for example `Runtime: OpenClaw Pi Default` or `Runtime: OpenAI Codex`.
- Harness choice is pinned per session id after the first embedded run. Config/env changes affect new or reset sessions, not an existing transcript. Legacy OpenAI sessions with transcript history but no recorded pin use Codex; stale OpenAI PI pins can be repaired with `openclaw doctor --fix`. `/status` reports the effective runtime, for example `Runtime: OpenClaw Pi Default` or `Runtime: OpenAI Codex`.
- This only controls text agent-turn execution. Media generation, vision, PDF, music, video, and TTS still use their provider/model settings.
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
| Alias | Model |
| ------------------- | ------------------------------------------ |
| `opus` | `anthropic/claude-opus-4-6` |
| `sonnet` | `anthropic/claude-sonnet-4-6` |
| `gpt` | `openai/gpt-5.5` or `openai-codex/gpt-5.5` |
| `gpt-mini` | `openai/gpt-5.4-mini` |
| `gpt-nano` | `openai/gpt-5.4-nano` |
| `gemini` | `google/gemini-3.1-pro-preview` |
| `gemini-flash` | `google/gemini-3-flash-preview` |
| `gemini-flash-lite` | `google/gemini-3.1-flash-lite-preview` |
| Alias | Model |
| ------------------- | -------------------------------------- |
| `opus` | `anthropic/claude-opus-4-6` |
| `sonnet` | `anthropic/claude-sonnet-4-6` |
| `gpt` | `openai/gpt-5.5` |
| `gpt-mini` | `openai/gpt-5.4-mini` |
| `gpt-nano` | `openai/gpt-5.4-nano` |
| `gemini` | `google/gemini-3.1-pro-preview` |
| `gemini-flash` | `google/gemini-3-flash-preview` |
| `gemini-flash-lite` | `google/gemini-3.1-flash-lite-preview` |
Your configured aliases always win over defaults.
@@ -1290,7 +1290,7 @@ Variables are case-insensitive. `{think}` is an alias for `{thinkingLevel}`.
- Per-channel overrides: `channels.<channel>.ackReaction`, `channels.<channel>.accounts.<id>.ackReaction`.
- Resolution order: account → channel → `messages.ackReaction` → identity fallback.
- Scope: `group-mentions` (default), `group-all`, `direct`, `all`.
- `removeAckAfterReply`: removes ack after reply on reaction-capable channels such as Slack, Discord, Telegram, WhatsApp, and BlueBubbles.
- `removeAckAfterReply`: removes ack after reply on reaction-capable channels such as Slack, Discord, Telegram, WhatsApp, and iMessage.
- `messages.statusReactions.enabled`: enables lifecycle status reactions on Slack, Discord, and Telegram.
On Slack and Discord, unset keeps status reactions enabled when ack reactions are active.
On Telegram, set it explicitly to `true` to enable lifecycle status reactions.

View File

@@ -581,32 +581,14 @@ When Mattermost native commands are enabled:
- `channels.signal.configWrites`: allow or deny Signal-initiated config writes.
- Optional `channels.signal.defaultAccount` overrides default account selection when it matches a configured account id.
### BlueBubbles
BlueBubbles is the legacy iMessage bridge (plugin-backed, configured under `channels.bluebubbles`). Existing setups remain supported, but new OpenClaw iMessage deployments should prefer `channels.imessage` when `imsg` can run on the Messages host.
```json5
{
channels: {
bluebubbles: {
enabled: true,
dmPolicy: "pairing",
// serverUrl, password, webhookPath, group controls, and advanced actions:
// see /channels/bluebubbles
},
},
}
```
- Core key paths covered here: `channels.bluebubbles`, `channels.bluebubbles.dmPolicy`.
- Optional `channels.bluebubbles.defaultAccount` overrides default account selection when it matches a configured account id.
- Top-level `bindings[]` entries with `type: "acp"` can bind BlueBubbles conversations to persistent ACP sessions. Use a BlueBubbles handle or target string (`chat_id:*`, `chat_guid:*`, `chat_identifier:*`) in `match.peer.id`. Shared field semantics: [ACP Agents](/tools/acp-agents#persistent-channel-bindings).
- Full BlueBubbles channel configuration and deprecation rationale are documented in [BlueBubbles](/channels/bluebubbles).
### iMessage
OpenClaw spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required. This is the preferred path for new OpenClaw iMessage setups when the host can grant Messages database and Automation permissions.
BlueBubbles is deprecated and no longer ships as a bundled OpenClaw channel. Migrate `channels.bluebubbles` configs to `channels.imessage`; third-party BlueBubbles bridges belong outside core.
If the Gateway is not running on the signed-in Messages Mac, keep `channels.imessage.enabled=true` and set `channels.imessage.cliPath` to an SSH wrapper that runs `imsg "$@"` on that Mac. The default local `imsg` path is macOS-only.
```json5
{
channels: {

View File

@@ -264,7 +264,7 @@ That stages grounded durable candidates into the short-term dreaming store while
If you previously added legacy OpenAI transport settings under `models.providers.openai-codex`, they can shadow the built-in Codex OAuth provider path that newer releases use automatically. Doctor warns when it sees those old transport settings alongside Codex OAuth so you can remove or rewrite the stale transport override and get the built-in routing/fallback behavior back. Custom proxies and header-only overrides are still supported and do not trigger this warning.
</Accordion>
<Accordion title="2f. Codex route repair">
Doctor checks for legacy `openai-codex/*` model refs. Native Codex harness routing uses canonical `openai/*` model refs plus `agentRuntime.id: "codex"` so the turn goes through the Codex app-server harness instead of the OpenClaw PI OpenAI path.
Doctor checks for legacy `openai-codex/*` model refs. Native Codex harness routing uses canonical `openai/*` model refs; OpenAI agent turns go through the Codex app-server harness instead of the OpenClaw PI OpenAI path.
In `--fix` / `--repair` mode, doctor rewrites affected default-agent and per-agent refs, including primary models, fallbacks, heartbeat/subagent/compaction overrides, hooks, channel model overrides, and stale persisted session route state:

View File

@@ -102,7 +102,7 @@ Outside heartbeats, stray `HEARTBEAT_OK` at the start/end of a message is stripp
lightContext: false, // default: false; true keeps only HEARTBEAT.md from workspace bootstrap files
isolatedSession: false, // default: false; true runs each heartbeat in a fresh session (no conversation history)
skipWhenBusy: false, // default: false; true also waits for subagent/nested lanes
target: "last", // default: none | options: last | none | <channel id> (core or plugin, e.g. "bluebubbles")
target: "last", // default: none | options: last | none | <channel id> (core or plugin, e.g. "imessage")
to: "+15551234567", // optional channel-specific override
accountId: "ops-bot", // optional multi-account channel id
prompt: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.",

View File

@@ -597,26 +597,26 @@ and troubleshooting see the main [FAQ](/help/faq).
`openai/gpt-5.5` with `agentRuntime.id: "codex"` for the common setup:
ChatGPT/Codex subscription auth plus native Codex app-server execution. Use
`openai-codex/gpt-5.5` only when you want Codex OAuth through the default
PI runner. Use `openai/gpt-5.5` without the Codex runtime override for
direct OpenAI API-key access.
Codex runtime. Direct OpenAI API-key access remains available for non-agent
OpenAI API surfaces and for agent models through an ordered
`openai-codex` API-key profile.
See [Model providers](/concepts/model-providers) and [Onboarding (CLI)](/start/wizard).
</Accordion>
<Accordion title="Why does OpenClaw still mention openai-codex?">
`openai-codex` is the provider and auth-profile id for ChatGPT/Codex OAuth.
It is also the explicit PI model prefix for Codex OAuth:
Older configs also used it as a model prefix:
- `openai/gpt-5.5` + `agentRuntime.id: "codex"` = ChatGPT/Codex subscription auth with native Codex runtime
- `openai-codex/gpt-5.5` = Codex OAuth route in PI
- `openai/gpt-5.5` without a Codex runtime override = direct OpenAI API-key route in PI
- `openai/gpt-5.5` = ChatGPT/Codex subscription auth with native Codex runtime for agent turns
- `openai-codex/gpt-5.5` = legacy model route repaired by `openclaw doctor --fix`
- `openai/gpt-5.5` plus an ordered `openai-codex` API-key profile = API-key auth for an OpenAI agent model
- `openai-codex:...` = auth profile id, not a model ref
If you want the direct OpenAI Platform billing/limit path, set
`OPENAI_API_KEY`. If you want ChatGPT/Codex subscription auth, sign in with
`openclaw models auth login --provider openai-codex`. For native Codex
runtime, keep the model ref as `openai/gpt-5.5` and set
`agentRuntime.id: "codex"`. Use `openai-codex/*` model refs only for PI
runs.
`openclaw models auth login --provider openai-codex`. Keep the model ref as
`openai/gpt-5.5`; `openai-codex/*` model refs are legacy config that
`openclaw doctor --fix` rewrites.
</Accordion>
@@ -670,22 +670,22 @@ and troubleshooting see the main [FAQ](/help/faq).
No. OpenClaw runs on macOS or Linux (Windows via WSL2). A Mac mini is optional - some people
buy one as an always-on host, but a small VPS, home server, or Raspberry Pi-class box works too.
You only need a Mac **for macOS-only tools**. For iMessage, use [BlueBubbles](/channels/bluebubbles) (recommended) - the BlueBubbles server runs on any Mac, and the Gateway can run on Linux or elsewhere. If you want other macOS-only tools, run the Gateway on a Mac or pair a macOS node.
You only need a Mac **for macOS-only tools**. For iMessage, use [iMessage](/channels/imessage) with `imsg` on any Mac signed into Messages. If the Gateway runs on Linux or elsewhere, set `channels.imessage.cliPath` to an SSH wrapper that runs `imsg` on that Mac. If you want other macOS-only tools, run the Gateway on a Mac or pair a macOS node.
Docs: [BlueBubbles](/channels/bluebubbles), [Nodes](/nodes), [Mac remote mode](/platforms/mac/remote).
Docs: [iMessage](/channels/imessage), [Nodes](/nodes), [Mac remote mode](/platforms/mac/remote).
</Accordion>
<Accordion title="Do I need a Mac mini for iMessage support?">
You need **some macOS device** signed into Messages. It does **not** have to be a Mac mini -
any Mac works. **Use [BlueBubbles](/channels/bluebubbles)** (recommended) for iMessage - the BlueBubbles server runs on macOS, while the Gateway can run on Linux or elsewhere.
any Mac works. **Use [iMessage](/channels/imessage)** with `imsg`; the Gateway can run on that Mac, or it can run elsewhere with an SSH wrapper `cliPath`.
Common setups:
- Run the Gateway on Linux/VPS, and run the BlueBubbles server on any Mac signed into Messages.
- Run the Gateway on Linux/VPS, and set `channels.imessage.cliPath` to an SSH wrapper that runs `imsg` on a Mac signed into Messages.
- Run everything on the Mac if you want the simplest single-machine setup.
Docs: [BlueBubbles](/channels/bluebubbles), [Nodes](/nodes),
Docs: [iMessage](/channels/imessage), [Nodes](/nodes),
[Mac remote mode](/platforms/mac/remote).
</Accordion>

View File

@@ -21,7 +21,7 @@ troubleshooting, see the main [FAQ](/help/faq).
agents.defaults.model.primary
```
Models are referenced as `provider/model` (example: `openai/gpt-5.5` or `openai-codex/gpt-5.5`). If you omit the provider, OpenClaw first tries an alias, then a unique configured-provider match for that exact model id, and only then falls back to the configured default provider as a deprecated compatibility path. If that provider no longer exposes the configured default model, OpenClaw falls back to the first configured provider/model instead of surfacing a stale removed-provider default. You should still **explicitly** set `provider/model`.
Models are referenced as `provider/model` (example: `openai/gpt-5.5` or `anthropic/claude-sonnet-4-6`). If you omit the provider, OpenClaw first tries an alias, then a unique configured-provider match for that exact model id, and only then falls back to the configured default provider as a deprecated compatibility path. If that provider no longer exposes the configured default model, OpenClaw falls back to the first configured provider/model instead of surfacing a stale removed-provider default. You should still **explicitly** set `provider/model`.
</Accordion>
@@ -147,9 +147,9 @@ troubleshooting, see the main [FAQ](/help/faq).
<Accordion title="Can I use GPT 5.5 for daily tasks and Codex 5.5 for coding?">
Yes. Treat model choice and runtime choice separately:
- **Native Codex coding agent:** set `agents.defaults.model.primary` to `openai/gpt-5.5` and `agents.defaults.agentRuntime.id` to `"codex"`. Sign in with `openclaw models auth login --provider openai-codex` when you want ChatGPT/Codex subscription auth.
- **Direct OpenAI API tasks through PI:** use `/model openai/gpt-5.5` without a Codex runtime override and configure `OPENAI_API_KEY`.
- **Codex OAuth through PI:** use `/model openai-codex/gpt-5.5` only when you intentionally want the normal PI runner with Codex OAuth.
- **Native Codex coding agent:** set `agents.defaults.model.primary` to `openai/gpt-5.5`. Sign in with `openclaw models auth login --provider openai-codex` when you want ChatGPT/Codex subscription auth.
- **Direct OpenAI API tasks outside the agent loop:** configure `OPENAI_API_KEY` for images, embeddings, speech, realtime, and other non-agent OpenAI API surfaces.
- **OpenAI agent API-key auth:** use `/model openai/gpt-5.5` with an ordered `openai-codex` API-key profile.
- **Sub-agents:** route coding tasks to a Codex-only agent with its own model and `agentRuntime` default.
See [Models](/concepts/models) and [Slash commands](/tools/slash-commands).
@@ -159,8 +159,8 @@ troubleshooting, see the main [FAQ](/help/faq).
<Accordion title="How do I configure fast mode for GPT 5.5?">
Use either a session toggle or a config default:
- **Per session:** send `/fast on` while the session is using `openai/gpt-5.5` or `openai-codex/gpt-5.5`.
- **Per model default:** set `agents.defaults.models["openai/gpt-5.5"].params.fastMode` or `agents.defaults.models["openai-codex/gpt-5.5"].params.fastMode` to `true`.
- **Per session:** send `/fast on` while the session is using `openai/gpt-5.5`.
- **Per model default:** set `agents.defaults.models["openai/gpt-5.5"].params.fastMode` to `true`.
Example:
@@ -271,7 +271,7 @@ troubleshooting, see the main [FAQ](/help/faq).
- `opus` → `anthropic/claude-opus-4-6`
- `sonnet` → `anthropic/claude-sonnet-4-6`
- `gpt` → `openai/gpt-5.5` for API-key setups, or `openai-codex/gpt-5.5` when configured for Codex OAuth
- `gpt` → `openai/gpt-5.5`
- `gpt-mini` → `openai/gpt-5.4-mini`
- `gpt-nano` → `openai/gpt-5.4-nano`
- `gemini` → `google/gemini-3.1-pro-preview`

View File

@@ -54,7 +54,7 @@ OpenClaw is a **self-hosted gateway** that connects your favorite chat apps and
- **Agent-native**: built for coding agents with tool use, sessions, memory, and multi-agent routing
- **Open source**: MIT licensed, community-driven
**What do you need?** Node 24 (recommended), or Node 22 LTS (`22.14+`) for compatibility, an API key from your chosen provider, and 5 minutes. For best quality and security, use the strongest latest-generation model available.
**What do you need?** Node 24 (recommended), or Node 22 LTS (`22.16+`) for compatibility, an API key from your chosen provider, and 5 minutes. For best quality and security, use the strongest latest-generation model available.
## How it works

View File

@@ -46,7 +46,7 @@ The Ansible playbook installs and configures:
1. **Tailscale** -- mesh VPN for secure remote access
2. **UFW firewall** -- SSH + Tailscale ports only
3. **Docker CE + Compose V2** -- for the default agent sandbox backend
4. **Node.js 24 + pnpm** -- runtime dependencies (Node 22 LTS, currently `22.14+`, remains supported)
4. **Node.js 24 + pnpm** -- runtime dependencies (Node 22 LTS, currently `22.16+`, remains supported)
5. **OpenClaw** -- host-based, not containerized
6. **Systemd service** -- auto-start with security hardening

View File

@@ -39,7 +39,7 @@ Bun is an optional local runtime for running TypeScript directly (`bun run ...`,
Bun blocks dependency lifecycle scripts unless explicitly trusted. For this repo, the commonly blocked scripts are not required:
- `@whiskeysockets/baileys` `preinstall` -- checks Node major >= 20 (OpenClaw defaults to Node 24 and still supports Node 22 LTS, currently `22.14+`)
- `@whiskeysockets/baileys` `preinstall` -- checks Node major >= 20 (OpenClaw defaults to Node 24 and still supports Node 22 LTS, currently `22.16+`)
- `protobufjs` `postinstall` -- emits warnings about incompatible version schemes (no build artifacts)
If you hit a runtime issue that requires these scripts, trust them explicitly:

View File

@@ -335,6 +335,32 @@ See [ClawDock](/install/clawdock) for the full helper guide.
`no-new-privileges` on both `openclaw-gateway` and `openclaw-cli`.
</Accordion>
<Accordion title="Docker Desktop DNS failures in openclaw-cli">
Some Docker Desktop setups fail DNS lookups from the shared-network
`openclaw-cli` sidecar after `NET_RAW` is dropped, which shows up as
`EAI_AGAIN` during npm-backed commands such as `openclaw plugins install`.
Keep the default hardened compose file for normal gateway operation. The
local override below loosens the CLI container's security posture by
restoring Docker's default capabilities, so use it only for the one-off CLI
command that needs package registry access, not as your default Compose
invocation:
```bash
printf '%s\n' \
'services:' \
' openclaw-cli:' \
' cap_drop: !reset []' \
> docker-compose.cli-no-dropped-caps.local.yml
docker compose -f docker-compose.yml -f docker-compose.cli-no-dropped-caps.local.yml run --rm openclaw-cli plugins install <package>
```
If you already created a long-running `openclaw-cli` container, recreate it
with the same override. `docker compose exec` and `docker exec` cannot
change Linux capabilities on an already-created container.
</Accordion>
<Accordion title="Permissions and EACCES">
The image runs as `node` (uid 1000). If you see permission errors on
`/home/node/.openclaw`, make sure your host bind mounts are owned by uid 1000:

View File

@@ -9,7 +9,7 @@ title: "Install"
## System requirements
- **Node 24** (recommended) or Node 22.14+ - the installer script handles this automatically
- **Node 24** (recommended) or Node 22.16+ - the installer script handles this automatically
- **macOS, Linux, or Windows** - both native Windows and WSL2 are supported; WSL2 is more stable. See [Windows](/platforms/windows).
- `pnpm` is only needed if you build from source

View File

@@ -71,7 +71,7 @@ Recommended for most interactive installs on macOS/Linux/WSL.
Supports macOS and Linux (including WSL). If macOS is detected, installs Homebrew if missing.
</Step>
<Step title="Ensure Node.js 24 by default">
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). OpenClaw still supports Node 22 LTS, currently `22.14+`, for compatibility.
Checks Node version and installs Node 24 if needed (Homebrew on macOS, NodeSource setup scripts on Linux apt/dnf/yum). OpenClaw still supports Node 22 LTS, currently `22.16+`, for compatibility.
</Step>
<Step title="Ensure Git">
Installs Git if missing.
@@ -284,7 +284,7 @@ by default, plus git-checkout installs under the same prefix flow.
Requires PowerShell 5+.
</Step>
<Step title="Ensure Node.js 24 by default">
If missing, attempts install via winget, then Chocolatey, then Scoop. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
If missing, attempts install via winget, then Chocolatey, then Scoop. Node 22 LTS, currently `22.16+`, remains supported for compatibility.
</Step>
<Step title="Install OpenClaw">
- `npm` method (default): global npm install using selected `-Tag`, launched from a writable installer temp directory so shells opened in protected folders such as `C:\` still work

View File

@@ -2,7 +2,7 @@
summary: "Run OpenClaw in a sandboxed macOS VM (local or hosted) when you need isolation or iMessage"
read_when:
- You want OpenClaw isolated from your main macOS environment
- You want iMessage integration (BlueBubbles) in a sandbox
- You want iMessage integration in a sandbox
- You want a resettable macOS environment you can clone
- You want to compare local vs hosted macOS VM options
title: "macOS VMs"
@@ -14,7 +14,7 @@ title: "macOS VMs"
- **Dedicated hardware** (Mac mini or Linux box) if you want full control and a **residential IP** for browser automation. Many sites block data center IPs, so local browsing often works better.
- **Hybrid:** keep the Gateway on a cheap VPS, and connect your Mac as a **node** when you need browser/UI automation. See [Nodes](/nodes) and [Gateway remote](/gateway/remote).
Use a macOS VM when you specifically need macOS-only capabilities (iMessage/BlueBubbles) or want strict isolation from your daily Mac.
Use a macOS VM when you specifically need macOS-only capabilities such as iMessage or want strict isolation from your daily Mac.
## macOS VM options
@@ -25,7 +25,7 @@ Run OpenClaw in a sandboxed macOS VM on your existing Apple Silicon Mac using [L
This gives you:
- Full macOS environment in isolation (your host stays clean)
- iMessage support via BlueBubbles (impossible on Linux/Windows)
- iMessage support via `imsg` (the default local path is impossible on Linux/Windows)
- Instant reset by cloning VMs
- No extra hardware or cloud costs
@@ -198,24 +198,24 @@ ssh youruser@192.168.64.X "openclaw status"
## Bonus: iMessage integration
This is the killer feature of running on macOS. Use [BlueBubbles](https://bluebubbles.app) to add iMessage to OpenClaw.
This is the killer feature of running on macOS. Use [iMessage](/channels/imessage) with `imsg` to add Messages to OpenClaw.
Inside the VM:
1. Download BlueBubbles from bluebubbles.app
2. Sign in with your Apple ID
3. Enable the Web API and set a password
4. Point BlueBubbles webhooks at your gateway (example: `https://your-gateway-host:3000/bluebubbles-webhook?password=<password>`)
1. Sign in to Messages.
2. Install `imsg`.
3. Grant Full Disk Access and Automation permission for the process running OpenClaw/`imsg`.
4. Verify RPC support with `imsg rpc --help`.
Add to your OpenClaw config:
```json5
{
channels: {
bluebubbles: {
serverUrl: "http://localhost:1234",
password: "your-api-password",
webhookPath: "/bluebubbles-webhook",
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
},
},
}
@@ -223,7 +223,7 @@ Add to your OpenClaw config:
Restart the gateway. Now your agent can send and receive iMessages.
Full setup details: [BlueBubbles channel](/channels/bluebubbles)
Full setup details: [iMessage channel](/channels/imessage)
---
@@ -274,7 +274,7 @@ For true always-on, consider a dedicated Mac mini or a small VPS. See [VPS hosti
- [VPS hosting](/vps)
- [Nodes](/nodes)
- [Gateway remote](/gateway/remote)
- [BlueBubbles channel](/channels/bluebubbles)
- [iMessage channel](/channels/imessage)
- [Lume Quickstart](https://cua.ai/docs/lume/guide/getting-started/quickstart)
- [Lume CLI Reference](https://cua.ai/docs/lume/reference/cli-reference)
- [Unattended VM Setup](https://cua.ai/docs/lume/guide/fundamentals/unattended-setup) (advanced)

View File

@@ -7,7 +7,7 @@ read_when:
- "npm install -g fails with permissions or PATH issues"
---
OpenClaw requires **Node 22.14 or newer**. **Node 24 is the default and recommended runtime** for installs, CI, and release workflows. Node 22 remains supported via the active LTS line. The [installer script](/install#alternative-install-methods) will detect and install Node automatically - this page is for when you want to set up Node yourself and make sure everything is wired up correctly (versions, PATH, global installs).
OpenClaw requires **Node 22.16 or newer**. **Node 24 is the default and recommended runtime** for installs, CI, and release workflows. Node 22 remains supported via the active LTS line. The [installer script](/install#alternative-install-methods) will detect and install Node automatically - this page is for when you want to set up Node yourself and make sure everything is wired up correctly (versions, PATH, global installs).
## Check your version
@@ -15,7 +15,7 @@ OpenClaw requires **Node 22.14 or newer**. **Node 24 is the default and recommen
node -v
```
If this prints `v24.x.x` or higher, you're on the recommended default. If it prints `v22.14.x` or higher, you're on the supported Node 22 LTS path, but we still recommend upgrading to Node 24 when convenient. If Node isn't installed or the version is too old, pick an install method below.
If this prints `v24.x.x` or higher, you're on the recommended default. If it prints `v22.16.x` or higher, you're on the supported Node 22 LTS path, but we still recommend upgrading to Node 24 when convenient. If Node isn't installed or the version is too old, pick an install method below.
## Install Node

View File

@@ -14,7 +14,7 @@ Native Linux companion apps are planned. Contributions are welcome if you want t
## Beginner quick path (VPS)
1. Install Node 24 (recommended; Node 22 LTS, currently `22.14+`, still works for compatibility)
1. Install Node 24 (recommended; Node 22 LTS, currently `22.16+`, still works for compatibility)
2. `npm i -g openclaw@latest`
3. `openclaw onboard --install-daemon`
4. From your laptop: `ssh -N -L 18789:127.0.0.1:18789 <user>@<host>`

View File

@@ -14,7 +14,7 @@ running (or attaches to an existing local Gateway if one is already running).
## Install the CLI (required for local mode)
Node 24 is the default runtime on the Mac. Node 22 LTS, currently `22.14+`, still works for compatibility. Then install `openclaw` globally:
Node 24 is the default runtime on the Mac. Node 22 LTS, currently `22.16+`, still works for compatibility. Then install `openclaw` globally:
```bash
npm install -g openclaw@<version>

View File

@@ -14,7 +14,7 @@ Build and run the OpenClaw macOS application from source.
Before building the app, ensure you have the following installed:
1. **Xcode 26.2+**: Required for Swift development.
2. **Node.js 24 & pnpm**: Recommended for the gateway, CLI, and packaging scripts. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
2. **Node.js 24 & pnpm**: Recommended for the gateway, CLI, and packaging scripts. Node 22 LTS, currently `22.16+`, remains supported for compatibility.
## 1. Install Dependencies

View File

@@ -14,7 +14,7 @@ This app is usually built from [`scripts/package-mac-app.sh`](https://github.com
- calls [`scripts/codesign-mac-app.sh`](https://github.com/openclaw/openclaw/blob/main/scripts/codesign-mac-app.sh) to sign the main binary and app bundle so macOS treats each rebuild as the same signed bundle and keeps TCC permissions (notifications, accessibility, screen recording, mic, speech). For stable permissions, use a real signing identity; ad-hoc is opt-in and fragile (see [macOS permissions](/platforms/mac/permissions)).
- uses `CODESIGN_TIMESTAMP=auto` by default; it enables trusted timestamps for Developer ID signatures. Set `CODESIGN_TIMESTAMP=off` to skip timestamping (offline debug builds).
- inject build metadata into Info.plist: `OpenClawBuildTimestamp` (UTC) and `OpenClawGitCommit` (short hash) so the About pane can show build, git, and debug/release channel.
- **Packaging defaults to Node 24**: the script runs TS builds and the Control UI build. Node 22 LTS, currently `22.14+`, remains supported for compatibility.
- **Packaging defaults to Node 24**: the script runs TS builds and the Control UI build. Node 22 LTS, currently `22.16+`, remains supported for compatibility.
- reads `SIGN_IDENTITY` from the environment. Add `export SIGN_IDENTITY="Apple Development: Your Name (TEAMID)"` (or your Developer ID Application cert) to your shell rc to always sign with your cert. Ad-hoc signing requires explicit opt-in via `ALLOW_ADHOC_SIGNING=1` or `SIGN_IDENTITY="-"` (not recommended for permission testing).
- runs a Team ID audit after signing and fails if any Mach-O inside the app bundle is signed by a different Team ID. Set `SKIP_TEAM_ID_CHECK=1` to bypass.

View File

@@ -106,10 +106,9 @@ The bundled `codex` plugin contributes several separate capabilities:
Enabling the plugin makes those capabilities available. It does **not**:
- start using Codex for every OpenAI model
- convert `openai-codex/*` model refs into the native runtime without doctor
verifying that Codex is installed, enabled, contributes the `codex` harness,
and is OAuth-ready
- replace direct OpenAI API-key surfaces such as images, embeddings, speech, or
realtime
- convert `openai-codex/*` model refs without `openclaw doctor --fix`
- make ACP/acpx the default Codex path
- hot-switch existing sessions that already recorded a PI runtime
- replace OpenClaw channel delivery, session files, auth-profile storage, or
@@ -141,29 +140,28 @@ tool-result writes.
For the plugin hook semantics themselves, see [Plugin hooks](/plugins/hooks)
and [Plugin guard behavior](/tools/plugin).
The harness is off by default. New configs should keep OpenAI model refs
canonical as `openai/gpt-*` and explicitly force
`agentRuntime.id: "codex"` or `OPENCLAW_AGENT_RUNTIME=codex` when they
want native app-server execution. Legacy `codex/*` model refs still auto-select
the harness for compatibility, but runtime-backed legacy provider prefixes are
not shown as normal model/provider choices.
OpenAI agent model refs use the harness by default. New configs should keep
OpenAI model refs canonical as `openai/gpt-*`; `agentRuntime.id: "codex"` is
still valid but no longer required for OpenAI agent turns. Legacy `codex/*`
model refs still auto-select the harness for compatibility, but
runtime-backed legacy provider prefixes are not shown as normal model/provider
choices.
If any configured model route is still `openai-codex/*`, `openclaw doctor --fix`
rewrites it to `openai/*`. For matching agent routes, it sets the agent runtime
to `codex` only when the Codex plugin is installed, enabled, contributes the
`codex` harness, and has usable OAuth; otherwise it sets the runtime to `pi`.
to `codex` and preserves existing `openai-codex` auth profile overrides.
## Route map
Use this table before changing config:
| Desired behavior | Model ref | Runtime config | Auth/profile route | Expected status label |
| ---------------------------------------------------- | -------------------------- | -------------------------------------- | ---------------------------- | ------------------------------ |
| ChatGPT/Codex subscription with native Codex runtime | `openai/gpt-*` | `agentRuntime.id: "codex"` | Codex OAuth or Codex account | `Runtime: OpenAI Codex` |
| OpenAI API through normal OpenClaw runner | `openai/gpt-*` | omitted or `runtime: "pi"` | OpenAI API key | `Runtime: OpenClaw Pi Default` |
| Legacy config that needs doctor repair | `openai-codex/gpt-*` | repaired to `codex` or `pi` | Existing configured auth | Recheck after `doctor --fix` |
| Mixed providers with conservative auto mode | provider-specific refs | `agentRuntime.id: "auto"` | Per selected provider | Depends on selected runtime |
| Explicit Codex ACP adapter session | ACP prompt/model dependent | `sessions_spawn` with `runtime: "acp"` | ACP backend auth | ACP task/session status |
| Desired behavior | Model ref | Runtime config | Auth/profile route | Expected status label |
| ---------------------------------------------------- | -------------------------- | -------------------------------------- | ------------------------------ | ---------------------------- |
| ChatGPT/Codex subscription with native Codex runtime | `openai/gpt-*` | omitted or `agentRuntime.id: "codex"` | Codex OAuth or Codex account | `Runtime: OpenAI Codex` |
| OpenAI API-key auth for agent models | `openai/gpt-*` | omitted or `agentRuntime.id: "codex"` | `openai-codex` API-key profile | `Runtime: OpenAI Codex` |
| Legacy config that needs doctor repair | `openai-codex/gpt-*` | repaired to `codex` | Existing configured auth | Recheck after `doctor --fix` |
| Mixed providers with conservative auto mode | provider-specific refs | `agentRuntime.id: "auto"` | Per selected provider | Depends on selected runtime |
| Explicit Codex ACP adapter session | ACP prompt/model dependent | `sessions_spawn` with `runtime: "acp"` | ACP backend auth | ACP task/session status |
The important split is provider versus runtime:
@@ -171,8 +169,7 @@ The important split is provider versus runtime:
- `agentRuntime.id: "codex"` requires the Codex harness and fails closed if it
is unavailable.
- `agentRuntime.id: "auto"` lets registered harnesses claim matching provider
routes, but canonical OpenAI refs are still PI-owned unless a harness supports
that provider/model pair.
routes; OpenAI agent refs resolve to Codex instead of PI.
- `/codex ...` answers "which native Codex conversation should this chat bind
or control?"
- ACP answers "which external harness process should acpx launch?"
@@ -180,14 +177,14 @@ The important split is provider versus runtime:
## Pick the right model prefix
OpenAI-family routes are prefix-specific. For the common subscription plus
native Codex runtime setup, use `openai/*` with `agentRuntime.id: "codex"`.
native Codex runtime setup, use `openai/*`.
Treat `openai-codex/*` as legacy config that doctor should rewrite:
| Model ref | Runtime path | Use when |
| --------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------- |
| `openai/gpt-5.4` | OpenAI provider through OpenClaw/PI plumbing | You want current direct OpenAI Platform API access with `OPENAI_API_KEY`. |
| `openai-codex/gpt-5.5` | Legacy route repaired by doctor | You are on old config; run `openclaw doctor --fix` to rewrite it. |
| `openai/gpt-5.5` + `agentRuntime.id: "codex"` | Codex app-server harness | You want ChatGPT/Codex subscription auth with native Codex execution. |
| Model ref | Runtime path | Use when |
| ------------------------------------------------- | ---------------------------------------- | ----------------------------------------------------------------- |
| `openai/gpt-5.4` | Codex app-server harness for agent turns | You want OpenAI agent models through Codex. |
| `openai-codex/gpt-5.5` | Legacy route repaired by doctor | You are on old config; run `openclaw doctor --fix` to rewrite it. |
| `openai/gpt-5.5` + `openai-codex` API-key profile | Codex app-server harness | You want API-key auth for an OpenAI agent model. |
GPT-5.5 can appear on both direct OpenAI API-key and Codex subscription routes
when your account exposes them. Use `openai/gpt-5.5` with the Codex app-server
@@ -219,13 +216,10 @@ state still use `openai-codex/*`. `openclaw doctor --fix` rewrites those routes
to:
- `openai/<model>`
- `agentRuntime.id: "codex"` when Codex is installed, enabled, contributes the
`codex` harness, and has usable OAuth
- `agentRuntime.id: "pi"` otherwise
- `agentRuntime.id: "codex"`
The `codex` route forces the native Codex harness. The `pi` route keeps the
agent on the default OpenClaw runner instead of enabling or installing Codex as
a side effect of legacy-route cleanup.
The `codex` route forces the native Codex harness. PI runtime config is not
allowed for OpenAI agent model turns.
Doctor also repairs stale persisted session pins across discovered agent session
stores so old conversations do not stay wedged on the removed route.
@@ -349,7 +343,7 @@ Agents should route user requests by intent, not by the word "Codex" alone:
| "Show Codex threads" | `/codex threads` |
| "File a support report for a bad Codex run" | `/diagnostics [note]` |
| "Only send Codex feedback for this attached thread" | `/codex diagnostics [note]` |
| "Use my ChatGPT/Codex subscription with Codex runtime" | `openai/*` plus `agentRuntime.id: "codex"` |
| "Use my ChatGPT/Codex subscription with Codex runtime" | `openai/*` |
| "Repair old `openai-codex/*` config/session pins" | `openclaw doctor --fix` |
| "Run Codex through ACP/acpx" | ACP `sessions_spawn({ runtime: "acp", ... })` |
| "Start Claude Code/Gemini/OpenCode/Cursor in a thread" | ACP/acpx, not `/codex` and not native sub-agents |

View File

@@ -144,7 +144,6 @@ uninstall, and publishing commands.
| Plugin | Description | Distribution | Surface |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| [acpx](/plugins/reference/acpx) | Embedded ACP runtime backend with plugin-owned session and transport management. | `@openclaw/acpx`<br />npm; ClawHub | skills |
| [bluebubbles](/plugins/reference/bluebubbles) | Adds the BlueBubbles channel surface for sending and receiving OpenClaw messages. | `@openclaw/bluebubbles`<br />npm; ClawHub | channels: bluebubbles |
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [codex](/plugins/reference/codex) | Codex app-server harness and Codex-managed GPT model catalog. | `@openclaw/codex`<br />npm; ClawHub | providers: codex; contracts: mediaUnderstandingProviders, migrationProviders |
| [diagnostics-otel](/plugins/reference/diagnostics-otel) | OpenClaw diagnostics OpenTelemetry exporter. | `@openclaw/diagnostics-otel`<br />npm; ClawHub: `clawhub:@openclaw/diagnostics-otel` | plugin |

View File

@@ -25,7 +25,6 @@ pnpm plugins:inventory:gen
| [anthropic-vertex](/plugins/reference/anthropic-vertex) | Adds Anthropic Vertex model provider support to OpenClaw. | `@openclaw/anthropic-vertex-provider`<br />included in OpenClaw | providers: anthropic-vertex |
| [arcee](/plugins/reference/arcee) | Adds Arcee model provider support to OpenClaw. | `@openclaw/arcee-provider`<br />included in OpenClaw | providers: arcee |
| [azure-speech](/plugins/reference/azure-speech) | Azure AI Speech text-to-speech (MP3, native Ogg/Opus voice notes, PCM telephony). | `@openclaw/azure-speech`<br />included in OpenClaw | contracts: speechProviders |
| [bluebubbles](/plugins/reference/bluebubbles) | Adds the BlueBubbles channel surface for sending and receiving OpenClaw messages. | `@openclaw/bluebubbles`<br />npm; ClawHub | channels: bluebubbles |
| [bonjour](/plugins/reference/bonjour) | Advertise the local OpenClaw gateway over Bonjour/mDNS. | `@openclaw/bonjour`<br />included in OpenClaw | plugin |
| [brave](/plugins/reference/brave) | Adds web search provider support. | `@openclaw/brave-plugin`<br />npm; ClawHub | contracts: webSearchProviders |
| [browser](/plugins/reference/browser) | Adds agent-callable tools. | `@openclaw/browser-plugin`<br />included in OpenClaw | contracts: tools; skills |

View File

@@ -1,23 +0,0 @@
---
summary: "Adds the BlueBubbles channel surface for sending and receiving OpenClaw messages."
read_when:
- You are installing, configuring, or auditing the bluebubbles plugin
title: "BlueBubbles plugin"
---
# BlueBubbles plugin
Adds the BlueBubbles channel surface for sending and receiving OpenClaw messages.
## Distribution
- Package: `@openclaw/bluebubbles`
- Install route: npm; ClawHub
## Surface
channels: bluebubbles
## Related docs
- [bluebubbles](/channels/bluebubbles)

View File

@@ -194,9 +194,10 @@ intentional silent replies such as `NO_REPLY` unclassified.
The bundled `codex` harness is the native Codex mode for embedded OpenClaw
agent turns. Enable the bundled `codex` plugin first, and include `codex` in
`plugins.allow` if your config uses a restrictive allowlist. Native app-server
configs should use `openai/gpt-*` with `agentRuntime.id: "codex"`.
Use `openai-codex/*` for Codex OAuth through PI instead. Legacy `codex/*`
model refs remain compatibility aliases for the native harness.
configs should use `openai/gpt-*`; OpenAI agent turns select the Codex harness
by default. Legacy `openai-codex/*` routes should be repaired with
`openclaw doctor --fix`, and legacy `codex/*` model refs remain compatibility
aliases for the native harness.
When this mode runs, Codex owns the native thread id, resume behavior,
compaction, and app-server execution. OpenClaw still owns the chat channel,

View File

@@ -253,9 +253,9 @@ OpenClaw supports Anthropic's prompt caching feature for API-key auth.
auto-resolves media capabilities from the configured Anthropic auth — no
additional config is needed.
| Property | Value |
| -------------- | -------------------- |
| Default model | `claude-opus-4-6` |
| Property | Value |
| --------------- | --------------------- |
| Default model | `claude-opus-4-7` |
| Supported input | Images, PDF documents |
When an image or PDF is attached to a conversation, OpenClaw automatically

View File

@@ -110,12 +110,12 @@ The onboarding preset sets `arcee/trinity-large-thinking` as the default model.
## Supported features
| Feature | Supported |
| --------------------------------------------- | ---------------------------- |
| Streaming | Yes |
| Tool use / function calling | Yes |
| Structured output (JSON mode and JSON schema) | Yes |
| Extended thinking | Yes (Trinity Large Thinking) |
| Feature | Supported |
| --------------------------------------------- | -------------------------------------------- |
| Streaming | Yes |
| Tool use / function calling | Yes (Trinity Mini, Trinity Large Preview) |
| Structured output (JSON mode and JSON schema) | Yes |
| Extended thinking | Yes (Trinity Large Thinking; tools disabled) |
<AccordionGroup>
<Accordion title="Environment note">

View File

@@ -11,14 +11,18 @@ OpenAI provides developer APIs for GPT models, and Codex is also available as a
ChatGPT-plan coding agent through OpenAI's Codex clients. OpenClaw keeps those
surfaces separate so config stays predictable.
OpenClaw supports three OpenAI-family routes. Most ChatGPT/Codex subscribers
who want Codex behavior should use the native Codex app-server runtime. The
model prefix selects the provider/model name; a separate runtime setting selects
who executes the embedded agent loop:
OpenClaw uses `openai/*` as the canonical OpenAI model route. Embedded agent
turns on OpenAI models run through the native Codex app-server runtime by
default; direct OpenAI API-key auth remains available for non-agent OpenAI
surfaces such as images, embeddings, speech, and realtime.
- **API key** - direct OpenAI Platform access with usage-based billing (`openai/*` models)
- **Codex subscription with native Codex runtime** - ChatGPT/Codex sign-in plus Codex app-server execution (`openai/*` models plus `agents.defaults.agentRuntime.id: "codex"`)
- **Codex subscription through PI** - ChatGPT/Codex sign-in with the normal OpenClaw PI runner (`openai-codex/*` models)
- **Agent models** - `openai/*` models through the Codex runtime; sign in with
`openai-codex` auth for ChatGPT/Codex subscription use, or configure an
`openai-codex` API-key profile when you intentionally want API-key auth.
- **Non-agent OpenAI APIs** - direct OpenAI Platform access with usage-based
billing through `OPENAI_API_KEY` or OpenAI API-key onboarding.
- **Legacy config** - `openai-codex/*` model refs are repaired by
`openclaw doctor --fix` to `openai/*` plus the Codex runtime.
OpenAI explicitly supports subscription OAuth usage in external tools and workflows like OpenClaw.
@@ -28,64 +32,67 @@ changing config.
## Quick choice
| Goal | Use | Notes |
| ---------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------- |
| ChatGPT/Codex subscription with native Codex runtime | `openai/gpt-5.5` plus `agentRuntime.id: "codex"` | Recommended Codex setup for most users. Sign in with `openai-codex` auth. |
| Direct API-key billing | `openai/gpt-5.5` | Set `OPENAI_API_KEY` or run OpenAI API-key onboarding. |
| ChatGPT/Codex subscription auth through PI | `openai-codex/gpt-5.5` | Use only when you intentionally want the normal PI runner. |
| Image generation or editing | `openai/gpt-image-2` | Works with either `OPENAI_API_KEY` or OpenAI Codex OAuth. |
| Transparent-background images | `openai/gpt-image-1.5` | Use `outputFormat=png` or `webp` and `openai.background=transparent`. |
| Goal | Use | Notes |
| ---------------------------------------------------- | ------------------------------------------------------- | --------------------------------------------------------------------- |
| ChatGPT/Codex subscription with native Codex runtime | `openai/gpt-5.5` | Default OpenAI agent setup. Sign in with `openai-codex` auth. |
| Direct API-key billing for agent models | `openai/gpt-5.5` plus an `openai-codex` API-key profile | Use `auth.order.openai-codex` to prefer that profile. |
| Direct API-key billing through explicit PI | `openai/gpt-5.5` plus `agentRuntime.id: "pi"` | Select a normal `openai` API-key profile. |
| Latest ChatGPT Instant API alias | `openai/chat-latest` | Direct API-key only. Moving alias for experiments, not the default. |
| ChatGPT/Codex subscription auth through explicit PI | `openai/gpt-5.5` plus `agentRuntime.id: "pi"` | Select an `openai-codex` auth profile for the compatibility route. |
| Image generation or editing | `openai/gpt-image-2` | Works with either `OPENAI_API_KEY` or OpenAI Codex OAuth. |
| Transparent-background images | `openai/gpt-image-1.5` | Use `outputFormat=png` or `webp` and `openai.background=transparent`. |
## Naming map
The names are similar but not interchangeable:
| Name you see | Layer | Meaning |
| ---------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------- |
| `openai` | Provider prefix | Direct OpenAI Platform API route. |
| `openai-codex` | Provider prefix | OpenAI Codex OAuth/subscription route through the normal OpenClaw PI runner. |
| `codex` plugin | Plugin | Bundled OpenClaw plugin that provides native Codex app-server runtime and `/codex` chat controls. |
| `agentRuntime.id: codex` | Agent runtime | Force the native Codex app-server harness for embedded turns. |
| `/codex ...` | Chat command set | Bind/control Codex app-server threads from a conversation. |
| `runtime: "acp", agentId: "codex"` | ACP session route | Explicit fallback path that runs Codex through ACP/acpx. |
| Name you see | Layer | Meaning |
| ---------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------- |
| `openai` | Provider prefix | Canonical OpenAI model route; agent turns use the Codex runtime. |
| `openai-codex` | Auth/profile prefix | OpenAI Codex OAuth/subscription auth profile provider. |
| `codex` plugin | Plugin | Bundled OpenClaw plugin that provides native Codex app-server runtime and `/codex` chat controls. |
| `agentRuntime.id: codex` | Agent runtime | Force the native Codex app-server harness for embedded turns. |
| `/codex ...` | Chat command set | Bind/control Codex app-server threads from a conversation. |
| `runtime: "acp", agentId: "codex"` | ACP session route | Explicit fallback path that runs Codex through ACP/acpx. |
This means a config can intentionally contain both `openai-codex/*` and the
`codex` plugin. That is valid when you want Codex OAuth through PI and also want
native `/codex` chat controls available. `openclaw doctor` warns about that
combination so you can confirm it is intentional; it does not rewrite it.
This means a config can intentionally contain both `openai/*` model refs and
`openai-codex` auth profiles. `openclaw doctor --fix` rewrites legacy
`openai-codex/*` model refs to the canonical OpenAI model route.
<Note>
GPT-5.5 is available through both direct OpenAI Platform API-key access and
subscription/OAuth routes. For ChatGPT/Codex subscription plus native Codex
execution, use `openai/gpt-5.5` with `agentRuntime.id: "codex"`. Use
`openai-codex/gpt-5.5` only for Codex OAuth through PI, or `openai/gpt-5.5`
without a Codex runtime override for direct `OPENAI_API_KEY` traffic.
execution, use `openai/gpt-5.5`; unset runtime config now selects the Codex
harness for OpenAI agent turns. Use OpenAI API-key profiles only when you want
direct API-key auth for an OpenAI agent model.
</Note>
<Note>
Enabling the OpenAI plugin, or selecting an `openai-codex/*` model, does not
enable the bundled Codex app-server plugin. OpenClaw enables that plugin only
when you explicitly select the native Codex harness with
`agentRuntime.id: "codex"` or use a legacy `codex/*` model ref.
If the bundled `codex` plugin is enabled but `openai-codex/*` still resolves
through PI, `openclaw doctor` warns and leaves the route unchanged.
OpenAI agent model turns require the bundled Codex app-server plugin. Explicit
PI runtime config remains available as an opt-in compatibility route. When PI is
explicitly selected with an `openai-codex` auth profile, OpenClaw keeps the
public model ref as `openai/*` and routes PI internally through the legacy
Codex-auth transport. Run `openclaw doctor --fix` to repair stale
`openai-codex/*` model refs or old PI session pins that do not come from
explicit runtime config.
</Note>
## OpenClaw feature coverage
| OpenAI capability | OpenClaw surface | Status |
| ------------------------- | ---------------------------------------------------------- | ------------------------------------------------------ |
| Chat / Responses | `openai/<model>` model provider | Yes |
| Codex subscription models | `openai-codex/<model>` with `openai-codex` OAuth | Yes |
| Codex app-server harness | `openai/<model>` with `agentRuntime.id: codex` | Yes |
| Server-side web search | Native OpenAI Responses tool | Yes, when web search is enabled and no provider pinned |
| Images | `image_generate` | Yes |
| Videos | `video_generate` | Yes |
| Text-to-speech | `messages.tts.provider: "openai"` / `tts` | Yes |
| Batch speech-to-text | `tools.media.audio` / media understanding | Yes |
| Streaming speech-to-text | Voice Call `streaming.provider: "openai"` | Yes |
| Realtime voice | Voice Call `realtime.provider: "openai"` / Control UI Talk | Yes |
| Embeddings | memory embedding provider | Yes |
| OpenAI capability | OpenClaw surface | Status |
| ------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------ |
| Chat / Responses | `openai/<model>` model provider | Yes |
| Codex subscription models | `openai/<model>` with `openai-codex` OAuth | Yes |
| Legacy Codex model refs | `openai-codex/<model>` | Repaired by doctor to `openai/<model>` |
| Codex app-server harness | `openai/<model>` with omitted runtime or `agentRuntime.id: codex` | Yes |
| Server-side web search | Native OpenAI Responses tool | Yes, when web search is enabled and no provider pinned |
| Images | `image_generate` | Yes |
| Videos | `video_generate` | Yes |
| Text-to-speech | `messages.tts.provider: "openai"` / `tts` | Yes |
| Batch speech-to-text | `tools.media.audio` / media understanding | Yes |
| Streaming speech-to-text | Voice Call `streaming.provider: "openai"` | Yes |
| Realtime voice | Voice Call `realtime.provider: "openai"` / Control UI Talk | Yes |
| Embeddings | memory embedding provider | Yes |
## Memory embeddings
@@ -145,15 +152,15 @@ Choose your preferred auth method and follow the setup steps.
| Model ref | Runtime config | Route | Auth |
| ---------------------- | -------------------------- | --------------------------- | ---------------- |
| `openai/gpt-5.5` | omitted / `agentRuntime.id: "pi"` | Direct OpenAI Platform API | `OPENAI_API_KEY` |
| `openai/gpt-5.4-mini` | omitted / `agentRuntime.id: "pi"` | Direct OpenAI Platform API | `OPENAI_API_KEY` |
| `openai/gpt-5.5` | `agentRuntime.id: "codex"` | Codex app-server harness | Codex app-server |
| `openai/gpt-5.5` | omitted / `agentRuntime.id: "codex"` | Codex app-server harness | `openai-codex` profile |
| `openai/gpt-5.4-mini` | omitted / `agentRuntime.id: "codex"` | Codex app-server harness | `openai-codex` profile |
| `openai/gpt-5.5` | `agentRuntime.id: "pi"` | PI embedded runtime | `openai` profile or selected `openai-codex` profile |
<Note>
`openai/*` is the direct OpenAI API-key route unless you explicitly force
the Codex app-server harness. Use `openai-codex/*` for Codex OAuth through
the default PI runner, or use `openai/gpt-5.5` with
`agentRuntime.id: "codex"` for native Codex app-server execution.
`openai/*` agent models use the Codex app-server harness. To use API-key
auth for an agent model, create an `openai-codex` API-key profile and order
it with `auth.order.openai-codex`; `OPENAI_API_KEY` remains the direct
fallback for non-agent OpenAI API surfaces.
</Note>
### Config example
@@ -165,6 +172,23 @@ Choose your preferred auth method and follow the setup steps.
}
```
To try ChatGPT's current Instant model from the OpenAI API, set the model
to `openai/chat-latest`:
```json5
{
env: { OPENAI_API_KEY: "sk-..." },
agents: { defaults: { model: { primary: "openai/chat-latest" } } },
}
```
`chat-latest` is a moving alias. OpenAI documents it as the latest Instant
model used in ChatGPT and recommends `gpt-5.5` for production API usage, so
keep `openai/gpt-5.5` as the stable default unless you explicitly want that
alias behavior. The alias currently accepts only `medium` text verbosity, so
OpenClaw normalizes incompatible OpenAI text-verbosity overrides for this
model.
<Warning>
OpenClaw does **not** expose `openai/gpt-5.3-codex-spark`. Live OpenAI API requests reject that model, and the current Codex catalog does not expose it either.
</Warning>
@@ -192,12 +216,14 @@ Choose your preferred auth method and follow the setup steps.
openclaw models auth login --provider openai-codex --device-code
```
</Step>
<Step title="Use the native Codex runtime">
<Step title="Use the canonical OpenAI model route">
```bash
openclaw config set plugins.entries.codex '{"enabled":true}' --strict-json --merge
openclaw config set agents.defaults.model.primary openai/gpt-5.5
openclaw config set agents.defaults.agentRuntime '{"id":"codex"}' --strict-json
```
No runtime config is required for the default path. OpenAI agent turns
select the native Codex app-server runtime automatically, and OpenClaw
installs or repairs the bundled Codex plugin when this route is chosen.
</Step>
<Step title="Verify Codex auth is available">
```bash
@@ -213,26 +239,22 @@ Choose your preferred auth method and follow the setup steps.
| Model ref | Runtime config | Route | Auth |
|-----------|----------------|-------|------|
| `openai/gpt-5.5` | `agentRuntime.id: "codex"` | Native Codex app-server harness | Codex sign-in or selected `openai-codex` profile |
| `openai-codex/gpt-5.5` | omitted / `runtime: "pi"` | ChatGPT/Codex OAuth through PI | Codex sign-in |
| `openai-codex/gpt-5.4-mini` | omitted / `runtime: "pi"` | ChatGPT/Codex OAuth through PI | Codex sign-in |
| `openai-codex/gpt-5.5` | `runtime: "auto"` | Still PI unless a plugin explicitly claims `openai-codex` | Codex sign-in |
| `openai/gpt-5.5` | omitted / `agentRuntime.id: "codex"` | Native Codex app-server harness | Codex sign-in or selected `openai-codex` profile |
| `openai/gpt-5.5` | `agentRuntime.id: "pi"` | PI embedded runtime with internal Codex-auth transport | Selected `openai-codex` profile |
| `openai-codex/gpt-5.5` | repaired by doctor | Legacy route rewritten to `openai/gpt-5.5` | Existing `openai-codex` profile |
<Warning>
Do not configure older `openai-codex/gpt-5.1*`, `openai-codex/gpt-5.2*`, or
`openai-codex/gpt-5.3*` model refs. ChatGPT/Codex OAuth accounts now reject
those models. Use `openai-codex/gpt-5.5` for the PI OAuth route, or
`openai/gpt-5.5` with `agentRuntime.id: "codex"` for native Codex runtime
execution.
those models. Use `openai/gpt-5.5`; OpenAI agent turns now select the Codex
runtime by default.
</Warning>
<Note>
Keep using the `openai-codex` provider id for auth/profile commands. The
`openai-codex/*` model prefix is also the explicit PI route for Codex OAuth.
It does not select or auto-enable the bundled Codex app-server harness. For
the common subscription plus native runtime setup, sign in with
`openai-codex` but keep the model ref as `openai/gpt-5.5` and set
`agentRuntime.id: "codex"`.
`openai-codex/*` model prefix is legacy config repaired by doctor. For the
common subscription plus native runtime setup, sign in with `openai-codex`
but keep the model ref as `openai/gpt-5.5`.
</Note>
### Config example
@@ -249,9 +271,6 @@ Choose your preferred auth method and follow the setup steps.
}
```
To keep Codex OAuth on the normal PI runner instead, use
`openai-codex/gpt-5.5` and omit the Codex runtime override.
<Note>
Onboarding no longer imports OAuth material from `~/.codex`. Sign in with browser OAuth (default) or the device-code flow above — OpenClaw manages the resulting credentials in its own agent auth store.
</Note>
@@ -275,12 +294,11 @@ Choose your preferred auth method and follow the setup steps.
openclaw models auth list --agent <id> --provider openai-codex
```
If a 2026.5.5 `doctor --fix` run changed a GPT-5.5 subscription setup from
`openai-codex/gpt-5.5` to `openai/gpt-5.5`, switch the default agent back
to the Codex OAuth PI route:
If an older config still has `openai-codex/gpt-*` or a stale OpenAI PI
session pin without explicit runtime config, repair it:
```bash
openclaw models set openai-codex/gpt-5.5
openclaw doctor --fix
openclaw config validate
```
@@ -292,31 +310,27 @@ Choose your preferred auth method and follow the setup steps.
openclaw models status --probe --probe-provider openai-codex
```
`openai-codex/*` means ChatGPT/Codex OAuth through PI. `openai/*` with
`agentRuntime.id: "codex"` means native Codex app-server execution.
`openai-codex` remains the auth/profile provider id. `openai/*` is the
model route for OpenAI agent turns through Codex.
### Status indicator
Chat `/status` shows which model runtime is active for the current session.
The default PI harness appears as `Runtime: OpenClaw Pi Default`. When the
bundled Codex app-server harness is selected, `/status` shows
`Runtime: OpenAI Codex`. Existing sessions keep their recorded harness id, so use
`/new` or `/reset` after changing `agentRuntime` if you want `/status` to
reflect a new PI/Codex choice.
The bundled Codex app-server harness appears as `Runtime: OpenAI Codex` for
OpenAI agent model turns. Stale PI session pins are repaired to Codex unless
config explicitly pins PI.
### Doctor warning
If the bundled `codex` plugin is enabled while an `openai-codex/*` route is
selected, `openclaw doctor` warns that the model still resolves through PI.
Keep the config unchanged only when that PI subscription-auth route is
intentional. Switch to `openai/<model>` plus `agentRuntime.id: "codex"` when
you want native Codex app-server execution.
If `openai-codex/*` routes or stale OpenAI PI pins remain in config or
session state, `openclaw doctor --fix` rewrites them to `openai/*` with the
Codex runtime unless PI is explicitly configured.
### Context window cap
OpenClaw treats model metadata and the runtime context cap as separate values.
For `openai-codex/gpt-5.5` through Codex OAuth:
For `openai/gpt-5.5` through the Codex OAuth catalog:
- Native `contextWindow`: `1000000`
- Default runtime `contextTokens` cap: `272000`
@@ -342,7 +356,7 @@ Choose your preferred auth method and follow the setup steps.
### Catalog recovery
OpenClaw uses upstream Codex catalog metadata for `gpt-5.5` when it is
present. If live Codex discovery omits the `openai-codex/gpt-5.5` row while
present. If live Codex discovery omits the `gpt-5.5` row while
the account is authenticated, OpenClaw synthesizes that OAuth model row so
cron, sub-agent, and configured default-model runs do not fail with
`Unknown model`.
@@ -352,8 +366,9 @@ Choose your preferred auth method and follow the setup steps.
## Native Codex app-server auth
The native Codex app-server harness uses `openai/*` model refs plus
`agentRuntime.id: "codex"`, but its auth is still account-based. OpenClaw
The native Codex app-server harness uses `openai/*` model refs plus omitted
runtime config or `agentRuntime.id: "codex"`, but its auth is still
account-based. OpenClaw
selects auth in this order:
1. An explicit OpenClaw `openai-codex` auth profile bound to the agent.
@@ -487,7 +502,7 @@ See [Video Generation](/tools/video-generation) for shared tool parameters, prov
## GPT-5 prompt contribution
OpenClaw adds a shared GPT-5 prompt contribution for GPT-5-family runs across providers. It applies by model id, so `openai-codex/gpt-5.5`, `openai/gpt-5.5`, `openrouter/openai/gpt-5.5`, `opencode/gpt-5.5`, and other compatible GPT-5 refs receive the same overlay. Older GPT-4.x models do not.
OpenClaw adds a shared GPT-5 prompt contribution for GPT-5-family runs across providers. It applies by model id, so `openai/gpt-5.5`, legacy pre-repair refs such as `openai-codex/gpt-5.5`, `openrouter/openai/gpt-5.5`, `opencode/gpt-5.5`, and other compatible GPT-5 refs receive the same overlay. Older GPT-4.x models do not.
The bundled native Codex harness uses the same GPT-5 behavior and heartbeat overlay through Codex app-server developer instructions, so `openai/gpt-5.x` sessions forced through `agentRuntime.id: "codex"` keep the same follow-through and proactive heartbeat guidance even though Codex owns the rest of the harness prompt.
@@ -775,7 +790,7 @@ the Server-side compaction accordion below.
<AccordionGroup>
<Accordion title="Transport (WebSocket vs SSE)">
OpenClaw uses WebSocket-first with SSE fallback (`"auto"`) for both `openai/*` and `openai-codex/*`.
OpenClaw uses WebSocket-first with SSE fallback (`"auto"`) for `openai/*`.
In `"auto"` mode, OpenClaw:
- Retries one early WebSocket failure before falling back to SSE
@@ -797,9 +812,6 @@ the Server-side compaction accordion below.
"openai/gpt-5.5": {
params: { transport: "auto" },
},
"openai-codex/gpt-5.5": {
params: { transport: "auto" },
},
},
},
},
@@ -813,7 +825,7 @@ the Server-side compaction accordion below.
</Accordion>
<Accordion title="WebSocket warm-up">
OpenClaw enables WebSocket warm-up by default for `openai/*` and `openai-codex/*` to reduce first-turn latency.
OpenClaw enables WebSocket warm-up by default for `openai/*` to reduce first-turn latency.
```json5
// Disable warm-up
@@ -833,7 +845,7 @@ the Server-side compaction accordion below.
</Accordion>
<Accordion title="Fast mode">
OpenClaw exposes a shared fast-mode toggle for `openai/*` and `openai-codex/*`:
OpenClaw exposes a shared fast-mode toggle for `openai/*`:
- **Chat/UI:** `/fast status|on|off`
- **Config:** `agents.defaults.models["<provider>/<model>"].params.fastMode`

View File

@@ -122,7 +122,7 @@ Use targeted local checks while iterating:
pnpm test extensions/canvas/src/host/server.test.ts extensions/canvas/src/host/server.state-dir.test.ts extensions/canvas/src/host/file-resolver.test.ts
pnpm test src/gateway/server.plugin-node-capability-auth.test.ts src/gateway/server-import-boundary.test.ts
pnpm test extensions/canvas/src/config-migration.test.ts src/commands/doctor-legacy-config.migrations.test.ts
pnpm test test/scripts/changed-lanes.test.ts test/scripts/build-all.test.ts test/scripts/bundle-a2ui.test.ts test/scripts/bundled-plugin-assets.test.ts src/scripts/canvas-a2ui-copy.test.ts src/infra/run-node.test.ts
pnpm test test/scripts/changed-lanes.test.ts test/scripts/build-all.test.ts extensions/canvas/scripts/bundle-a2ui.test.ts test/scripts/bundled-plugin-assets.test.ts extensions/canvas/scripts/copy-a2ui.test.ts src/infra/run-node.test.ts
pnpm tsgo:extensions
pnpm plugins:inventory:check
pnpm plugin-sdk:api:check

View File

@@ -59,12 +59,13 @@ the maintainer-only release runbook.
intentionally carried.
4. Create `release/YYYY.M.D` from current `main`; do not do normal release work
directly on `main`.
5. Bump every required version location for the intended tag, run
`pnpm plugins:sync` so publishable plugin packages share the release
version and compatibility metadata, then run the local deterministic preflight:
5. Bump every required version location for the intended tag, then run
`pnpm release:prep`. It refreshes plugin versions, plugin inventory, config
schema, bundled channel config metadata, config docs baseline, plugin SDK
exports, and plugin SDK API baseline in the right order. Commit any generated
drift before tagging. Then run the local deterministic preflight:
`pnpm check:test-types`, `pnpm check:architecture`,
`pnpm build && pnpm ui:build`, `pnpm plugins:sync:check`, and
`pnpm release:check`.
`pnpm build && pnpm ui:build`, and `pnpm release:check`.
6. Run `OpenClaw NPM Release` with `preflight_only=true`. Before a tag exists,
a full 40-character release-branch SHA is allowed for validation-only
preflight. Save the successful `preflight_run_id`.
@@ -80,9 +81,20 @@ the maintainer-only release runbook.
dispatches all publishable plugin packages to npm and the same set to
ClawHub in parallel, and then promotes the prepared OpenClaw npm preflight
artifact with the matching dist-tag as soon as plugin npm publish succeeds.
After the OpenClaw npm publish child succeeds, it creates or updates the
matching GitHub release/prerelease page from the complete matching
`CHANGELOG.md` section. Stable releases published to npm `latest` become the
GitHub latest release; stable maintenance releases kept on npm `beta` are
created with GitHub `latest=false`.
ClawHub publishing may still be running while OpenClaw npm publishes, but the
release publish workflow does not finish until both plugin publish paths and
the OpenClaw npm publish path have completed successfully. After publish, run
release publish workflow prints the child run IDs immediately. By default it
does not wait for ClawHub after dispatching it, so OpenClaw npm availability
is not blocked by slower ClawHub approvals or registry work; set
`wait_for_clawhub=true` when ClawHub must block workflow completion. The
ClawHub path retries transient CLI dependency install failures, publishes
preview-passing plugins even when one preview cell flakes, and ends with
registry verification for every expected plugin version so partial publishes
remain visible and retryable. After publish, run
the post-publish package
acceptance against the published `openclaw@YYYY.M.D-beta.N` or
`openclaw@beta` package. If a pushed or published prerelease needs a fix,
@@ -95,9 +107,8 @@ the maintainer-only release runbook.
packaged `.zip`, `.dmg`, `.dSYM.zip`, and updated `appcast.xml` on `main`.
11. After publish, run the npm post-publish verifier, optional standalone
published-npm Telegram E2E when you need post-publish channel proof,
dist-tag promotion when needed, GitHub release/prerelease notes from the
complete matching `CHANGELOG.md` section, and the release announcement
steps.
dist-tag promotion when needed, verify the generated GitHub release page,
and run the release announcement steps.
## Release preflight
@@ -108,12 +119,13 @@ the maintainer-only release runbook.
- Run `pnpm build && pnpm ui:build` before `pnpm release:check` so the expected
`dist/*` release artifacts and Control UI bundle exist for the pack
validation step
- Run `pnpm plugins:sync` after the root version bump and before tagging. It
updates publishable plugin package versions, OpenClaw peer/API compatibility
metadata, build metadata, and plugin changelog stubs to match the core
release version. `pnpm plugins:sync:check` is the non-mutating release guard;
the publish workflow fails before any registry mutation if this step was
forgotten.
- Run `pnpm release:prep` after the root version bump and before tagging. It
runs every deterministic release generator that commonly drifts after a
version/config/API change: plugin versions, plugin inventory, base config
schema, bundled channel config metadata, config docs baseline, plugin SDK
exports, and plugin SDK API baseline. `pnpm release:check` re-runs those
guards in check mode and reports every generated drift failure it finds in one
pass before running package release checks.
- Run the manual `Full Release Validation` workflow before release approval to
kick off all pre-release test boxes from one entrypoint. It accepts a branch,
tag, or full commit SHA, dispatches manual `CI`, and dispatches

View File

@@ -17,11 +17,9 @@ OpenClaw integrates external CLIs via JSON-RPC. Two patterns are used today.
See [Signal](/channels/signal) for setup and endpoints.
## Pattern B: stdio child process (legacy: imsg)
## Pattern B: stdio child process (imsg)
> **Note:** For new iMessage setups, use [BlueBubbles](/channels/bluebubbles) instead.
- OpenClaw spawns `imsg rpc` as a child process (legacy iMessage integration).
- OpenClaw spawns `imsg rpc` as a child process for [iMessage](/channels/imessage).
- JSON-RPC is line-delimited over stdin/stdout (one JSON object per line).
- No TCP port, no daemon required.

View File

@@ -82,8 +82,6 @@ Scope intent:
- `channels.irc.nickserv.password`
- `channels.irc.accounts.*.password`
- `channels.irc.accounts.*.nickserv.password`
- `channels.bluebubbles.password`
- `channels.bluebubbles.accounts.*.password`
- `channels.feishu.appSecret`
- `channels.feishu.encryptKey`
- `channels.feishu.verificationToken`

View File

@@ -60,20 +60,6 @@
"optIn": true,
"notes": "Compatibility exception: sibling ref field remains canonical."
},
{
"id": "channels.bluebubbles.accounts.*.password",
"configFile": "openclaw.json",
"path": "channels.bluebubbles.accounts.*.password",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "channels.bluebubbles.password",
"configFile": "openclaw.json",
"path": "channels.bluebubbles.password",
"secretShape": "secret_input",
"optIn": true
},
{
"id": "channels.discord.accounts.*.pluralkit.token",
"configFile": "openclaw.json",

View File

@@ -102,8 +102,7 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
- [Google Chat](/channels/googlechat): service account JSON + webhook audience.
- [Mattermost](/channels/mattermost) (plugin): bot token + base URL.
- [Signal](/channels/signal): optional `signal-cli` install + account config.
- [BlueBubbles](/channels/bluebubbles): **recommended for iMessage**; server URL + password + webhook.
- [iMessage](/channels/imessage): legacy `imsg` CLI path + DB access.
- [iMessage](/channels/imessage): `imsg` CLI path + Messages DB access; use an SSH wrapper when the Gateway runs off-Mac.
- DM security: default is pairing. First DM sends a code; approve via `openclaw pairing approve <channel> <code>` or use allowlists.
</Step>
@@ -249,5 +248,5 @@ will prompt to install it (npm or a local path) before it can be configured.
- Onboarding overview: [Onboarding (CLI)](/start/wizard)
- macOS app onboarding: [Onboarding](/start/onboarding)
- Config reference: [Gateway configuration](/gateway/configuration)
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [BlueBubbles](/channels/bluebubbles) (iMessage), [iMessage](/channels/imessage) (legacy)
- Providers: [WhatsApp](/channels/whatsapp), [Telegram](/channels/telegram), [Discord](/channels/discord), [Google Chat](/channels/googlechat), [Signal](/channels/signal), [iMessage](/channels/imessage)
- Skills: [Skills](/tools/skills), [Skills config](/tools/skills-config)

View File

@@ -220,3 +220,12 @@ proxy:
- Gateway control-plane proxy bypass is intentionally limited to `localhost` and literal loopback IP URLs. Use `ws://127.0.0.1:18789`, `ws://[::1]:18789`, or `ws://localhost:18789` for local direct Gateway control-plane connections; other hostnames route like ordinary hostname-based traffic.
- OpenClaw does not inspect, test, or certify your proxy policy.
- Treat proxy policy changes as security-sensitive operational changes.
| Surface | Managed proxy status |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `fetch`, `node:http`, `node:https`, common WebSocket clients | Routed through managed proxy hooks when configured. |
| APNs direct HTTP/2 | Routed through the APNs managed CONNECT helper. |
| Gateway control-plane loopback | Direct only for the configured local loopback Gateway URL. |
| Debug proxy upstream forwarding | Disabled while managed proxy mode is active unless explicitly enabled for local diagnostics. |
| IRC | Raw TCP/TLS; not proxied by managed HTTP proxy mode. Disable unless direct IRC egress is approved. |
| Other raw `net`, `tls`, or `http2` client calls | Must be classified by the raw socket guard before landing. |

View File

@@ -39,7 +39,6 @@ For a complete map of the docs, see [Docs hubs](/start/hubs).
- [Telegram](/channels/telegram)
- [Discord](/channels/discord)
- [Mattermost](/channels/mattermost)
- [BlueBubbles (legacy iMessage bridge)](/channels/bluebubbles)
- [QQ Bot](/channels/qqbot)
- [iMessage](/channels/imessage)
- [Groups](/channels/groups)

View File

@@ -12,7 +12,7 @@ and a working chat session.
## What you need
- **Node.js** — Node 24 recommended (Node 22.14+ also supported)
- **Node.js** — Node 24 recommended (Node 22.16+ also supported)
- **An API key** from a model provider (Anthropic, OpenAI, Google, etc.) — onboarding will prompt you
<Tip>

View File

@@ -73,7 +73,6 @@ Use these hubs to discover every page, including deep dives and reference docs t
- [Discord](/channels/discord)
- [Mattermost](/channels/mattermost)
- [Signal](/channels/signal)
- [BlueBubbles (legacy iMessage bridge)](/channels/bluebubbles)
- [QQ Bot](/channels/qqbot)
- [iMessage](/channels/imessage)
- [Location parsing](/channels/location)

View File

@@ -31,7 +31,7 @@ Regardless of which path you choose, onboarding sets up:
2. **Workspace** — directory for agent files, bootstrap templates, and memory
3. **Gateway** — port, bind address, auth mode
4. **Channels** (optional) — built-in and bundled chat channels such as
BlueBubbles, Discord, Feishu, Google Chat, Mattermost, Microsoft Teams,
iMessage, Discord, Feishu, Google Chat, Mattermost, Microsoft Teams,
Telegram, WhatsApp, and more
5. **Daemon** (optional) — background service so the Gateway starts automatically

View File

@@ -21,7 +21,7 @@ Pick a setup workflow based on how often you want updates and whether you want t
## Prereqs (from source)
- Node 24 recommended (Node 22 LTS, currently `22.14+`, still supported)
- Node 24 recommended (Node 22 LTS, currently `22.16+`, still supported)
- `pnpm` required for source checkouts. OpenClaw loads bundled plugins from the
`extensions/*` pnpm workspace packages in dev mode, so root `npm install` does
not prepare the full source tree.

View File

@@ -17,7 +17,7 @@ Local mode (default) walks you through:
- Model and auth setup (OpenAI Code subscription OAuth, Anthropic Claude CLI or API key, plus MiniMax, GLM, Ollama, Moonshot, StepFun, and AI Gateway options)
- Workspace location and bootstrap files
- Gateway settings (port, bind, auth, tailscale)
- Channels and providers (Telegram, WhatsApp, Discord, Google Chat, Mattermost, Signal, BlueBubbles, and other bundled channel plugins)
- Channels and providers (Telegram, WhatsApp, Discord, Google Chat, Mattermost, Signal, iMessage, and other bundled channel plugins)
- Daemon install (LaunchAgent, systemd user unit, or native Windows Scheduled Task with Startup-folder fallback)
- Health check
- Skills setup
@@ -70,8 +70,7 @@ It does not install or modify anything on the remote host.
- [Google Chat](/channels/googlechat): service account JSON + webhook audience
- [Mattermost](/channels/mattermost): bot token + base URL
- [Signal](/channels/signal): optional `signal-cli` install + account config
- [BlueBubbles](/channels/bluebubbles): recommended for iMessage; server URL + password + webhook
- [iMessage](/channels/imessage): legacy `imsg` CLI path + DB access
- [iMessage](/channels/imessage): `imsg` CLI path + Messages DB access; use an SSH wrapper when the Gateway runs off-Mac
- DM security: default is pairing. First DM sends a code; approve via
`openclaw pairing approve <channel> <code>` or use allowlists.
</Step>

View File

@@ -77,7 +77,7 @@ Onboarding starts with **QuickStart** (defaults) vs **Advanced** (full control).
3. **Gateway** — Port, bind address, auth mode, Tailscale exposure.
In interactive token mode, choose default plaintext token storage or opt into SecretRef.
Non-interactive token SecretRef path: `--gateway-token-ref-env <ENV_VAR>`.
4. **Channels** — built-in and bundled chat channels such as BlueBubbles, Discord, Feishu, Google Chat, Mattermost, Microsoft Teams, QQ Bot, Signal, Slack, Telegram, WhatsApp, and more.
4. **Channels** — built-in and bundled chat channels such as iMessage, Discord, Feishu, Google Chat, Mattermost, Microsoft Teams, QQ Bot, Signal, Slack, Telegram, WhatsApp, and more.
5. **Daemon** — Installs a LaunchAgent (macOS), systemd user unit (Linux/WSL2), or native Windows Scheduled Task with per-user Startup-folder fallback.
If token auth requires a token and `gateway.auth.token` is SecretRef-managed, daemon install validates it but does not persist the resolved token into supervisor service environment metadata.
If token auth requires a token and the configured token SecretRef is unresolved, daemon install is blocked with actionable guidance.

View File

@@ -184,8 +184,8 @@ Quick `/acp` flow from chat:
</Accordion>
<Accordion title="Model / provider / runtime selection cheat sheet">
- `openai-codex/*` - PI Codex OAuth/subscription route.
- `openai/*` plus `agentRuntime.id: "codex"` - native Codex app-server embedded runtime.
- `openai-codex/*` - legacy Codex OAuth/subscription model route repaired by doctor.
- `openai/*` - native Codex app-server embedded runtime for OpenAI agent turns.
- `/codex ...` - native Codex conversation control.
- `/acp ...` or `runtime: "acp"` - explicit ACP/acpx control.
@@ -336,7 +336,6 @@ top-level `bindings[]` entries.
- **Discord channel/thread:** `match.channel="discord"` + `match.peer.id="<channelOrThreadId>"`
- **Telegram forum topic:** `match.channel="telegram"` + `match.peer.id="<chatId>:topic:<topicId>"`
- **BlueBubbles DM/group:** `match.channel="bluebubbles"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`. Prefer `chat_id:*` or `chat_identifier:*` for stable group bindings.
- **iMessage DM/group:** `match.channel="imessage"` + `match.peer.id="<handle|chat_id:*|chat_guid:*|chat_identifier:*>"`. Prefer `chat_id:*` for stable group bindings.
</ParamField>

View File

@@ -85,6 +85,23 @@ Returns `details.json` containing the parsed JSON (and validates against
## Example: Lobster workflow step
### Important limitation
The example below assumes the **standalone Lobster CLI** is running in an environment where `openclaw.invoke` already has the correct gateway URL/auth context.
For the bundled **embedded** Lobster runner inside OpenClaw, this nested CLI pattern is **not currently reliable**:
```lobster
openclaw.invoke --tool llm-task --action json --args-json '{ ... }'
```
Until embedded Lobster has a supported bridge for this flow, prefer either:
- direct `llm-task` tool calls outside Lobster, or
- Lobster steps that do not rely on nested `openclaw.invoke` calls.
Standalone Lobster CLI example:
```lobster
openclaw.invoke --tool llm-task --action json --args-json '{
"prompt": "Given the input email, return intent and draft.",

View File

@@ -100,7 +100,19 @@ Enable the tool:
}
```
Use it in a pipeline:
### Important limitation: embedded Lobster vs `openclaw.invoke`
The bundled Lobster plugin runs workflows **in-process** inside the gateway. In that embedded mode, `openclaw.invoke` does **not** automatically inherit a gateway URL/auth context for nested OpenClaw CLI tool calls.
That means this pattern is **not currently reliable in the embedded runner**:
```lobster
openclaw.invoke --tool llm-task --action json --args-json '{ ... }'
```
Use the example below only when running the **standalone Lobster CLI** in an environment where `openclaw.invoke` is already configured with the correct gateway/auth context.
Use it in a standalone Lobster CLI pipeline:
```lobster
openclaw.invoke --tool llm-task --action json --args-json '{
@@ -119,6 +131,11 @@ openclaw.invoke --tool llm-task --action json --args-json '{
}'
```
If you are using the embedded Lobster plugin today, prefer either:
- a direct `llm-task` tool call outside Lobster, or
- non-`openclaw.invoke` steps inside the Lobster pipeline until a supported embedded bridge is added.
See [LLM Task](/tools/llm-task) for details and configuration options.
## Workflow files (.lobster)

View File

@@ -235,7 +235,6 @@ current OpenClaw or a local checkout until a newer npm package is published.
| Plugin | Package | Docs |
| --------------- | -------------------------- | ------------------------------------------ |
| BlueBubbles | `@openclaw/bluebubbles` | [BlueBubbles](/channels/bluebubbles) |
| Discord | `@openclaw/discord` | [Discord](/channels/discord) |
| Feishu | `@openclaw/feishu` | [Feishu](/channels/feishu) |
| Matrix | `@openclaw/matrix` | [Matrix](/channels/matrix) |

View File

@@ -709,8 +709,6 @@ delivery.
`audio/ogg; codecs=opus`. If conversion fails, Feishu receives the original
file as an attachment; WhatsApp send fails rather than posting an incompatible
PTT payload.
- **BlueBubbles**: keeps provider synthesis on the normal audio-file path; MP3
and CAF outputs are marked for iMessage voice memo delivery.
- **Other channels**: MP3 (`mp3_44100_128` from ElevenLabs, `mp3` from OpenAI).
- 44.1kHz / 128kbps is the default balance for speech clarity.
- **MiniMax**: MP3 (`speech-2.8-hd` model, 32kHz sample rate) for normal audio attachments. For channel-advertised voice-note targets, OpenClaw transcodes the MiniMax MP3 to 48kHz Opus with `ffmpeg` before delivery when the channel advertises transcoding.

View File

@@ -105,7 +105,7 @@ Imported themes are stored only in the current browser profile. They are not wri
- Channels: built-in plus bundled/external plugin channels status, QR login, and per-channel config (`channels.status`, `web.login.*`, `config.patch`).
- Channel probe refreshes keep the previous snapshot visible while slow provider checks finish, and partial snapshots are labeled when a probe or audit exceeds its UI budget.
- Instances: presence list + refresh (`system-presence`).
- Sessions: list + per-session model/thinking/fast/verbose/trace/reasoning overrides (`sessions.list`, `sessions.patch`).
- Sessions: list configured-agent sessions by default, fall back from stale unconfigured agent session keys, and apply per-session model/thinking/fast/verbose/trace/reasoning overrides (`sessions.list`, `sessions.patch`).
- Dreams: dreaming status, enable/disable toggle, and Dream Diary reader (`doctor.memory.status`, `doctor.memory.dreamDiary`, `config.patch`).
</Accordion>
@@ -164,6 +164,7 @@ Imported themes are stored only in the current browser profile. They are not wri
- On desktop widths, chat controls stay on one compact row and collapse while scrolling down the transcript; scrolling up, returning to the top, or reaching the bottom restores the controls.
- Consecutive duplicate text-only messages render as one bubble with a count badge. Messages that carry images, attachments, tool output, or canvas previews are left uncollapsed.
- The chat header model and thinking pickers patch the active session immediately through `sessions.patch`; they are persistent session overrides, not one-turn-only send options.
- If you send a message while a model picker change for the same session is still saving, the composer waits for that session patch before calling `chat.send` so the send uses the selected model.
- Typing `/new` in the Control UI creates and switches to the same fresh dashboard session as New Chat. Typing `/reset` keeps the Gateway's explicit in-place reset for the current session.
- The chat model picker requests the Gateway's configured model view. If `agents.defaults.models` is present, that allowlist drives the picker. Otherwise the picker shows explicit `models.providers.*.models` entries plus providers with usable auth. The full catalog stays available through the debug `models.list` RPC with `view: "all"`.
- When fresh Gateway session usage reports include current context tokens, the chat composer area shows a compact context usage indicator. It switches to warning styling at high context pressure and, at recommended compaction levels, shows a compact button that runs the normal session compaction path. Stale token snapshots are hidden until the Gateway reports fresh usage again.

View File

@@ -440,6 +440,108 @@ describe("active-memory plugin", () => {
expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1);
});
it("blocks gateway callers without admin scope from changing global active-memory config", async () => {
const command = registeredCommands["active-memory"];
for (const { args, gatewayClientScopes } of [
{ args: "off --global", gatewayClientScopes: ["operator.write"] },
{ args: "on --global", gatewayClientScopes: ["operator.write"] },
{ args: "disable --global", gatewayClientScopes: ["operator.write"] },
{ args: "enable --global", gatewayClientScopes: ["operator.write"] },
{ args: "disabled --global", gatewayClientScopes: ["operator.write"] },
{ args: "enabled --global", gatewayClientScopes: ["operator.write"] },
{ args: "off --global", gatewayClientScopes: [] },
]) {
const result = await command.handler({
channel: "gateway",
isAuthorizedSender: true,
gatewayClientScopes,
args,
commandBody: `/active-memory ${args}`,
config: {},
requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
detachConversationBinding: async () => ({ removed: false }),
getCurrentConversationBinding: async () => null,
});
expect(result.text).toContain("global enable/disable changes require operator.admin");
}
expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
});
it("allows admin-scoped gateway callers to change global active-memory config", async () => {
const command = registeredCommands["active-memory"];
const result = await command.handler({
channel: "gateway",
isAuthorizedSender: true,
gatewayClientScopes: ["operator.admin"],
args: "off --global",
commandBody: "/active-memory off --global",
config: {},
requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
detachConversationBinding: async () => ({ removed: false }),
getCurrentConversationBinding: async () => null,
});
expect(result.text).toBe("Active Memory: off globally.");
expect(api.runtime.config.replaceConfigFile).toHaveBeenCalledTimes(1);
expect(configFile).toMatchObject({
plugins: {
entries: {
"active-memory": {
enabled: true,
config: {
enabled: false,
agents: ["main"],
},
},
},
},
});
});
it("keeps write-scoped gateway callers on non-global-write active-memory paths", async () => {
const command = registeredCommands["active-memory"];
const sessionKey = "agent:main:write-scoped-active-memory";
hoisted.sessionStore[sessionKey] = {
sessionId: "s-write-scoped-active-memory",
updatedAt: 0,
};
const globalStatusResult = await command.handler({
channel: "gateway",
isAuthorizedSender: true,
gatewayClientScopes: ["operator.write"],
args: "status --global",
commandBody: "/active-memory status --global",
config: {},
requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
detachConversationBinding: async () => ({ removed: false }),
getCurrentConversationBinding: async () => null,
});
expect(globalStatusResult.text).toBe("Active Memory: on globally.");
expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
const sessionOffResult = await command.handler({
channel: "gateway",
isAuthorizedSender: true,
gatewayClientScopes: ["operator.write"],
sessionKey,
args: "off",
commandBody: "/active-memory off",
config: {},
requestConversationBinding: async () => ({ status: "error", message: "unsupported" }),
detachConversationBinding: async () => ({ removed: false }),
getCurrentConversationBinding: async () => null,
});
expect(sessionOffResult.text).toBe("Active Memory: off for this session.");
expect(api.runtime.config.replaceConfigFile).not.toHaveBeenCalled();
});
it("uses live runtime config for before_prompt_build enablement", async () => {
configFile = {
plugins: {

View File

@@ -782,6 +782,13 @@ function updateActiveMemoryGlobalEnabledInConfig(
};
}
function requiresAdminToMutateActiveMemoryGlobal(gatewayClientScopes?: readonly string[]): boolean {
return Array.isArray(gatewayClientScopes) && !gatewayClientScopes.includes("operator.admin");
}
const ACTIVE_MEMORY_GLOBAL_MUTATION_ADMIN_REQUIRED_TEXT =
"⚠️ /active-memory global enable/disable changes require operator.admin for gateway clients.";
function normalizePluginConfig(pluginConfig: unknown): ResolvedActiveRecallPluginConfig {
const raw = (
pluginConfig && typeof pluginConfig === "object" ? pluginConfig : {}
@@ -2819,6 +2826,11 @@ export default definePluginEntry({
text: `Active Memory: ${isActiveMemoryGloballyEnabled(currentConfig) ? "on" : "off"} globally.`,
};
}
if (requiresAdminToMutateActiveMemoryGlobal(ctx.gatewayClientScopes)) {
return {
text: ACTIVE_MEMORY_GLOBAL_MUTATION_ADMIN_REQUIRED_TEXT,
};
}
if (action === "on" || action === "enable" || action === "enabled") {
const nextConfig = updateActiveMemoryGlobalEnabledInConfig(currentConfig, true);
await api.runtime.config.replaceConfigFile({

View File

@@ -92,6 +92,12 @@ describe("arcee provider plugin", () => {
"trinity-large-preview",
"trinity-large-thinking",
]);
expect(
catalogProvider.models?.find((model) => model.id === "trinity-large-thinking")?.compat,
).toMatchObject({
supportsTools: false,
supportsReasoningEffort: false,
});
});
it("builds the OpenRouter-backed Arcee AI model catalog", async () => {
@@ -112,6 +118,12 @@ describe("arcee provider plugin", () => {
"arcee/trinity-large-preview",
"arcee/trinity-large-thinking",
]);
expect(
catalogProvider.models?.find((model) => model.id === "arcee/trinity-large-thinking")?.compat,
).toMatchObject({
supportsTools: false,
supportsReasoningEffort: false,
});
});
it("normalizes Arcee OpenRouter models to vendor-prefixed runtime ids", async () => {

View File

@@ -45,6 +45,7 @@ export const ARCEE_MODEL_CATALOG: ModelDefinitionConfig[] = [
cacheWrite: 0.25,
},
compat: {
supportsTools: false,
supportsReasoningEffort: false,
},
},

View File

@@ -1,45 +0,0 @@
# BlueBubbles extension (developer reference)
This package contains the **BlueBubbles external channel plugin** for OpenClaw.
If youre looking for **how to use BlueBubbles as an agent/tool user**, see:
- `skills/bluebubbles/SKILL.md`
## Layout
- Package entry: `index.ts`.
- Channel implementation: `src/channel.ts`.
- Webhook handling: `src/monitor.ts` (register per-account route via `registerPluginHttpRoute`).
- REST helpers: `src/send.ts` + `src/probe.ts`.
- Runtime bridge: `src/runtime.ts` (set via `api.runtime`).
- Catalog entry for setup selection: `src/channels/plugins/catalog.ts`.
## Internal helpers (use these, not raw API calls)
- `probeBlueBubbles` in `src/probe.ts` for health checks.
- `sendMessageBlueBubbles` in `src/send.ts` for text delivery.
- `resolveChatGuidForTarget` in `src/send.ts` for chat lookup.
- `sendBlueBubblesReaction` in `src/reactions.ts` for tapbacks.
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `src/chat.ts`.
- `downloadBlueBubblesAttachment` in `src/attachments.ts` for inbound media.
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `src/types.ts` for shared REST plumbing.
## Webhooks
- BlueBubbles posts JSON to the gateway HTTP server.
- Normalize sender/chat IDs defensively (payloads vary by version).
- Skip messages marked as from self.
- Route into core reply pipeline via the plugin runtime (`api.runtime`) and `openclaw/plugin-sdk` helpers.
- For attachments/stickers, use `<media:...>` placeholders when text is empty and attach media paths via `MediaUrl(s)` in the inbound context.
## Config (core)
- `channels.bluebubbles.serverUrl` (base URL), `channels.bluebubbles.password`, `channels.bluebubbles.webhookPath`.
- Action gating: `channels.bluebubbles.actions.reactions` (default true).
## Message tool notes
- **Reactions:** the `react` action requires a `target` (phone number or chat identifier) in addition to `messageId`.
Example:
`action=react target=+15551234567 messageId=ABC123 emoji=❤️`

View File

@@ -1,18 +0,0 @@
export { bluebubblesPlugin } from "./src/channel.js";
export { bluebubblesSetupPlugin } from "./src/channel.setup.js";
export {
matchBlueBubblesAcpConversation,
normalizeBlueBubblesAcpConversationId,
resolveBlueBubblesConversationIdFromTarget,
resolveBlueBubblesInboundConversationId,
} from "./src/conversation-id.js";
export {
__testing,
createBlueBubblesConversationBindingManager,
} from "./src/conversation-bindings.js";
export { collectBlueBubblesStatusIssues } from "./src/status-issues.js";
export {
resolveBlueBubblesGroupRequireMention,
resolveBlueBubblesGroupToolPolicy,
} from "./src/group-policy.js";
export { isAllowedBlueBubblesSender } from "./src/targets.js";

View File

@@ -1 +0,0 @@
export { BlueBubblesChannelConfigSchema } from "./src/config-schema.js";

View File

@@ -1,3 +0,0 @@
// Keep bundled channel entry imports narrow so bootstrap/discovery paths do
// not drag setup-only BlueBubbles surfaces into lightweight channel plugin loads.
export { bluebubblesPlugin } from "./src/channel.js";

View File

@@ -1,8 +0,0 @@
export {
collectRuntimeConfigAssignments,
secretTargetRegistryEntries,
} from "./src/secret-contract.js";
export {
__testing as blueBubblesConversationBindingTesting,
createBlueBubblesConversationBindingManager,
} from "./src/conversation-bindings.js";

View File

@@ -1 +0,0 @@
export { normalizeCompatibilityConfig, legacyConfigRules } from "./src/doctor-contract.js";

View File

@@ -1,20 +0,0 @@
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
export default defineBundledChannelEntry({
id: "bluebubbles",
name: "BlueBubbles",
description: "BlueBubbles channel plugin (macOS app)",
importMetaUrl: import.meta.url,
plugin: {
specifier: "./channel-plugin-api.js",
exportName: "bluebubblesPlugin",
},
secrets: {
specifier: "./secret-contract-api.js",
exportName: "channelSecrets",
},
runtime: {
specifier: "./runtime-api.js",
exportName: "setBlueBubblesRuntime",
},
});

View File

@@ -1,12 +0,0 @@
{
"id": "bluebubbles",
"activation": {
"onStartup": false
},
"channels": ["bluebubbles"],
"configSchema": {
"type": "object",
"additionalProperties": false,
"properties": {}
}
}

Some files were not shown because too many files have changed in this diff Show More