mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 14:01:24 +08:00
Compare commits
104 Commits
v2026.3.28
...
codex/xai-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb77ee5f0c | ||
|
|
ad6ac51fc8 | ||
|
|
1dc3d7a7e1 | ||
|
|
ed17f0ab5f | ||
|
|
d3c0618525 | ||
|
|
798e5f9501 | ||
|
|
56640a6725 | ||
|
|
2d2e386b94 | ||
|
|
ba7911bd16 | ||
|
|
354bc01f29 | ||
|
|
637b4c8193 | ||
|
|
edc58a6864 | ||
|
|
8109195ad8 | ||
|
|
24d16c39ad | ||
|
|
e6116769b4 | ||
|
|
2c9bc0bb78 | ||
|
|
2dd29db464 | ||
|
|
f1af7d66d2 | ||
|
|
0a01386756 | ||
|
|
e0f0a1aa1f | ||
|
|
2c8c4e45f1 | ||
|
|
fc3f6fa51f | ||
|
|
5f85c4e69f | ||
|
|
ee701d6bad | ||
|
|
92d0b3a557 | ||
|
|
17c36b5093 | ||
|
|
270d0c5158 | ||
|
|
88ca0b2c3f | ||
|
|
571da81a35 | ||
|
|
e06069c8c2 | ||
|
|
443295448c | ||
|
|
a7a89fb680 | ||
|
|
81193336d0 | ||
|
|
5adc50ce6b | ||
|
|
7c50138f62 | ||
|
|
cea7162490 | ||
|
|
e28fdb08b8 | ||
|
|
2899ce5198 | ||
|
|
af694def5b | ||
|
|
f897aba69a | ||
|
|
3aac43e30b | ||
|
|
57882f0351 | ||
|
|
4d54376483 | ||
|
|
9c185faba9 | ||
|
|
6c85c82ba3 | ||
|
|
341e617c84 | ||
|
|
caeeecf399 | ||
|
|
8e0ab35b0e | ||
|
|
1738d540f4 | ||
|
|
9777781001 | ||
|
|
d6a4ec6a3d | ||
|
|
aec58d4cde | ||
|
|
f4d60478c9 | ||
|
|
ebb919e311 | ||
|
|
08b5206b19 | ||
|
|
8bdb518bde | ||
|
|
c48e0f8e6a | ||
|
|
04c976b43d | ||
|
|
c664b67796 | ||
|
|
4a5885df3a | ||
|
|
bc9c074b2c | ||
|
|
b29e180ef4 | ||
|
|
1a0c3bf400 | ||
|
|
598f539be5 | ||
|
|
41c30f0c59 | ||
|
|
4b137da582 | ||
|
|
e816d0968a | ||
|
|
c7330eb716 | ||
|
|
efa4e3d83e | ||
|
|
d458e1d05c | ||
|
|
7e7e45c2f3 | ||
|
|
dd61171f5b | ||
|
|
eee8e9679e | ||
|
|
7a16a48198 | ||
|
|
5d81b64343 | ||
|
|
d5e59621a7 | ||
|
|
1791c7c304 | ||
|
|
aee7992629 | ||
|
|
3a43401924 | ||
|
|
83808fe494 | ||
|
|
e3faa99c6a | ||
|
|
bb9394e123 | ||
|
|
5a0bd9036c | ||
|
|
384a590e54 | ||
|
|
27188fa39f | ||
|
|
6883f688e8 | ||
|
|
ec7f19e2ef | ||
|
|
a2e4707cfe | ||
|
|
64da916590 | ||
|
|
dc64a86eb8 | ||
|
|
61a0b02931 | ||
|
|
a0407c7254 | ||
|
|
e61ffa68f1 | ||
|
|
1c8758fbd5 | ||
|
|
7cf87c4e53 | ||
|
|
14832ff9f0 | ||
|
|
38d7e808d9 | ||
|
|
69a0a0edc5 | ||
|
|
f1970b8aef | ||
|
|
89881379dc | ||
|
|
3ce48aff66 | ||
|
|
6f7ff545dd | ||
|
|
9449e54f4f | ||
|
|
f93ccc3443 |
2
.github/workflows/ci-bun.yml
vendored
2
.github/workflows/ci-bun.yml
vendored
@@ -78,7 +78,7 @@ jobs:
|
||||
needs: [preflight, build-bun-artifacts]
|
||||
if: needs.preflight.outputs.run_bun_checks == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.bun_checks_matrix) }}
|
||||
|
||||
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -282,7 +282,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_checks_fast == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.checks_fast_matrix) }}
|
||||
@@ -325,7 +325,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: always() && needs.preflight.outputs.run_checks == 'true' && needs.build-artifacts.result == 'success'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.checks_matrix) }}
|
||||
@@ -416,7 +416,7 @@ jobs:
|
||||
needs: [preflight]
|
||||
if: needs.preflight.outputs.run_extension_fast == 'true'
|
||||
runs-on: blacksmith-16vcpu-ubuntu-2404
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.preflight.outputs.extension_fast_matrix) }}
|
||||
@@ -716,7 +716,7 @@ jobs:
|
||||
needs: [preflight, build-artifacts]
|
||||
if: always() && needs.preflight.outputs.run_checks_windows == 'true' && needs.build-artifacts.result == 'success'
|
||||
runs-on: blacksmith-32vcpu-windows-2025
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
# Keep total concurrency predictable on the 32 vCPU runner.
|
||||
|
||||
7
.github/workflows/workflow-sanity.yml
vendored
7
.github/workflows/workflow-sanity.yml
vendored
@@ -60,8 +60,11 @@ jobs:
|
||||
ACTIONLINT_VERSION="1.7.11"
|
||||
archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
||||
base_url="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}"
|
||||
curl -sSfL -o "${archive}" "${base_url}/${archive}"
|
||||
curl -sSfL -o checksums.txt "${base_url}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
|
||||
# GitHub release downloads occasionally return transient 5xx responses.
|
||||
# Retry all curl errors here so workflow-sanity does not fail closed on
|
||||
# a one-off release edge outage.
|
||||
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o "${archive}" "${base_url}/${archive}"
|
||||
curl --retry 5 --retry-delay 2 --retry-all-errors -sSfL -o checksums.txt "${base_url}/actionlint_${ACTIONLINT_VERSION}_checksums.txt"
|
||||
grep " ${archive}\$" checksums.txt | sha256sum -c -
|
||||
tar -xzf "${archive}" actionlint
|
||||
sudo install -m 0755 actionlint /usr/local/bin/actionlint
|
||||
|
||||
33
AGENTS.md
33
AGENTS.md
@@ -1,7 +1,7 @@
|
||||
# Repository Guidelines
|
||||
|
||||
- Repo: https://github.com/openclaw/openclaw
|
||||
- In chat replies, file references must be repo-root relative only (example: `extensions/bluebubbles/src/channel.ts:80`); never absolute paths or `~/...`.
|
||||
- In chat replies, file references must be repo-root relative only (example: `src/telegram/index.ts:80`); never absolute paths or `~/...`.
|
||||
- Do not edit files covered by security-focused `CODEOWNERS` rules unless a listed owner explicitly asked for the change or is already reviewing it with you. Treat those paths as restricted surfaces, not drive-by cleanup.
|
||||
|
||||
## Project Structure & Module Organization
|
||||
@@ -9,28 +9,28 @@
|
||||
- Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, web provider in `src/provider-web.ts`, infra in `src/infra`, media pipeline in `src/media`).
|
||||
- Tests: colocated `*.test.ts`.
|
||||
- Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`.
|
||||
- Nomenclature: use "plugin" / "plugins" in docs, UI, changelogs, and contributor guidance. `extensions/*` remains the internal directory/package path to avoid repo-wide churn from a rename.
|
||||
- Bundled plugin naming: for repo-owned workspace plugins, keep the canonical plugin id aligned across `openclaw.plugin.json:id`, `extensions/<id>` by default, and package names anchored to the same id (`@openclaw/<id>` or approved suffix forms like `-provider`, `-plugin`, `-speech`, `-sandbox`, `-media-understanding`). Keep `openclaw.install.npmSpec` equal to the package name and `openclaw.channel.id` equal to the plugin id when present. Exceptions must be explicit and covered by the repo invariant test.
|
||||
- Plugins: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
|
||||
- Nomenclature: use "plugin" / "plugins" in docs, UI, changelogs, and contributor guidance. The bundled workspace plugin tree remains the internal package layout to avoid repo-wide churn from a rename.
|
||||
- Bundled plugin naming: for repo-owned workspace plugins, keep the canonical plugin id aligned across `openclaw.plugin.json:id`, the default workspace folder name, and package names anchored to the same id (`@openclaw/<id>` or approved suffix forms like `-provider`, `-plugin`, `-speech`, `-sandbox`, `-media-understanding`). Keep `openclaw.install.npmSpec` equal to the package name and `openclaw.channel.id` equal to the plugin id when present. Exceptions must be explicit and covered by the repo invariant test.
|
||||
- Plugins: live in the bundled workspace plugin tree (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
|
||||
- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `openclaw` in `devDependencies` or `peerDependencies` instead (runtime resolves `openclaw/plugin-sdk` via jiti alias).
|
||||
- Import boundaries: extension production code should treat `openclaw/plugin-sdk/*` plus local `api.ts` / `runtime-api.ts` barrels as the public surface. Do not import core `src/**`, `src/plugin-sdk-internal/**`, or another extension's `src/**` directly.
|
||||
- Installers served from `https://openclaw.ai/*`: live in the sibling repo `../openclaw.ai` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
|
||||
- Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
|
||||
- Core channel docs: `docs/channels/`
|
||||
- Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
|
||||
- Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
|
||||
- When adding channels/extensions/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/extension label colors).
|
||||
- Bundled plugin channels: the workspace plugin tree (for example Matrix, Zalo, ZaloUser, Voice Call)
|
||||
- When adding channels/plugins/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/plugin label colors).
|
||||
|
||||
## Architecture Boundaries
|
||||
|
||||
- Start here for the repo map:
|
||||
- `extensions/*` = bundled plugins and the closest example surface for third-party plugins
|
||||
- bundled workspace plugin tree = bundled plugins and the closest example surface for third-party plugins
|
||||
- `src/plugin-sdk/*` = the public plugin contract that extensions are allowed to import
|
||||
- `src/channels/*` = core channel implementation details behind the plugin/channel boundary
|
||||
- `src/plugins/*` = plugin discovery, manifest validation, loader, registry, and contract enforcement
|
||||
- `src/gateway/protocol/*` = typed Gateway control-plane and node wire protocol
|
||||
- Progressive disclosure lives in local boundary guides:
|
||||
- `extensions/AGENTS.md`
|
||||
- bundled-plugin-tree `AGENTS.md`
|
||||
- `src/plugin-sdk/AGENTS.md`
|
||||
- `src/channels/AGENTS.md`
|
||||
- `src/plugins/AGENTS.md`
|
||||
@@ -39,7 +39,7 @@
|
||||
- Public docs: `docs/plugins/building-plugins.md`, `docs/plugins/architecture.md`, `docs/plugins/sdk-overview.md`, `docs/plugins/sdk-entrypoints.md`, `docs/plugins/sdk-runtime.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/sdk-provider-plugins.md`
|
||||
- Definition files: `src/plugin-sdk/plugin-entry.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/channel-contract.ts`, `scripts/lib/plugin-sdk-entrypoints.json`, `package.json`
|
||||
- Rule: extensions must cross into core only through `openclaw/plugin-sdk/*`, manifest metadata, and documented runtime helpers. Do not import `src/**` from extension production code.
|
||||
- Rule: core code and tests must not deep-import bundled plugin internals such as `extensions/<id>/src/**` or `extensions/<id>/onboard.js`. If core needs a bundled plugin helper, expose it through `extensions/<id>/api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/<id>.ts`.
|
||||
- Rule: core code and tests must not deep-import bundled plugin internals such as a plugin's `src/**` files or `onboard.js`. If core needs a bundled plugin helper, expose it through that plugin's `api.ts` and, when it is a real cross-package contract, through `src/plugin-sdk/<id>.ts`.
|
||||
- Compatibility: new plugin seams are allowed, but they must be added as documented, backwards-compatible, versioned contracts. We have third-party plugins in the wild and do not break them casually.
|
||||
- Channel boundary:
|
||||
- Public docs: `docs/plugins/sdk-channel-plugins.md`, `docs/plugins/architecture.md`
|
||||
@@ -57,11 +57,11 @@
|
||||
- Rule: protocol changes are contract changes. Prefer additive evolution; incompatible changes require explicit versioning, docs, and client/codegen follow-through.
|
||||
- Bundled plugin contract boundary:
|
||||
- Public docs: `docs/plugins/architecture.md`, `docs/plugins/manifest.md`, `docs/plugins/sdk-overview.md`
|
||||
- Definition files: `src/plugins/contracts/registry.ts`, `src/plugins/types.ts`, `src/extensions/public-artifacts.ts`
|
||||
- Definition files: `src/plugins/contracts/registry.ts`, `src/plugins/types.ts`, `src/plugins/public-artifacts.ts`
|
||||
- Rule: keep manifest metadata, runtime registration, public SDK exports, and contract tests aligned. Do not create a hidden path around the declared plugin interfaces.
|
||||
- Extension test boundary:
|
||||
- Keep extension-owned onboarding/config/provider coverage under `extensions/<id>/**` when feasible.
|
||||
- If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/<id>.ts` facades or `extensions/<id>/api.ts`, not private extension modules.
|
||||
- Keep extension-owned onboarding/config/provider coverage under the owning bundled plugin package when feasible.
|
||||
- If core tests need bundled plugin behavior, consume it through public `src/plugin-sdk/<id>.ts` facades or the plugin's `api.ts`, not private extension modules.
|
||||
|
||||
## Docs Linking (Mintlify)
|
||||
|
||||
@@ -145,10 +145,17 @@
|
||||
- Formatting/linting via Oxlint and Oxfmt.
|
||||
- Never add `@ts-nocheck` and do not add inline lint suppressions by default. Fix root causes first; only keep a suppression when the code is intentionally correct, the rule cannot express that safely, and the comment explains why.
|
||||
- Do not disable `no-explicit-any`; prefer real types, `unknown`, or a narrow adapter/helper instead. Update Oxlint/Oxfmt config only when required.
|
||||
- Prefer `zod` or existing schema helpers at external boundaries such as config, webhook payloads, CLI/JSON output, persisted JSON, and third-party API responses.
|
||||
- Prefer discriminated unions when parameter shape changes runtime behavior.
|
||||
- Prefer `Result<T, E>`-style outcomes and closed error-code unions for recoverable runtime decisions.
|
||||
- Keep human-readable strings for logs, CLI output, and UI; do not use freeform strings as the source of truth for internal branching.
|
||||
- Avoid `?? 0`, empty-string, empty-object, or magic-string sentinels when they can change runtime meaning silently.
|
||||
- If introducing a new optional field or nullable semantic in core logic, prefer an explicit union or dedicated type when the value changes behavior.
|
||||
- New runtime control-flow code should not branch on `error: string` or `reason: string` when a closed code union would be reasonable.
|
||||
- Dynamic import guardrail: do not mix `await import("x")` and static `import ... from "x"` for the same module in production code paths. If you need lazy loading, create a dedicated `*.runtime.ts` boundary (that re-exports from `x`) and dynamically import that boundary from lazy callers only.
|
||||
- Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run `pnpm build` and check for `[INEFFECTIVE_DYNAMIC_IMPORT]` warnings before submitting.
|
||||
- Extension SDK self-import guardrail: inside an extension package, do not import that same extension via `openclaw/plugin-sdk/<extension>` from production files. Route internal imports through a local barrel such as `./api.ts` or `./runtime-api.ts`, and keep the `plugin-sdk/<extension>` path as the external contract only.
|
||||
- Extension package boundary guardrail: inside `extensions/<id>/**`, do not use relative imports/exports that resolve outside that same `extensions/<id>` package root. If shared code belongs in the plugin SDK, import `openclaw/plugin-sdk/<subpath>` instead of reaching into `src/plugin-sdk/**` or other repo paths via `../`.
|
||||
- Extension package boundary guardrail: inside a bundled plugin package, do not use relative imports/exports that resolve outside that same package root. If shared code belongs in the plugin SDK, import `openclaw/plugin-sdk/<subpath>` instead of reaching into `src/plugin-sdk/**` or other repo paths via `../`.
|
||||
- Extension API surface rule: `openclaw/plugin-sdk/<subpath>` is the only public cross-package contract for extension-facing SDK code. If an extension needs a new seam, add a public subpath first; do not reach into `src/plugin-sdk/**` by relative path.
|
||||
- Never share class behavior via prototype mutation (`applyPrototypeMixins`, `Object.defineProperty` on `.prototype`, or exporting `Class.prototype` for merges). Use explicit inheritance/composition (`A extends B extends C`) or helper composition so TypeScript can typecheck.
|
||||
- If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed.
|
||||
|
||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -4,8 +4,21 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changes
|
||||
|
||||
- LINE/outbound media: add LINE image, video, and audio outbound sends on the LINE-specific delivery path, including explicit preview/tracking handling for videos while keeping generic media sends on the existing image-only route. (#45826) Thanks @masatohoshino.
|
||||
|
||||
### Fixes
|
||||
|
||||
- ACP/sessions_spawn: register ACP child runs for completion tracking and lifecycle cleanup, and make registration-failure cleanup explicitly best-effort so callers do not assume an already-started ACP turn was fully aborted. (#40885) Thanks @xaeon2026 and @vincentkoc.
|
||||
- ACPX/runtime: derive the bundled ACPX expected version from the extension package metadata instead of hardcoding a separate literal, so plugin-local ACPX installs stop drifting out of health-check parity after version bumps. (#49089) Thanks @jiejiesks and @vincentkoc.
|
||||
- Gateway/auth: make local-direct `trusted-proxy` fallback require the configured shared token instead of silently authenticating same-host callers, while keeping same-host reverse proxy identity-header flows on the normal trusted-proxy path. Thanks @zhangning-agent and @vincentkoc.
|
||||
- Agents/sandbox: honor `tools.sandbox.tools.alsoAllow`, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.
|
||||
- LINE/ACP: add current-conversation binding and inbound binding-routing parity so `/acp spawn ... --thread here`, configured ACP bindings, and active conversation-bound ACP sessions work on LINE like the other conversation channels.
|
||||
- LINE/markdown: preserve underscores inside Latin, Cyrillic, and CJK words when stripping markdown, while still removing standalone `_italic_` markers on the shared text-runtime path used by LINE and TTS. (#47465) Thanks @jackjin1997.
|
||||
- TTS/Microsoft: auto-switch the default Edge voice to Chinese for CJK-dominant text without overriding explicitly selected Microsoft voices. (#52355) Thanks @extrasmall0.
|
||||
- Agents/context pruning: count supplementary-plane CJK characters with the shared code-point-aware estimator so context pruning stops underestimating Japanese and Chinese text that uses Extension B ideographs. (#39985) Thanks @Edward-Qiang-2024.
|
||||
- Slack/status reactions: add a reaction lifecycle for queued, thinking, tool, done, and error phases in Slack monitors, with safer cleanup so queued ack reactions stay correct across silent runs, pre-reply failures, and delayed transitions. (#56430) Thanks @hsiaoa.
|
||||
- macOS/local gateway: stop OpenClaw.app from killing healthy local gateway listeners after startup by recognizing the current `openclaw-gateway` process title and using the current `openclaw gateway` launch shape.
|
||||
- Memory/QMD: resolve slugified `memory_search` file hints back to the indexed filesystem path before returning search hits, so `memory_get` works again for mixed-case and spaced paths. (#50313) Thanks @erra9x.
|
||||
- Memory/QMD: weight CJK-heavy text correctly when estimating chunk sizes, preserve surrogate-pair characters during fine splits, and keep long Latin lines on the old chunk boundaries so memory indexing produces better-sized chunks for CJK notes. (#40271) Thanks @AaronLuo00.
|
||||
@@ -14,6 +27,26 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/health: carry webhook-vs-polling account mode from channel descriptors into runtime snapshots so passive channels like LINE and BlueBubbles skip false stale-socket health failures. (#47488) Thanks @karesansui-u.
|
||||
- Memory/QMD: honor `memory.qmd.update.embedInterval` even when regular QMD update cadence is disabled or slower by arming a dedicated embed-cadence maintenance timer, while avoiding redundant timers when regular updates are already frequent enough. (#37326) Thanks @barronlroth.
|
||||
- Agents/memory flush: keep daily memory flush files append-only during embedded attempts so compaction writes do not overwrite earlier notes. (#53725) Thanks @HPluseven.
|
||||
- Web UI/markdown: stop bare auto-links from swallowing adjacent CJK text while preserving valid mixed-script path and query characters in rendered links. (#48410) Thanks @jnuyao.
|
||||
- Sandbox/browser: install `fonts-noto-cjk` in the sandbox browser image so screenshots render Chinese, Japanese, and Korean text correctly instead of tofu boxes. Fixes #35597. Thanks @carrotRakko and @vincentkoc.
|
||||
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
- TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
|
||||
- TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
|
||||
- Agents/Kimi: preserve already-valid Anthropic-compatible tool call argument objects while still clearing cached repairs when later trailing junk exceeds the repair allowance. (#54491) Thanks @yuanaichi.
|
||||
- Docker/setup: force BuildKit for local image builds (including sandbox image builds) so `./docker-setup.sh` no longer fails on `RUN --mount=...` when hosts default to Docker's legacy builder. (#56681) Thanks @zhanghui-china.
|
||||
- Control UI/agents: auto-load agent workspace files on initial Files panel open, and populate overview model/workspace/fallbacks from effective runtime agent metadata so defaulted models no longer show as `Not set`. (#56637) Thanks @dxsx84.
|
||||
- Control UI/slash commands: make `/steer` and `/redirect` work from the chat command palette with visible pending state for active-run `/steer`, correct redirected-run tracking, and a single canonical `/steer` entry in the command menu. (#54625) Thanks @fuller-stack-dev.
|
||||
- Exec: fail closed when the implicit sandbox host has no sandbox runtime, and stop denied async approval followups from reusing prior command output from the same session. (#56800) Thanks @scoootscooob.
|
||||
- Exec/node: stop gateway-side workdir fallback from rewriting explicit `host=node` cwd values to the gateway filesystem, so remote node exec approval and runs keep using the intended node-local directory. (#50961) Thanks @openperf.
|
||||
- Plugins/ClawHub: sanitize temporary archive filenames for scoped package names and slash-containing skill slugs so `openclaw plugins install @scope/name` no longer fails with `ENOENT` during archive download. (#56452) Thanks @soimy.
|
||||
- Telegram/polling: keep the watchdog from aborting long-running reply delivery by treating recent non-polling API activity as bounded liveness instead of a hard stall. (#56343) Thanks @openperf.
|
||||
- Memory/FTS: keep provider-less keyword hits visible at the default memory-search threshold, so FTS-only recall works without requiring `--min-score 0`. (#56473) Thanks @opriz.
|
||||
- Memory/LanceDB: resolve runtime dependency manifest lookup from the bundled `extensions/memory-lancedb` path (including flattened dist chunks) so startup no longer fails with a missing `@lancedb/lancedb` dependency error. (#56623) Thanks @LUKSOAgent.
|
||||
- Tools/web_search: localize the shared search cache to module scope so same-process global symbol lookups can no longer inspect or mutate cached web-search responses. Thanks @vincentkoc.
|
||||
- Agents/silent turns: fail closed on silent memory-flush runs so narrated `NO_REPLY` self-talk cannot stream or finalize into external replies even when block streaming is enabled. (#52593)
|
||||
- Browser/plugins: auto-enable the bundled browser plugin when browser config or browser tool policy already references it, and show a clearer CLI error when `plugins.allow` excludes `browser`.
|
||||
- Matrix/plugin loading: ship and source-load the crypto bootstrap runtime sidecar correctly so current `main` stops warning about failed Matrix bootstrap loads and `matrix/index` plugin-id mismatches on every invocation. (#53298) thanks @keithce.
|
||||
|
||||
## 2026.3.28
|
||||
|
||||
@@ -41,10 +74,12 @@ Docs: https://docs.openclaw.ai
|
||||
- Memory/plugins: move the pre-compaction memory flush plan behind the active memory plugin contract so `memory-core` owns flush prompts and target-path policy instead of hardcoded core logic.
|
||||
- MiniMax: trim model catalog to M2.7 only, removing legacy M2, M2.1, M2.5, and VL-01 models. (#54487) Thanks @liyuan97.
|
||||
- Plugins/runtime: expose `runHeartbeatOnce` in the plugin runtime `system` namespace so plugins can trigger a single heartbeat cycle with an explicit delivery target override (e.g. `heartbeat: { target: "last" }`). (#40299) Thanks @loveyana.
|
||||
- Background tasks: keep durable lifecycle records for ACP/subagent spawned work and deliver ACP completion/failure updates through the real requester chat path instead of session-only stream events.
|
||||
- Agents/compaction: preserve the post-compaction AGENTS refresh on stale-usage preflight compaction for both immediate replies and queued followups. (#49479) Thanks @jared596.
|
||||
- Agents/compaction: surface safeguard-specific cancel reasons and relabel benign manual `/compact` no-op cases as skipped instead of failed. (#51072) Thanks @afurm.
|
||||
- Docs: add `pnpm docs:check-links:anchors` for Mintlify anchor validation while keeping `scripts/docs-link-audit.mjs` as the stable link-audit entrypoint. (#55912) Thanks @velvet-shark.
|
||||
- Tavily: mark outbound API requests with `X-Client-Source: openclaw` so Tavily can attribute OpenClaw-originated traffic. (#55335) Thanks @lakshyaag-tavily.
|
||||
- Matrix/streaming: add `streaming: "partial"` draft replies that stay on a single editable preview message, stop preview streaming once text no longer fits one Matrix event, and clear stale previews before media-only finals. (#56387) thanks @jrusz.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -87,11 +122,13 @@ Docs: https://docs.openclaw.ai
|
||||
- Mattermost/replies: keep pairing replies, slash-command fallback replies, and model-picker messages on the resolved config path so `exec:` SecretRef bot tokens work across all outbound reply branches. (#48347) thanks @mathiasnagler.
|
||||
- Microsoft Teams/config: accept the existing `welcomeCard`, `groupWelcomeCard`, `promptStarters`, and feedback/reflection keys in strict config validation so already-supported Teams runtime settings stop failing schema checks. (#54679) Thanks @gumclaw.
|
||||
- MCP/channels: add a Gateway-backed channel MCP bridge with Codex/Claude-facing conversation tools, Claude channel notifications, and safer stdio bridge lifecycle handling for reconnects and routed session discovery.
|
||||
- Plugins/SDK: thread `moduleUrl` through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory (e.g. `~/.openclaw/extensions/`) correctly resolve `openclaw/plugin-sdk/*` subpath imports, and gate `plugin-sdk:check-exports` in `release:check`. (#54283) Thanks @xieyongliang.
|
||||
- Plugins/SDK: thread `moduleUrl` through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory correctly resolve `openclaw/plugin-sdk/*` subpath imports, and gate `plugin-sdk:check-exports` in `release:check`. (#54283) Thanks @xieyongliang.
|
||||
- Config/web fetch: allow the documented `tools.web.fetch.maxResponseBytes` setting in runtime schema validation so valid configs no longer fail with unrecognized-key errors. (#53401) Thanks @erhhung.
|
||||
- Message tool/buttons: keep the shared `buttons` schema optional in merged tool definitions so plain `action=send` calls stop failing validation when no buttons are provided. (#54418) Thanks @adzendo.
|
||||
- Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate `tool_call_id` values with HTTP 400. (#40996) Thanks @xaeon2026.
|
||||
- Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition `strict` fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.
|
||||
- Subagents/announcements: preserve the requester agent id for inline deterministic tool spawns so named agents without channel bindings can still announce completions through the correct owner session. (#55437) Thanks @kAIborg24.
|
||||
- Telegram/Anthropic streaming: replace raw invalid stream-order provider errors with a safe retry message so internal `message_start/message_stop` failures do not leak into chats. (#55408) Thanks @imydal.
|
||||
- Plugins/context engines: retry strict legacy `assemble()` calls without the new `prompt` field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.
|
||||
- CLI/update status: explicitly say `up to date` when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.
|
||||
- Daemon/Linux: stop flagging non-gateway systemd services as duplicate gateways just because their unit files mention OpenClaw, reducing false-positive doctor/log noise. (#45328) Thanks @gregretkowski.
|
||||
@@ -113,7 +150,7 @@ Docs: https://docs.openclaw.ai
|
||||
- GitHub Copilot/auth refresh: treat large `expires_at` values as seconds epochs and clamp far-future runtime auth refresh timers so Copilot token refresh cannot fall into a `setTimeout` overflow hot loop. (#55360) Thanks @michael-abdo.
|
||||
- Agents/status: use the persisted runtime session model in `session_status` when no explicit override exists, and honor per-agent `thinkingDefault` in both `session_status` and `/status`. (#55425) Thanks @scoootscooob, @xaeon2026, and @ysfbsf.
|
||||
- Heartbeat/runner: guarantee the interval timer is re-armed after heartbeat runs and unexpected runner errors so scheduled heartbeats do not silently stop after an interrupted cycle. (#52270) Thanks @MiloStack.
|
||||
- Config/Doctor: rewrite stale bundled plugin load paths from legacy `extensions/*` locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.
|
||||
- Config/Doctor: rewrite stale bundled plugin load paths from legacy bundled-plugin locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.
|
||||
- WhatsApp/mentions: stop treating mentions embedded in quoted messages as direct mentions so replying to a message that @mentioned the bot no longer falsely triggers mention gating. (#52711) Thanks @lurebat.
|
||||
- Matrix: keep separate 2-person rooms out of DM routing after `m.direct` seeds successfully, while still honoring explicit `is_direct` state and startup fallback recovery. (#54890) thanks @private-peter
|
||||
- Agents/ollama fallback: surface non-2xx Ollama HTTP errors with a leading status code so HTTP 503 responses trigger model fallback again. (#55214) Thanks @bugkill3r.
|
||||
@@ -145,6 +182,9 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/Matrix: load bundled `@matrix-org/matrix-sdk-crypto-nodejs` through `createRequire(...)` so E2EE media send and receive keep the package-local native binding lookup working in packaged ESM builds. (#54566) thanks @joelnishanth.
|
||||
- Plugins/Matrix: encrypt E2EE image thumbnails with `thumbnail_file` while keeping unencrypted-room previews on `thumbnail_url`, so encrypted Matrix image events keep thumbnail metadata without leaking plaintext previews. (#54711) thanks @frischeDaten.
|
||||
- Telegram/forum topics: keep native `/new` and `/reset` routed to the active topic by preserving the topic target on forum-thread command context. (#35963)
|
||||
- Status/port diagnostics: treat single-process dual-stack loopback gateway listeners as healthy in `openclaw status --all`, suppressing false “port already in use” conflict warnings. (#53398) Thanks @DanWebb1949.
|
||||
- Memory/builtin: keep memory-file indexing active in FTS-only mode (no embedding provider) so forced reindexes no longer swap in an empty index and wipe existing memory chunks. (#42714) Thanks @asamimei.
|
||||
- CLI/status: detect node-only hosts in `openclaw status` and `openclaw status --all`, show the configured remote gateway target instead of a false local `ECONNREFUSED`, and suppress contradictory local-gateway diagnosis output.
|
||||
|
||||
## 2026.3.24
|
||||
|
||||
@@ -170,6 +210,8 @@ Docs: https://docs.openclaw.ai
|
||||
- Control UI/agents: add a "Not set" placeholder to the default agent model selector dropdown. (#53411) Thanks @BunsDev.
|
||||
- Runtime/install: lower the supported Node 22 floor to `22.14+` while continuing to recommend Node 24, so npm installs and self-updates do not strand Node 22.14 users on older releases.
|
||||
- CLI/update: preflight the target npm package `engines.node` before `openclaw update` runs a global package install, so outdated Node runtimes fail with a clear upgrade message instead of attempting an unsupported latest release.
|
||||
- Agents/BTW: force `/btw` side questions to disable provider reasoning so Anthropic adaptive-thinking sessions stop failing with `No BTW response generated`. Fixes #55376. Thanks @Catteres and @vincentkoc.
|
||||
- Auth profiles/OAuth: refresh runtime auth snapshots when saving rotated credentials so OAuth providers do not reuse consumed refresh tokens after the first token rotation. Fixes #55389. Thanks @sam26880 and @vincentkoc.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -283,6 +325,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/session policy: require sender ownership for `/send` policy changes so command-authorized non-owners cannot rewrite owner-only session delivery policy.
|
||||
- Security/bash stop: route `/bash stop` through the hardened process-tree killer so invalid or attacker-influenced SIGKILL targets cannot escape the intended bash-session scope.
|
||||
- Security/installer: hide staged project `.npmrc` files during skill and package installs so npm registry and git settings inside the stage directory cannot hijack trusted installs.
|
||||
- Agents/tool-call repair: recover malformed Kimi/OpenRouter tool-call argument streams when provider preambles appear before JSON payloads, and fail closed on non-tool leading text so fragment strings do not leak into filesystem path arguments during sub-agent runs. (#56560) Thanks @Originalwhite.
|
||||
|
||||
## 2026.3.23
|
||||
|
||||
@@ -437,7 +480,7 @@ Docs: https://docs.openclaw.ai
|
||||
- secrets: harden read-only SecretRef command paths and diagnostics. (#47794) Thanks @joshavant.
|
||||
- Scope message SecretRef resolution and harden doctor/status paths. (#48728) Thanks @joshavant.
|
||||
- Build/memory tools: emit `dist/cli/memory-cli.js` as a stable core entry so runtime `memory_search` loading no longer depends on hashed `memory-cli-*` bundle names. (#51759) Thanks @oliviareid-svg.
|
||||
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-extension-only test bridges out of `extensions/` into private repo test helpers.
|
||||
- Plugins/testing: add a public `openclaw/plugin-sdk/testing` surface for plugin-author test helpers, and move bundled-plugin-only test bridges into private repo test helpers.
|
||||
- Agents/steering docs: update embedded Pi steering docs and runner comments for the current upstream behavior, where queued steering is injected after the active assistant turn finishes its tool calls instead of skipping the remaining tools mid-turn. Thanks @vincentkoc.
|
||||
- Doctor/refactor: start splitting doctor provider checks into `src/commands/doctor/providers/*` by extracting Telegram first-run and group allowlist warnings into a provider-specific module, keeping the current setup guidance and warning behavior intact. Thanks @vincentkoc.
|
||||
- Refactor/channels: remove the legacy channel shim directories and point channel-specific imports directly at the extension-owned implementations. (#45967) Thanks @scoootscooob.
|
||||
@@ -592,7 +635,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Zalo/plugin runtime: export `resolveClientIp` from `openclaw/plugin-sdk/zalo` so installed builds no longer crash on startup when the webhook monitor loads from the packaged extension instead of the monorepo source tree. (#46549) Thanks @No898.
|
||||
- Docker/live tests: mount external CLI auth homes into writable container copies, derive Codex OAuth expiry from JWT `exp`, refresh synced CLI creds instead of trusting stale cached expiry, and make gateway live probes wait on transcript output so `pnpm test:docker:all` stays green in Linux.
|
||||
- Gateway/watch mode: restart on bundled-plugin package and manifest metadata changes, rebuild `dist` for extension source and `tsdown.config.ts` changes, and still ignore extension docs. (#47571) Thanks @gumadeiras.
|
||||
- Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing `dist/extensions/*/openclaw.plugin.json` manifests after a rebuild. Thanks @gumadeiras.
|
||||
- Gateway/watch mode: recreate bundled plugin runtime metadata after clean or stale `dist` states, so `pnpm gateway:watch` no longer fails on missing bundled-plugin manifests after a rebuild. Thanks @gumadeiras.
|
||||
- Control UI: scope persisted session selection per gateway, prevent stale session bleed across tokenized gateway opens, and cap stored gateway session history. (#47453) Thanks @sallyom.
|
||||
- Models/OpenAI Codex OAuth: start the remote manual-input race for Codex login and keep the pasted-input prompt aligned with the actual accepted values, so remote/VPS auth no longer stalls waiting on an unreachable localhost callback. (#51631) Thanks @cash-echo-bot.
|
||||
- Group mention gating: reject invalid and unsafe nested-repetition `mentionPatterns`, reuse the shared safe config-regex compiler across mention stripping and detection, and cache strip-time regex compilation so noisy groups avoid repeated recompiles.
|
||||
@@ -604,11 +647,11 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/agent events: stop broadcasting false end-of-run `seq gap` errors to clients, and isolate node-driven ingress turns with per-turn run IDs so stale tail events cannot leak into later session runs. (#43751) Thanks @caesargattuso.
|
||||
- Nodes/pending actions: re-check queued foreground actions against the current node command policy before returning them to the node. (#46815) Thanks @zpbrent and @vincentkoc.
|
||||
- Windows/gateway status: accept `schtasks` `Last Result` output as an alias for `Last Run Result`, so running scheduled-task installs no longer show `Runtime: unknown`. (#47844) Thanks @MoerAI.
|
||||
- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under `dist/extensions/acpx` instead of escaping to `dist/extensions` and failing runtime setup. (#47601) Thanks @ngutman.
|
||||
- ACP/acpx: resolve the bundled plugin root from the actual plugin directory so plugin-local installs stay under the bundled dist plugin tree instead of escaping upward and failing runtime setup. (#47601) Thanks @ngutman.
|
||||
- Gateway/WS handshake: raise the default pre-auth handshake timeout to 10 seconds and add `OPENCLAW_HANDSHAKE_TIMEOUT_MS` as a runtime override so busy local gateways stop dropping healthy CLI connections at 3 seconds. (#49262) Thanks @fuller-stack-dev.
|
||||
- Gateway/websocket pairing bypass for disabled auth: skip device-pairing enforcement for Control UI operator sessions when `gateway.auth.mode=none`, so reverse-proxied dashboards no longer get stuck on `pairing required` despite auth being explicitly disabled. (#47148) Thanks @ademczuk.
|
||||
- Agents/usage tracking: stop forcing `supportsUsageInStreaming: false` on non-native OpenAI-completions providers so compatible backends report token usage and cost again instead of showing all zeros. (#46500) Fixes #46142. Thanks @ademczuk.
|
||||
- ACP/acpx: keep plugin-local backend installs under `extensions/acpx` in live repo checkouts so rebuilds no longer delete the runtime binary, and avoid package-lock churn during runtime repair.
|
||||
- ACP/acpx: keep plugin-local backend installs under the bundled ACPX plugin package in live repo checkouts so rebuilds no longer delete the runtime binary, and avoid package-lock churn during runtime repair.
|
||||
- Agents/compaction: rerun transcript repair after `session.compact()` so orphaned `tool_result` blocks cannot survive compaction and break later Anthropic requests. (#16095) thanks @claw-sylphx.
|
||||
- Agents/compaction: trigger overflow recovery from the tool-result guard once post-compaction context still exceeds the safe threshold, so long tool loops compact before the next model call hard-fails. (#29371) thanks @keshav55.
|
||||
- macOS/exec approvals: harden exec-host request HMAC verification to use a timing-safe compare and keep malformed or truncated signatures fail-closed in focused IPC auth coverage.
|
||||
@@ -1378,7 +1421,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/loopback announce URLs: treat `http://` and `https://` aliases with the same loopback/private-network policy as websocket URLs so loopback cron announce delivery no longer fails secure URL validation. (#39064) Thanks @Narcooo.
|
||||
- Models/default provider fallback: when the hardcoded default provider is removed from `models.providers`, resolve defaults from configured providers instead of reporting stale removed-provider defaults in status output. (#38947) Thanks @davidemanuelDEV.
|
||||
- Agents/cache-trace stability: guard stable stringify against circular references in trace payloads so near-limit payloads no longer crash with `Maximum call stack size exceeded`; adds regression coverage. (#38935) Thanks @MumuTW.
|
||||
- Extensions/diffs CI stability: add `headers` to the `localReq` test helper in `extensions/diffs/index.test.ts` so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng.
|
||||
- Extensions/diffs CI stability: add `headers` to the diffs plugin `localReq` test helper so forwarding-hint checks no longer crash with `req.headers` undefined. (supersedes #39063) Thanks @Shennng.
|
||||
- Agents/compaction thresholding: apply `agents.defaults.contextTokens` cap to the model passed into embedded run and `/compact` session creation so auto-compaction thresholds use the effective context window, not native model max context. (#39099) Thanks @MumuTW.
|
||||
- Models/merge mode provider precedence: when `models.mode: "merge"` is active and config explicitly sets a provider `baseUrl`, keep config as source of truth instead of preserving stale runtime `models.json` `baseUrl` values; includes normalized provider-key coverage. (#39103) Thanks @BigUncle.
|
||||
- UI/Control chat tool streaming: render tool events live in webchat without requiring refresh by enabling `tool-events` capability, fixing stream/event correlation, and resetting/reloading stream state around tool results and terminal events. (#39104) Thanks @jakepresent.
|
||||
@@ -2586,7 +2629,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Security/Agents: make owner-ID obfuscation use a dedicated HMAC secret from configuration (`ownerDisplaySecret`) and update hashing behavior so obfuscation is decoupled from gateway token handling for improved control. (#7343) Thanks @vincentkoc.
|
||||
- Security/Infra: switch gateway lock and tool-call synthetic IDs from SHA-1 to SHA-256 with unchanged truncation length to strengthen hash basis while keeping deterministic behavior and lock key format. (#7343) Thanks @vincentkoc.
|
||||
- Dependencies/Tooling: add non-blocking dead-code scans in CI via Knip/ts-prune/ts-unused-exports to surface unused dependencies and exports earlier. (#22468) Thanks @vincentkoc.
|
||||
- Dependencies/Unused Dependencies: remove or scope unused root and extension deps (`@larksuiteoapi/node-sdk`, `signal-utils`, `ollama`, `lit`, `@lit/context`, `@lit-labs/signals`, `@microsoft/agents-hosting-express`, `@microsoft/agents-hosting-extensions-teams`, and plugin-local `openclaw` devDeps in `extensions/open-prose`, `extensions/lobster`, and `extensions/llm-task`). (#22471, #22495) Thanks @vincentkoc.
|
||||
- Dependencies/Unused Dependencies: remove or scope unused root and extension deps (`@larksuiteoapi/node-sdk`, `signal-utils`, `ollama`, `lit`, `@lit/context`, `@lit-labs/signals`, `@microsoft/agents-hosting-express`, `@microsoft/agents-hosting-extensions-teams`, and plugin-local `openclaw` devDeps in the Open Prose, Lobster, and LLM Task plugin packages). (#22471, #22495) Thanks @vincentkoc.
|
||||
- Dependencies/A2UI: harden dependency resolution after root cleanup (resolve `lit`, `@lit/context`, `@lit-labs/signals`, and `signal-utils` from workspace/root) and simplify bundling fallback behavior, including `pnpm dlx rolldown` compatibility. (#22481, #22507) Thanks @vincentkoc.
|
||||
|
||||
### Fixes
|
||||
@@ -3389,7 +3432,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Feishu: probe status uses the resolved account context for multi-account credential checks. (#11233) Thanks @onevcat.
|
||||
- Feishu: add streaming card replies via Card Kit API and preserve `renderMode=auto` fallback behavior for plain-text responses. (#10379) Thanks @xzq-xu.
|
||||
- Feishu DocX: preserve top-level converted block order using `firstLevelBlockIds` when writing/appending documents. (#13994) Thanks @Cynosure159.
|
||||
- Feishu plugin packaging: remove `workspace:*` `openclaw` dependency from `extensions/feishu` and sync lockfile for install compatibility. (#14423) Thanks @jackcooper2015.
|
||||
- Feishu plugin packaging: remove `workspace:*` `openclaw` dependency from the Feishu plugin package and sync lockfile for install compatibility. (#14423) Thanks @jackcooper2015.
|
||||
- CLI/Wizard: exit with code 1 when `configure`, `agents add`, or interactive `onboard` wizards are canceled, so `set -e` automation stops correctly. (#14156) Thanks @0xRaini.
|
||||
- Media: strip `MEDIA:` lines with local paths instead of leaking as visible text. (#14399) Thanks @0xRaini.
|
||||
- Config/Cron: exclude `maxTokens` from config redaction and honor `deleteAfterRun` on skipped cron jobs. (#13342) Thanks @niceysam.
|
||||
|
||||
24
Dockerfile
24
Dockerfile
@@ -5,15 +5,16 @@
|
||||
#
|
||||
# Multi-stage build produces a minimal runtime image without build tools,
|
||||
# source code, or Bun. Works with Docker, Buildx, and Podman.
|
||||
# The ext-deps stage extracts only the package.json files we need from
|
||||
# extensions/, so the main build layer is not invalidated by unrelated
|
||||
# extension source changes.
|
||||
# The ext-deps stage extracts only the package.json files we need from the
|
||||
# bundled plugin workspace tree, so the main build layer is not invalidated by
|
||||
# unrelated plugin source changes.
|
||||
#
|
||||
# Two runtime variants:
|
||||
# Default (bookworm): docker build .
|
||||
# Slim (bookworm-slim): docker build --build-arg OPENCLAW_VARIANT=slim .
|
||||
ARG OPENCLAW_EXTENSIONS=""
|
||||
ARG OPENCLAW_VARIANT=default
|
||||
ARG OPENCLAW_BUNDLED_PLUGIN_DIR=extensions
|
||||
ARG OPENCLAW_DOCKER_APT_UPGRADE=1
|
||||
ARG OPENCLAW_NODE_BOOKWORM_IMAGE="node:24-bookworm@sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
|
||||
ARG OPENCLAW_NODE_BOOKWORM_DIGEST="sha256:3a09aa6354567619221ef6c45a5051b671f953f0a1924d1f819ffb236e520e6b"
|
||||
@@ -27,18 +28,20 @@ ARG OPENCLAW_NODE_BOOKWORM_SLIM_DIGEST="sha256:e8e2e91b1378f83c5b2dd15f0247f3411
|
||||
|
||||
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS ext-deps
|
||||
ARG OPENCLAW_EXTENSIONS
|
||||
COPY extensions /tmp/extensions
|
||||
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
|
||||
COPY ${OPENCLAW_BUNDLED_PLUGIN_DIR} /tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}
|
||||
# Copy package.json for opted-in extensions so pnpm resolves their deps.
|
||||
RUN mkdir -p /out && \
|
||||
for ext in $OPENCLAW_EXTENSIONS; do \
|
||||
if [ -f "/tmp/extensions/$ext/package.json" ]; then \
|
||||
if [ -f "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" ]; then \
|
||||
mkdir -p "/out/$ext" && \
|
||||
cp "/tmp/extensions/$ext/package.json" "/out/$ext/package.json"; \
|
||||
cp "/tmp/${OPENCLAW_BUNDLED_PLUGIN_DIR}/$ext/package.json" "/out/$ext/package.json"; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# ── Stage 2: Build ──────────────────────────────────────────────
|
||||
FROM ${OPENCLAW_NODE_BOOKWORM_IMAGE} AS build
|
||||
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
|
||||
|
||||
# Install Bun (required for build scripts). Retry the whole bootstrap flow to
|
||||
# tolerate transient 5xx failures from bun.sh/GitHub during CI image builds.
|
||||
@@ -62,7 +65,7 @@ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
|
||||
COPY ui/package.json ./ui/package.json
|
||||
COPY patches ./patches
|
||||
|
||||
COPY --from=ext-deps /out/ ./extensions/
|
||||
COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/
|
||||
|
||||
# Reduce OOM risk on low-memory hosts during dependency installation.
|
||||
# Docker builds on small VMs may otherwise fail with "Killed" (exit 137).
|
||||
@@ -73,7 +76,7 @@ COPY . .
|
||||
|
||||
# Normalize extension paths now so runtime COPY preserves safe modes
|
||||
# without adding a second full extensions layer.
|
||||
RUN for dir in /app/extensions /app/.agent /app/.agents; do \
|
||||
RUN for dir in /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} /app/.agent /app/.agents; do \
|
||||
if [ -d "$dir" ]; then \
|
||||
find "$dir" -type d -exec chmod 755 {} +; \
|
||||
find "$dir" -type f -exec chmod 644 {} +; \
|
||||
@@ -114,6 +117,7 @@ LABEL org.opencontainers.image.base.name="docker.io/library/node:24-bookworm-sli
|
||||
# ── Stage 3: Runtime ────────────────────────────────────────────
|
||||
FROM base-${OPENCLAW_VARIANT}
|
||||
ARG OPENCLAW_VARIANT
|
||||
ARG OPENCLAW_BUNDLED_PLUGIN_DIR
|
||||
ARG OPENCLAW_DOCKER_APT_UPGRADE
|
||||
|
||||
# OCI base-image metadata for downstream image consumers.
|
||||
@@ -148,13 +152,13 @@ COPY --from=runtime-assets --chown=node:node /app/dist ./dist
|
||||
COPY --from=runtime-assets --chown=node:node /app/node_modules ./node_modules
|
||||
COPY --from=runtime-assets --chown=node:node /app/package.json .
|
||||
COPY --from=runtime-assets --chown=node:node /app/openclaw.mjs .
|
||||
COPY --from=runtime-assets --chown=node:node /app/extensions ./extensions
|
||||
COPY --from=runtime-assets --chown=node:node /app/${OPENCLAW_BUNDLED_PLUGIN_DIR} ./${OPENCLAW_BUNDLED_PLUGIN_DIR}
|
||||
COPY --from=runtime-assets --chown=node:node /app/skills ./skills
|
||||
COPY --from=runtime-assets --chown=node:node /app/docs ./docs
|
||||
|
||||
# In npm-installed Docker images, prefer the copied source extension tree for
|
||||
# bundled discovery so package metadata that points at source entries stays valid.
|
||||
ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/extensions
|
||||
ENV OPENCLAW_BUNDLED_PLUGINS_DIR=/app/${OPENCLAW_BUNDLED_PLUGIN_DIR}
|
||||
|
||||
# Keep pnpm available in the runtime image for container-local workflows.
|
||||
# Use a shared Corepack home so the non-root `node` user does not need a
|
||||
|
||||
@@ -14,6 +14,7 @@ RUN --mount=type=cache,id=openclaw-sandbox-bookworm-apt-cache,target=/var/cache/
|
||||
chromium \
|
||||
curl \
|
||||
fonts-liberation \
|
||||
fonts-noto-cjk \
|
||||
fonts-noto-color-emoji \
|
||||
git \
|
||||
jq \
|
||||
|
||||
217
appcast.xml
217
appcast.xml
@@ -2,6 +2,147 @@
|
||||
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
|
||||
<channel>
|
||||
<title>OpenClaw</title>
|
||||
<item>
|
||||
<title>2026.3.28</title>
|
||||
<pubDate>Sun, 29 Mar 2026 02:10:40 +0000</pubDate>
|
||||
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
|
||||
<sparkle:version>2026032890</sparkle:version>
|
||||
<sparkle:shortVersionString>2026.3.28</sparkle:shortVersionString>
|
||||
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
|
||||
<description><![CDATA[<h2>OpenClaw 2026.3.28</h2>
|
||||
<h3>Breaking</h3>
|
||||
<ul>
|
||||
<li>Providers/Qwen: remove the deprecated <code>qwen-portal-auth</code> OAuth integration for <code>portal.qwen.ai</code>; migrate to Model Studio with <code>openclaw onboard --auth-choice modelstudio-api-key</code>. (#52709) Thanks @pomelo-nwu.</li>
|
||||
<li>Config/Doctor: drop automatic config migrations older than two months; very old legacy keys now fail validation instead of being rewritten on load or by <code>openclaw doctor</code>.</li>
|
||||
</ul>
|
||||
<h3>Changes</h3>
|
||||
<ul>
|
||||
<li>xAI/tools: move the bundled xAI provider to the Responses API, add first-class <code>x_search</code>, and auto-enable the xAI plugin from owned web-search and tool config so bundled Grok auth/configured search flows work without manual plugin toggles. (#56048) Thanks @huntharo.</li>
|
||||
<li>xAI/onboarding: let the bundled Grok web-search plugin offer optional <code>x_search</code> setup during <code>openclaw onboard</code> and <code>openclaw configure --section web</code>, including an x_search model picker with the shared xAI key.</li>
|
||||
<li>MiniMax: add image generation provider for <code>image-01</code> model, supporting generate and image-to-image editing with aspect ratio control. (#54487) Thanks @liyuan97.</li>
|
||||
<li>Plugins/hooks: add async <code>requireApproval</code> to <code>before_tool_call</code> hooks, letting plugins pause tool execution and prompt the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the <code>/approve</code> command on any channel. The <code>/approve</code> command now handles both exec and plugin approvals with automatic fallback. (#55339) Thanks @vaclavbelak and @joshavant.</li>
|
||||
<li>ACP/channels: add current-conversation ACP binds for Discord, BlueBubbles, and iMessage so <code>/acp spawn codex --bind here</code> can turn the current chat into a Codex-backed workspace without creating a child thread, and document the distinction between chat surface, ACP session, and runtime workspace.</li>
|
||||
<li>OpenAI/apply_patch: enable <code>apply_patch</code> by default for OpenAI and OpenAI Codex models, and align its sandbox policy access with <code>write</code> permissions.</li>
|
||||
<li>Plugins/CLI backends: move bundled Claude CLI, Codex CLI, and Gemini CLI inference defaults onto the plugin surface, add bundled Gemini CLI backend support, and replace <code>gateway run --claude-cli-logs</code> with generic <code>--cli-backend-logs</code> while keeping the old flag as a compatibility alias.</li>
|
||||
<li>Plugins/startup: auto-load bundled provider and CLI-backend plugins from explicit config refs, so bundled Claude CLI, Codex CLI, and Gemini CLI message-provider setups no longer need manual <code>plugins.allow</code> entries.</li>
|
||||
<li>Podman: simplify the container setup around the current rootless user, install the launch helper under <code>~/.local/bin</code>, and document the host-CLI <code>openclaw --container <name> ...</code> workflow instead of a dedicated <code>openclaw</code> service user.</li>
|
||||
<li>Slack/tool actions: add an explicit <code>upload-file</code> Slack action that routes file uploads through the existing Slack upload transport, with optional filename/title/comment overrides for channels and DMs.</li>
|
||||
<li>Message actions/files: start unifying file-first sends on the canonical <code>upload-file</code> action by adding explicit support for Microsoft Teams and Google Chat, and by exposing BlueBubbles file sends through <code>upload-file</code> while keeping the legacy <code>sendAttachment</code> alias.</li>
|
||||
<li>Plugins/Matrix TTS: send auto-TTS replies as native Matrix voice bubbles instead of generic audio attachments. (#37080) thanks @Matthew19990919.</li>
|
||||
<li>CLI: add <code>openclaw config schema</code> to print the generated JSON schema for <code>openclaw.json</code>. (#54523) Thanks @kvokka.</li>
|
||||
<li>Config/TTS: auto-migrate legacy speech config on normal reads and secret resolution, keep legacy diagnostics for Doctor, and remove regular-mode runtime fallback for old bundled <code>tts.<provider></code> API-key shapes.</li>
|
||||
<li>Memory/plugins: move the pre-compaction memory flush plan behind the active memory plugin contract so <code>memory-core</code> owns flush prompts and target-path policy instead of hardcoded core logic.</li>
|
||||
<li>MiniMax: trim model catalog to M2.7 only, removing legacy M2, M2.1, M2.5, and VL-01 models. (#54487) Thanks @liyuan97.</li>
|
||||
<li>Plugins/runtime: expose <code>runHeartbeatOnce</code> in the plugin runtime <code>system</code> namespace so plugins can trigger a single heartbeat cycle with an explicit delivery target override (e.g. <code>heartbeat: { target: "last" }</code>). (#40299) Thanks @loveyana.</li>
|
||||
<li>Agents/compaction: preserve the post-compaction AGENTS refresh on stale-usage preflight compaction for both immediate replies and queued followups. (#49479) Thanks @jared596.</li>
|
||||
<li>Agents/compaction: surface safeguard-specific cancel reasons and relabel benign manual <code>/compact</code> no-op cases as skipped instead of failed. (#51072) Thanks @afurm.</li>
|
||||
<li>Docs: add <code>pnpm docs:check-links:anchors</code> for Mintlify anchor validation while keeping <code>scripts/docs-link-audit.mjs</code> as the stable link-audit entrypoint. (#55912) Thanks @velvet-shark.</li>
|
||||
<li>Tavily: mark outbound API requests with <code>X-Client-Source: openclaw</code> so Tavily can attribute OpenClaw-originated traffic. (#55335) Thanks @lakshyaag-tavily.</li>
|
||||
</ul>
|
||||
<h3>Fixes</h3>
|
||||
<ul>
|
||||
<li>Agents/Anthropic: recover unhandled provider stop reasons (e.g. <code>sensitive</code>) as structured assistant errors instead of crashing the agent run. (#56639)</li>
|
||||
<li>Google/models: resolve Gemini 3.1 pro, flash, and flash-lite for all Google provider aliases by passing the actual runtime provider ID and adding a template-provider fallback; fix flash-lite prefix ordering. (#56567)</li>
|
||||
<li>OpenAI Codex/image tools: register Codex for media understanding and route image prompts through Codex instructions so image analysis no longer fails on missing provider registration or missing <code>instructions</code>. (#54829) Thanks @neeravmakwana.</li>
|
||||
<li>Agents/image tool: restore the generic image-runtime fallback when no provider-specific media-understanding provider is registered, so image analysis works again for providers like <code>openrouter</code> and <code>minimax-portal</code>. (#54858) Thanks @MonkeyLeeT.</li>
|
||||
<li>WhatsApp: fix infinite echo loop in self-chat DM mode where the bot's own outbound replies were re-processed as new inbound user messages. (#54570) Thanks @joelnishanth</li>
|
||||
<li>Telegram/splitting: replace proportional text estimate with verified HTML-length search so long messages split at word boundaries instead of mid-word; gracefully degrade when tag overhead exceeds the limit. (#56595)</li>
|
||||
<li>Telegram/delivery: skip whitespace-only and hook-blanked text replies in bot delivery to prevent GrammyError 400 empty-text crashes. (#56620)</li>
|
||||
<li>Telegram/send: validate <code>replyToMessageId</code> at all four API sinks with a shared normalizer that rejects non-numeric, NaN, and mixed-content strings. (#56587)</li>
|
||||
<li>Mistral: normalize OpenAI-compatible request flags so official Mistral API runs no longer fail with remaining <code>422 status code (no body)</code> chat errors.</li>
|
||||
<li>Control UI/config: keep sensitive raw config hidden by default, replace the blank blocked editor with an explicit reveal-to-edit state, and restore raw JSON editing without auto-exposing secrets. Fixes #55322.</li>
|
||||
<li>CLI/zsh: defer <code>compdef</code> registration until <code>compinit</code> is available so zsh completion loads cleanly with plugin managers and manual setups. (#56555)</li>
|
||||
<li>BlueBubbles/debounce: guard debounce flush against null message text by sanitizing at the enqueue boundary and adding an independent combiner guard. (#56573)</li>
|
||||
<li>Auto-reply: suppress JSON-wrapped <code>{"action":"NO_REPLY"}</code> control envelopes before channel delivery with a strict single-key detector; preserves media when text is only a silent envelope. (#56612)</li>
|
||||
<li>ACP/ACPX agent registry: align OpenClaw's ACPX built-in agent mirror with the latest <code>openclaw/acpx</code> command defaults and built-in aliases, pin versioned <code>npx</code> built-ins to exact versions, and stop unknown ACP agent ids from falling through to raw <code>--agent</code> command execution on the MCP-proxy path. (#28321) Thanks @m0nkmaster and @vincentkoc.</li>
|
||||
<li>Security/audit: extend web search key audit to recognize Gemini, Grok/xAI, Kimi, Moonshot, and OpenRouter credentials via a boundary-safe bundled-web-search registry shim. (#56540)</li>
|
||||
<li>Docs/FAQ: remove broken Xfinity SSL troubleshooting cross-links from English and zh-CN FAQ entries — both sections already contain the full workaround inline. (#56500)</li>
|
||||
<li>Telegram: deliver verbose tool summaries inside forum topic sessions again, so threaded topic chats now match DM verbose behavior. (#43236) Thanks @frankbuild.</li>
|
||||
<li>BlueBubbles/CLI agents: restore inbound prompt image refs for CLI routed turns, reapply embedded runner image size guardrails, and cover both CLI image transport paths with regression tests. (#51373)</li>
|
||||
<li>BlueBubbles/groups: optionally enrich unnamed participant lists with local macOS Contacts names after group gating passes, so group member context can show names instead of only raw phone numbers.</li>
|
||||
<li>Discord/reconnect: drain stale gateway sockets, clear cached resume state before forced fresh reconnects, and fail closed when old sockets refuse to die so Discord recovery stops looping on poisoned resume state. (#54697) Thanks @ngutman.</li>
|
||||
<li>iMessage: stop leaking inline <code>[[reply_to:...]]</code> tags into delivered text by sending <code>reply_to</code> as RPC metadata and stripping stray directive tags from outbound messages. (#39512) Thanks @mvanhorn.</li>
|
||||
<li>CLI/plugins: make routed commands use the same auto-enabled bundled-channel snapshot as gateway startup, so configured bundled channels like Slack load without requiring a prior config rewrite. (#54809) Thanks @neeravmakwana.</li>
|
||||
<li>CLI/message send: write manual <code>openclaw message send</code> deliveries into the resolved agent session transcript again by always threading the default CLI agent through outbound mirroring. (#54187) Thanks @KevInTheCloud5617.</li>
|
||||
<li>CLI/onboarding: show the Kimi Code API key option again in the Moonshot setup menu so the interactive picker includes all Kimi setup paths together. Fixes #54412 Thanks @sparkyrider</li>
|
||||
<li>Agents/status: use provider-aware context window lookup for fresh Anthropic 4.6 model overrides so <code>/status</code> shows the correct 1.0m window instead of an underreported shared-cache minimum. (#54796) Thanks @neeravmakwana.</li>
|
||||
<li>OpenAI/WebSocket: preserve reasoning replay metadata and tool-call item ids on WebSocket tool turns, and start a fresh response chain when full-context resend is required. (#53856) Thanks @xujingchen1996.</li>
|
||||
<li>OpenAI/WS: restore reasoning blocks for Responses WebSocket runs and keep reasoning/tool-call replay metadata intact so resumed sessions do not lose or break follow-up reasoning-capable turns. (#53856) Thanks @xujingchen1996.</li>
|
||||
<li>Agents/errors: surface provider quota/reset details when available, but keep HTML/Cloudflare rate-limit pages on the generic fallback so raw error pages are not shown to users. (#54512) Thanks @bugkill3r.</li>
|
||||
<li>Claude CLI: switch the bundled Claude CLI backend to <code>stream-json</code> output so watchdogs see progress on long runs, and keep session/usage metadata even when Claude finishes with an empty result line. (#49698) Thanks @felear2022.</li>
|
||||
<li>Claude CLI/MCP: always pass a strict generated <code>--mcp-config</code> overlay for background Claude CLI runs, including the empty-server case, so Claude does not inherit ambient user/global MCP servers. (#54961) Thanks @markojak.</li>
|
||||
<li>Agents/embedded replies: surface mid-turn 429 and overload failures when embedded runs end without a user-visible reply, while preserving successful media-only replies that still use legacy <code>mediaUrl</code>. (#50930) Thanks @infichen.</li>
|
||||
<li>Chat/UI: move the chat send button onto the shared ghost-button theme styling, while keeping the stop button icon readable on the danger state. (#55075) Thanks @bottenbenny.</li>
|
||||
<li>WhatsApp/allowFrom: show a specific allowFrom policy error for valid blocked targets instead of the misleading <code><E.164|group JID></code> format hint. Thanks @mcaxtr.</li>
|
||||
<li>Agents/cooldowns: scope rate-limit cooldowns per model so one 429 no longer blocks every model on the same auth profile, replace the exponential 1 min -> 1 h escalation with a stepped 30 s / 1 min / 5 min ladder, and surface a user-facing countdown message when all models are rate-limited. (#49834) Thanks @kiranvk-2011.</li>
|
||||
<li>Agents/embedded transport errors: distinguish common network failures like connection refused, DNS lookup failure, and interrupted sockets from true timeouts in embedded-run user messaging and lifecycle diagnostics. (#51419) Thanks @scoootscooob.</li>
|
||||
<li>Telegram/pairing: ignore self-authored DM <code>message</code> updates so bot-pinned status cards and similar service updates do not trigger bogus pairing requests or re-enter inbound dispatch. (#54530) thanks @huntharo</li>
|
||||
<li>Mattermost/replies: keep pairing replies, slash-command fallback replies, and model-picker messages on the resolved config path so <code>exec:</code> SecretRef bot tokens work across all outbound reply branches. (#48347) thanks @mathiasnagler.</li>
|
||||
<li>Microsoft Teams/config: accept the existing <code>welcomeCard</code>, <code>groupWelcomeCard</code>, <code>promptStarters</code>, and feedback/reflection keys in strict config validation so already-supported Teams runtime settings stop failing schema checks. (#54679) Thanks @gumclaw.</li>
|
||||
<li>MCP/channels: add a Gateway-backed channel MCP bridge with Codex/Claude-facing conversation tools, Claude channel notifications, and safer stdio bridge lifecycle handling for reconnects and routed session discovery.</li>
|
||||
<li>Plugins/SDK: thread <code>moduleUrl</code> through plugin-sdk alias resolution so user-installed plugins outside the openclaw directory correctly resolve <code>openclaw/plugin-sdk/*</code> subpath imports, and gate <code>plugin-sdk:check-exports</code> in <code>release:check</code>. (#54283) Thanks @xieyongliang.</li>
|
||||
<li>Config/web fetch: allow the documented <code>tools.web.fetch.maxResponseBytes</code> setting in runtime schema validation so valid configs no longer fail with unrecognized-key errors. (#53401) Thanks @erhhung.</li>
|
||||
<li>Message tool/buttons: keep the shared <code>buttons</code> schema optional in merged tool definitions so plain <code>action=send</code> calls stop failing validation when no buttons are provided. (#54418) Thanks @adzendo.</li>
|
||||
<li>Agents/openai-compatible tool calls: deduplicate repeated tool call ids across live assistant messages and replayed history so OpenAI-compatible backends no longer reject duplicate <code>tool_call_id</code> values with HTTP 400. (#40996) Thanks @xaeon2026.</li>
|
||||
<li>Models/openai-completions: default non-native OpenAI-compatible providers to omit tool-definition <code>strict</code> fields unless users explicitly opt back in, so tool calling keeps working on providers that reject that option. (#45497) Thanks @sahancava.</li>
|
||||
<li>Plugins/context engines: retry strict legacy <code>assemble()</code> calls without the new <code>prompt</code> field when older engines reject it, preserving prompt-aware retrieval compatibility for pre-prompt plugins. (#50848) thanks @danhdoan.</li>
|
||||
<li>CLI/update status: explicitly say <code>up to date</code> when the local version already matches npm latest, while keeping the availability logic unchanged. (#51409) Thanks @dongzhenye.</li>
|
||||
<li>Daemon/Linux: stop flagging non-gateway systemd services as duplicate gateways just because their unit files mention OpenClaw, reducing false-positive doctor/log noise. (#45328) Thanks @gregretkowski.</li>
|
||||
<li>Feishu: close WebSocket connections on monitor stop/abort so ghost connections no longer persist, preventing duplicate event processing and resource leaks across restart cycles. (#52844) Thanks @schumilin.</li>
|
||||
<li>Feishu: use the original message <code>create_time</code> instead of <code>Date.now()</code> for inbound timestamps so offline-retried messages carry the correct authoring time, preventing mis-targeted agent actions on stale instructions. (#52809) Thanks @schumilin.</li>
|
||||
<li>Control UI/Skills: open skill detail dialogs with the browser modal lifecycle so clicking a skill row keeps the panel centered instead of rendering it off-screen at the bottom of the page.</li>
|
||||
<li>Matrix/replies: include quoted poll question/options in inbound reply context so the agent sees the original poll content when users reply to Matrix poll messages. (#55056) Thanks @alberthild.</li>
|
||||
<li>Matrix/plugins: keep plugin bootstrap from crashing when built runtime mixes bare and deep <code>matrix-js-sdk</code> entrypoints, so unrelated channels do not get taken down during plugin load. (#56273) Thanks @aquaright1.</li>
|
||||
<li>Agents/sandbox: honor <code>tools.sandbox.tools.alsoAllow</code>, let explicit sandbox re-allows remove matching built-in default-deny tools, and keep sandbox explain/error guidance aligned with the effective sandbox tool policy. (#54492) Thanks @ngutman.</li>
|
||||
<li>Agents/sandbox: make blocked-tool guidance glob-aware again, redact/sanitize session-specific explain hints for safer copy-paste, and avoid leaking control-character session keys in those hints. (#54684) Thanks @ngutman.</li>
|
||||
<li>Agents/compaction: trigger timeout recovery compaction before retrying high-context LLM timeouts so embedded runs stop repeating oversized requests. (#46417) thanks @joeykrug.</li>
|
||||
<li>Agents/compaction: reconcile <code>sessions.json.compactionCount</code> after a late embedded auto-compaction success so persisted session counts catch up once the handler reports completion. (#45493) Thanks @jackal092927.</li>
|
||||
<li>Agents/failover: classify Codex accountId token extraction failures as auth errors so model fallback continues to the next configured candidate. (#55206) Thanks @cosmicnet.</li>
|
||||
<li>Plugins/runtime: reuse only compatible active plugin registries across tools, providers, web search, and channel bootstrap, align <code>/tools/invoke</code> plugin loading with the session workspace, and retry outbound channel recovery when the pinned channel surface changes so plugin tools and channels stop disappearing or re-registering from mismatched runtime loads. Thanks @gumadeiras.</li>
|
||||
<li>Talk/macOS: stop direct system-voice failures from replaying system speech, use app-locale fallback for shared watchdog timing, and add regression coverage for the macOS fallback route and language-aware timeout policy. (#53511) thanks @hongsw.</li>
|
||||
<li>Discord/gateway cleanup: keep late Carbon reconnect-exhausted errors suppressed through startup/dispose cleanup so Discord monitor shutdown no longer crashes on late gateway close events. (#55373) Thanks @Takhoffman.</li>
|
||||
<li>Discord/gateway shutdown: treat expected reconnect-exhausted events during intentional lifecycle stop as clean shutdowns so startup-abort cleanup no longer surfaces false gateway failures. (#55324) Thanks @joelnishanth.</li>
|
||||
<li>Discord/gateway shutdown: suppress reconnect-exhausted events that were already buffered before teardown flips <code>lifecycleStopping</code>, so stale-socket Discord restarts no longer crash the whole gateway. Fixes #55403 and #55421. Thanks @lml2468 and @vincentkoc.</li>
|
||||
<li>GitHub Copilot/auth refresh: treat large <code>expires_at</code> values as seconds epochs and clamp far-future runtime auth refresh timers so Copilot token refresh cannot fall into a <code>setTimeout</code> overflow hot loop. (#55360) Thanks @michael-abdo.</li>
|
||||
<li>Agents/status: use the persisted runtime session model in <code>session_status</code> when no explicit override exists, and honor per-agent <code>thinkingDefault</code> in both <code>session_status</code> and <code>/status</code>. (#55425) Thanks @scoootscooob, @xaeon2026, and @ysfbsf.</li>
|
||||
<li>Heartbeat/runner: guarantee the interval timer is re-armed after heartbeat runs and unexpected runner errors so scheduled heartbeats do not silently stop after an interrupted cycle. (#52270) Thanks @MiloStack.</li>
|
||||
<li>Config/Doctor: rewrite stale bundled plugin load paths from legacy bundled-plugin locations to the packaged bundled path, including directory-name mismatches and slash-suffixed config entries. (#55054) Thanks @SnowSky1.</li>
|
||||
<li>WhatsApp/mentions: stop treating mentions embedded in quoted messages as direct mentions so replying to a message that @mentioned the bot no longer falsely triggers mention gating. (#52711) Thanks @lurebat.</li>
|
||||
<li>Matrix: keep separate 2-person rooms out of DM routing after <code>m.direct</code> seeds successfully, while still honoring explicit <code>is_direct</code> state and startup fallback recovery. (#54890) thanks @private-peter</li>
|
||||
<li>Agents/ollama fallback: surface non-2xx Ollama HTTP errors with a leading status code so HTTP 503 responses trigger model fallback again. (#55214) Thanks @bugkill3r.</li>
|
||||
<li>Feishu/tools: stop synthetic agent ids like <code>agent-spawner</code> from being treated as Feishu account ids during tool execution, so tools fall back to the configured/default Feishu account unless the contextual id is a real enabled Feishu account. (#55627) Thanks @MonkeyLeeT.</li>
|
||||
<li>Google/tools: strip empty <code>required: []</code> arrays from Gemini tool schemas so optional-only tool parameters no longer trigger Google validator 400s. (#52106) Thanks @oliviareid-svg.</li>
|
||||
<li>Onboarding/TUI/local gateways: show the resolved gateway port in setup output, clarify no-daemon local health/dashboard messaging, and preserve loopback Control UI auth on reruns and explicit local gateway URLs so local quickstart flows recover cleanly. (#55730) Thanks @shakkernerd.</li>
|
||||
<li>TUI/chat log: keep system messages as single logical entries and prune overflow at whole-message boundaries so wrapped system spacing stays intact. (#55732) Thanks @shakkernerd.</li>
|
||||
<li>TUI/activation: validate <code>/activation</code> arguments in the TUI and reject invalid values instead of silently coercing them to <code>mention</code>. (#55733) Thanks @shakkernerd.</li>
|
||||
<li>Agents/model switching: apply <code>/model</code> changes to active embedded runs at the next safe retry boundary, so overloaded or retrying turns switch to the newly selected model instead of staying pinned to the old provider.</li>
|
||||
<li>Agents/Codex fallback: classify Codex <code>server_error</code> payloads as failoverable, sanitize <code>Codex error:</code> payloads before they reach chat, preserve context-overflow guidance for prefixed <code>invalid_request_error</code> payloads, and omit provider <code>request_id</code> values from user-facing UI copy. (#42892) Thanks @xaeon2026.</li>
|
||||
<li>Memory/search: share memory embedding provider registrations across split plugin runtimes so memory search no longer fails with unknown provider errors after memory-core registers built-in adapters. (#55945) Thanks @glitch418x.</li>
|
||||
<li>Discord/Carbon beta: update <code>@buape/carbon</code> to the latest beta and pass the new <code>RateLimitError</code> request argument so Discord stays compatible with the upstream beta constructor change. (#55980) Thanks @ngutman.</li>
|
||||
<li>Plugins/inbound claims: pass full inbound attachment arrays through <code>inbound_claim</code> hook metadata while keeping the legacy singular media attachment fields for compatibility. (#55452) Thanks @huntharo.</li>
|
||||
<li>Plugins/Matrix: preserve sender filenames for inbound media by forwarding <code>originalFilename</code> to <code>saveMediaBuffer</code>. (#55692) thanks @esrehmki.</li>
|
||||
<li>Matrix/mentions: recognize <code>matrix.to</code> mentions whose visible label uses the bot's room display name, so <code>requireMention: true</code> rooms respond correctly in modern Matrix clients. (#55393) thanks @nickludlam.</li>
|
||||
<li>Ollama/thinking off: route <code>thinkingLevel=off</code> through the live Ollama extension request path so thinking-capable Ollama models now receive top-level <code>think: false</code> instead of silently generating hidden reasoning tokens. (#53200) Thanks @BruceMacD.</li>
|
||||
<li>Plugins/diffs: stage bundled <code>@pierre/diffs</code> runtime dependencies during packaged updates so the bundled diff viewer keeps loading after global installs and updates. (#56077) Thanks @gumadeiras.</li>
|
||||
<li>Plugins/diffs: load bundled Pierre themes without JSON module imports so diff rendering keeps working on newer Node builds. (#45869) thanks @NickHood1984.</li>
|
||||
<li>Plugins/uninstall: remove owned <code>channels.<id></code> config when uninstalling channel plugins, and keep the uninstall preview aligned with explicit channel ownership so built-in channels and shared keys stay intact. (#35915) Thanks @wbxl2000.</li>
|
||||
<li>Plugins/Matrix: prefer explicit DM signals when choosing outbound direct rooms and routing unmapped verification summaries, so strict 2-person fallback rooms do not outrank the real DM. (#56076) thanks @gumadeiras</li>
|
||||
<li>Plugins/Matrix: resolve env-backed <code>accessToken</code> and <code>password</code> SecretRefs against the active Matrix config env path during startup, and officially accept SecretRef <code>accessToken</code> config values. (#54980) thanks @kakahu2015.</li>
|
||||
<li>Microsoft Teams/proactive DMs: prefer the freshest personal conversation reference for <code>user:<aadObjectId></code> sends when multiple stored references exist, so replies stop targeting stale DM threads. (#54702) Thanks @gumclaw.</li>
|
||||
<li>Gateway/plugins: reuse the session workspace when building HTTP <code>/tools/invoke</code> tool lists and harden tool construction to infer the session agent workspace by default, so workspace plugins do not re-register on repeated HTTP tool calls. (#56101) thanks @neeravmakwana</li>
|
||||
<li>Brave/web search: normalize unsupported Brave <code>country</code> filters to <code>ALL</code> before request and cache-key generation so locale-derived values like <code>VN</code> stop failing with upstream 422 validation errors. (#55695) Thanks @chen-zhang-cs-code.</li>
|
||||
<li>Discord/replies: preserve leading indentation when stripping inline reply tags so reply-tagged plain text and fenced code blocks keep their formatting. (#55960) Thanks @Nanako0129.</li>
|
||||
<li>Daemon/status: surface immediate gateway close reasons from lightweight probes and prefer those concrete auth or pairing failures over generic timeouts in <code>openclaw daemon status</code>. (#56282) Thanks @mbelinky.</li>
|
||||
<li>Agents/failover: classify HTTP 410 errors as retryable timeouts by default while still preserving explicit session-expired, billing, and auth signals from the payload. (#55201) thanks @nikus-pan.</li>
|
||||
<li>Agents/subagents: restore completion announce delivery for extension channels like BlueBubbles. (#56348)</li>
|
||||
<li>Plugins/Matrix: load bundled <code>@matrix-org/matrix-sdk-crypto-nodejs</code> through <code>createRequire(...)</code> so E2EE media send and receive keep the package-local native binding lookup working in packaged ESM builds. (#54566) thanks @joelnishanth.</li>
|
||||
<li>Plugins/Matrix: encrypt E2EE image thumbnails with <code>thumbnail_file</code> while keeping unencrypted-room previews on <code>thumbnail_url</code>, so encrypted Matrix image events keep thumbnail metadata without leaking plaintext previews. (#54711) thanks @frischeDaten.</li>
|
||||
<li>Telegram/forum topics: keep native <code>/new</code> and <code>/reset</code> routed to the active topic by preserving the topic target on forum-thread command context. (#35963)</li>
|
||||
</ul>
|
||||
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
|
||||
]]></description>
|
||||
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.28/OpenClaw-2026.3.28.zip" length="25811288" type="application/octet-stream" sparkle:edSignature="SJp4ptVaGlOIXRPevS89DbfN2WKP0bKMXQoaT0fmLhy7pataDfHN0kxC3zu6P0Q/HtsxaESEhJUw48SCUNNKDA=="/>
|
||||
</item>
|
||||
<item>
|
||||
<title>2026.3.24</title>
|
||||
<pubDate>Wed, 25 Mar 2026 17:06:31 +0000</pubDate>
|
||||
@@ -95,81 +236,5 @@
|
||||
]]></description>
|
||||
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.23/OpenClaw-2026.3.23.zip" length="24522883" type="application/octet-stream" sparkle:edSignature="ptBgHYLBqq/TSdONYCfIB5d6aP/ij/9G0gYQ5mJI9jf8Y31sbQIh5CqpJVxEEWLTMIGQKsHQir/kXZjtRvvZAg=="/>
|
||||
</item>
|
||||
<item>
|
||||
<title>2026.3.13</title>
|
||||
<pubDate>Sat, 14 Mar 2026 05:19:48 +0000</pubDate>
|
||||
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
|
||||
<sparkle:version>2026031390</sparkle:version>
|
||||
<sparkle:shortVersionString>2026.3.13</sparkle:shortVersionString>
|
||||
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
|
||||
<description><![CDATA[<h2>OpenClaw 2026.3.13</h2>
|
||||
<h3>Changes</h3>
|
||||
<ul>
|
||||
<li>Android/chat settings: redesign the chat settings sheet with grouped device and media sections, refresh the Connect and Voice tabs, and tighten the chat composer/session header for a denser mobile layout. (#44894) Thanks @obviyus.</li>
|
||||
<li>iOS/onboarding: add a first-run welcome pager before gateway setup, stop auto-opening the QR scanner, and show <code>/pair qr</code> instructions on the connect step. (#45054) Thanks @ngutman.</li>
|
||||
<li>Browser/existing-session: add an official Chrome DevTools MCP attach mode for signed-in live Chrome sessions, with docs for <code>chrome://inspect/#remote-debugging</code> enablement and direct backlinks to Chrome’s own setup guides.</li>
|
||||
<li>Browser/agents: add built-in <code>profile="user"</code> for the logged-in host browser and <code>profile="chrome-relay"</code> for the extension relay, so agent browser calls can prefer the real signed-in browser without the extra <code>browserSession</code> selector.</li>
|
||||
<li>Browser/act automation: add batched actions, selector targeting, and delayed clicks for browser act requests with normalized batch dispatch. Thanks @vincentkoc.</li>
|
||||
<li>Docker/timezone override: add <code>OPENCLAW_TZ</code> so <code>docker-setup.sh</code> can pin gateway and CLI containers to a chosen IANA timezone instead of inheriting the daemon default. (#34119) Thanks @Lanfei.</li>
|
||||
<li>Dependencies/pi: bump <code>@mariozechner/pi-agent-core</code>, <code>@mariozechner/pi-ai</code>, <code>@mariozechner/pi-coding-agent</code>, and <code>@mariozechner/pi-tui</code> to <code>0.58.0</code>.</li>
|
||||
</ul>
|
||||
<h3>Fixes</h3>
|
||||
<ul>
|
||||
<li>Dashboard/chat UI: stop reloading full chat history on every live tool result in dashboard v2 so tool-heavy runs no longer trigger UI freeze/re-render storms while the final event still refreshes persisted history. (#45541) Thanks @BunsDev.</li>
|
||||
<li>Gateway/client requests: reject unanswered gateway RPC calls after a bounded timeout and clear their pending state, so stalled connections no longer leak hanging <code>GatewayClient.request()</code> promises indefinitely.</li>
|
||||
<li>Build/plugin-sdk bundling: bundle plugin-sdk subpath entries in one shared build pass so published packages stop duplicating shared chunks and avoid the recent plugin-sdk memory blow-up. (#45426) Thanks @TarasShyn.</li>
|
||||
<li>Ollama/reasoning visibility: stop promoting native <code>thinking</code> and <code>reasoning</code> fields into final assistant text so local reasoning models no longer leak internal thoughts in normal replies. (#45330) Thanks @xi7ang.</li>
|
||||
<li>Android/onboarding QR scan: switch setup QR scanning to Google Code Scanner so onboarding uses a more reliable scanner instead of the legacy embedded ZXing flow. (#45021) Thanks @obviyus.</li>
|
||||
<li>Browser/existing-session: harden driver validation and session lifecycle so transport errors trigger reconnects while tool-level errors preserve the session, and extract shared ARIA role sets to deduplicate Playwright and Chrome MCP snapshot paths. (#45682) Thanks @odysseus0.</li>
|
||||
<li>Browser/existing-session: accept text-only <code>list_pages</code> and <code>new_page</code> responses from Chrome DevTools MCP so live-session tab discovery and new-tab open flows keep working when the server omits structured page metadata.</li>
|
||||
<li>Control UI/insecure auth: preserve explicit shared token and password auth on plain-HTTP Control UI connects so LAN and reverse-proxy sessions no longer drop shared auth before the first WebSocket handshake. (#45088) Thanks @velvet-shark.</li>
|
||||
<li>Gateway/session reset: preserve <code>lastAccountId</code> and <code>lastThreadId</code> across gateway session resets so replies keep routing back to the same account and thread after <code>/reset</code>. (#44773) Thanks @Lanfei.</li>
|
||||
<li>macOS/onboarding: avoid self-restarting freshly bootstrapped launchd gateways and give new daemon installs longer to become healthy, so <code>openclaw onboard --install-daemon</code> no longer false-fails on slower Macs and fresh VM snapshots.</li>
|
||||
<li>Gateway/status: add <code>openclaw gateway status --require-rpc</code> and clearer Linux non-interactive daemon-install failure reporting so automation can fail hard on probe misses instead of treating a printed RPC error as green.</li>
|
||||
<li>macOS/exec approvals: respect per-agent exec approval settings in the gateway prompter, including allowlist fallback when the native prompt cannot be shown, so gateway-triggered <code>system.run</code> requests follow configured policy instead of always prompting or denying unexpectedly. (#13707) Thanks @sliekens.</li>
|
||||
<li>Telegram/media downloads: thread the same direct or proxy transport policy into SSRF-guarded file fetches so inbound attachments keep working when Telegram falls back between env-proxy and direct networking. (#44639) Thanks @obviyus.</li>
|
||||
<li>Telegram/inbound media IPv4 fallback: retry SSRF-guarded Telegram file downloads once with the same IPv4 fallback policy as Bot API calls so fresh installs on IPv6-broken hosts no longer fail to download inbound images.</li>
|
||||
<li>Windows/gateway install: bound <code>schtasks</code> calls and fall back to the Startup-folder login item when task creation hangs, so native <code>openclaw gateway install</code> fails fast instead of wedging forever on broken Scheduled Task setups.</li>
|
||||
<li>Windows/gateway stop: resolve Startup-folder fallback listeners from the installed <code>gateway.cmd</code> port, so <code>openclaw gateway stop</code> now actually kills fallback-launched gateway processes before restart.</li>
|
||||
<li>Windows/gateway status: reuse the installed service command environment when reading runtime status, so startup-fallback gateways keep reporting the configured port and running state in <code>gateway status --json</code> instead of falling back to <code>gateway port unknown</code>.</li>
|
||||
<li>Windows/gateway auth: stop attaching device identity on local loopback shared-token and password gateway calls, so native Windows agent replies no longer log stale <code>device signature expired</code> fallback noise before succeeding.</li>
|
||||
<li>Discord/gateway startup: treat plain-text and transient <code>/gateway/bot</code> metadata fetch failures as transient startup errors so Discord gateway boot no longer crashes on unhandled rejections. (#44397) Thanks @jalehman.</li>
|
||||
<li>Slack/probe: keep <code>auth.test()</code> bot and team metadata mapping stable while simplifying the probe result path. (#44775) Thanks @Cafexss.</li>
|
||||
<li>Dashboard/chat UI: render oversized plain-text replies as normal paragraphs instead of capped gray code blocks, so long desktop chat responses stay readable without tab-switching refreshes.</li>
|
||||
<li>Dashboard/chat UI: restore the <code>chat-new-messages</code> class on the New messages scroll pill so the button uses its existing compact styling instead of rendering as a full-screen SVG overlay. (#44856) Thanks @Astro-Han.</li>
|
||||
<li>Gateway/Control UI: restore the operator-only device-auth bypass and classify browser connect failures so origin and device-identity problems no longer show up as auth errors in the Control UI and web chat. (#45512) thanks @sallyom.</li>
|
||||
<li>macOS/voice wake: stop crashing wake-word command extraction when speech segment ranges come from a different transcript instance.</li>
|
||||
<li>Discord/allowlists: honor raw <code>guild_id</code> when hydrated guild objects are missing so allowlisted channels and threads like <code>#maintainers</code> no longer get false-dropped before channel allowlist checks.</li>
|
||||
<li>macOS/runtime locator: require Node >=22.16.0 during macOS runtime discovery so the app no longer accepts Node versions that the main runtime guard rejects later. Thanks @sumleo.</li>
|
||||
<li>Agents/custom providers: preserve blank API keys for loopback OpenAI-compatible custom providers by clearing the synthetic Authorization header at runtime, while keeping explicit apiKey and oauth/token config from silently downgrading into fake bearer auth. (#45631) Thanks @xinhuagu.</li>
|
||||
<li>Models/google-vertex Gemini flash-lite normalization: apply existing bare-ID preview normalization to <code>google-vertex</code> model refs and provider configs so <code>google-vertex/gemini-3.1-flash-lite</code> resolves as <code>gemini-3.1-flash-lite-preview</code>. (#42435) thanks @scoootscooob.</li>
|
||||
<li>iMessage/remote attachments: reject unsafe remote attachment paths before spawning SCP, so sender-controlled filenames can no longer inject shell metacharacters into remote media staging. Thanks @lintsinghua.</li>
|
||||
<li>Telegram/webhook auth: validate the Telegram webhook secret before reading or parsing request bodies, so unauthenticated requests are rejected immediately instead of consuming up to 1 MB first. Thanks @space08.</li>
|
||||
<li>Security/device pairing: make bootstrap setup codes single-use so pending device pairing requests cannot be silently replayed and widened to admin before approval. Thanks @tdjackey.</li>
|
||||
<li>Security/external content: strip zero-width and soft-hyphen marker-splitting characters during boundary sanitization so spoofed <code>EXTERNAL_UNTRUSTED_CONTENT</code> markers fall back to the existing hardening path instead of bypassing marker normalization.</li>
|
||||
<li>Security/exec approvals: unwrap more <code>pnpm</code> runtime forms during approval binding, including <code>pnpm --reporter ... exec</code> and direct <code>pnpm node</code> file runs, with matching regression coverage and docs updates.</li>
|
||||
<li>Security/exec approvals: fail closed for Perl <code>-M</code> and <code>-I</code> approval flows so preload and load-path module resolution stays outside approval-backed runtime execution unless the operator uses a broader explicit trust path.</li>
|
||||
<li>Security/exec approvals: recognize PowerShell <code>-File</code> and <code>-f</code> wrapper forms during inline-command extraction so approval and command-analysis paths treat file-based PowerShell launches like the existing <code>-Command</code> variants.</li>
|
||||
<li>Security/exec approvals: unwrap <code>env</code> dispatch wrappers inside shell-segment allowlist resolution on macOS so <code>env FOO=bar /path/to/bin</code> resolves against the effective executable instead of the wrapper token.</li>
|
||||
<li>Security/exec approvals: treat backslash-newline as shell line continuation during macOS shell-chain parsing so line-continued <code>$(</code> substitutions fail closed instead of slipping past command-substitution checks.</li>
|
||||
<li>Security/exec approvals: bind macOS skill auto-allow trust to both executable name and resolved path so same-basename binaries no longer inherit trust from unrelated skill bins.</li>
|
||||
<li>Build/plugin-sdk bundling: bundle plugin-sdk subpath entries in one shared build pass so published packages stop duplicating shared chunks and avoid the recent plugin-sdk memory blow-up. (#45426) Thanks @TarasShyn.</li>
|
||||
<li>Cron/isolated sessions: route nested cron-triggered embedded runner work onto the nested lane so isolated cron jobs no longer deadlock when compaction or other queued inner work runs. Thanks @vincentkoc.</li>
|
||||
<li>Agents/OpenAI-compatible compat overrides: respect explicit user <code>models[].compat</code> opt-ins for non-native <code>openai-completions</code> endpoints so usage-in-streaming capability overrides no longer get forced off when the endpoint actually supports them. (#44432) Thanks @cheapestinference.</li>
|
||||
<li>Agents/Azure OpenAI startup prompts: rephrase the built-in <code>/new</code>, <code>/reset</code>, and post-compaction startup instruction so Azure OpenAI deployments no longer hit HTTP 400 false positives from the content filter. (#43403) Thanks @xingsy97.</li>
|
||||
<li>Agents/memory bootstrap: load only one root memory file, preferring <code>MEMORY.md</code> and using <code>memory.md</code> as a fallback, so case-insensitive Docker mounts no longer inject duplicate memory context. (#26054) Thanks @Lanfei.</li>
|
||||
<li>Agents/compaction: compare post-compaction token sanity checks against full-session pre-compaction totals and skip the check when token estimation fails, so sessions with large bootstrap context keep real token counts instead of falling back to unknown. (#28347) thanks @efe-arv.</li>
|
||||
<li>Agents/compaction: preserve safeguard compaction summary language continuity via default and configurable custom instructions so persona drift is reduced after auto-compaction. (#10456) Thanks @keepitmello.</li>
|
||||
<li>Agents/tool warnings: distinguish gated core tools like <code>apply_patch</code> from plugin-only unknown entries in <code>tools.profile</code> warnings, so unavailable core tools now report current runtime/provider/model/config gating instead of suggesting a missing plugin.</li>
|
||||
<li>Config/validation: accept documented <code>agents.list[].params</code> per-agent overrides in strict config validation so <code>openclaw config validate</code> no longer rejects runtime-supported <code>cacheRetention</code>, <code>temperature</code>, and <code>maxTokens</code> settings. (#41171) Thanks @atian8179.</li>
|
||||
<li>Config/web fetch: restore runtime validation for documented <code>tools.web.fetch.readability</code> and <code>tools.web.fetch.firecrawl</code> settings so valid web fetch configs no longer fail with unrecognized-key errors. (#42583) Thanks @stim64045-spec.</li>
|
||||
<li>Signal/config validation: add <code>channels.signal.groups</code> schema support so per-group <code>requireMention</code>, <code>tools</code>, and <code>toolsBySender</code> overrides no longer get rejected during config validation. (#27199) Thanks @unisone.</li>
|
||||
<li>Config/discovery: accept <code>discovery.wideArea.domain</code> in strict config validation so unicast DNS-SD gateway configs no longer fail with an unrecognized-key error. (#35615) Thanks @ingyukoh.</li>
|
||||
<li>Telegram/media errors: redact Telegram file URLs before building media fetch errors so failed inbound downloads do not leak bot tokens into logs. Thanks @space08.</li>
|
||||
</ul>
|
||||
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
|
||||
]]></description>
|
||||
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.3.13/OpenClaw-2026.3.13.zip" length="23640917" type="application/octet-stream" sparkle:edSignature="Me63UHSpFLocTo5Lt7Iqsl0Hq61y3jTcZ9DUkiFl9xQvTE0+ORuqRMFWqPgYwfaKMgcgQmUbrV/uFzEoTIRHBA=="/>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026032800
|
||||
versionName = "2026.3.28"
|
||||
versionCode = 2026032900
|
||||
versionName = "2026.3.29"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Shared iOS version defaults.
|
||||
// Generated overrides live in build/Version.xcconfig (git-ignored).
|
||||
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.28
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.28
|
||||
OPENCLAW_BUILD_VERSION = 2026032800
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.29
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.29
|
||||
OPENCLAW_BUILD_VERSION = 2026032900
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -65,9 +65,9 @@ Release behavior:
|
||||
- Beta release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, and `OpenClawPushAPNsEnvironment=production`.
|
||||
- The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
|
||||
- Root `package.json.version` is the only version source for iOS.
|
||||
- A root version like `2026.3.28-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.28`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.28`
|
||||
- A root version like `2026.3.29-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.29`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.29`
|
||||
|
||||
Required env for beta builds:
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.3.28</string>
|
||||
<string>2026.3.29</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026032800</string>
|
||||
<string>2026032900</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -2235,21 +2235,29 @@ public struct AgentSummary: Codable, Sendable {
|
||||
public let id: String
|
||||
public let name: String?
|
||||
public let identity: [String: AnyCodable]?
|
||||
public let workspace: String?
|
||||
public let model: [String: AnyCodable]?
|
||||
|
||||
public init(
|
||||
id: String,
|
||||
name: String?,
|
||||
identity: [String: AnyCodable]?)
|
||||
identity: [String: AnyCodable]?,
|
||||
workspace: String?,
|
||||
model: [String: AnyCodable]?)
|
||||
{
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.identity = identity
|
||||
self.workspace = workspace
|
||||
self.model = model
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
case identity
|
||||
case workspace
|
||||
case model
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2235,21 +2235,29 @@ public struct AgentSummary: Codable, Sendable {
|
||||
public let id: String
|
||||
public let name: String?
|
||||
public let identity: [String: AnyCodable]?
|
||||
public let workspace: String?
|
||||
public let model: [String: AnyCodable]?
|
||||
|
||||
public init(
|
||||
id: String,
|
||||
name: String?,
|
||||
identity: [String: AnyCodable]?)
|
||||
identity: [String: AnyCodable]?,
|
||||
workspace: String?,
|
||||
model: [String: AnyCodable]?)
|
||||
{
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.identity = identity
|
||||
self.workspace = workspace
|
||||
self.model = model
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case id
|
||||
case name
|
||||
case identity
|
||||
case workspace
|
||||
case model
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2608,6 +2608,26 @@
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "agents.defaults.memorySearch.store.fts",
|
||||
"kind": "core",
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": true
|
||||
},
|
||||
{
|
||||
"path": "agents.defaults.memorySearch.store.fts.tokenizer",
|
||||
"kind": "core",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "agents.defaults.memorySearch.store.path",
|
||||
"kind": "core",
|
||||
@@ -5028,6 +5048,26 @@
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "agents.list.*.memorySearch.store.fts",
|
||||
"kind": "core",
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": true
|
||||
},
|
||||
{
|
||||
"path": "agents.list.*.memorySearch.store.fts.tokenizer",
|
||||
"kind": "core",
|
||||
"type": "string",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "agents.list.*.memorySearch.store.path",
|
||||
"kind": "core",
|
||||
@@ -21273,6 +21313,66 @@
|
||||
],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings",
|
||||
"kind": "channel",
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": true
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings.enabled",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings.idleHours",
|
||||
"kind": "channel",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings.maxAgeHours",
|
||||
"kind": "channel",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings.spawnAcpSessions",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.threadBindings.spawnSubagentSessions",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.accounts.*.tokenFile",
|
||||
"kind": "channel",
|
||||
@@ -21562,6 +21662,66 @@
|
||||
],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings",
|
||||
"kind": "channel",
|
||||
"type": "object",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": true
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings.enabled",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings.idleHours",
|
||||
"kind": "channel",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings.maxAgeHours",
|
||||
"kind": "channel",
|
||||
"type": "number",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings.spawnAcpSessions",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.threadBindings.spawnSubagentSessions",
|
||||
"kind": "channel",
|
||||
"type": "boolean",
|
||||
"required": false,
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.line.tokenFile",
|
||||
"kind": "channel",
|
||||
@@ -22583,6 +22743,23 @@
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.matrix.streaming",
|
||||
"kind": "channel",
|
||||
"type": [
|
||||
"boolean",
|
||||
"string"
|
||||
],
|
||||
"required": false,
|
||||
"enumValues": [
|
||||
"partial",
|
||||
"off"
|
||||
],
|
||||
"deprecated": false,
|
||||
"sensitive": false,
|
||||
"tags": [],
|
||||
"hasChildren": false
|
||||
},
|
||||
{
|
||||
"path": "channels.matrix.textChunkLimit",
|
||||
"kind": "channel",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5576}
|
||||
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5593}
|
||||
{"recordType":"path","path":"acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"ACP","help":"ACP runtime controls for enabling dispatch, selecting backends, constraining allowed agent targets, and tuning streamed turn projection behavior.","hasChildren":true}
|
||||
{"recordType":"path","path":"acp.allowedAgents","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"ACP Allowed Agents","help":"Allowlist of ACP target agent ids permitted for ACP runtime sessions. Empty means no additional allowlist restriction.","hasChildren":true}
|
||||
{"recordType":"path","path":"acp.allowedAgents.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -217,6 +217,8 @@
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.sources.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.fts","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.fts.tokenizer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.path","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Memory Search Index Path","help":"Sets where the SQLite memory index is stored on disk for each agent. Keep the default `~/.openclaw/memory/{agentId}.sqlite` unless you need custom storage placement or backup policy alignment.","hasChildren":false}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.vector","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.defaults.memorySearch.store.vector.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Memory Search Vector Index","help":"Enables the sqlite-vec extension used for vector similarity queries in memory search (default: true). Keep this enabled for normal semantic recall; disable only for debugging or fallback-only operation.","hasChildren":false}
|
||||
@@ -443,6 +445,8 @@
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.sources.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.driver","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.fts","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.fts.tokenizer","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.path","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.vector","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"agents.list.*.memorySearch.store.vector.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
@@ -1893,6 +1897,12 @@
|
||||
{"recordType":"path","path":"channels.line.accounts.*.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.secretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.idleHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.maxAgeHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.spawnAcpSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.threadBindings.spawnSubagentSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.accounts.*.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
@@ -1918,6 +1928,12 @@
|
||||
{"recordType":"path","path":"channels.line.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.secretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.line.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.threadBindings.idleHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.threadBindings.maxAgeHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.threadBindings.spawnAcpSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.threadBindings.spawnSubagentSessions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.line.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Matrix","help":"open protocol; install the plugin to enable.","hasChildren":true}
|
||||
@@ -2011,6 +2027,7 @@
|
||||
{"recordType":"path","path":"channels.matrix.rooms.*.users.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.startupVerification","kind":"channel","type":"string","required":false,"enumValues":["off","if-unverified"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.startupVerificationCooldownHours","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.streaming","kind":"channel","type":["boolean","string"],"required":false,"enumValues":["partial","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.textChunkLimit","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
{"recordType":"path","path":"channels.matrix.threadBindings","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
|
||||
{"recordType":"path","path":"channels.matrix.threadBindings.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Group chat behavior across surfaces (WhatsApp/Telegram/Discord/Slack/Signal/iMessage/Microsoft Teams/Zalo)"
|
||||
summary: "Group chat behavior across surfaces (Discord/iMessage/Matrix/Microsoft Teams/Signal/Slack/Telegram/WhatsApp/Zalo)"
|
||||
read_when:
|
||||
- Changing group chat behavior or mention gating
|
||||
title: "Groups"
|
||||
@@ -7,7 +7,7 @@ title: "Groups"
|
||||
|
||||
# Groups
|
||||
|
||||
OpenClaw treats group chats consistently across surfaces: WhatsApp, Telegram, Discord, Slack, Signal, iMessage, Microsoft Teams, Zalo.
|
||||
OpenClaw treats group chats consistently across surfaces: Discord, iMessage, Matrix, Microsoft Teams, Signal, Slack, Telegram, WhatsApp, Zalo.
|
||||
|
||||
## Beginner intro (2 minutes)
|
||||
|
||||
@@ -187,7 +187,7 @@ Notes:
|
||||
- DM pairing approvals (`*-allowFrom` store entries) apply to DM access only; group sender authorization stays explicit to group allowlists.
|
||||
- Discord: allowlist uses `channels.discord.guilds.<id>.channels`.
|
||||
- Slack: allowlist uses `channels.slack.channels`.
|
||||
- Matrix: allowlist uses `channels.matrix.groups` (room IDs, aliases, or names). Use `channels.matrix.groupAllowFrom` to restrict senders; per-room `users` allowlists are also supported.
|
||||
- Matrix: allowlist uses `channels.matrix.groups`. Prefer room IDs or aliases; joined-room name lookup is best-effort, and unresolved names are ignored at runtime. Use `channels.matrix.groupAllowFrom` to restrict senders; per-room `users` allowlists are also supported.
|
||||
- Group DMs are controlled separately (`channels.discord.dm.*`, `channels.slack.dm.*`).
|
||||
- Telegram allowlist can match user IDs (`"123456789"`, `"telegram:123456789"`, `"tg:123456789"`) or usernames (`"@alice"` or `"alice"`); prefixes are case-insensitive.
|
||||
- Default is `groupPolicy: "allowlist"`; if your group allowlist is empty, group messages are blocked.
|
||||
|
||||
@@ -28,7 +28,7 @@ openclaw plugins install @openclaw/line
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/line
|
||||
openclaw plugins install ./path/to/local/line-plugin
|
||||
```
|
||||
|
||||
## Setup
|
||||
@@ -184,6 +184,25 @@ The LINE plugin also ships a `/card` command for Flex message presets:
|
||||
/card info "Welcome" "Thanks for joining!"
|
||||
```
|
||||
|
||||
## ACP support
|
||||
|
||||
LINE supports ACP (Agent Communication Protocol) conversation bindings:
|
||||
|
||||
- `/acp spawn <agent> --bind here` binds the current LINE chat to an ACP session without creating a child thread.
|
||||
- Configured ACP bindings and active conversation-bound ACP sessions work on LINE like other conversation channels.
|
||||
|
||||
See [ACP agents](/tools/acp-agents) for details.
|
||||
|
||||
## Outbound media
|
||||
|
||||
The LINE plugin supports sending images, videos, and audio files through the agent message tool. Media is sent via the LINE-specific delivery path with appropriate preview and tracking handling:
|
||||
|
||||
- **Images**: sent as LINE image messages with automatic preview generation.
|
||||
- **Videos**: sent with explicit preview and content-type handling.
|
||||
- **Audio**: sent as LINE audio messages.
|
||||
|
||||
Generic media sends fall back to the existing image-only route when a LINE-specific path is not available.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Webhook verification fails:** ensure the webhook URL is HTTPS and the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "Inbound channel location parsing (Telegram + WhatsApp) and context fields"
|
||||
summary: "Inbound channel location parsing (Telegram/WhatsApp/Matrix) and context fields"
|
||||
read_when:
|
||||
- Adding or modifying channel location parsing
|
||||
- Using location context fields in agent prompts or tools
|
||||
|
||||
@@ -24,7 +24,7 @@ openclaw plugins install @openclaw/matrix
|
||||
Install from a local checkout:
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/matrix
|
||||
openclaw plugins install ./path/to/local/matrix-plugin
|
||||
```
|
||||
|
||||
See [Plugins](/tools/plugin) for plugin behavior and install rules.
|
||||
@@ -157,14 +157,41 @@ This is a practical baseline config with DM pairing, room allowlist, and E2EE en
|
||||
autoJoinAllowlist: ["!roomid:example.org"],
|
||||
threadReplies: "inbound",
|
||||
replyToMode: "off",
|
||||
streaming: "partial",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## E2EE setup
|
||||
## Streaming previews
|
||||
|
||||
## Bot to bot rooms
|
||||
Matrix reply streaming is opt-in.
|
||||
|
||||
Set `channels.matrix.streaming` to `"partial"` when you want OpenClaw to send a single draft reply,
|
||||
edit that draft in place while the model is generating text, and then finalize it when the reply is
|
||||
done:
|
||||
|
||||
```json5
|
||||
{
|
||||
channels: {
|
||||
matrix: {
|
||||
streaming: "partial",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `streaming: "off"` is the default. OpenClaw waits for the final reply and sends it once.
|
||||
- `streaming: "partial"` creates one editable preview message instead of sending multiple partial messages.
|
||||
- If the preview no longer fits in one Matrix event, OpenClaw stops preview streaming and falls back to normal final delivery.
|
||||
- Media replies still send attachments normally. If a stale preview can no longer be reused safely, OpenClaw redacts it before sending the final media reply.
|
||||
- Preview edits cost extra Matrix API calls. Leave streaming off if you want the most conservative rate-limit behavior.
|
||||
|
||||
## Encryption and verification
|
||||
|
||||
In encrypted (E2EE) rooms, outbound image events use `thumbnail_file` so image previews are encrypted alongside the full attachment. Unencrypted rooms still use plain `thumbnail_url`. No configuration is needed — the plugin detects E2EE state automatically.
|
||||
|
||||
### Bot to bot rooms
|
||||
|
||||
By default, Matrix messages from other configured OpenClaw Matrix accounts are ignored.
|
||||
|
||||
@@ -401,6 +428,19 @@ Planned improvement:
|
||||
|
||||
- add SecretRef support for persistent Matrix key material so recovery keys and related store-encryption secrets can be sourced from OpenClaw secrets providers instead of only local files
|
||||
|
||||
## Profile management
|
||||
|
||||
Update the Matrix self-profile for the selected account with:
|
||||
|
||||
```bash
|
||||
openclaw matrix profile set --name "OpenClaw Assistant"
|
||||
openclaw matrix profile set --avatar-url https://cdn.example.org/avatar.png
|
||||
```
|
||||
|
||||
Add `--account <id>` when you want to target a named Matrix account explicitly.
|
||||
|
||||
Matrix accepts `mxc://` avatar URLs directly. When you pass an `http://` or `https://` avatar URL, OpenClaw uploads it to Matrix first and stores the resolved `mxc://` URL back into `channels.matrix.avatarUrl` (or the selected account override).
|
||||
|
||||
## Automatic verification notices
|
||||
|
||||
Matrix now posts verification lifecycle notices directly into the strict DM verification room as `m.notice` messages.
|
||||
@@ -673,6 +713,7 @@ Live directory lookup uses the logged-in Matrix account:
|
||||
- `groupAllowFrom`: allowlist of user IDs for room traffic.
|
||||
- `groupAllowFrom` entries should be full Matrix user IDs. Unresolved names are ignored at runtime.
|
||||
- `replyToMode`: `off`, `first`, or `all`.
|
||||
- `streaming`: `off` (default) or `partial`. `partial` enables single-message draft previews with edit-in-place updates.
|
||||
- `threadReplies`: `off`, `inbound`, or `always`.
|
||||
- `threadBindings`: per-channel overrides for thread-bound session routing and lifecycle.
|
||||
- `startupVerification`: automatic self-verification request mode on startup (`if-unverified`, `off`).
|
||||
@@ -683,7 +724,7 @@ Live directory lookup uses the logged-in Matrix account:
|
||||
- `ackReaction`: optional ack reaction override for this channel/account.
|
||||
- `ackReactionScope`: optional ack reaction scope override (`group-mentions`, `group-all`, `direct`, `all`, `none`, `off`).
|
||||
- `reactionNotifications`: inbound reaction notification mode (`own`, `off`).
|
||||
- `mediaMaxMb`: outbound media size cap in MB.
|
||||
- `mediaMaxMb`: media size cap in MB for Matrix media handling. It applies to outbound sends and inbound media processing.
|
||||
- `autoJoin`: invite auto-join policy (`always`, `allowlist`, `off`). Default: `off`.
|
||||
- `autoJoinAllowlist`: rooms/aliases allowed when `autoJoin` is `allowlist`. Alias entries are resolved to room IDs during invite handling; OpenClaw does not trust alias state claimed by the invited room.
|
||||
- `dm`: DM policy block (`enabled`, `policy`, `allowFrom`).
|
||||
|
||||
@@ -25,7 +25,7 @@ openclaw plugins install @openclaw/mattermost
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/mattermost
|
||||
openclaw plugins install ./path/to/local/mattermost-plugin
|
||||
```
|
||||
|
||||
If you choose Mattermost during setup and a git checkout is detected,
|
||||
|
||||
@@ -30,7 +30,7 @@ openclaw plugins install @openclaw/msteams
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/msteams
|
||||
openclaw plugins install ./path/to/local/msteams-plugin
|
||||
```
|
||||
|
||||
If you choose Teams during setup and a git checkout is detected,
|
||||
@@ -242,7 +242,7 @@ This is often easier than hand-editing JSON manifests.
|
||||
|
||||
1. **Install the Microsoft Teams plugin**
|
||||
- From npm: `openclaw plugins install @openclaw/msteams`
|
||||
- From a local checkout: `openclaw plugins install ./extensions/msteams`
|
||||
- From a local checkout: `openclaw plugins install ./path/to/local/msteams-plugin`
|
||||
|
||||
2. **Bot registration**
|
||||
- Create an Azure Bot (see above) and note:
|
||||
|
||||
@@ -22,7 +22,7 @@ openclaw plugins install @openclaw/nextcloud-talk
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/nextcloud-talk
|
||||
openclaw plugins install ./path/to/local/nextcloud-talk-plugin
|
||||
```
|
||||
|
||||
If you choose Nextcloud Talk during setup and a git checkout is detected,
|
||||
|
||||
@@ -35,7 +35,7 @@ openclaw plugins install @openclaw/nostr
|
||||
Use a local checkout (dev workflows):
|
||||
|
||||
```bash
|
||||
openclaw plugins install --link <path-to-openclaw>/extensions/nostr
|
||||
openclaw plugins install --link <path-to-local-nostr-plugin>
|
||||
```
|
||||
|
||||
Restart the Gateway after installing or enabling plugins.
|
||||
|
||||
@@ -19,7 +19,7 @@ Synology Chat is plugin-based and not part of the default core channel install.
|
||||
Install from a local checkout:
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/synology-chat
|
||||
openclaw plugins install ./path/to/local/synology-chat-plugin
|
||||
```
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
@@ -27,7 +27,7 @@ openclaw plugins install @openclaw/tlon
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/tlon
|
||||
openclaw plugins install ./path/to/local/tlon-plugin
|
||||
```
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
@@ -22,7 +22,7 @@ openclaw plugins install @openclaw/twitch
|
||||
Local checkout (when running from a git repo):
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/twitch
|
||||
openclaw plugins install ./path/to/local/twitch-plugin
|
||||
```
|
||||
|
||||
Details: [Plugins](/tools/plugin)
|
||||
|
||||
@@ -20,7 +20,7 @@ Zalo ships as a plugin and is not bundled with the core install.
|
||||
## Quick setup (beginner)
|
||||
|
||||
1. Install the Zalo plugin:
|
||||
- From a source checkout: `openclaw plugins install ./extensions/zalo`
|
||||
- From a source checkout: `openclaw plugins install ./path/to/local/zalo-plugin`
|
||||
- From npm (if published): `openclaw plugins install @openclaw/zalo`
|
||||
- Or pick **Zalo** in setup and confirm the install prompt
|
||||
2. Set the token:
|
||||
|
||||
@@ -17,7 +17,7 @@ Status: experimental. This integration automates a **personal Zalo account** via
|
||||
Zalo Personal ships as a plugin and is not bundled with the core install.
|
||||
|
||||
- Install via CLI: `openclaw plugins install @openclaw/zalouser`
|
||||
- Or from a source checkout: `openclaw plugins install ./extensions/zalouser`
|
||||
- Or from a source checkout: `openclaw plugins install ./path/to/local/zalouser-plugin`
|
||||
- Details: [Plugins](/tools/plugin)
|
||||
|
||||
No external `zca`/`openzca` CLI binary is required.
|
||||
|
||||
@@ -32,6 +32,27 @@ openclaw browser --browser-profile openclaw open https://example.com
|
||||
openclaw browser --browser-profile openclaw snapshot
|
||||
```
|
||||
|
||||
## If the command is missing
|
||||
|
||||
If `openclaw browser` is an unknown command, check `plugins.allow` in
|
||||
`~/.openclaw/openclaw.json`.
|
||||
|
||||
When `plugins.allow` is present, the bundled browser plugin must be listed
|
||||
explicitly:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
allow: ["telegram", "browser"],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
`browser.enabled=true` does not restore the CLI subcommand when the plugin
|
||||
allowlist excludes `browser`.
|
||||
|
||||
Related: [Browser tool](/tools/browser#missing-browser-command-or-tool)
|
||||
|
||||
## Profiles
|
||||
|
||||
Profiles are named browser routing configs. In practice:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
summary: "CLI reference for `openclaw channels` (accounts, status, login/logout, logs)"
|
||||
read_when:
|
||||
- You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage)
|
||||
- You want to add/remove channel accounts (WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Matrix)
|
||||
- You want to check channel status or tail channel logs
|
||||
title: "channels"
|
||||
---
|
||||
|
||||
@@ -9,7 +9,7 @@ title: "message"
|
||||
# `openclaw message`
|
||||
|
||||
Single outbound command for sending messages and channel actions
|
||||
(Discord/Google Chat/Slack/Mattermost (plugin)/Telegram/WhatsApp/Signal/iMessage/Microsoft Teams).
|
||||
(Discord/Google Chat/iMessage/Matrix/Mattermost (plugin)/Microsoft Teams/Signal/Slack/Telegram/WhatsApp).
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -21,7 +21,7 @@ Channel selection:
|
||||
|
||||
- `--channel` required if more than one channel is configured.
|
||||
- If exactly one channel is configured, it becomes the default.
|
||||
- Values: `whatsapp|telegram|discord|googlechat|slack|mattermost|signal|imessage|msteams` (Mattermost requires plugin)
|
||||
- Values: `discord|googlechat|imessage|matrix|mattermost|msteams|signal|slack|telegram|whatsapp` (Mattermost requires plugin)
|
||||
|
||||
Target formats (`--target`):
|
||||
|
||||
@@ -33,6 +33,7 @@ Target formats (`--target`):
|
||||
- Mattermost (plugin): `channel:<id>`, `user:<id>`, or `@username` (bare ids are treated as channels)
|
||||
- Signal: `+E.164`, `group:<id>`, `signal:+E.164`, `signal:group:<id>`, or `username:<name>`/`u:<name>`
|
||||
- iMessage: handle, `chat_id:<id>`, `chat_guid:<guid>`, or `chat_identifier:<id>`
|
||||
- Matrix: `@user:server`, `!room:server`, or `#alias:server`
|
||||
- Microsoft Teams: conversation id (`19:...@thread.tacv2`) or `conversation:<id>` or `user:<aad-object-id>`
|
||||
|
||||
Name lookup:
|
||||
@@ -65,7 +66,7 @@ Name lookup:
|
||||
### Core
|
||||
|
||||
- `send`
|
||||
- Channels: WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Microsoft Teams
|
||||
- Channels: WhatsApp/Telegram/Discord/Google Chat/Slack/Mattermost (plugin)/Signal/iMessage/Matrix/Microsoft Teams
|
||||
- Required: `--target`, plus `--message` or `--media`
|
||||
- Optional: `--media`, `--reply-to`, `--thread-id`, `--gif-playback`
|
||||
- Telegram only: `--buttons` (requires `channels.telegram.capabilities.inlineButtons` to allow it)
|
||||
@@ -82,7 +83,7 @@ Name lookup:
|
||||
- Telegram only: `--poll-duration-seconds` (5-600), `--silent`, `--poll-anonymous` / `--poll-public`, `--thread-id`
|
||||
|
||||
- `react`
|
||||
- Channels: Discord/Google Chat/Slack/Telegram/WhatsApp/Signal
|
||||
- Channels: Discord/Google Chat/Slack/Telegram/WhatsApp/Signal/Matrix
|
||||
- Required: `--message-id`, `--target`
|
||||
- Optional: `--emoji`, `--remove`, `--participant`, `--from-me`, `--target-author`, `--target-author-uuid`
|
||||
- Note: `--remove` requires `--emoji` (omit `--emoji` to clear own reactions where supported; see /tools/reactions)
|
||||
@@ -90,35 +91,36 @@ Name lookup:
|
||||
- Signal group reactions: `--target-author` or `--target-author-uuid` required
|
||||
|
||||
- `reactions`
|
||||
- Channels: Discord/Google Chat/Slack
|
||||
- Channels: Discord/Google Chat/Slack/Matrix
|
||||
- Required: `--message-id`, `--target`
|
||||
- Optional: `--limit`
|
||||
|
||||
- `read`
|
||||
- Channels: Discord/Slack
|
||||
- Channels: Discord/Slack/Matrix
|
||||
- Required: `--target`
|
||||
- Optional: `--limit`, `--before`, `--after`
|
||||
- Discord only: `--around`
|
||||
|
||||
- `edit`
|
||||
- Channels: Discord/Slack
|
||||
- Channels: Discord/Slack/Matrix
|
||||
- Required: `--message-id`, `--message`, `--target`
|
||||
|
||||
- `delete`
|
||||
- Channels: Discord/Slack/Telegram
|
||||
- Channels: Discord/Slack/Telegram/Matrix
|
||||
- Required: `--message-id`, `--target`
|
||||
|
||||
- `pin` / `unpin`
|
||||
- Channels: Discord/Slack
|
||||
- Channels: Discord/Slack/Matrix
|
||||
- Required: `--message-id`, `--target`
|
||||
|
||||
- `pins` (list)
|
||||
- Channels: Discord/Slack
|
||||
- Channels: Discord/Slack/Matrix
|
||||
- Required: `--target`
|
||||
|
||||
- `permissions`
|
||||
- Channels: Discord
|
||||
- Channels: Discord/Matrix
|
||||
- Required: `--target`
|
||||
- Matrix only: available when Matrix encryption is enabled and verification actions are allowed
|
||||
|
||||
- `search`
|
||||
- Channels: Discord
|
||||
|
||||
@@ -161,7 +161,7 @@ the plugin allowlist, and linked `plugins.load.paths` entries when applicable.
|
||||
For active memory plugins, the memory slot resets to `memory-core`.
|
||||
|
||||
By default, uninstall also removes the plugin install directory under the active
|
||||
state dir extensions root (`$OPENCLAW_STATE_DIR/extensions/<id>`). Use
|
||||
state-dir plugin root. Use
|
||||
`--keep-files` to keep files on disk.
|
||||
|
||||
`--keep-config` is supported as a deprecated alias for `--keep-files`.
|
||||
|
||||
@@ -247,7 +247,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- Example model: `kilocode/anthropic/claude-opus-4.6`
|
||||
- CLI: `openclaw onboard --kilocode-api-key <key>`
|
||||
- Base URL: `https://api.kilo.ai/api/gateway/`
|
||||
- Expanded built-in catalog includes GLM-5 Free, MiniMax M2.5 Free, GPT-5.2, Gemini 3 Pro Preview, Gemini 3 Flash Preview, Grok Code Fast 1, and Kimi K2.5.
|
||||
- Expanded built-in catalog includes GLM-5 Free, MiniMax M2.7 Free, GPT-5.2, Gemini 3 Pro Preview, Gemini 3 Flash Preview, Grok Code Fast 1, and Kimi K2.5.
|
||||
|
||||
See [/providers/kilocode](/providers/kilocode) for setup details.
|
||||
|
||||
@@ -538,8 +538,8 @@ Example (OpenAI‑compatible):
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "lmstudio/minimax-m2.5-gs32" },
|
||||
models: { "lmstudio/minimax-m2.5-gs32": { alias: "Minimax" } },
|
||||
model: { primary: "lmstudio/my-local-model" },
|
||||
models: { "lmstudio/my-local-model": { alias: "Local" } },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
@@ -550,8 +550,8 @@ Example (OpenAI‑compatible):
|
||||
api: "openai-completions",
|
||||
models: [
|
||||
{
|
||||
id: "minimax-m2.5-gs32",
|
||||
name: "MiniMax M2.5",
|
||||
id: "my-local-model",
|
||||
name: "Local Model",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
|
||||
@@ -617,7 +617,7 @@ terms before depending on subscription auth.
|
||||
{
|
||||
agent: {
|
||||
workspace: "~/.openclaw/workspace",
|
||||
model: { primary: "lmstudio/minimax-m2.5-gs32" },
|
||||
model: { primary: "lmstudio/my-local-model" },
|
||||
},
|
||||
models: {
|
||||
mode: "merge",
|
||||
@@ -628,8 +628,8 @@ terms before depending on subscription auth.
|
||||
api: "openai-responses",
|
||||
models: [
|
||||
{
|
||||
id: "minimax-m2.5-gs32",
|
||||
name: "MiniMax M2.5 GS32",
|
||||
id: "my-local-model",
|
||||
name: "Local Model",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
|
||||
@@ -2356,13 +2356,13 @@ Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw on
|
||||
```
|
||||
|
||||
Set `MINIMAX_API_KEY`. Shortcut: `openclaw onboard --auth-choice minimax-api`.
|
||||
`MiniMax-M2.5` and `MiniMax-M2.5-highspeed` remain available if you prefer the older text models.
|
||||
The model catalog now defaults to M2.7 only.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Local models (LM Studio)">
|
||||
|
||||
See [Local Models](/gateway/local-models). TL;DR: run MiniMax M2.5 via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
|
||||
See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -13,34 +13,34 @@ Local is doable, but OpenClaw expects large context + strong defenses against pr
|
||||
|
||||
If you want the lowest-friction local setup, start with [Ollama](/providers/ollama) and `openclaw onboard`. This page is the opinionated guide for higher-end local stacks and custom OpenAI-compatible local servers.
|
||||
|
||||
## Recommended: LM Studio + MiniMax M2.5 (Responses API, full-size)
|
||||
## Recommended: LM Studio + large local model (Responses API)
|
||||
|
||||
Best current local stack. Load MiniMax M2.5 in LM Studio, enable the local server (default `http://127.0.0.1:1234`), and use Responses API to keep reasoning separate from final text.
|
||||
Best current local stack. Load a large model in LM Studio (for example, a full-size Qwen, DeepSeek, or Llama build), enable the local server (default `http://127.0.0.1:1234`), and use Responses API to keep reasoning separate from final text.
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: "lmstudio/minimax-m2.5-gs32" },
|
||||
model: { primary: “lmstudio/my-local-model” },
|
||||
models: {
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
"lmstudio/minimax-m2.5-gs32": { alias: "Minimax" },
|
||||
“anthropic/claude-opus-4-6”: { alias: “Opus” },
|
||||
“lmstudio/my-local-model”: { alias: “Local” },
|
||||
},
|
||||
},
|
||||
},
|
||||
models: {
|
||||
mode: "merge",
|
||||
mode: “merge”,
|
||||
providers: {
|
||||
lmstudio: {
|
||||
baseUrl: "http://127.0.0.1:1234/v1",
|
||||
apiKey: "lmstudio",
|
||||
api: "openai-responses",
|
||||
baseUrl: “http://127.0.0.1:1234/v1”,
|
||||
apiKey: “lmstudio”,
|
||||
api: “openai-responses”,
|
||||
models: [
|
||||
{
|
||||
id: "minimax-m2.5-gs32",
|
||||
name: "MiniMax M2.5 GS32",
|
||||
id: “my-local-model”,
|
||||
name: “Local Model”,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
input: [“text”],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 196608,
|
||||
maxTokens: 8192,
|
||||
@@ -55,7 +55,8 @@ Best current local stack. Load MiniMax M2.5 in LM Studio, enable the local serve
|
||||
**Setup checklist**
|
||||
|
||||
- Install LM Studio: [https://lmstudio.ai](https://lmstudio.ai)
|
||||
- In LM Studio, download the **largest MiniMax M2.5 build available** (avoid “small”/heavily quantized variants), start the server, confirm `http://127.0.0.1:1234/v1/models` lists it.
|
||||
- In LM Studio, download the **largest model build available** (avoid “small”/heavily quantized variants), start the server, confirm `http://127.0.0.1:1234/v1/models` lists it.
|
||||
- Replace `my-local-model` with the actual model ID shown in LM Studio.
|
||||
- Keep the model loaded; cold-load adds startup latency.
|
||||
- Adjust `contextWindow`/`maxTokens` if your LM Studio build differs.
|
||||
- For WhatsApp, stick to Responses API so only final text is sent.
|
||||
@@ -70,11 +71,11 @@ Keep hosted models configured even when running local; use `models.mode: "merge"
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "anthropic/claude-sonnet-4-6",
|
||||
fallbacks: ["lmstudio/minimax-m2.5-gs32", "anthropic/claude-opus-4-6"],
|
||||
fallbacks: ["lmstudio/my-local-model", "anthropic/claude-opus-4-6"],
|
||||
},
|
||||
models: {
|
||||
"anthropic/claude-sonnet-4-6": { alias: "Sonnet" },
|
||||
"lmstudio/minimax-m2.5-gs32": { alias: "MiniMax Local" },
|
||||
"lmstudio/my-local-model": { alias: "Local" },
|
||||
"anthropic/claude-opus-4-6": { alias: "Opus" },
|
||||
},
|
||||
},
|
||||
@@ -88,8 +89,8 @@ Keep hosted models configured even when running local; use `models.mode: "merge"
|
||||
api: "openai-responses",
|
||||
models: [
|
||||
{
|
||||
id: "minimax-m2.5-gs32",
|
||||
name: "MiniMax M2.5 GS32",
|
||||
id: "my-local-model",
|
||||
name: "Local Model",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
|
||||
@@ -191,7 +191,7 @@ If more than one person can DM your bot:
|
||||
- **Local disk hygiene** (permissions, symlinks, config includes, “synced folder” paths).
|
||||
- **Plugins** (extensions exist without an explicit allowlist).
|
||||
- **Policy drift/misconfig** (sandbox docker settings configured but sandbox mode off; ineffective `gateway.nodes.denyCommands` patterns because matching is exact command-name only (for example `system.run`) and does not inspect shell text; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).
|
||||
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which runs directly on the gateway host).
|
||||
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which now fails closed because no sandbox runtime is available).
|
||||
- **Model hygiene** (warn when configured models look legacy; not a hard block).
|
||||
|
||||
If you run `--deep`, OpenClaw also attempts a best-effort live Gateway probe.
|
||||
@@ -253,8 +253,8 @@ High-signal `checkId` values you will most likely see in real deployments (not e
|
||||
| `logging.redact_off` | warn | Sensitive values leak to logs/status | `logging.redactSensitive` | yes |
|
||||
| `sandbox.docker_config_mode_off` | warn | Sandbox Docker config present but inactive | `agents.*.sandbox.mode` | no |
|
||||
| `sandbox.dangerous_network_mode` | critical | Sandbox Docker network uses `host` or `container:*` namespace-join mode | `agents.*.sandbox.docker.network` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` resolves to host exec when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` resolves to host exec when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_defaults` | warn | `exec host=sandbox` fails closed when sandbox is off | `tools.exec.host`, `agents.defaults.sandbox.mode` | no |
|
||||
| `tools.exec.host_sandbox_no_sandbox_agents` | warn | Per-agent `exec host=sandbox` fails closed when sandbox is off | `agents.list[].tools.exec.host`, `agents.list[].sandbox.mode` | no |
|
||||
| `tools.exec.security_full_configured` | warn/critical | Host exec is running with `security="full"` | `tools.exec.security`, `agents.list[].tools.exec.security` | no |
|
||||
| `tools.exec.auto_allow_skills_enabled` | warn | Exec approvals trust skill bins implicitly | `~/.openclaw/exec-approvals.json` | no |
|
||||
| `tools.exec.allowlist_interpreter_without_strict_inline_eval` | warn | Interpreter allowlists permit inline eval without forced reapproval | `tools.exec.strictInlineEval`, `agents.list[].tools.exec.strictInlineEval`, exec approvals allowlist | no |
|
||||
@@ -459,7 +459,7 @@ Plugins run **in-process** with the Gateway. Treat them as trusted code:
|
||||
- Review plugin config before enabling.
|
||||
- Restart the Gateway after plugin changes.
|
||||
- If you install plugins (`openclaw plugins install <package>`), treat it like running untrusted code:
|
||||
- The install path is `~/.openclaw/extensions/<pluginId>/` (or `$OPENCLAW_STATE_DIR/extensions/<pluginId>/`).
|
||||
- The install path is the per-plugin directory under the active plugin install root.
|
||||
- OpenClaw uses `npm pack` and then runs `npm install --omit=dev` in that directory (npm lifecycle scripts can execute code during install).
|
||||
- Prefer pinned, exact versions (`@scope/pkg@1.2.3`), and inspect the unpacked code on disk before enabling.
|
||||
|
||||
@@ -534,7 +534,7 @@ Even with strong system prompts, **prompt injection is not solved**. System prom
|
||||
- Prefer mention gating in groups; avoid “always-on” bots in public rooms.
|
||||
- Treat links, attachments, and pasted instructions as hostile by default.
|
||||
- Run sensitive tool execution in a sandbox; keep secrets out of the agent’s reachable filesystem.
|
||||
- Note: sandboxing is opt-in. If sandbox mode is off, exec runs on the gateway host even though tools.exec.host defaults to sandbox, and host exec does not require approvals unless you set host=gateway and configure exec approvals.
|
||||
- Note: sandboxing is opt-in. If sandbox mode is off, `host=sandbox` fails closed even though tools.exec.host defaults to sandbox. To run on the gateway host, set `host=gateway` and configure exec approvals.
|
||||
- Limit high-risk tools (`exec`, `browser`, `web_fetch`, `web_search`) to trusted agents or explicit allowlists.
|
||||
- If you allowlist interpreters (`python`, `node`, `ruby`, `perl`, `php`, `lua`, `osascript`), enable `tools.exec.strictInlineEval` so inline eval forms still need explicit approval.
|
||||
- **Model choice matters:** older/smaller/legacy models are significantly less robust against prompt injection and tool misuse. For tool-enabled agents, use the strongest latest-generation, instruction-hardened model available.
|
||||
@@ -859,7 +859,7 @@ Assume anything under `~/.openclaw/` (or `$OPENCLAW_STATE_DIR/`) may contain sec
|
||||
- `secrets.json` (optional): file-backed secret payload used by `file` SecretRef providers (`secrets.providers`).
|
||||
- `agents/<agentId>/agent/auth.json`: legacy compatibility file. Static `api_key` entries are scrubbed when discovered.
|
||||
- `agents/<agentId>/sessions/**`: session transcripts (`*.jsonl`) + routing metadata (`sessions.json`) that can contain private messages and tool output.
|
||||
- `extensions/**`: installed plugins (plus their `node_modules/`).
|
||||
- bundled plugin packages: installed plugins (plus their `node_modules/`).
|
||||
- `sandboxes/**`: tool sandbox workspaces; can accumulate copies of files you read/write inside the sandbox.
|
||||
|
||||
Hardening tips:
|
||||
|
||||
@@ -287,12 +287,15 @@ openclaw doctor
|
||||
|
||||
Look for:
|
||||
|
||||
- Whether `plugins.allow` is set and includes `browser`.
|
||||
- Valid browser executable path.
|
||||
- CDP profile reachability.
|
||||
- Local Chrome availability for `existing-session` / `user` profiles.
|
||||
|
||||
Common signatures:
|
||||
|
||||
- `unknown command "browser"` or `unknown command 'browser'` → the bundled browser plugin is excluded by `plugins.allow`.
|
||||
- browser tool missing / unavailable while `browser.enabled=true` → `plugins.allow` excludes `browser`, so the plugin never loaded.
|
||||
- `Failed to start Chrome CDP on port` → browser process failed to launch.
|
||||
- `browser.executablePath not found` → configured path is invalid.
|
||||
- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs.
|
||||
|
||||
@@ -633,7 +633,7 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Is a local model OK for casual chats?">
|
||||
Usually no. OpenClaw needs large context + strong safety; small cards truncate and leak. If you must, run the **largest** MiniMax M2.5 build you can locally (LM Studio) and see [/gateway/local-models](/gateway/local-models). Smaller/quantized models increase prompt-injection risk - see [Security](/gateway/security).
|
||||
Usually no. OpenClaw needs large context + strong safety; small cards truncate and leak. If you must, run the **largest** model build you can locally (LM Studio) and see [/gateway/local-models](/gateway/local-models). Smaller/quantized models increase prompt-injection risk - see [Security](/gateway/security).
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I keep hosted model traffic in a specific region?">
|
||||
|
||||
@@ -45,7 +45,7 @@ Think of the suites as “increasing realism” (and increasing flakiness/cost):
|
||||
|
||||
- Command: `pnpm test`
|
||||
- Config: `scripts/test-parallel.mjs` (runs `vitest.unit.config.ts`, `vitest.extensions.config.ts`, `vitest.gateway.config.ts`)
|
||||
- Files: `src/**/*.test.ts`, `extensions/**/*.test.ts`
|
||||
- Files: `src/**/*.test.ts`, bundled plugin `**/*.test.ts`
|
||||
- Scope:
|
||||
- Pure unit tests
|
||||
- In-process integration tests (gateway auth, routing, tooling, parsing, config)
|
||||
|
||||
@@ -282,6 +282,7 @@ flowchart TD
|
||||
|
||||
Common log signatures:
|
||||
|
||||
- `unknown command "browser"` or `unknown command 'browser'` → `plugins.allow` is set and does not include `browser`.
|
||||
- `Failed to start Chrome CDP on port` → local browser launch failed.
|
||||
- `browser.executablePath not found` → configured binary path is wrong.
|
||||
- `No Chrome tabs found for profile="user"` → the Chrome MCP attach profile has no open local Chrome tabs.
|
||||
@@ -290,6 +291,7 @@ flowchart TD
|
||||
Deep pages:
|
||||
|
||||
- [/gateway/troubleshooting#browser-tool-fails](/gateway/troubleshooting#browser-tool-fails)
|
||||
- [/tools/browser#missing-browser-command-or-tool](/tools/browser#missing-browser-command-or-tool)
|
||||
- [/tools/browser-linux-troubleshooting](/tools/browser-linux-troubleshooting)
|
||||
- [/tools/browser-wsl2-windows-remote-cdp-troubleshooting](/tools/browser-wsl2-windows-remote-cdp-troubleshooting)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ update **without** changing your persisted channel:
|
||||
|
||||
```bash
|
||||
# Install a specific version
|
||||
openclaw update --tag 2026.3.28-beta.1
|
||||
openclaw update --tag 2026.3.29-beta.1
|
||||
|
||||
# Install from the beta dist-tag (one-off, does not persist)
|
||||
openclaw update --tag beta
|
||||
@@ -57,7 +57,7 @@ openclaw update --tag beta
|
||||
openclaw update --tag main
|
||||
|
||||
# Install a specific npm package spec
|
||||
openclaw update --tag openclaw@2026.3.28-beta.1
|
||||
openclaw update --tag openclaw@2026.3.29-beta.1
|
||||
```
|
||||
|
||||
Notes:
|
||||
@@ -75,7 +75,7 @@ Preview what `openclaw update` would do without making changes:
|
||||
```bash
|
||||
openclaw update --dry-run
|
||||
openclaw update --channel beta --dry-run
|
||||
openclaw update --tag 2026.3.28-beta.1 --dry-run
|
||||
openclaw update --tag 2026.3.29-beta.1 --dry-run
|
||||
openclaw update --dry-run --json
|
||||
```
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ If the old store reports room keys that were never backed up, OpenClaw warns ins
|
||||
`Legacy Matrix encrypted state was detected, but the Matrix plugin helper is unavailable. Install or repair @openclaw/matrix so OpenClaw can inspect the old rust crypto store before upgrading.`
|
||||
|
||||
- Meaning: OpenClaw found old encrypted Matrix state, but it could not load the helper entrypoint from the Matrix plugin that normally inspects that store.
|
||||
- What to do: reinstall or repair the Matrix plugin (`openclaw plugins install @openclaw/matrix`, or `openclaw plugins install ./extensions/matrix` for a repo checkout), then rerun `openclaw doctor --fix` or restart the gateway.
|
||||
- What to do: reinstall or repair the Matrix plugin (`openclaw plugins install @openclaw/matrix`, or `openclaw plugins install ./path/to/local/matrix-plugin` for a repo checkout), then rerun `openclaw doctor --fix` or restart the gateway.
|
||||
|
||||
`Matrix plugin helper path is unsafe: ... Reinstall @openclaw/matrix and try again.`
|
||||
|
||||
@@ -312,7 +312,7 @@ If you accept losing unrecoverable old encrypted history, you can instead reset
|
||||
`Matrix is installed from a custom path that no longer exists: ...`
|
||||
|
||||
- Meaning: your plugin install record points at a local path that is gone.
|
||||
- What to do: reinstall with `openclaw plugins install @openclaw/matrix`, or if you are running from a repo checkout, `openclaw plugins install ./extensions/matrix`.
|
||||
- What to do: reinstall with `openclaw plugins install @openclaw/matrix`, or if you are running from a repo checkout, `openclaw plugins install ./path/to/local/matrix-plugin`.
|
||||
|
||||
## If encrypted history still does not come back
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ pnpm test -- \
|
||||
"src/agents/pi-tools*.test.ts" \
|
||||
"src/agents/pi-settings.test.ts" \
|
||||
"src/agents/pi-tool-definition-adapter*.test.ts" \
|
||||
"src/agents/pi-extensions/**/*.test.ts"
|
||||
"src/agents/pi-hooks/**/*.test.ts"
|
||||
```
|
||||
|
||||
To include the live provider exercise:
|
||||
@@ -44,7 +44,7 @@ This covers the main Pi unit suites:
|
||||
- `src/agents/pi-tools*.test.ts`
|
||||
- `src/agents/pi-settings.test.ts`
|
||||
- `src/agents/pi-tool-definition-adapter.test.ts`
|
||||
- `src/agents/pi-extensions/*.test.ts`
|
||||
- `src/agents/pi-hooks/*.test.ts`
|
||||
|
||||
## Manual Testing
|
||||
|
||||
|
||||
16
docs/pi.md
16
docs/pi.md
@@ -88,7 +88,7 @@ src/agents/
|
||||
├── pi-tools.types.ts # AnyAgentTool type alias
|
||||
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
|
||||
├── pi-settings.ts # Settings overrides
|
||||
├── pi-extensions/ # Custom pi extensions
|
||||
├── pi-hooks/ # Custom pi hooks
|
||||
│ ├── compaction-safeguard.ts # Safeguard extension
|
||||
│ ├── compaction-safeguard-runtime.ts
|
||||
│ ├── context-pruning.ts # Cache-TTL context pruning extension
|
||||
@@ -132,10 +132,10 @@ src/agents/
|
||||
Channel-specific message action runtimes now live in the plugin-owned extension
|
||||
directories instead of under `src/agents/tools`, for example:
|
||||
|
||||
- `extensions/discord/src/actions/runtime*.ts`
|
||||
- `extensions/slack/src/action-runtime.ts`
|
||||
- `extensions/telegram/src/action-runtime.ts`
|
||||
- `extensions/whatsapp/src/action-runtime.ts`
|
||||
- the Discord plugin action runtime files
|
||||
- the Slack plugin action runtime file
|
||||
- the Telegram plugin action runtime file
|
||||
- the WhatsApp plugin action runtime file
|
||||
|
||||
## Core Integration Flow
|
||||
|
||||
@@ -390,7 +390,7 @@ OpenClaw loads custom pi extensions for specialized behavior:
|
||||
|
||||
### Compaction Safeguard
|
||||
|
||||
`src/agents/pi-extensions/compaction-safeguard.ts` adds guardrails to compaction, including adaptive token budgeting plus tool failure and file operation summaries:
|
||||
`src/agents/pi-hooks/compaction-safeguard.ts` adds guardrails to compaction, including adaptive token budgeting plus tool failure and file operation summaries:
|
||||
|
||||
```typescript
|
||||
if (resolveCompactionMode(params.cfg) === "safeguard") {
|
||||
@@ -401,7 +401,7 @@ if (resolveCompactionMode(params.cfg) === "safeguard") {
|
||||
|
||||
### Context Pruning
|
||||
|
||||
`src/agents/pi-extensions/context-pruning.ts` implements cache-TTL based context pruning:
|
||||
`src/agents/pi-hooks/context-pruning.ts` implements cache-TTL based context pruning:
|
||||
|
||||
```typescript
|
||||
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
|
||||
@@ -558,7 +558,7 @@ Pi integration coverage spans these suites:
|
||||
- `src/agents/pi-tools*.test.ts`
|
||||
- `src/agents/pi-tool-definition-adapter*.test.ts`
|
||||
- `src/agents/pi-settings.test.ts`
|
||||
- `src/agents/pi-extensions/**/*.test.ts`
|
||||
- `src/agents/pi-hooks/**/*.test.ts`
|
||||
|
||||
Live/opt-in:
|
||||
|
||||
|
||||
@@ -969,16 +969,17 @@ authoring plugins:
|
||||
New code should import the narrower primitives instead.
|
||||
- Bundled extension internals remain private. External plugins should use only
|
||||
`openclaw/plugin-sdk/*` subpaths. OpenClaw core/test code may use the repo
|
||||
public entry points under `extensions/<id>/index.js`, `api.js`, `runtime-api.js`,
|
||||
`setup-entry.js`, and narrowly scoped files such as `login-qr-api.js`. Never
|
||||
import `extensions/<id>/src/*` from core or from another extension.
|
||||
public entry points under a plugin package root such as `index.js`, `api.js`,
|
||||
`runtime-api.js`, `setup-entry.js`, and narrowly scoped files such as
|
||||
`login-qr-api.js`. Never import a plugin package's `src/*` from core or from
|
||||
another extension.
|
||||
- Repo entry point split:
|
||||
`extensions/<id>/api.js` is the helper/types barrel,
|
||||
`extensions/<id>/runtime-api.js` is the runtime-only barrel,
|
||||
`extensions/<id>/index.js` is the bundled plugin entry,
|
||||
and `extensions/<id>/setup-entry.js` is the setup plugin entry.
|
||||
`<plugin-package-root>/api.js` is the helper/types barrel,
|
||||
`<plugin-package-root>/runtime-api.js` is the runtime-only barrel,
|
||||
`<plugin-package-root>/index.js` is the bundled plugin entry,
|
||||
and `<plugin-package-root>/setup-entry.js` is the setup plugin entry.
|
||||
- No bundled channel-branded public subpaths remain. Channel-specific helper and
|
||||
runtime seams live under `extensions/<id>/api.js` and `extensions/<id>/runtime-api.js`;
|
||||
runtime seams live under `<plugin-package-root>/api.js` and `<plugin-package-root>/runtime-api.js`;
|
||||
the public SDK contract is the generic shared primitives instead.
|
||||
|
||||
Compatibility note:
|
||||
@@ -1216,7 +1217,7 @@ Example:
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/nextcloud-talk",
|
||||
"localPath": "extensions/nextcloud-talk",
|
||||
"localPath": "<bundled-plugin-local-path>",
|
||||
"defaultChoice": "npm"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,10 +115,10 @@ and provider plugins have dedicated guides linked above.
|
||||
|
||||
OpenClaw checks ClawHub first, then falls back to npm.
|
||||
|
||||
**In-repo plugins:** place under `extensions/` — automatically discovered.
|
||||
**In-repo plugins:** place under the bundled plugin workspace tree — automatically discovered.
|
||||
|
||||
```bash
|
||||
pnpm test -- extensions/my-plugin/
|
||||
pnpm test -- <bundled-plugin-root>/my-plugin/
|
||||
```
|
||||
|
||||
</Step>
|
||||
@@ -149,9 +149,12 @@ Hook guard semantics to keep in mind:
|
||||
|
||||
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_tool_call`: `{ block: false }` is treated as no decision.
|
||||
- `before_tool_call`: `{ requireApproval: true }` pauses agent execution and prompts the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the `/approve` command on any channel.
|
||||
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
|
||||
- `message_sending`: `{ cancel: false }` is treated as no decision.
|
||||
|
||||
The `/approve` command handles both exec and plugin approvals with automatic fallback. Plugin approval forwarding can be configured independently via `approvals.plugin` in config.
|
||||
|
||||
See [SDK Overview hook decision semantics](/plugins/sdk-overview#hook-decision-semantics) for details.
|
||||
|
||||
## Registering agent tools
|
||||
@@ -222,7 +225,7 @@ internal imports — never import your own plugin through its SDK path.
|
||||
<Check>Entry point uses `defineChannelPluginEntry` or `definePluginEntry`</Check>
|
||||
<Check>All imports use focused `plugin-sdk/<subpath>` paths</Check>
|
||||
<Check>Internal imports use local modules, not SDK self-imports</Check>
|
||||
<Check>Tests pass (`pnpm test -- extensions/my-plugin/`)</Check>
|
||||
<Check>Tests pass (`pnpm test -- <bundled-plugin-root>/my-plugin/`)</Check>
|
||||
<Check>`pnpm check` passes (in-repo plugins)</Check>
|
||||
|
||||
## Beta Release Testing
|
||||
|
||||
@@ -265,7 +265,7 @@ dispatch.
|
||||
|
||||
// Your inbound handler dispatches the message to OpenClaw.
|
||||
// The exact wiring depends on your platform SDK —
|
||||
// see a real example in extensions/msteams or extensions/googlechat.
|
||||
// see a real example in the bundled Microsoft Teams or Google Chat plugin package.
|
||||
await handleAcmeChatInbound(api, event);
|
||||
|
||||
res.statusCode = 200;
|
||||
@@ -279,7 +279,7 @@ dispatch.
|
||||
<Note>
|
||||
Inbound message handling is channel-specific. Each channel plugin owns
|
||||
its own inbound pipeline. Look at bundled channel plugins
|
||||
(e.g. `extensions/msteams`, `extensions/googlechat`) for real patterns.
|
||||
(for example the Microsoft Teams or Google Chat plugin package) for real patterns.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
@@ -320,7 +320,7 @@ dispatch.
|
||||
```
|
||||
|
||||
```bash
|
||||
pnpm test -- extensions/acme-chat/
|
||||
pnpm test -- <bundled-plugin-root>/acme-chat/
|
||||
```
|
||||
|
||||
For shared test helpers, see [Testing](/plugins/sdk-testing).
|
||||
@@ -331,7 +331,7 @@ dispatch.
|
||||
## File structure
|
||||
|
||||
```
|
||||
extensions/acme-chat/
|
||||
<bundled-plugin-root>/acme-chat/
|
||||
├── package.json # openclaw.channel metadata
|
||||
├── openclaw.plugin.json # Manifest with config schema
|
||||
├── index.ts # defineChannelPluginEntry
|
||||
|
||||
@@ -386,7 +386,7 @@ API key auth, and dynamic model resolution.
|
||||
## File structure
|
||||
|
||||
```
|
||||
extensions/acme-ai/
|
||||
<bundled-plugin-root>/acme-ai/
|
||||
├── package.json # openclaw.providers metadata
|
||||
├── openclaw.plugin.json # Manifest with providerAuthEnvVars
|
||||
├── index.ts # definePluginEntry + registerProvider
|
||||
|
||||
@@ -274,7 +274,7 @@ const setupWizard: ChannelSetupWizard = {
|
||||
|
||||
The `ChannelSetupWizard` type supports `credentials`, `textInputs`,
|
||||
`dmPolicy`, `allowFrom`, `groupAccess`, `prepare`, `finalize`, and more.
|
||||
See bundled plugins (e.g. `extensions/discord/src/channel.setup.ts`) for
|
||||
See bundled plugin packages (for example the Discord plugin `src/channel.setup.ts`) for
|
||||
full examples.
|
||||
|
||||
For DM allowlist prompts that only need the standard
|
||||
@@ -319,7 +319,7 @@ openclaw plugins install clawhub:@myorg/openclaw-my-plugin # ClawHub only
|
||||
openclaw plugins install npm:@myorg/openclaw-my-plugin # npm only
|
||||
```
|
||||
|
||||
**In-repo plugins:** place under `extensions/` and they are automatically
|
||||
**In-repo plugins:** place under the bundled plugin workspace tree and they are automatically
|
||||
discovered during build.
|
||||
|
||||
**Users can browse and install:**
|
||||
|
||||
@@ -209,7 +209,7 @@ These tests assert:
|
||||
For a specific plugin:
|
||||
|
||||
```bash
|
||||
pnpm test -- extensions/my-channel/
|
||||
pnpm test -- <bundled-plugin-root>/my-channel/
|
||||
```
|
||||
|
||||
For contract tests only:
|
||||
@@ -240,10 +240,10 @@ OpenClaw uses Vitest with V8 coverage thresholds. For plugin tests:
|
||||
pnpm test
|
||||
|
||||
# Run specific plugin tests
|
||||
pnpm test -- extensions/my-channel/src/channel.test.ts
|
||||
pnpm test -- <bundled-plugin-root>/my-channel/src/channel.test.ts
|
||||
|
||||
# Run with a specific test name filter
|
||||
pnpm test -- extensions/my-channel/ -t "resolves account"
|
||||
pnpm test -- <bundled-plugin-root>/my-channel/ -t "resolves account"
|
||||
|
||||
# Run with coverage
|
||||
pnpm test:coverage
|
||||
|
||||
@@ -44,8 +44,9 @@ Restart the Gateway afterwards.
|
||||
### Option B: install from a local folder (dev, no copying)
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/voice-call
|
||||
cd ./extensions/voice-call && pnpm install
|
||||
PLUGIN_SRC=./path/to/local/voice-call-plugin
|
||||
openclaw plugins install "$PLUGIN_SRC"
|
||||
cd "$PLUGIN_SRC" && pnpm install
|
||||
```
|
||||
|
||||
Restart the Gateway afterwards.
|
||||
|
||||
@@ -37,8 +37,9 @@ Restart the Gateway afterwards.
|
||||
### Option B: install from a local folder (dev)
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/zalouser
|
||||
cd ./extensions/zalouser && pnpm install
|
||||
PLUGIN_SRC=./path/to/local/zalouser-plugin
|
||||
openclaw plugins install "$PLUGIN_SRC"
|
||||
cd "$PLUGIN_SRC" && pnpm install
|
||||
```
|
||||
|
||||
Restart the Gateway afterwards.
|
||||
|
||||
@@ -29,7 +29,7 @@ openclaw plugins enable open-prose
|
||||
|
||||
Restart the Gateway after enabling the plugin.
|
||||
|
||||
Dev/local checkout: `openclaw plugins install ./extensions/open-prose`
|
||||
Dev/local checkout: `openclaw plugins install ./path/to/local/open-prose-plugin`
|
||||
|
||||
Related docs: [Plugins](/tools/plugin), [Plugin manifest](/plugins/manifest), [Skills](/tools/skills).
|
||||
|
||||
|
||||
@@ -14,6 +14,29 @@ OpenClaw's MiniMax provider defaults to **MiniMax M2.7**.
|
||||
|
||||
- `MiniMax-M2.7`: default hosted text model.
|
||||
- `MiniMax-M2.7-highspeed`: faster M2.7 text tier.
|
||||
- `image-01`: image generation model (generate and image-to-image editing).
|
||||
|
||||
## Image generation
|
||||
|
||||
The MiniMax plugin registers the `image-01` model for the `image_generate` tool. It supports:
|
||||
|
||||
- **Text-to-image generation** with aspect ratio control.
|
||||
- **Image-to-image editing** (subject reference) with aspect ratio control.
|
||||
- Supported aspect ratios: `1:1`, `16:9`, `4:3`, `3:2`, `2:3`, `3:4`, `9:16`, `21:9`.
|
||||
|
||||
To use MiniMax for image generation, set it as the image generation provider:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
imageGenerationModel: { primary: "minimax/image-01" },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The plugin uses the same `MINIMAX_API_KEY` or OAuth auth as the text models. No additional configuration is needed if MiniMax is already set up.
|
||||
|
||||
## Choose a setup
|
||||
|
||||
@@ -34,7 +57,7 @@ You will be prompted to select an endpoint:
|
||||
- **Global** - International users (`api.minimax.io`)
|
||||
- **CN** - Users in China (`api.minimaxi.com`)
|
||||
|
||||
See [MiniMax plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax) for details.
|
||||
See the MiniMax plugin package README in the OpenClaw repo for details.
|
||||
|
||||
### MiniMax M2.7 (API key)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ background.
|
||||
## Recommended: Model Studio (Alibaba Cloud Coding Plan)
|
||||
|
||||
Use [Model Studio](/providers/modelstudio) for officially supported access to
|
||||
Qwen models (Qwen 3.5 Plus, GLM-4.7, Kimi K2.5, MiniMax M2.5, and more).
|
||||
Qwen models (Qwen 3.5 Plus, GLM-4.7, Kimi K2.5, and more).
|
||||
|
||||
```bash
|
||||
# Global endpoint
|
||||
|
||||
@@ -74,7 +74,7 @@ override with a custom `baseUrl` in config.
|
||||
- **qwen3-coder-plus**, **qwen3-coder-next** — Qwen coding models
|
||||
- **GLM-5** — GLM models via Alibaba
|
||||
- **Kimi K2.5** — Moonshot AI via Alibaba
|
||||
- **MiniMax-M2.5** — MiniMax via Alibaba
|
||||
- **MiniMax-M2.7** — MiniMax via Alibaba
|
||||
|
||||
Some models (qwen3.5-plus, kimi-k2.5) support image input. Context windows range from 200K to 1M tokens.
|
||||
|
||||
|
||||
@@ -422,6 +422,7 @@ Notes:
|
||||
- `vectorWeight` + `textWeight` is normalized to 1.0 in config resolution, so weights behave as percentages.
|
||||
- If embeddings are unavailable (or the provider returns a zero-vector), we still run BM25 and return keyword matches.
|
||||
- If FTS5 can't be created, we keep vector-only search (no hard failure).
|
||||
- **CJK support**: FTS5 uses configurable trigram tokenization with a short-substring fallback so Chinese, Japanese, and Korean text is searchable without breaking mixed-length queries. CJK-heavy text is also weighted correctly during chunk size estimation, and surrogate-pair characters are preserved during fine splits.
|
||||
|
||||
This isn't "IR-theory perfect", but it's simple, fast, and tends to improve recall/precision on real notes.
|
||||
If we want to get fancier later, common next steps are Reciprocal Rank Fusion (RRF) or score normalization
|
||||
|
||||
@@ -46,7 +46,7 @@ For a high-level overview, see [Onboarding (CLI)](/start/wizard).
|
||||
- More detail: [Vercel AI Gateway](/providers/vercel-ai-gateway)
|
||||
- **Cloudflare AI Gateway**: prompts for Account ID, Gateway ID, and `CLOUDFLARE_AI_GATEWAY_API_KEY`.
|
||||
- More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway)
|
||||
- **MiniMax**: config is auto-written; hosted default is `MiniMax-M2.7` and `MiniMax-M2.5` stays available.
|
||||
- **MiniMax**: config is auto-written; hosted default is `MiniMax-M2.7`.
|
||||
- More detail: [MiniMax](/providers/minimax)
|
||||
- **Synthetic (Anthropic-compatible)**: prompts for `SYNTHETIC_API_KEY`.
|
||||
- More detail: [Synthetic](/providers/synthetic)
|
||||
@@ -209,7 +209,7 @@ Typical fields in `~/.openclaw/openclaw.json`:
|
||||
- `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved)
|
||||
- `gateway.*` (mode, bind, auth, tailscale)
|
||||
- `session.dmScope` (behavior details: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals))
|
||||
- `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*`
|
||||
- `channels.telegram.botToken`, `channels.discord.token`, `channels.matrix.*`, `channels.signal.*`, `channels.imessage.*`
|
||||
- Channel allowlists (Slack/Discord/Matrix/Microsoft Teams) when you opt in during the prompts (names resolve to IDs when possible).
|
||||
- `skills.install.nodeManager`
|
||||
- `wizard.lastRunAt`
|
||||
|
||||
@@ -174,7 +174,7 @@ What you set:
|
||||
More detail: [Cloudflare AI Gateway](/providers/cloudflare-ai-gateway).
|
||||
</Accordion>
|
||||
<Accordion title="MiniMax">
|
||||
Config is auto-written. Hosted default is `MiniMax-M2.7`; `MiniMax-M2.5` stays available.
|
||||
Config is auto-written. Hosted default is `MiniMax-M2.7`.
|
||||
More detail: [MiniMax](/providers/minimax).
|
||||
</Accordion>
|
||||
<Accordion title="Synthetic (Anthropic-compatible)">
|
||||
@@ -258,7 +258,7 @@ Typical fields in `~/.openclaw/openclaw.json`:
|
||||
- `tools.profile` (local onboarding defaults to `"coding"` when unset; existing explicit values are preserved)
|
||||
- `gateway.*` (mode, bind, auth, tailscale)
|
||||
- `session.dmScope` (local onboarding defaults this to `per-channel-peer` when unset; existing explicit values are preserved)
|
||||
- `channels.telegram.botToken`, `channels.discord.token`, `channels.signal.*`, `channels.imessage.*`
|
||||
- `channels.telegram.botToken`, `channels.discord.token`, `channels.matrix.*`, `channels.signal.*`, `channels.imessage.*`
|
||||
- Channel allowlists (Slack, Discord, Matrix, Microsoft Teams) when you opt in during prompts (names resolve to IDs when possible)
|
||||
- `skills.install.nodeManager`
|
||||
- `wizard.lastRunAt`
|
||||
|
||||
@@ -624,7 +624,7 @@ openclaw config set plugins.entries.acpx.enabled true
|
||||
Local workspace install during development:
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./extensions/acpx
|
||||
openclaw plugins install ./path/to/local/acpx-plugin
|
||||
```
|
||||
|
||||
Then verify backend health:
|
||||
@@ -637,7 +637,7 @@ Then verify backend health:
|
||||
|
||||
By default, the bundled acpx backend plugin (`acpx`) uses the plugin-local pinned binary:
|
||||
|
||||
1. Command defaults to `extensions/acpx/node_modules/.bin/acpx`.
|
||||
1. Command defaults to the plugin-local `node_modules/.bin/acpx` inside the ACPX plugin package.
|
||||
2. Expected version defaults to the extension pin.
|
||||
3. Startup registers ACP backend immediately as not-ready.
|
||||
4. A background ensure job verifies `acpx --version`.
|
||||
|
||||
@@ -42,6 +42,9 @@ openclaw browser --browser-profile openclaw snapshot
|
||||
If you get “Browser disabled”, enable it in config (see below) and restart the
|
||||
Gateway.
|
||||
|
||||
If `openclaw browser` is missing entirely, or the agent says the browser tool
|
||||
is unavailable, jump to [Missing browser command or tool](/tools/browser#missing-browser-command-or-tool).
|
||||
|
||||
## Plugin control
|
||||
|
||||
The default `browser` tool is now a bundled plugin that ships enabled by
|
||||
@@ -73,13 +76,52 @@ replacement plugin to reuse.
|
||||
|
||||
The bundled browser plugin also owns the browser runtime implementation now.
|
||||
Core keeps only shared Plugin SDK helpers plus compatibility re-exports for
|
||||
older internal import paths. In practice, removing or replacing
|
||||
`extensions/browser` removes the browser feature set instead of leaving a
|
||||
second core-owned runtime behind.
|
||||
older internal import paths. In practice, removing or replacing the browser
|
||||
plugin package removes the browser feature set instead of leaving a second
|
||||
core-owned runtime behind.
|
||||
|
||||
Browser config changes still require a Gateway restart so the bundled plugin
|
||||
can re-register its browser service with the new settings.
|
||||
|
||||
## Missing browser command or tool
|
||||
|
||||
If `openclaw browser` suddenly becomes an unknown command after an upgrade, or
|
||||
the agent reports that the browser tool is missing, the most common cause is a
|
||||
restrictive `plugins.allow` list that does not include `browser`.
|
||||
|
||||
Example broken config:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
allow: ["telegram"],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Fix it by adding `browser` to the plugin allowlist:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
allow: ["telegram", "browser"],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Important notes:
|
||||
|
||||
- `browser.enabled=true` is not enough by itself when `plugins.allow` is set.
|
||||
- `plugins.entries.browser.enabled=true` is also not enough by itself when `plugins.allow` is set.
|
||||
- `tools.alsoAllow: ["browser"]` does **not** load the bundled browser plugin. It only adjusts tool policy after the plugin is already loaded.
|
||||
- If you do not need a restrictive plugin allowlist, removing `plugins.allow` also restores the default bundled browser behavior.
|
||||
|
||||
Typical symptoms:
|
||||
|
||||
- `openclaw browser` is an unknown command.
|
||||
- `browser.request` is missing.
|
||||
- The agent reports the browser tool as unavailable or missing.
|
||||
|
||||
## Profiles: `openclaw` vs `user`
|
||||
|
||||
- `openclaw`: managed, isolated browser (no extension required).
|
||||
|
||||
@@ -85,7 +85,7 @@ For a new capability, expect to touch these areas:
|
||||
- `src/plugins/runtime/index.ts`
|
||||
- `src/plugin-sdk/<capability>.ts`
|
||||
- `src/plugin-sdk/<capability>-runtime.ts`
|
||||
- one or more `extensions/<vendor>/...`
|
||||
- one or more bundled plugin packages
|
||||
- config/docs/tests
|
||||
|
||||
## Example: image generation
|
||||
|
||||
@@ -30,7 +30,7 @@ Background sessions are scoped per agent; `process` only sees sessions from the
|
||||
Notes:
|
||||
|
||||
- `host` defaults to `sandbox`.
|
||||
- `elevated` is ignored when sandboxing is off (exec already runs on the host).
|
||||
- `elevated` forces `host=gateway`; it is only available when elevated access is enabled for the current session/provider.
|
||||
- `gateway`/`node` approvals are controlled by `~/.openclaw/exec-approvals.json`.
|
||||
- `node` requires a paired node (companion app or headless node host).
|
||||
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
|
||||
@@ -41,9 +41,9 @@ Notes:
|
||||
- Host execution (`gateway`/`node`) rejects `env.PATH` and loader overrides (`LD_*`/`DYLD_*`) to
|
||||
prevent binary hijacking or injected code.
|
||||
- OpenClaw sets `OPENCLAW_SHELL=exec` in the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context.
|
||||
- Important: sandboxing is **off by default**. If sandboxing is off and `host=sandbox` is explicitly
|
||||
configured/requested, exec now fails closed instead of silently running on the gateway host.
|
||||
Enable sandboxing or use `host=gateway` with approvals.
|
||||
- Important: sandboxing is **off by default**. If sandboxing is off and exec resolves to
|
||||
`host=sandbox` (including the implicit default), exec fails closed instead of silently
|
||||
running on the gateway host. Enable sandboxing or use `host=gateway` with approvals.
|
||||
- Script preflight checks (for common Python/Node shell-syntax mistakes) only inspect files inside the
|
||||
effective `workdir` boundary. If a script path resolves outside `workdir`, preflight is skipped for
|
||||
that file.
|
||||
|
||||
@@ -156,11 +156,11 @@ OpenClaw scans for plugins in this order (first match wins):
|
||||
</Step>
|
||||
|
||||
<Step title="Workspace extensions">
|
||||
`\<workspace\>/.openclaw/extensions/*.ts` and `\<workspace\>/.openclaw/extensions/*/index.ts`.
|
||||
`\<workspace\>/.openclaw/<plugin-root>/*.ts` and `\<workspace\>/.openclaw/<plugin-root>/*/index.ts`.
|
||||
</Step>
|
||||
|
||||
<Step title="Global extensions">
|
||||
`~/.openclaw/extensions/*.ts` and `~/.openclaw/extensions/*/index.ts`.
|
||||
`~/.openclaw/<plugin-root>/*.ts` and `~/.openclaw/<plugin-root>/*/index.ts`.
|
||||
</Step>
|
||||
|
||||
<Step title="Bundled plugins">
|
||||
|
||||
@@ -1,47 +1,57 @@
|
||||
---
|
||||
title: Pi 开发工作流程
|
||||
read_when:
|
||||
- 在 OpenClaw 中处理 Pi 集成代码或测试时
|
||||
- 运行 Pi 专用的代码规范检查、类型检查和实时测试流程时
|
||||
summary: Pi 集成的开发工作流:构建、测试和实时验证
|
||||
title: Pi 开发工作流
|
||||
x-i18n:
|
||||
generated_at: "2026-02-03T10:07:59Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 65bd0580dd03df05321ced35a036ce6fb815ce3ddac1d35c9976279adcbf87c0
|
||||
generated_at: "2026-03-29T04:06:57Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 7be1c0f9ecf4315115b2e8188f7472eebba2a8424296661184a02bf5ad6e90c5
|
||||
source_path: pi-dev.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Pi 开发工作流程
|
||||
# Pi 开发工作流
|
||||
|
||||
本指南总结了在 OpenClaw 中开发 Pi 集成的合理工作流程。
|
||||
本指南总结了在 OpenClaw 中处理 Pi 集成时的一套合理工作流。
|
||||
|
||||
## 类型检查和代码检查
|
||||
## 类型检查与代码规范检查
|
||||
|
||||
- 类型检查和构建:`pnpm build`
|
||||
- 代码检查:`pnpm lint`
|
||||
- 代码规范检查:`pnpm lint`
|
||||
- 格式检查:`pnpm format`
|
||||
- 推送前完整检查:`pnpm lint && pnpm build && pnpm test`
|
||||
- 推送前的完整检查:`pnpm lint && pnpm build && pnpm test`
|
||||
|
||||
## 运行 Pi 测试
|
||||
|
||||
使用专用脚本运行 Pi 集成测试集:
|
||||
直接使用 Vitest 运行面向 Pi 的测试集:
|
||||
|
||||
```bash
|
||||
scripts/pi/run-tests.sh
|
||||
pnpm test -- \
|
||||
"src/agents/pi-*.test.ts" \
|
||||
"src/agents/pi-embedded-*.test.ts" \
|
||||
"src/agents/pi-tools*.test.ts" \
|
||||
"src/agents/pi-settings.test.ts" \
|
||||
"src/agents/pi-tool-definition-adapter*.test.ts" \
|
||||
"src/agents/pi-hooks/**/*.test.ts"
|
||||
```
|
||||
|
||||
要包含执行真实提供商行为的实时测试:
|
||||
如果还要包含提供商的实时演练:
|
||||
|
||||
```bash
|
||||
scripts/pi/run-tests.sh --live
|
||||
OPENCLAW_LIVE_TEST=1 pnpm test -- src/agents/pi-embedded-runner-extraparams.live.test.ts
|
||||
```
|
||||
|
||||
该脚本通过以下 glob 模式运行所有 Pi 相关的单元测试:
|
||||
这涵盖了主要的 Pi 单元测试套件:
|
||||
|
||||
- `src/agents/pi-*.test.ts`
|
||||
- `src/agents/pi-embedded-*.test.ts`
|
||||
- `src/agents/pi-tools*.test.ts`
|
||||
- `src/agents/pi-settings.test.ts`
|
||||
- `src/agents/pi-tool-definition-adapter.test.ts`
|
||||
- `src/agents/pi-extensions/*.test.ts`
|
||||
- `src/agents/pi-hooks/*.test.ts`
|
||||
|
||||
## 手动测试
|
||||
|
||||
@@ -51,27 +61,27 @@ scripts/pi/run-tests.sh --live
|
||||
- `pnpm gateway:dev`
|
||||
- 直接触发智能体:
|
||||
- `pnpm openclaw agent --message "Hello" --thinking low`
|
||||
- 使用 TUI 进行交互式调试:
|
||||
- 使用终端界面进行交互式调试:
|
||||
- `pnpm tui`
|
||||
|
||||
对于工具调用行为,提示执行 `read` 或 `exec` 操作,以便查看工具流式传输和负载处理。
|
||||
对于工具调用行为,请提示执行 `read` 或 `exec` 操作,这样你就可以看到工具的流式传输和负载处理。
|
||||
|
||||
## 完全重置
|
||||
## 彻底重置
|
||||
|
||||
状态存储在 OpenClaw 状态目录下。默认为 `~/.openclaw`。如果设置了 `OPENCLAW_STATE_DIR`,则使用该目录。
|
||||
状态存储在 OpenClaw 状态目录下。默认路径是 `~/.openclaw`。如果设置了 `OPENCLAW_STATE_DIR`,则改用该目录。
|
||||
|
||||
要重置所有内容:
|
||||
如需重置全部内容:
|
||||
|
||||
- `openclaw.json` 用于配置
|
||||
- `credentials/` 用于认证配置文件和 token
|
||||
- `credentials/` 用于凭证配置文件和令牌
|
||||
- `agents/<agentId>/sessions/` 用于智能体会话历史
|
||||
- `agents/<agentId>/sessions.json` 用于会话索引
|
||||
- `sessions/` 如果存在旧版路径
|
||||
- `workspace/` 如果你想要一个空白工作区
|
||||
- `sessions/`,如果存在旧版路径
|
||||
- `workspace/`,如果你想要一个空白工作区
|
||||
|
||||
如果只想重置会话,删除该智能体的 `agents/<agentId>/sessions/` 和 `agents/<agentId>/sessions.json`。如果不想重新认证,保留 `credentials/`。
|
||||
如果你只想重置会话,请删除该智能体的 `agents/<agentId>/sessions/` 和 `agents/<agentId>/sessions.json`。如果你不想重新进行身份验证,请保留 `credentials/`。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- https://docs.openclaw.ai/testing
|
||||
- https://docs.openclaw.ai/start/getting-started
|
||||
- [测试](/help/testing)
|
||||
- [入门指南](/start/getting-started)
|
||||
|
||||
282
docs/zh-CN/pi.md
282
docs/zh-CN/pi.md
@@ -1,45 +1,49 @@
|
||||
---
|
||||
read_when:
|
||||
- 理解 OpenClaw 中 Pi SDK 集成设计时
|
||||
- 修改 Pi 的智能体会话生命周期、工具或提供商接线时
|
||||
summary: OpenClaw 内嵌 Pi 智能体集成与会话生命周期的架构
|
||||
title: Pi 集成架构
|
||||
x-i18n:
|
||||
generated_at: "2026-02-03T07:53:24Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 98b12f1211f70b1a25f58e68c7a4d0fe3827412ca53ba0ea2cd41ac9c0448458
|
||||
generated_at: "2026-03-29T04:10:02Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 43a5d646ed66fab1492b6f18fb1623d895922ecf539e52e069d99e7e83c0be11
|
||||
source_path: pi.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Pi 集成架构
|
||||
|
||||
本文档描述了 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其相关包(`pi-ai`、`pi-agent-core`、`pi-tui`)集成以实现其 AI 智能体能力。
|
||||
本文档介绍 OpenClaw 如何与 [pi-coding-agent](https://github.com/badlogic/pi-mono/tree/main/packages/coding-agent) 及其同级软件包(`pi-ai`、`pi-agent-core`、`pi-tui`)集成,以提供其 AI 智能体能力。
|
||||
|
||||
## 概述
|
||||
## 概览
|
||||
|
||||
OpenClaw 使用 pi SDK 将 AI 编码智能体嵌入到其消息 Gateway 网关架构中。OpenClaw 不是将 pi 作为子进程生成或使用 RPC 模式,而是通过 `createAgentSession()` 直接导入并实例化 pi 的 `AgentSession`。这种嵌入式方法提供了:
|
||||
OpenClaw 使用 pi SDK 将 AI 编码智能体嵌入到其消息 Gateway 网关架构中。OpenClaw 不会将 pi 作为子进程启动,也不会使用 RPC 模式,而是通过 `createAgentSession()` 直接导入并实例化 pi 的 `AgentSession`。这种内嵌方式提供了:
|
||||
|
||||
- 对会话生命周期和事件处理的完全控制
|
||||
- 自定义工具注入(消息、沙箱、渠道特定操作)
|
||||
- 每个渠道/上下文的系统提示自定义
|
||||
- 支持分支/压缩的会话持久化
|
||||
- 带故障转移的多账户认证配置文件轮换
|
||||
- 按渠道 / 上下文定制系统提示词
|
||||
- 支持分支 / 压缩的会话持久化
|
||||
- 带故障转移的多账户凭证配置轮换
|
||||
- 与提供商无关的模型切换
|
||||
|
||||
## 包依赖
|
||||
## 软件包依赖
|
||||
|
||||
```json
|
||||
{
|
||||
"@mariozechner/pi-agent-core": "0.49.3",
|
||||
"@mariozechner/pi-ai": "0.49.3",
|
||||
"@mariozechner/pi-coding-agent": "0.49.3",
|
||||
"@mariozechner/pi-tui": "0.49.3"
|
||||
"@mariozechner/pi-agent-core": "0.61.1",
|
||||
"@mariozechner/pi-ai": "0.61.1",
|
||||
"@mariozechner/pi-coding-agent": "0.61.1",
|
||||
"@mariozechner/pi-tui": "0.61.1"
|
||||
}
|
||||
```
|
||||
|
||||
| 包 | 用途 |
|
||||
| 软件包 | 用途 |
|
||||
| ----------------- | ------------------------------------------------------------------------------------------ |
|
||||
| `pi-ai` | 核心 LLM 抽象:`Model`、`streamSimple`、消息类型、提供商 API |
|
||||
| `pi-agent-core` | 智能体循环、工具执行、`AgentMessage` 类型 |
|
||||
| `pi-coding-agent` | 高级 SDK:`createAgentSession`、`SessionManager`、`AuthStorage`、`ModelRegistry`、内置工具 |
|
||||
| `pi-coding-agent` | 高层 SDK:`createAgentSession`、`SessionManager`、`AuthStorage`、`ModelRegistry`、内置工具 |
|
||||
| `pi-tui` | 终端 UI 组件(用于 OpenClaw 的本地 TUI 模式) |
|
||||
|
||||
## 文件结构
|
||||
@@ -91,7 +95,7 @@ src/agents/
|
||||
├── pi-tools.types.ts # AnyAgentTool type alias
|
||||
├── pi-tool-definition-adapter.ts # AgentTool -> ToolDefinition adapter
|
||||
├── pi-settings.ts # Settings overrides
|
||||
├── pi-extensions/ # Custom pi extensions
|
||||
├── pi-hooks/ # Custom pi hooks
|
||||
│ ├── compaction-safeguard.ts # Safeguard extension
|
||||
│ ├── compaction-safeguard-runtime.ts
|
||||
│ ├── context-pruning.ts # Cache-TTL context pruning extension
|
||||
@@ -122,24 +126,28 @@ src/agents/
|
||||
│ ├── browser-tool.ts
|
||||
│ ├── canvas-tool.ts
|
||||
│ ├── cron-tool.ts
|
||||
│ ├── discord-actions*.ts
|
||||
│ ├── gateway-tool.ts
|
||||
│ ├── image-tool.ts
|
||||
│ ├── message-tool.ts
|
||||
│ ├── nodes-tool.ts
|
||||
│ ├── session*.ts
|
||||
│ ├── slack-actions.ts
|
||||
│ ├── telegram-actions.ts
|
||||
│ ├── web-*.ts
|
||||
│ └── whatsapp-actions.ts
|
||||
│ └── ...
|
||||
└── ...
|
||||
```
|
||||
|
||||
渠道特定的消息操作运行时现在位于插件自有的扩展目录中,而不是放在 `src/agents/tools` 下,例如:
|
||||
|
||||
- `extensions/discord/src/actions/runtime*.ts`
|
||||
- `extensions/slack/src/action-runtime.ts`
|
||||
- `extensions/telegram/src/action-runtime.ts`
|
||||
- `extensions/whatsapp/src/action-runtime.ts`
|
||||
|
||||
## 核心集成流程
|
||||
|
||||
### 1. 运行嵌入式智能体
|
||||
### 1. 运行内嵌智能体
|
||||
|
||||
主入口点是 `pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`:
|
||||
主入口是 `pi-embedded-runner/run.ts` 中的 `runEmbeddedPiAgent()`:
|
||||
|
||||
```typescript
|
||||
import { runEmbeddedPiAgent } from "./agents/pi-embedded-runner.js";
|
||||
@@ -161,9 +169,9 @@ const result = await runEmbeddedPiAgent({
|
||||
});
|
||||
```
|
||||
|
||||
### 2. 会话创建
|
||||
### 2. 创建会话
|
||||
|
||||
在 `runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,使用 pi SDK:
|
||||
在 `runEmbeddedAttempt()`(由 `runEmbeddedPiAgent()` 调用)内部,会使用 pi SDK:
|
||||
|
||||
```typescript
|
||||
import {
|
||||
@@ -200,7 +208,7 @@ applySystemPromptOverrideToSession(session, systemPromptOverride);
|
||||
|
||||
### 3. 事件订阅
|
||||
|
||||
`subscribeEmbeddedPiSession()` 订阅 pi 的 `AgentSession` 事件:
|
||||
`subscribeEmbeddedPiSession()` 会订阅 pi 的 `AgentSession` 事件:
|
||||
|
||||
```typescript
|
||||
const subscription = subscribeEmbeddedPiSession({
|
||||
@@ -219,37 +227,39 @@ const subscription = subscribeEmbeddedPiSession({
|
||||
|
||||
处理的事件包括:
|
||||
|
||||
- `message_start` / `message_end` / `message_update`(流式文本/思考)
|
||||
- `message_start` / `message_end` / `message_update`(流式文本 / 思考)
|
||||
- `tool_execution_start` / `tool_execution_update` / `tool_execution_end`
|
||||
- `turn_start` / `turn_end`
|
||||
- `agent_start` / `agent_end`
|
||||
- `auto_compaction_start` / `auto_compaction_end`
|
||||
|
||||
### 4. 提示
|
||||
### 4. 发送提示
|
||||
|
||||
设置完成后,会话被提示:
|
||||
完成设置后,会向会话发送提示:
|
||||
|
||||
```typescript
|
||||
await session.prompt(effectivePrompt, { images: imageResult.images });
|
||||
```
|
||||
|
||||
SDK 处理完整的智能体循环:发送到 LLM、执行工具调用、流式响应。
|
||||
SDK 会处理完整的智能体循环:发送给 LLM、执行工具调用、流式返回响应。
|
||||
|
||||
图像注入仅限当前提示:OpenClaw 会从当前提示中加载图像引用,并仅通过 `images` 将其传入该轮。它不会重新扫描较早的历史轮次来重新注入图像负载。
|
||||
|
||||
## 工具架构
|
||||
|
||||
### 工具管道
|
||||
### 工具流水线
|
||||
|
||||
1. **基础工具**:pi 的 `codingTools`(read、bash、edit、write)
|
||||
2. **自定义替换**:OpenClaw 将 bash 替换为 `exec`/`process`,为沙箱自定义 read/edit/write
|
||||
3. **OpenClaw 工具**:消息、浏览器、画布、会话、定时任务、Gateway 网关等
|
||||
4. **渠道工具**:Discord/Telegram/Slack/WhatsApp 特定的操作工具
|
||||
5. **策略过滤**:工具按配置文件、提供商、智能体、群组、沙箱策略过滤
|
||||
6. **Schema 规范化**:为 Gemini/OpenAI 的特殊情况清理 Schema
|
||||
7. **AbortSignal 包装**:工具被包装以尊重中止信号
|
||||
2. **自定义替换**:OpenClaw 用 `exec` / `process` 替换 bash,并为沙箱定制 read / edit / write
|
||||
3. **OpenClaw 工具**:消息、浏览器、画布、会话、cron、Gateway 网关 等
|
||||
4. **渠道工具**:Discord / Telegram / Slack / WhatsApp 特定操作工具
|
||||
5. **策略过滤**:按配置、提供商、智能体、群组、沙箱策略过滤工具
|
||||
6. **模式归一化**:清理模式以适配 Gemini / OpenAI 的特殊行为
|
||||
7. **AbortSignal 包装**:包装工具以遵循中止信号
|
||||
|
||||
### 工具定义适配器
|
||||
|
||||
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` 有不同的 `execute` 签名。`pi-tool-definition-adapter.ts` 中的适配器桥接了这一点:
|
||||
pi-agent-core 的 `AgentTool` 与 pi-coding-agent 的 `ToolDefinition` 在 `execute` 签名上不同。`pi-tool-definition-adapter.ts` 中的适配器用于桥接这一差异:
|
||||
|
||||
```typescript
|
||||
export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
|
||||
@@ -268,7 +278,7 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] {
|
||||
|
||||
### 工具拆分策略
|
||||
|
||||
`splitSdkTools()` 通过 `customTools` 传递所有工具:
|
||||
`splitSdkTools()` 会通过 `customTools` 传入所有工具:
|
||||
|
||||
```typescript
|
||||
export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled: boolean }) {
|
||||
@@ -279,13 +289,13 @@ export function splitSdkTools(options: { tools: AnyAgentTool[]; sandboxEnabled:
|
||||
}
|
||||
```
|
||||
|
||||
这确保 OpenClaw 的策略过滤、沙箱集成和扩展工具集在各提供商之间保持一致。
|
||||
这样可以确保 OpenClaw 的策略过滤、沙箱集成和扩展工具集在不同提供商之间保持一致。
|
||||
|
||||
## 系统提示构建
|
||||
## 系统提示词构建
|
||||
|
||||
系统提示在 `buildAgentSystemPrompt()`(`system-prompt.ts`)中构建。它组装一个完整的提示,包含工具、工具调用风格、安全护栏、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据等部分,以及启用时的记忆和反应,还有可选的上下文文件和额外系统提示内容。部分内容在子智能体使用的最小提示模式下会被裁剪。
|
||||
系统提示词在 `buildAgentSystemPrompt()`(`system-prompt.ts`)中构建。它会组装完整提示词,包含工具、工具调用风格、安全护栏、OpenClaw CLI 参考、Skills、文档、工作区、沙箱、消息、回复标签、语音、静默回复、心跳、运行时元数据等部分,并在启用时包含 Memory 和 Reactions,以及可选的上下文文件和额外系统提示词内容。为子智能体使用的最小提示词模式会对各部分进行裁剪。
|
||||
|
||||
提示在会话创建后通过 `applySystemPromptOverrideToSession()` 应用:
|
||||
系统提示词会在会话创建后通过 `applySystemPromptOverrideToSession()` 应用:
|
||||
|
||||
```typescript
|
||||
const systemPromptOverride = createSystemPromptOverride(appendPrompt);
|
||||
@@ -296,17 +306,17 @@ applySystemPromptOverrideToSession(session, systemPromptOverride);
|
||||
|
||||
### 会话文件
|
||||
|
||||
会话是具有树结构(id/parentId 链接)的 JSONL 文件。Pi 的 `SessionManager` 处理持久化:
|
||||
会话是具有树状结构(通过 id / parentId 关联)的 JSONL 文件。Pi 的 `SessionManager` 负责持久化:
|
||||
|
||||
```typescript
|
||||
const sessionManager = SessionManager.open(params.sessionFile);
|
||||
```
|
||||
|
||||
OpenClaw 用 `guardSessionManager()` 包装它以确保工具结果安全。
|
||||
OpenClaw 通过 `guardSessionManager()` 对其进行包装,以保证工具结果安全。
|
||||
|
||||
### 会话缓存
|
||||
|
||||
`session-manager-cache.ts` 缓存 SessionManager 实例以避免重复的文件解析:
|
||||
`session-manager-cache.ts` 会缓存 `SessionManager` 实例,以避免重复解析文件:
|
||||
|
||||
```typescript
|
||||
await prewarmSessionFile(params.sessionFile);
|
||||
@@ -316,11 +326,11 @@ trackSessionManagerAccess(params.sessionFile);
|
||||
|
||||
### 历史限制
|
||||
|
||||
`limitHistoryTurns()` 根据渠道类型(私信 vs 群组)裁剪对话历史。
|
||||
`limitHistoryTurns()` 会根据渠道类型(私信 与群组)裁剪对话历史。
|
||||
|
||||
### 压缩
|
||||
|
||||
自动压缩在上下文溢出时触发。`compactEmbeddedPiSessionDirect()` 处理手动压缩:
|
||||
上下文溢出时会触发自动压缩。`compactEmbeddedPiSessionDirect()` 负责手动压缩:
|
||||
|
||||
```typescript
|
||||
const compactResult = await compactEmbeddedPiSessionDirect({
|
||||
@@ -328,18 +338,18 @@ const compactResult = await compactEmbeddedPiSessionDirect({
|
||||
});
|
||||
```
|
||||
|
||||
## 认证与模型解析
|
||||
## 身份验证与模型解析
|
||||
|
||||
### 认证配置文件
|
||||
### 凭证配置
|
||||
|
||||
OpenClaw 维护一个认证配置文件存储,每个提供商有多个 API 密钥:
|
||||
OpenClaw 维护一个凭证配置存储,为每个提供商保存多个 API 密钥:
|
||||
|
||||
```typescript
|
||||
const authStore = ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false });
|
||||
const profileOrder = resolveAuthProfileOrder({ cfg, store: authStore, provider, preferredProfile });
|
||||
```
|
||||
|
||||
配置文件在失败时轮换,并带有冷却跟踪:
|
||||
配置会在失败时轮换,并跟踪冷却状态:
|
||||
|
||||
```typescript
|
||||
await markAuthProfileFailure({ store, profileId, reason, cfg, agentDir });
|
||||
@@ -364,7 +374,7 @@ authStorage.setRuntimeApiKey(model.provider, apiKeyInfo.apiKey);
|
||||
|
||||
### 故障转移
|
||||
|
||||
`FailoverError` 在配置了回退时触发模型回退:
|
||||
配置了回退时,`FailoverError` 会触发模型故障转移:
|
||||
|
||||
```typescript
|
||||
if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
|
||||
@@ -380,11 +390,11 @@ if (fallbackConfigured && isFailoverErrorMessage(errorText)) {
|
||||
|
||||
## Pi 扩展
|
||||
|
||||
OpenClaw 加载自定义 pi 扩展以实现特殊行为:
|
||||
OpenClaw 会加载自定义的 pi 扩展,以实现专门行为:
|
||||
|
||||
### 压缩安全护栏
|
||||
### 压缩保护
|
||||
|
||||
`pi-extensions/compaction-safeguard.ts` 为压缩添加护栏,包括自适应令牌预算以及工具失败和文件操作摘要:
|
||||
`src/agents/pi-hooks/compaction-safeguard.ts` 会为压缩添加护栏,包括自适应 token 预算,以及工具失败和文件操作摘要:
|
||||
|
||||
```typescript
|
||||
if (resolveCompactionMode(params.cfg) === "safeguard") {
|
||||
@@ -395,7 +405,7 @@ if (resolveCompactionMode(params.cfg) === "safeguard") {
|
||||
|
||||
### 上下文裁剪
|
||||
|
||||
`pi-extensions/context-pruning.ts` 实现基于缓存 TTL 的上下文裁剪:
|
||||
`src/agents/pi-hooks/context-pruning.ts` 实现了基于 cache-TTL 的上下文裁剪:
|
||||
|
||||
```typescript
|
||||
if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
|
||||
@@ -409,19 +419,19 @@ if (cfg?.agents?.defaults?.contextPruning?.mode === "cache-ttl") {
|
||||
}
|
||||
```
|
||||
|
||||
## 流式传输与块回复
|
||||
## 流式传输与分块回复
|
||||
|
||||
### 块分块
|
||||
### 分块处理
|
||||
|
||||
`EmbeddedBlockChunker` 管理将流式文本分成离散的回复块:
|
||||
`EmbeddedBlockChunker` 负责将流式文本管理为离散的回复块:
|
||||
|
||||
```typescript
|
||||
const blockChunker = blockChunking ? new EmbeddedBlockChunker(blockChunking) : null;
|
||||
```
|
||||
|
||||
### 思考/最终标签剥离
|
||||
### 思考 / 最终标签剥离
|
||||
|
||||
流式输出被处理以剥离 `<think>`/`<thinking>` 块并提取 `<final>` 内容:
|
||||
流式输出会经过处理,以去除 `<think>` / `<thinking>` 块并提取 `<final>` 内容:
|
||||
|
||||
```typescript
|
||||
const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean }) => {
|
||||
@@ -432,7 +442,7 @@ const stripBlockTags = (text: string, state: { thinking: boolean; final: boolean
|
||||
|
||||
### 回复指令
|
||||
|
||||
回复指令如 `[[media:url]]`、`[[voice]]`、`[[reply:id]]` 被解析和提取:
|
||||
会解析并提取诸如 `[[media:url]]`、`[[voice]]`、`[[reply:id]]` 之类的回复指令:
|
||||
|
||||
```typescript
|
||||
const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDirectives(chunk);
|
||||
@@ -442,7 +452,7 @@ const { text: cleanedText, mediaUrls, audioAsVoice, replyToId } = consumeReplyDi
|
||||
|
||||
### 错误分类
|
||||
|
||||
`pi-embedded-helpers.ts` 对错误进行分类以进行适当处理:
|
||||
`pi-embedded-helpers.ts` 会对错误进行分类,以便进行适当处理:
|
||||
|
||||
```typescript
|
||||
isContextOverflowError(errorText) // Context too large
|
||||
@@ -455,7 +465,7 @@ classifyFailoverReason(errorText) // "auth" | "rate_limit" | "quota" | "time
|
||||
|
||||
### 思考级别回退
|
||||
|
||||
如果思考级别不受支持,它会回退:
|
||||
如果某个思考级别不受支持,它会回退:
|
||||
|
||||
```typescript
|
||||
const fallbackThinking = pickFallbackThinkingLevel({
|
||||
@@ -470,7 +480,7 @@ if (fallbackThinking) {
|
||||
|
||||
## 沙箱集成
|
||||
|
||||
当启用沙箱模式时,工具和路径受到约束:
|
||||
启用沙箱模式时,工具和路径都会受到约束:
|
||||
|
||||
```typescript
|
||||
const sandbox = await resolveSandboxContext({
|
||||
@@ -490,24 +500,24 @@ if (sandboxRoot) {
|
||||
|
||||
### Anthropic
|
||||
|
||||
- 拒绝魔术字符串清除
|
||||
- 连续角色的回合验证
|
||||
- 清理拒绝魔法字符串
|
||||
- 针对连续角色的轮次验证
|
||||
- Claude Code 参数兼容性
|
||||
|
||||
### Google/Gemini
|
||||
### Google / Gemini
|
||||
|
||||
- 回合排序修复(`applyGoogleTurnOrderingFix`)
|
||||
- 工具 schema 清理(`sanitizeToolsForGoogle`)
|
||||
- 会话历史清理(`sanitizeSessionHistory`)
|
||||
- 轮次顺序修复(`applyGoogleTurnOrderingFix`)
|
||||
- 工具模式净化(`sanitizeToolsForGoogle`)
|
||||
- 会话历史净化(`sanitizeSessionHistory`)
|
||||
|
||||
### OpenAI
|
||||
|
||||
- Codex 模型的 `apply_patch` 工具
|
||||
- 面向 Codex 模型的 `apply_patch` 工具
|
||||
- 思考级别降级处理
|
||||
|
||||
## TUI 集成
|
||||
|
||||
OpenClaw 还有一个本地 TUI 模式,直接使用 pi-tui 组件:
|
||||
OpenClaw 还提供本地 TUI 模式,可直接使用 pi-tui 组件:
|
||||
|
||||
```typescript
|
||||
// src/tui/tui.ts
|
||||
@@ -516,104 +526,46 @@ import { ... } from "@mariozechner/pi-tui";
|
||||
|
||||
这提供了与 pi 原生模式类似的交互式终端体验。
|
||||
|
||||
## 与 Pi CLI 的主要区别
|
||||
## 与 Pi CLI 的关键差异
|
||||
|
||||
| 方面 | Pi CLI | OpenClaw 嵌入式 |
|
||||
| -------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` 的 SDK |
|
||||
| 工具 | 默认编码工具 | 自定义 OpenClaw 工具套件 |
|
||||
| 系统提示 | AGENTS.md + prompts | 按渠道/上下文动态生成 |
|
||||
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/`) |
|
||||
| 认证 | 单一凭证 | 带轮换的多配置文件 |
|
||||
| 扩展 | 从磁盘加载 | 编程方式 + 磁盘路径 |
|
||||
| 事件处理 | TUI 渲染 | 基于回调(onBlockReply 等) |
|
||||
| 方面 | Pi CLI | OpenClaw 内嵌版 |
|
||||
| ---------- | ----------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| 调用方式 | `pi` 命令 / RPC | 通过 `createAgentSession()` 使用 SDK |
|
||||
| 工具 | 默认编码工具 | 自定义 OpenClaw 工具套件 |
|
||||
| 系统提示词 | AGENTS.md + prompts | 按渠道 / 上下文动态生成 |
|
||||
| 会话存储 | `~/.pi/agent/sessions/` | `~/.openclaw/agents/<agentId>/sessions/`(或 `$OPENCLAW_STATE_DIR/agents/<agentId>/sessions/`) |
|
||||
| 身份验证 | 单一凭证 | 支持轮换的多配置 |
|
||||
| 扩展 | 从磁盘加载 | 通过编程方式 + 磁盘路径 |
|
||||
| 事件处理 | TUI 渲染 | 基于回调(`onBlockReply` 等) |
|
||||
|
||||
## 未来考虑
|
||||
|
||||
可能需要重构的领域:
|
||||
潜在的重构方向包括:
|
||||
|
||||
1. **工具签名对齐**:目前在 pi-agent-core 和 pi-coding-agent 签名之间适配
|
||||
2. **会话管理器包装**:`guardSessionManager` 增加了安全性但增加了复杂性
|
||||
1. **工具签名对齐**:当前需要在 pi-agent-core 与 pi-coding-agent 的签名之间进行适配
|
||||
2. **会话管理器包装**:`guardSessionManager` 增加了安全性,但也提高了复杂度
|
||||
3. **扩展加载**:可以更直接地使用 pi 的 `ResourceLoader`
|
||||
4. **流式处理器复杂性**:`subscribeEmbeddedPiSession` 已经变得很大
|
||||
5. **提供商特殊情况**:许多提供商特定的代码路径,pi 可能可以处理
|
||||
4. **流式处理器复杂度**:`subscribeEmbeddedPiSession` 已经变得较大
|
||||
5. **提供商特殊行为**:存在许多提供商特定代码路径,未来 pi 或许可以统一处理
|
||||
|
||||
## 测试
|
||||
|
||||
所有涵盖 pi 集成及其扩展的现有测试:
|
||||
Pi 集成的覆盖范围包括以下测试套件:
|
||||
|
||||
- `src/agents/pi-embedded-block-chunker.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.buildbootstrapcontextfiles.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.classifyfailoverreason.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.downgradeopenai-reasoning.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.formatrawassistanterrorforui.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.image-dimension-error.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.image-size-error.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.isautherrormessage.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.isbillingerrormessage.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.iscloudcodeassistformaterror.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.iscompactionfailureerror.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.iscontextoverflowerror.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.isfailovererrormessage.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.islikelycontextoverflowerror.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.ismessagingtoolduplicate.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.messaging-duplicate.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.normalizetextforcomparison.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.resolvebootstrapmaxchars.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.keeps-tool-call-tool-result-ids-unchanged.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitize-session-messages-images.removes-empty-assistant-text-blocks-but-preserves.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitizegoogleturnordering.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitizesessionmessagesimages-thought-signature-stripping.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitizetoolcallid.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.sanitizeuserfacingtext.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.stripthoughtsignatures.test.ts`
|
||||
- `src/agents/pi-embedded-helpers.validate-turns.test.ts`
|
||||
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(实时)
|
||||
- `src/agents/pi-embedded-runner-extraparams.test.ts`
|
||||
- `src/agents/pi-embedded-runner.applygoogleturnorderingfix.test.ts`
|
||||
- `src/agents/pi-embedded-runner.buildembeddedsandboxinfo.test.ts`
|
||||
- `src/agents/pi-embedded-runner.createsystempromptoverride.test.ts`
|
||||
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.falls-back-provider-default-per-dm-not.test.ts`
|
||||
- `src/agents/pi-embedded-runner.get-dm-history-limit-from-session-key.returns-undefined-sessionkey-is-undefined.test.ts`
|
||||
- `src/agents/pi-embedded-runner.google-sanitize-thinking.test.ts`
|
||||
- `src/agents/pi-embedded-runner.guard.test.ts`
|
||||
- `src/agents/pi-embedded-runner.limithistoryturns.test.ts`
|
||||
- `src/agents/pi-embedded-runner.resolvesessionagentids.test.ts`
|
||||
- `src/agents/pi-embedded-runner.run-embedded-pi-agent.auth-profile-rotation.test.ts`
|
||||
- `src/agents/pi-embedded-runner.sanitize-session-history.test.ts`
|
||||
- `src/agents/pi-embedded-runner.splitsdktools.test.ts`
|
||||
- `src/agents/pi-embedded-runner.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.code-span-awareness.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.reply-tags.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.calls-onblockreplyflush-before-tool-execution-start-preserve.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-append-text-end-content-is.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-call-onblockreplyflush-callback-is-not.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-duplicate-text-end-repeats-full.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.does-not-emit-duplicate-block-replies-text.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-block-replies-text-end-does-not.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.emits-reasoning-as-separate-message-enabled.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.filters-final-suppresses-output-without-start-tag.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.includes-canvas-action-metadata-tool-summaries.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-assistanttexts-final-answer-block-replies-are.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.keeps-indented-fenced-blocks-intact.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.reopens-fenced-blocks-splitting-inside-them.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.splits-long-single-line-fenced-blocks-reopen.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.streams-soft-chunks-paragraph-preference.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.subscribeembeddedpisession.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.suppresses-message-end-block-replies-message-tool.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.subscribe-embedded-pi-session.waits-multiple-compaction-retries-before-resolving.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe.tools.test.ts`
|
||||
- `src/agents/pi-embedded-utils.test.ts`
|
||||
- `src/agents/pi-extensions/compaction-safeguard.test.ts`
|
||||
- `src/agents/pi-extensions/context-pruning.test.ts`
|
||||
- `src/agents/pi-*.test.ts`
|
||||
- `src/agents/pi-auth-json.test.ts`
|
||||
- `src/agents/pi-embedded-*.test.ts`
|
||||
- `src/agents/pi-embedded-helpers*.test.ts`
|
||||
- `src/agents/pi-embedded-runner*.test.ts`
|
||||
- `src/agents/pi-embedded-runner/**/*.test.ts`
|
||||
- `src/agents/pi-embedded-subscribe*.test.ts`
|
||||
- `src/agents/pi-tools*.test.ts`
|
||||
- `src/agents/pi-tool-definition-adapter*.test.ts`
|
||||
- `src/agents/pi-settings.test.ts`
|
||||
- `src/agents/pi-tool-definition-adapter.test.ts`
|
||||
- `src/agents/pi-tools-agent-config.test.ts`
|
||||
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-b.test.ts`
|
||||
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-d.test.ts`
|
||||
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping-f.test.ts`
|
||||
- `src/agents/pi-tools.create-openclaw-coding-tools.adds-claude-style-aliases-schemas-without-dropping.test.ts`
|
||||
- `src/agents/pi-tools.policy.test.ts`
|
||||
- `src/agents/pi-tools.safe-bins.test.ts`
|
||||
- `src/agents/pi-tools.workspace-paths.test.ts`
|
||||
- `src/agents/pi-hooks/**/*.test.ts`
|
||||
|
||||
实时 / 按需启用:
|
||||
|
||||
- `src/agents/pi-embedded-runner-extraparams.live.test.ts`(启用 `OPENCLAW_LIVE_TEST=1`)
|
||||
|
||||
有关当前运行命令,请参见 [Pi 开发工作流](/pi-dev)。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"description": "OpenClaw ACP runtime backend via acpx",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -102,7 +102,7 @@ Required behavior when ACP backend is unavailable:
|
||||
|
||||
1. Do not immediately ask the user to pick an alternate path.
|
||||
2. First attempt automatic local repair:
|
||||
- ensure plugin-local pinned acpx is installed in `extensions/acpx`
|
||||
- ensure plugin-local pinned acpx is installed in the bundled ACPX plugin package
|
||||
- verify `${ACPX_CMD} --version`
|
||||
3. After reinstall/repair, restart the gateway and explicitly offer to run that restart for the user.
|
||||
4. Retry ACP thread spawn once after repair.
|
||||
@@ -120,20 +120,21 @@ Do not default to subagent runtime for these requests.
|
||||
For this repo, direct `acpx` calls must follow the same pinned policy as the `@openclaw/acpx` extension package.
|
||||
|
||||
1. Prefer plugin-local binary, not global PATH:
|
||||
- `./extensions/acpx/node_modules/.bin/acpx`
|
||||
- `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx`
|
||||
2. Resolve pinned version from extension dependency:
|
||||
- `node -e "console.log(require('./extensions/acpx/package.json').dependencies.acpx)"`
|
||||
- `node -e "console.log(require(process.env.ACPX_PLUGIN_ROOT + '/package.json').dependencies.acpx)"`
|
||||
3. If binary is missing or version mismatched, install plugin-local pinned version:
|
||||
- `cd extensions/acpx && npm install --omit=dev --no-save acpx@<pinnedVersion>`
|
||||
- `cd "$ACPX_PLUGIN_ROOT" && npm install --omit=dev --no-save acpx@<pinnedVersion>`
|
||||
4. Verify before use:
|
||||
- `./extensions/acpx/node_modules/.bin/acpx --version`
|
||||
- `${ACPX_PLUGIN_ROOT}/node_modules/.bin/acpx --version`
|
||||
5. If install/repair changed ACPX artifacts, restart the gateway and offer to run the restart.
|
||||
6. Do not run `npm install -g acpx` unless the user explicitly asks for global install.
|
||||
|
||||
Set and reuse:
|
||||
|
||||
```bash
|
||||
ACPX_CMD="./extensions/acpx/node_modules/.bin/acpx"
|
||||
ACPX_PLUGIN_ROOT="<bundled-acpx-plugin-root>"
|
||||
ACPX_CMD="$ACPX_PLUGIN_ROOT/node_modules/.bin/acpx"
|
||||
```
|
||||
|
||||
## Direct acpx path ("telephone game")
|
||||
@@ -227,7 +228,7 @@ If your local Cursor install still exposes ACP as `agent acp`, set that as the `
|
||||
### Failure handling
|
||||
|
||||
- `acpx: command not found`:
|
||||
- for thread-spawn ACP requests, install plugin-local pinned acpx in `extensions/acpx` immediately
|
||||
- for thread-spawn ACP requests, install plugin-local pinned acpx in the bundled ACPX plugin package immediately
|
||||
- restart gateway after install and offer to run the restart automatically
|
||||
- then retry once
|
||||
- do not ask for install permission first unless policy explicitly requires it
|
||||
|
||||
@@ -3,6 +3,10 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
bundledDistPluginRootAt,
|
||||
bundledPluginRootAt,
|
||||
} from "../../../test/helpers/bundled-plugin-paths.js";
|
||||
import {
|
||||
ACPX_BUNDLED_BIN,
|
||||
ACPX_PINNED_VERSION,
|
||||
@@ -39,10 +43,10 @@ describe("acpx plugin config parsing", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("prefers the workspace plugin root for dist/extensions/acpx bundles", () => {
|
||||
it("prefers the workspace plugin root for dist plugin bundles", () => {
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "acpx-root-workspace-"));
|
||||
const workspacePluginRoot = path.join(repoRoot, "extensions", "acpx");
|
||||
const bundledPluginRoot = path.join(repoRoot, "dist", "extensions", "acpx");
|
||||
const workspacePluginRoot = bundledPluginRootAt(repoRoot, "acpx");
|
||||
const bundledPluginRoot = bundledDistPluginRootAt(repoRoot, "acpx");
|
||||
try {
|
||||
fs.mkdirSync(workspacePluginRoot, { recursive: true });
|
||||
fs.mkdirSync(bundledPluginRoot, { recursive: true });
|
||||
|
||||
@@ -11,7 +11,6 @@ export type AcpxPermissionMode = (typeof ACPX_PERMISSION_MODES)[number];
|
||||
export const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"] as const;
|
||||
export type AcpxNonInteractivePermissionPolicy = (typeof ACPX_NON_INTERACTIVE_POLICIES)[number];
|
||||
|
||||
export const ACPX_PINNED_VERSION = "0.3.1";
|
||||
export const ACPX_VERSION_ANY = "any";
|
||||
const ACPX_BIN_NAME = process.platform === "win32" ? "acpx.cmd" : "acpx";
|
||||
|
||||
@@ -58,6 +57,14 @@ export function resolveAcpxPluginRoot(moduleUrl: string = import.meta.url): stri
|
||||
}
|
||||
|
||||
export const ACPX_PLUGIN_ROOT = resolveAcpxPluginRoot();
|
||||
const pluginPkg = JSON.parse(fs.readFileSync(path.join(ACPX_PLUGIN_ROOT, "package.json"), "utf8"));
|
||||
const acpxVersion: unknown = pluginPkg?.dependencies?.acpx;
|
||||
if (typeof acpxVersion !== "string" || acpxVersion.trim() === "") {
|
||||
throw new Error(
|
||||
`Could not read acpx version from ${path.join(ACPX_PLUGIN_ROOT, "package.json")} — expected a non-empty string at dependencies.acpx`
|
||||
);
|
||||
}
|
||||
export const ACPX_PINNED_VERSION: string = acpxVersion.replace(/^[^0-9]*/, "");
|
||||
export const ACPX_BUNDLED_BIN = path.join(ACPX_PLUGIN_ROOT, "node_modules", ".bin", ACPX_BIN_NAME);
|
||||
export function buildAcpxLocalInstallCommand(version: string = ACPX_PINNED_VERSION): string {
|
||||
return `npm install --omit=dev --no-save --package-lock=false acpx@${version}`;
|
||||
|
||||
@@ -3,9 +3,10 @@ import { chmod, mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { bundledPluginFile } from "../../../../test/helpers/bundled-plugin-paths.js";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
const proxyPath = path.resolve("extensions/acpx/src/runtime-internals/mcp-proxy.mjs");
|
||||
const proxyPath = path.resolve(bundledPluginFile("acpx", "src/runtime-internals/mcp-proxy.mjs"));
|
||||
|
||||
async function makeTempScript(name: string, content: string): Promise<string> {
|
||||
const dir = await mkdtemp(path.join(os.tmpdir(), "openclaw-acpx-mcp-proxy-"));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/extensions/plugin-registration.js";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
import amazonBedrockPlugin from "./index.js";
|
||||
|
||||
describe("amazon-bedrock provider plugin", () => {
|
||||
|
||||
@@ -41,6 +41,10 @@ export default definePluginEntry({
|
||||
},
|
||||
},
|
||||
resolveConfigApiKey: ({ env }) => resolveBedrockConfigApiKey(env),
|
||||
capabilities: {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
wrapStreamFn: ({ modelId, streamFn }) =>
|
||||
isAnthropicBedrockModel(modelId) ? streamFn : createBedrockNoCacheWrapper(streamFn),
|
||||
resolveDefaultThinkingLevel: ({ modelId }) =>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js";
|
||||
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
|
||||
|
||||
describeProviderContracts("amazon-bedrock");
|
||||
|
||||
59
extensions/anthropic-vertex/index.test.ts
Normal file
59
extensions/anthropic-vertex/index.test.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { registerSingleProviderPlugin } from "../../test/helpers/plugins/plugin-registration.js";
|
||||
import anthropicVertexPlugin from "./index.js";
|
||||
|
||||
describe("anthropic-vertex provider plugin", () => {
|
||||
it("resolves the ADC marker through the provider hook", () => {
|
||||
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
|
||||
|
||||
expect(
|
||||
provider.resolveConfigApiKey?.({
|
||||
provider: "anthropic-vertex",
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
} as NodeJS.ProcessEnv,
|
||||
} as never),
|
||||
).toBe("gcp-vertex-credentials");
|
||||
});
|
||||
|
||||
it("merges the implicit Vertex catalog into explicit provider overrides", async () => {
|
||||
const provider = registerSingleProviderPlugin(anthropicVertexPlugin);
|
||||
|
||||
const result = await provider.catalog?.run({
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
"anthropic-vertex": {
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
env: {
|
||||
ANTHROPIC_VERTEX_USE_GCP_METADATA: "true",
|
||||
GOOGLE_CLOUD_LOCATION: "us-east5",
|
||||
} as NodeJS.ProcessEnv,
|
||||
resolveProviderApiKey: () => ({ apiKey: undefined }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: undefined,
|
||||
discoveryApiKey: undefined,
|
||||
mode: "none",
|
||||
source: "none",
|
||||
}),
|
||||
} as never);
|
||||
|
||||
expect(result).toEqual({
|
||||
provider: {
|
||||
api: "anthropic-messages",
|
||||
apiKey: "gcp-vertex-credentials",
|
||||
baseUrl: "https://europe-west4-aiplatform.googleapis.com",
|
||||
headers: { "x-test-header": "1" },
|
||||
models: [
|
||||
expect.objectContaining({ id: "claude-opus-4-6" }),
|
||||
expect.objectContaining({ id: "claude-sonnet-4-6" }),
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
44
extensions/anthropic-vertex/index.ts
Normal file
44
extensions/anthropic-vertex/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import {
|
||||
mergeImplicitAnthropicVertexProvider,
|
||||
resolveAnthropicVertexConfigApiKey,
|
||||
resolveImplicitAnthropicVertexProvider,
|
||||
} from "./api.js";
|
||||
|
||||
const PROVIDER_ID = "anthropic-vertex";
|
||||
|
||||
export default definePluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Anthropic Vertex Provider",
|
||||
description: "Bundled Anthropic Vertex provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Anthropic Vertex",
|
||||
docsPath: "/providers/models",
|
||||
auth: [],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: async (ctx) => {
|
||||
const implicit = await resolveImplicitAnthropicVertexProvider({
|
||||
env: ctx.env,
|
||||
});
|
||||
if (!implicit) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
provider: mergeImplicitAnthropicVertexProvider({
|
||||
existing: ctx.config.models?.providers?.[PROVIDER_ID],
|
||||
implicit,
|
||||
}),
|
||||
};
|
||||
},
|
||||
},
|
||||
resolveConfigApiKey: ({ env }) => resolveAnthropicVertexConfigApiKey(env),
|
||||
capabilities: {
|
||||
providerFamily: "anthropic",
|
||||
dropThinkingBlockModelHints: ["claude"],
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
10
extensions/anthropic-vertex/openclaw.plugin.json
Normal file
10
extensions/anthropic-vertex/openclaw.plugin.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "anthropic-vertex",
|
||||
"enabledByDefault": true,
|
||||
"providers": ["anthropic-vertex"],
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {}
|
||||
}
|
||||
}
|
||||
12
extensions/anthropic-vertex/package.json
Normal file
12
extensions/anthropic-vertex/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
3
extensions/anthropic-vertex/provider.contract.test.ts
Normal file
3
extensions/anthropic-vertex/provider.contract.test.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
|
||||
|
||||
describeProviderContracts("anthropic-vertex");
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-provider",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js";
|
||||
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
|
||||
|
||||
describePluginRegistrationContract({
|
||||
pluginId: "anthropic",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/extensions/provider-runtime-contract.js";
|
||||
import { describeAnthropicProviderRuntimeContract } from "../../test/helpers/plugins/provider-runtime-contract.js";
|
||||
|
||||
describeAnthropicProviderRuntimeContract();
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { describeProviderContracts } from "../../test/helpers/extensions/provider-contract.js";
|
||||
import { describeProviderContracts } from "../../test/helpers/plugins/provider-contract.js";
|
||||
|
||||
describeProviderContracts("anthropic");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# BlueBubbles extension (developer reference)
|
||||
|
||||
This directory contains the **BlueBubbles external channel plugin** for OpenClaw.
|
||||
This package contains the **BlueBubbles external channel plugin** for OpenClaw.
|
||||
|
||||
If you’re looking for **how to use BlueBubbles as an agent/tool user**, see:
|
||||
|
||||
@@ -8,22 +8,22 @@ If you’re looking for **how to use BlueBubbles as an agent/tool user**, see:
|
||||
|
||||
## Layout
|
||||
|
||||
- Extension package: `extensions/bluebubbles/` (entry: `index.ts`).
|
||||
- Channel implementation: `extensions/bluebubbles/src/channel.ts`.
|
||||
- Webhook handling: `extensions/bluebubbles/src/monitor.ts` (register per-account route via `registerPluginHttpRoute`).
|
||||
- REST helpers: `extensions/bluebubbles/src/send.ts` + `extensions/bluebubbles/src/probe.ts`.
|
||||
- Runtime bridge: `extensions/bluebubbles/src/runtime.ts` (set via `api.runtime`).
|
||||
- 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 `extensions/bluebubbles/src/probe.ts` for health checks.
|
||||
- `sendMessageBlueBubbles` in `extensions/bluebubbles/src/send.ts` for text delivery.
|
||||
- `resolveChatGuidForTarget` in `extensions/bluebubbles/src/send.ts` for chat lookup.
|
||||
- `sendBlueBubblesReaction` in `extensions/bluebubbles/src/reactions.ts` for tapbacks.
|
||||
- `sendBlueBubblesTyping` + `markBlueBubblesChatRead` in `extensions/bluebubbles/src/chat.ts`.
|
||||
- `downloadBlueBubblesAttachment` in `extensions/bluebubbles/src/attachments.ts` for inbound media.
|
||||
- `buildBlueBubblesApiUrl` + `blueBubblesFetchWithTimeout` in `extensions/bluebubbles/src/types.ts` for shared REST plumbing.
|
||||
- `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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describePackageManifestContract } from "../../test/helpers/extensions/package-manifest-contract.js";
|
||||
import { describePackageManifestContract } from "../../test/helpers/plugins/package-manifest-contract.js";
|
||||
|
||||
describePackageManifestContract({
|
||||
pluginId: "bluebubbles",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "@openclaw/bluebubbles",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"description": "OpenClaw BlueBubbles channel plugin",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.3.28"
|
||||
"openclaw": ">=2026.3.29"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -38,9 +38,8 @@
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/bluebubbles",
|
||||
"localPath": "extensions/bluebubbles",
|
||||
"defaultChoice": "npm",
|
||||
"minHostVersion": ">=2026.3.28"
|
||||
"minHostVersion": ">=2026.3.29"
|
||||
},
|
||||
"release": {
|
||||
"publishToNpm": true
|
||||
|
||||
@@ -5,9 +5,11 @@ import {
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "../../../test/helpers/extensions/bluebubbles-monitor.js";
|
||||
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
|
||||
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { fetchBlueBubblesHistory } from "./history.js";
|
||||
import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js";
|
||||
import type { NormalizedWebhookMessage } from "./monitor-normalize.js";
|
||||
import { resetBlueBubblesSelfChatCache } from "./monitor-self-chat-cache.js";
|
||||
import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js";
|
||||
import {
|
||||
@@ -27,8 +29,6 @@ import {
|
||||
resetBlueBubblesParticipantContactNameCacheForTest,
|
||||
setBlueBubblesParticipantContactDepsForTest,
|
||||
} from "./participant-contact-names.js";
|
||||
import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js";
|
||||
import type { NormalizedWebhookMessage } from "./monitor-normalize.js";
|
||||
import type { OpenClawConfig, PluginRuntime } from "./runtime-api.js";
|
||||
|
||||
// Mock dependencies
|
||||
@@ -155,7 +155,10 @@ function installTimingAwareInboundDebouncer(core: PluginRuntime) {
|
||||
core.channel.debounce.createInboundDebouncer = vi.fn((params: any) => {
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
type Item = any;
|
||||
const buckets = new Map<string, { items: Item[]; timer: ReturnType<typeof setTimeout> | null }>();
|
||||
const buckets = new Map<
|
||||
string,
|
||||
{ items: Item[]; timer: ReturnType<typeof setTimeout> | null }
|
||||
>();
|
||||
|
||||
const flush = async (key: string) => {
|
||||
const bucket = buckets.get(key);
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "../../../test/helpers/extensions/bluebubbles-monitor.js";
|
||||
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
|
||||
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { fetchBlueBubblesHistory } from "./history.js";
|
||||
import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js";
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
createTestWizardPrompter,
|
||||
runSetupWizardConfigure,
|
||||
type WizardPrompter,
|
||||
} from "../../../test/helpers/extensions/setup-wizard.js";
|
||||
} from "../../../test/helpers/plugins/setup-wizard.js";
|
||||
import { resolveBlueBubblesAccount } from "./accounts.js";
|
||||
import { BlueBubblesConfigSchema } from "./config-schema.js";
|
||||
import {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { describeBundledWebSearchFastPathContract } from "../../test/helpers/extensions/bundled-web-search-fast-path-contract.js";
|
||||
import { describeBundledWebSearchFastPathContract } from "../../test/helpers/plugins/bundled-web-search-fast-path-contract.js";
|
||||
|
||||
describeBundledWebSearchFastPathContract("brave");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/brave-plugin",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Brave plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describePluginRegistrationContract } from "../../test/helpers/extensions/plugin-registration-contract.js";
|
||||
import { describePluginRegistrationContract } from "../../test/helpers/plugins/plugin-registration-contract.js";
|
||||
|
||||
describePluginRegistrationContract({
|
||||
pluginId: "brave",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { describeWebSearchProviderContracts } from "../../test/helpers/extensions/web-search-provider-contract.js";
|
||||
import { describeWebSearchProviderContracts } from "../../test/helpers/plugins/web-search-provider-contract.js";
|
||||
|
||||
describeWebSearchProviderContracts("brave");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createTestPluginApi } from "../../test/helpers/extensions/plugin-api.js";
|
||||
import { createTestPluginApi } from "../../test/helpers/plugins/plugin-api.js";
|
||||
import type { OpenClawPluginApi } from "./runtime-api.js";
|
||||
|
||||
const runtimeApiMocks = vi.hoisted(() => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/browser-plugin",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw browser tool plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -5,7 +5,7 @@ export {
|
||||
type CliRuntimeCapture,
|
||||
} from "../../src/cli/test-runtime-capture.js";
|
||||
export type { OpenClawConfig } from "openclaw/plugin-sdk/browser-support";
|
||||
export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/extensions/auth-token-assertions.ts";
|
||||
export { withEnv, withEnvAsync } from "../../test/helpers/extensions/env.ts";
|
||||
export { withFetchPreconnect, type FetchMock } from "../../test/helpers/extensions/fetch-mock.ts";
|
||||
export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/extensions/temp-home.ts";
|
||||
export { expectGeneratedTokenPersistedToGatewayAuth } from "../../test/helpers/plugins/auth-token-assertions.ts";
|
||||
export { withEnv, withEnvAsync } from "../../test/helpers/plugins/env.ts";
|
||||
export { withFetchPreconnect, type FetchMock } from "../../test/helpers/plugins/fetch-mock.ts";
|
||||
export { createTempHomeEnv, type TempHomeEnv } from "../../test/helpers/plugins/temp-home.ts";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/byteplus-provider",
|
||||
"version": "2026.3.28",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw BytePlus provider plugin",
|
||||
"type": "module",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user