Compare commits

..

1393 Commits

Author SHA1 Message Date
Vincent Koc
b2ae58a348 chore(changelog): add anthropic failover entry 2026-03-30 12:49:41 +09:00
Vincent Koc
ae3bbc430b Merge branch 'main' into fix/api-error-unexpected-transient 2026-03-29 20:49:12 -07:00
Vincent Koc
22f56433e0 fix(perf): bypass matrix facade for core helpers 2026-03-30 12:43:55 +09:00
Vincent Koc
c142a396f4 fix(memory): rebind qmd collections on pattern drift (#57438) 2026-03-30 12:43:44 +09:00
Gustavo Madeira Santana
313fdf5adf Memory Host: make backend-config path tests portable 2026-03-29 23:41:42 -04:00
Vincent Koc
8c163c14bc feat(tasks): harden maintenance repair paths (#57439)
* feat(tasks): harden maintenance repair paths

* fix(tasks): address follow-up maintenance review comments
2026-03-30 12:37:31 +09:00
Gustavo Madeira Santana
aaf47ca54b WhatsApp: align deliver-reply test mocks with imports 2026-03-29 23:36:38 -04:00
Gustavo Madeira Santana
8b0cbebe43 Telegram: align channel test with runtime probe precedence 2026-03-29 23:34:03 -04:00
zijiess
169d5f9e7e fix(agents): classify Anthropic "unexpected error" api_error as transient (#57010)
Anthropic sometimes returns api_error payloads with message "An unexpected
error occurred while processing the response" during mid-stream failures.
This was not matched by API_ERROR_TRANSIENT_SIGNALS_RE, causing the error
to surface as terminal instead of triggering retry/fallback.

Add "unexpected error" to the transient signal regex. This follows the
same pattern as prior fixes for "Internal server error" (#23193) and
overloaded_error 529 (#34535).

Closes #57010
2026-03-30 11:21:37 +08:00
Gustavo Madeira Santana
0fe193db6a Discord: restore message utils media-runtime mocks 2026-03-29 23:21:34 -04:00
Gustavo Madeira Santana
3efcc90034 Tests: read packed manifest without shell tar 2026-03-29 23:21:24 -04:00
Gustavo Madeira Santana
1482afae57 Status: keep fast JSON off task audit runtime 2026-03-29 23:17:28 -04:00
Gustavo Madeira Santana
e86a2183df Tests: type package contract npm pack helper 2026-03-29 23:10:58 -04:00
Vincent Koc
c52fac836c feat(tasks): add status health and maintenance command (#57423)
* feat(tasks): add status health and maintenance command

* fix(tasks): address status and maintenance review feedback
2026-03-30 12:10:44 +09:00
Vincent Koc
d28349c48e fix(test): align channel mocks with runtime exports 2026-03-30 12:08:27 +09:00
Vincent Koc
da35718cb2 fix(memory): add qmd mcporter search tool override (#57363)
* fix(memory): add qmd mcporter search tool override

* fix(memory): tighten qmd search tool override guards

* chore(config): drop generated docs baselines from qmd pr

* fix(memory): keep explicit qmd query override on v2 args

* docs(changelog): normalize qmd search tool attribution

* fix(memory): reuse v1 qmd tool after query fallback
2026-03-30 12:07:32 +09:00
Gustavo Madeira Santana
e7984272a7 Tests: make package contract guardrails Windows-safe 2026-03-29 23:07:27 -04:00
Vincent Koc
1421fced04 Update CHANGELOG.md 2026-03-30 12:06:19 +09:00
Yauheni Shauchenka
a6bc51f944 feat(openai): forward text verbosity (#47106)
* feat(openai): forward text verbosity across responses transports

* fix(openai): remove stale verbosity rebase artifact

* chore(changelog): add openai text verbosity entry

---------

Co-authored-by: Ubuntu <ubuntu@vps-1c82b947.vps.ovh.net>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 12:04:35 +09:00
Gustavo Madeira Santana
51e053d0e8 OpenAI: preserve oauth exports in index tests 2026-03-29 22:54:48 -04:00
Vincent Koc
03a03c2dc4 fix(ci): restore skill fixtures and security doc anchors 2026-03-30 11:41:08 +09:00
Gustavo Madeira Santana
f380305ee4 Tests: restore extension plugin test seams 2026-03-29 22:38:44 -04:00
Gustavo Madeira Santana
ecb14338d4 Matrix: keep runtime wrapper off monolithic sdk root 2026-03-29 22:38:44 -04:00
Vincent Koc
e57b3618fc feat(tasks): move task ledger to sqlite and add audit CLI (#57361)
* feat(tasks): move task ledger to sqlite

* feat(tasks): add task run audit command

* style(tasks): normalize audit command formatting

* fix(tasks): address audit summary and sqlite perms

* fix(tasks): avoid duplicate lost audit findings
2026-03-30 11:34:51 +09:00
Ayaan Zaidi
6f09a68ae7 fix: finalize LLM idle timeout landing (#55072) (thanks @liuy) 2026-03-30 08:04:42 +05:30
Ayaan Zaidi
179f713c88 fix: unify idle timeout with runner abort path 2026-03-30 08:04:42 +05:30
Liu Yuan
84b72e66b9 feat: add LLM idle timeout for streaming responses
Problem: When LLM stops responding, the agent hangs for ~5 minutes with no feedback.
Users had to use /stop to recover.

Solution: Add idle timeout detection for LLM streaming responses.
2026-03-30 08:04:42 +05:30
qsam
47839d3b9a fix(mattermost): detect stale websocket after bot disable/enable cycle (#53604)
Merged via squash.

Prepared head SHA: 818d437a54
Co-authored-by: Qinsam <19649380+Qinsam@users.noreply.github.com>
Co-authored-by: mukhtharcm <56378562+mukhtharcm@users.noreply.github.com>
Reviewed-by: @mukhtharcm
2026-03-30 07:54:59 +05:30
openperf
c6ded0fa54 fix(gateway): coerce streaming tool-call argument deltas to object in client tools 2026-03-30 07:53:51 +05:30
Gustavo Madeira Santana
6fe24a6f2c Matrix: fix source-checkout runtime wrapper 2026-03-29 22:20:41 -04:00
Robin Waslander
acf8470f09 fix(status): cap cache hit rate at 100% in status display (#57400)
formatTokensCompact() computed cache rate using totalTokens as the
denominator. Legacy rows with undersized totalTokens produced
impossible values like 120%. Use inputTokens + cacheRead + cacheWrite
when prompt-side fields are available, falling back to
max(totalTokens, cacheRead + cacheWrite) for legacy data.

Fixes #26643
2026-03-30 04:09:57 +02:00
Peter Steinberger
25074de838 fix: restore lightweight root help startup 2026-03-30 03:08:53 +01:00
Gustavo Madeira Santana
2481c0a9b6 Tests: keep channel helpers on public plugin surfaces 2026-03-29 22:08:19 -04:00
Ayaan Zaidi
b3c69b941e fix: add orphaned session key migration (#57217)
* fix: add orphaned session key migration

* fix: address session migration review comments
2026-03-30 07:36:46 +05:30
Peter Steinberger
9d005e6fbb fix: stabilize telegram contract runtime coverage 2026-03-30 03:02:25 +01:00
MerlinMiao88888888
9c2d22e77f scripts: respect gateway.bind config when OPENCLAW_GATEWAY_BIND not set (#55453)
* scripts: respect gateway.bind config when OPENCLAW_GATEWAY_BIND not set

* scripts: keep Podman bind precedence focused

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-03-29 22:00:21 -04:00
Gustavo Madeira Santana
acea28a9bb Telegram: fix extension-fast test seams 2026-03-29 21:54:47 -04:00
Josh Avant
5e4a64848f fix(exec): harden async approval followup delivery in webchat-only sessions (#57359)
* fix(exec): harden approval followup delivery fallback

* refactor(delivery): share best-effort followup routing helpers

* test(subagents): cover webchat-only completion announce delivery

* docs(exec): clarify async followup delivery behavior

* fix(exec): harden delivery downgrade logging

* test(gateway): cover multi-channel best-effort fallback

* fix(exec): preserve webchat origin on session-only followups

* fix(subagents): keep internal announces channel-less
2026-03-29 20:54:13 -05:00
Gustavo Madeira Santana
f44174cf61 Extensions: stabilize telegram registry contracts 2026-03-29 21:42:58 -04:00
Peter Steinberger
e92440e9f4 fix: replace weak randomness in feishu test support 2026-03-30 02:41:25 +01:00
Peter Steinberger
fec51572a3 fix: stabilize gate and extension boundary checks 2026-03-30 02:37:36 +01:00
Vincent Koc
66f8fb9e9b docs: fix P2 in security -- normalize Security audit checklist heading to sentence case 2026-03-30 10:19:51 +09:00
Vincent Koc
542f17a674 docs: fix P2s in hooks -- text language tag on log block, claude-sonnet-4-6 model example 2026-03-30 10:19:51 +09:00
Vincent Koc
7ed5a4a33d docs: fix P2s in faq -- remote mode accordion title, gpt-5.4 model refs, stable/beta cross-reference 2026-03-30 10:19:51 +09:00
Vincent Koc
d19ccde297 docs: fix P2s in configuration-reference -- built-in model catalog, PI_CODING_AGENT_DIR legacy note, deduplicate Identity section 2026-03-30 10:19:51 +09:00
Vincent Koc
0c94420164 docs: fix remaining CLI P1s -- global flags, onboard, logs options
- Add --container global flag
- onboard: add --skip-search, --cloudflare-ai-gateway-account-id, --cloudflare-ai-gateway-gateway-id
- logs: add full options list (--limit, --max-bytes, --follow, --interval, --local-time, etc.)
2026-03-30 10:19:51 +09:00
Vincent Koc
50d815579c docs: consolidate security page structure and add navigation
- Merge 3 duplicate trust-model sections into one (Scope first + Deployment/host trust)
- Promote "What the audit checks" from h3 to h2 (standalone topic, not child of Shared inbox)
- Add "On this page" navigation links at the top for the 1200+ line page
2026-03-30 10:19:51 +09:00
Vincent Koc
726ae0b8af docs: fix discord.md P1s -- internal terminology and wrong CLI command
- Replace "Carbon component instances" with public description
- Fix "openclaw gateway restart" (no such subcommand) with correct restart guidance
2026-03-30 10:19:51 +09:00
Vincent Koc
381bfdf031 docs: fix architecture.md P1s and P2s
- Remove stale "Qwen portal" reference (no such bundled plugin)
- Add activate hook note (legacy alias for register)
- Add api.runtime.imageGeneration documentation (generate, listProviders)
- Fix install command: document --omit=dev flag
- Document external catalog aliases (packages, plugins accepted alongside entries)
2026-03-30 10:19:51 +09:00
Vincent Koc
89f9433fbf docs: add complete plugin hook reference and fix context fields in hooks.md
- Add reference table for all 27 plugin hook names with execution model and return types
- Fix agent:bootstrap context: add missing sessionKey, sessionId, agentId fields
- Fix session patch context: add fastMode, spawnedWorkspaceDir, subagentRole, subagentControlScope
- Fix responseUsage: add backwards-compat "on" value
- Add session:compact:before and session:compact:after payload field documentation
- Remove internal PR reference (#20800)
- Fix handler file resolution: document handler.js and index.js fallbacks
2026-03-30 10:19:51 +09:00
Vincent Koc
cb428aca1c docs: add 11 missing config sections to configuration-reference
Add documentation for config schema sections that existed in source but had
zero coverage in the reference doc:

- diagnostics (otel, cacheTrace, flags, stuckSessionWarnMs)
- update (channel, checkOnStart, auto.*)
- acp (enabled, dispatch, backend, stream.*, runtime.*)
- gateway.tls (enabled, autoGenerate, certPath, keyPath, caPath)
- gateway.reload (mode, debounceMs, deferralTimeoutMs)
- cron.retry (maxAttempts, backoffMs, retryOn)
- cron.failureAlert (enabled, after, cooldownMs, mode)
- auth.cooldowns (billingBackoffHours, billingMaxHours, failureWindowHours)
- logging.maxFileBytes
- session.scope (per-sender vs global)
- session.agentToAgent.maxPingPongTurns (range 0-5)
2026-03-30 10:19:51 +09:00
Gustavo Madeira Santana
b952e404fa Control UI: clear queued connect timeout on stop (#57338)
Merged via squash.

Prepared head SHA: a359fe8367
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 20:54:21 -04:00
Vincent Koc
fb81e3fc7f docs: fix remaining CLI P1s -- doctor, sessions, agents, models auth
- doctor: add --repair/--fix, --force, --generate-gateway-token options
- sessions: add --agent, --all-agents options and cleanup subcommand
- agents: add bindings, bind, unbind, set-identity to command tree
- models auth: add login and login-github-copilot subcommands
2026-03-30 09:51:17 +09:00
Vincent Koc
169bbc82f2 docs: fix security page P1s -- dmScope, heading style, roadmap language
- Add missing per-peer dmScope value to isolation options
- Fix heading style: 3./4. -> 3)/4) for consistency with other numbered sections
- Add channel qualifier to 'Separate Numbers' heading (WhatsApp/Signal/Telegram)
- Remove roadmap speculation ('We may add readOnlyMode later')
2026-03-30 09:46:57 +09:00
Vincent Koc
d3429e0c70 docs: fix FAQ P1s -- model aliases and heartbeat OAuth default
- Remove /model haiku (not a built-in alias); add gemini-flash-lite
- Note custom aliases can be added via config
- Heartbeat: note 1h default for OAuth auth
2026-03-30 09:46:15 +09:00
Vincent Koc
445fed9dc5 docs: add missing field docs and fix config-reference P1s
- Document verboseDefault (off|on|full) and elevatedDefault (off|on|ask|full)
- Heartbeat every: note OAuth default (1h) and disable value (0m)
- Replace internal 'Nano Banana' code name with 'native Gemini image generation'
2026-03-30 09:45:48 +09:00
Vincent Koc
dc64280f1d docs: fix stale CLI reference -- auth choices, agent options, tasks command
- Rebuild --auth-choice enum from BuiltInAuthChoice source (remove ollama, minimax-api,
  minimax-api-lightning; add 15+ missing choices including deepseek, copilot, volcengine, etc.)
- Fix agent command: --verbose accepts on|off not on|full|off; add missing --agent, --reply-to,
  --reply-channel, --reply-account, -m/-t short flags; fix --thinking description
- Add tasks command to command tree and body (list/show/notify/cancel)
- Mark anthropic-cli as deprecated legacy alias
- Remove stale ollama references from custom-base-url/custom-model-id
2026-03-30 09:45:06 +09:00
Vincent Koc
9355925690 docs: fix Mintlify callout syntax in security page
Replace GitHub-flavor > [!WARNING] with Mintlify <Warning> component.
The old syntax renders as a plain blockquote in Mintlify, hiding the most
safety-critical content on the page.
2026-03-30 09:43:33 +09:00
Vincent Koc
b7e2e1b399 docs: fix stale Future Events and wrong log example in hooks.md
- Future Events: session:start/session:end are live plugin hooks, clarify they are planned for internal event stream only
- Log example: session-memory listens to command:new AND command:reset, not just command:new
2026-03-30 09:43:01 +09:00
Vincent Koc
82b6bd7457 docs: fix wrong imports and removed API in architecture.md
- Multi-capability example: use correct SDK subpaths (plugin-entry, media-understanding)
- Remove buildOpenAISpeechProvider (internal, not in public SDK)
- registerHttpHandler: 'obsolete' -> 'removed' (causes hard plugin-load error)
2026-03-30 09:42:11 +09:00
Vincent Koc
32ba94b7b3 docs: fix wrong defaults and config path in FAQ
- session.idleMinutes default: 60 -> 0 (disabled by default, per DEFAULT_IDLE_MINUTES)
- crossContext config path: agents.defaults.tools.message.crossContext -> tools.message.crossContext (matches schema)
- Remove incorrect per-agent tools.message override claim
2026-03-30 09:41:30 +09:00
Vincent Koc
12c92b5fb2 docs: fix wrong defaults and heading in configuration-reference
- maxConcurrent default: 1 -> 4 (matches DEFAULT_AGENT_MAX_CONCURRENT)
- subagents.maxConcurrent example: 1 -> 8 (matches DEFAULT_SUBAGENT_MAX_CONCURRENT)
- Fix section heading: tools.subagents -> agents.defaults.subagents (matches actual config path)
2026-03-30 09:41:09 +09:00
Vincent Koc
9b33380fb6 fix(test): harden qmd release callback typing 2026-03-30 09:37:21 +09:00
Michel Belleau
26f34be20c fix(gateway): /v1/responses tool schema should use flat Responses API format (#57166)
* gateway: fix /v1/responses tool schema to use flat Responses API format

* gateway: fix remaining stale wrapped-format tools in parity tests

* gateway: propagate strict flag through extractClientTools normalization

* fix(gateway): cover responses tool boundary

* Delete docs/internal/vincentkoc/2026-03-30-pr-57166-responses-tool-schema-followup.md

---------

Co-authored-by: Michel Belleau <mbelleau@Michels-MacBook-Pro.local>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 09:36:54 +09:00
Vincent Koc
3034adfdb3 fix(commands): harden fast status and Telegram callbacks 2026-03-30 09:32:53 +09:00
Vincent Koc
8657b65b05 fix(ci): rename extension test support for boundary guards 2026-03-30 09:31:33 +09:00
Vincent Koc
dce61dc920 fix(cli): import task summary helper 2026-03-30 09:31:33 +09:00
Vincent Koc
b82fd50472 fix(test): add extension-safe test helper bridges 2026-03-30 09:31:33 +09:00
Vincent Koc
5bac1aad04 fix(memory): support qmd glob collection flags (#57351) 2026-03-30 09:30:49 +09:00
Harold Hunt
8bf86b4cdf agents: remove xAI auth trace logging (#57342) 2026-03-29 20:29:51 -04:00
Peter Steinberger
f3bf7fe53a chore: bump version to 2026.3.30 2026-03-30 09:28:29 +09:00
Vincent Koc
d26d7c797b fix(memory): add QMD sync parity hooks (#57354)
* fix(memory): add qmd sync parity hooks

* fix(memory): avoid blocking qmd session warm searches
2026-03-30 09:25:37 +09:00
Peter Steinberger
9857d40923 fix(runtime): stabilize image generation auth/runtime loading 2026-03-30 01:14:29 +01:00
Gustavo Madeira Santana
bb42027699 Docs: hide ClawDock from install menu 2026-03-29 20:11:02 -04:00
Vincent Koc
e6445c22aa feat(status): surface task run pressure (#57350)
* feat(status): surface task run pressure

* Update src/commands/tasks.ts

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

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-30 09:09:10 +09:00
Gustavo Madeira Santana
93dd25e6b2 Tests: drop dead telegram exec approval imports 2026-03-29 20:08:15 -04:00
Gustavo Madeira Santana
cc04153d01 Agents: reuse shared subagent hook runner type 2026-03-29 20:06:05 -04:00
Daniel Olshansky
6e1f00dc86 [ClawDock] Iteration on the first submission; bug fixes, UX improvements, etc (#23912)
Merged via squash.

Prepared head SHA: 30c5ef37a4
Co-authored-by: Olshansk <1892194+Olshansk@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 20:05:41 -04:00
Peter Steinberger
c2cbdea28c refactor: add approval auth capabilities to more channels 2026-03-30 09:04:08 +09:00
Peter Steinberger
63cbc097b5 refactor(channels): route core through registered plugin capabilities 2026-03-30 01:03:42 +01:00
Peter Steinberger
471e059b69 refactor(plugin-sdk): remove channel-specific sdk shims 2026-03-30 01:03:24 +01:00
Peter Steinberger
bff6a6a9c1 test(config): align optimistic write helpers 2026-03-30 01:02:25 +01:00
Peter Steinberger
47216702f4 refactor(config): use source snapshots for config mutations 2026-03-30 01:02:25 +01:00
Gustavo Madeira Santana
f9bf76067f Agents: fix subagent spawn hook typing 2026-03-29 20:00:52 -04:00
hnshah
19113637e8 fix(memory): keep qmd embeddings active in search mode (#54509)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 08:59:50 +09:00
yuna78
0033f64e19 gateway: narrow already-running exit code (#26718)
Co-authored-by: ImLukeF <92253590+ImLukeF@users.noreply.github.com>
2026-03-30 10:59:32 +11:00
dadgo
2885c65c74 fix(memory): point qmd config dir at nested path (#39078)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 08:59:08 +09:00
Amine Harch el korane
219d4f03bd fix: wire memorySearch.extraPaths to QMD indexing (#57315)
* fix: wire memorySearch.extraPaths to QMD indexing

The 'agents.defaults.memorySearch.extraPaths' config field was documented
to add extra directories to the memory index, but the paths were never
actually passed to the QMD backend. Only 'memory.qmd.paths' worked.

This fix reads extraPaths from the memorySearch config and maps them
to QMD custom path collections, so users can simply configure:

  memorySearch:
    extraPaths:
      - odd-vault
      - /Users/odd/workspace
      - /Users/odd/docs

And have those directories indexed alongside the default memory files.

Closes #57302

* fix: handle per-agent memorySearch.extraPaths overrides + add tests

- Read per-agent overrides from agents.list[].memorySearch.extraPaths
- Agent-specific overrides take priority over defaults
- Falls back to defaults when agent has no overrides
- Added 3 test cases for the feature

* fix: merge defaults + agent overrides instead of replacing

* fix: remove any types from tests, fix merge behavior assertion

* fix(memory): merge qmd extra path collections

* fix(memory): normalize qmd extra path resolution

* fix(memory): type qmd extra path merge

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 08:58:42 +09:00
Peter Steinberger
cbceb1db76 docs(i18n): sync zh-CN node exec wording 2026-03-30 00:57:27 +01:00
Peter Steinberger
6b8a1b77a0 refactor(nodes): split media and invoke handlers 2026-03-30 00:57:27 +01:00
Vincent Koc
475defdf82 Anthropic: wire explicit service tier params (#45453)
* Anthropic: add explicit service tier wrapper

* Runner: wire explicit Anthropic service tiers

* Tests: cover explicit Anthropic service tiers

* Changelog: note Anthropic service tier follow-up

* fix(agents): make Anthropic service tiers override fast mode

* fix(config): drop duplicate healed sourceConfig

* docs(anthropic): update fast mode service tier guidance

* fix(agents): remove dead Anthropic Bedrock exports

* fix(agents): avoid cross-provider Anthropic tier warnings

* fix(agents): avoid cross-provider OpenAI tier warnings
2026-03-30 08:54:56 +09:00
Peter Steinberger
feed2c42dd test: stabilize subagent spawn harnesses 2026-03-30 00:54:09 +01:00
Vincent Koc
170a3a39d4 fix(test): restore subagent announce timeout mocks 2026-03-30 08:52:04 +09:00
Gustavo Madeira Santana
188fcbfa34 Docs: add zh-CN Diffs page 2026-03-29 19:48:08 -04:00
Gustavo Madeira Santana
c191dc9928 Control UI: preserve seq-gap reconnect state 2026-03-29 19:48:08 -04:00
Peter Steinberger
cf84a03ecf docs: clarify generic channel approval capabilities 2026-03-30 08:46:44 +09:00
Peter Steinberger
3b878e6b86 refactor: move approval auth and payload hooks to generic channel capabilities 2026-03-30 08:46:44 +09:00
Vincent Koc
7008379ff0 test(agents): restore cli runner test seams 2026-03-30 08:43:37 +09:00
Vincent Koc
408f6a5b0b test(matrix): stabilize file sync store persistence checks 2026-03-30 08:43:37 +09:00
Vincent Koc
d6a3580347 fix(lint): clear current main lint blockers 2026-03-30 08:43:37 +09:00
Peter Steinberger
193f781fad fix: stabilize ci and serial test gate 2026-03-30 00:43:01 +01:00
Vincent Koc
0da610a8ec fix(tasks): prefer ACP spawn metadata on merge 2026-03-30 08:42:31 +09:00
Peter Steinberger
c1137ef00d docs(nodes): remove nodes.run references 2026-03-30 00:41:06 +01:00
Peter Steinberger
2255e04b07 test(nodes): update coverage after exec consolidation 2026-03-30 00:41:06 +01:00
Peter Steinberger
5dae663ea4 refactor(nodes): remove nodes.run execution path 2026-03-30 00:41:06 +01:00
Peter Steinberger
dd8d66fc44 refactor: inject subagent announce test seams 2026-03-30 00:40:32 +01:00
Peter Steinberger
f914cd598a refactor(gateway): dedupe self-write config reloads 2026-03-30 00:39:39 +01:00
Peter Steinberger
a27ccee5d9 refactor(config): use source snapshots for config writes 2026-03-30 00:39:39 +01:00
Vincent Koc
c5baf63fa5 docs: deep audit of memory section -- fix icons, beef up engine pages, restructure config reference 2026-03-30 08:39:18 +09:00
Gustavo Madeira Santana
1600c1726e Control UI: reconnect on seq gaps 2026-03-29 19:31:01 -04:00
Peter Steinberger
15c3aa82bf refactor: unify approval forwarding and rendering 2026-03-30 08:28:33 +09:00
Peter Steinberger
8720070fe0 refactor: rename channel approval capabilities 2026-03-30 08:28:33 +09:00
Vincent Koc
53bcd5769e refactor(tasks): unify the shared task run registry (#57324)
* refactor(tasks): simplify shared task run registry

* refactor(tasks): remove legacy task registry aliases

* fix(cron): normalize timeout task status and harden ledger writes

* fix(cron): keep manual runs resilient to ledger failures
2026-03-30 08:28:17 +09:00
Peter Steinberger
e4466c72a2 test: stabilize runner and acp mocks
- reuse the shared cli-runner harness in claude runner tests
- make ACP session metadata and startup tests use stable static mocks
2026-03-30 00:27:52 +01:00
Vincent Koc
bf63264c62 docs: fix Honcho tool names and add CLI commands 2026-03-30 08:26:52 +09:00
Robin Waslander
bdd9bc93f1 fix(cron): deliver full announce output instead of last chunk only (#57322)
resolveCronPayloadOutcome() collapsed announce delivery to the last
deliverable payload. Replace with pickDeliverablePayloads() that
preserves all successful text payloads. Error-only runs fall back to
the last error payload only.

Extract shared isDeliverablePayload() helper. Keep
deliveryPayloadHasStructuredContent scoped to the last payload
to preserve downstream finalizeTextDelivery safeguards.

Fixes #13812
2026-03-30 01:24:45 +02:00
Peter Steinberger
0a4c11061d test: stabilize targeted harnesses
- reduce module-reset mock churn in auth/acp tests
- simplify runtime web mock cleanup
- make canvas reload test use in-memory websocket tracking
2026-03-30 00:23:38 +01:00
Radek Sienkiewicz
4680335b2a docs: fix English link audits (#57039)
Merged via squash.

Prepared head SHA: d20a3b620f
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
2026-03-30 01:21:00 +02:00
Gustavo Madeira Santana
6c91b27756 Docs: remove internal CLI metadata note 2026-03-29 19:20:43 -04:00
Gustavo Madeira Santana
b0077904a7 Plugins: align CLI metadata loader behavior 2026-03-29 19:20:42 -04:00
Peter Steinberger
9a97c30fad style(test): sort mutate helper imports 2026-03-30 00:17:23 +01:00
Peter Steinberger
f928b81279 test(config): align snapshot fixtures and isolation 2026-03-30 00:17:23 +01:00
Peter Steinberger
89a4f2a34e refactor(config): centralize runtime config state 2026-03-30 00:17:23 +01:00
Armand du Plessis
b888741462 fix: QMD 1.1+ mcporter compatibility with legacy fallback [AI-assisted] (#54728)
* fix: QMD 2.0 mcporter compatibility with v1 fallback [AI-assisted]

QMD 2.0 unified all search modes under a single 'query' MCP tool
with typed sub-queries, replacing search/vector_search/deep_search.

- Default to QMD 2.0 'query' tool with {searches: [...]} format
- Auto-detect version on first call and cache for the session
- Fall back to v1 tool names if 'query' is not found
- Backwards compatible: v1 users get one retry then cached

AI-assisted: Built with Claude (Opus 4.6) via OpenClaw. Fully tested
against QMD 2.0 with mcporter 0.7.3 daemon. v1 fallback path not
live-tested (no v1 instance available). Code reviewed and understood.

* test: add QMD v2 tool format and v1 fallback tests

- Verify mcporter bridge uses 'query' tool with {searches: [...]} format (v2)
- Verify fallback to 'deep_search' with {query, limit} format when v2 not found
- Verify v1 fallback logs a warning for visibility

* fix: address review feedback — multi-collection v1 fallback + test cleanup

- Fix multi-collection v1 fallback: resolve effectiveTool at the top
  of runQmdSearchViaMcporter so stale 'query' tool names from the
  loop are corrected once qmdMcpToolVersion is set to 'v1'
- Assert callCount in v1 fallback test (one v2 attempt + one v1 retry)
- Remove spurious global state reset (qmdMcpToolVersion is per-instance)

* docs: correct version references — breaking change was QMD 1.5, not 2.0

The MCP tool removal (search/vector_search/deep_search → query) happened
in QMD 1.5, not 2.0. QMD 2.0 was the SDK/library refactor.
Updated all comments, test names, and documentation to reflect this.

* fix: respect searchMode when building v2 mcporter queries

When searchMode is 'search' (BM25), only send lex sub-query.
When 'vsearch', only send vec. Default 'query' sends all three
(lex + vec + hyde) for full hybrid search with reranking.

Previously all three sub-queries were always sent regardless of
the configured searchMode, which could trigger unnecessary vector
embedding and HyDE LLM work on setups explicitly requesting
lexical-only search.

Addresses Codex P2 review feedback.

* docs: correct to QMD 1.1.0 — that's the actual version that removed the tools

Per CHANGELOG.md, MCP tools search/vector_search/deep_search were removed
in QMD 1.1.0 (2026-02-20), not 1.5 (which doesn't exist). Versions go
1.0.7 → 1.1.0 → 1.1.1 → 1.1.2 → 1.1.5 → 1.1.6 → 2.0.0.

* fix: remove redundant v1 guard (race condition) + tighten error matching

1. Remove qmdMcpToolVersion !== 'v1' guard from catch block. It's
   redundant (effectiveTool === 'query' already prevents infinite retry)
   and introduces a race condition: concurrent searches that both probe
   with 'query' while version is null would fail after the first sets
   version to 'v1'.

2. Tighten isToolNotFoundError regex to require 'Tool' near 'not found'
   (within 40 chars, no sentence boundary). Prevents false-positives
   when user query text in the mcporter args contains both words.

Addresses Greptile P1 (concurrent-search race) and Codex P2
(overly broad error matching).

* fix: address claude code review — type safety, minScore docs, explicit switch

- Restore type union for tool param instead of bare string
- Add comment explaining minScore omission for v2 (QMD 1.1+ uses
  its own reranking pipeline, no minScore parameter)
- Make buildV2Searches switch exhaustive with explicit case 'query'

* fix: resolve CI failures — oxfmt formatting + TypeScript type errors

- Run oxfmt to fix formatting issues
- Add union return type to resolveQmdMcpTool() and
  runMcporterAcrossCollections() tool param to satisfy tsc
  (string was not assignable to the union type)

* fix(memory): align qmd query collection filters

* fix(memory): narrow qmd missing-tool fallback detection

* fix(memory): ignore qmd timeout text for v1 fallback

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 08:07:14 +09:00
Peter Steinberger
0fdf724125 test: trim stale extension boundary allowlist 2026-03-30 08:05:59 +09:00
Peter Steinberger
809833ef9d fix(config): recover clobbered config and isolate test paths 2026-03-30 00:05:36 +01:00
Peter Steinberger
d45b997ba9 docs: clarify shared approval delivery 2026-03-30 08:03:59 +09:00
Peter Steinberger
52fb4a149a refactor: share approval interactive renderers 2026-03-30 08:03:59 +09:00
Peter Steinberger
cfac0e8698 refactor: move plugin-owned test support into plugins 2026-03-30 08:03:04 +09:00
Vincent Koc
1ace91ee00 fix(bluebubbles): coalesce URL-only inbound shares 2026-03-30 08:00:25 +09:00
Cypher
924c264a74 fix: inject anthropic service_tier for OAuth auth (#55922)
* fix: inject anthropic service_tier for OAuth auth

Remove the OAuth-token exclusion from createAnthropicFastModeWrapper
so that sk-ant-oat-* requests receive service_tier injection, matching
Claude Code CLI behavior and reducing avoidable 529 overload cascades.

isAnthropicOAuthApiKey remains in use in createAnthropicBetaHeadersWrapper
for beta header selection — it is not dead code after this change.

Fixes #55758

* docs(changelog): note anthropic oauth service tier fix

* Update CHANGELOG.md

---------

Co-authored-by: Cypherm <28184436+Cypherm@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-30 07:54:24 +09:00
Gustavo Madeira Santana
e5dac0c39e CLI: keep root help plugin descriptors non-activating (#57294)
Merged via squash.

Prepared head SHA: c8da48f689
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 18:49:57 -04:00
Peter Steinberger
1efef8205c fix: stabilize extensions surface test gate 2026-03-30 07:47:58 +09:00
Peter Steinberger
07c6981c70 refactor: localize zalo test support 2026-03-30 07:47:28 +09:00
Vincent Koc
ed2528e6fb docs: nest memory engine pages under Memory sub-group 2026-03-30 07:45:20 +09:00
Vincent Koc
92d4c62d59 style: format Honcho docs tables 2026-03-30 07:44:35 +09:00
Vincent Koc
70b7f32c7e docs: add Honcho memory engine page 2026-03-30 07:44:35 +09:00
Peter Steinberger
8861cdbb6f refactor(plugin-sdk): untangle extension test seams 2026-03-29 23:43:53 +01:00
Peter Steinberger
c942bd798f docs: purge internal notes 2026-03-29 23:38:25 +01:00
Peter Steinberger
a9984e2bf9 fix(config): reuse in-memory gateway write reloads 2026-03-29 23:38:25 +01:00
Vincent Koc
0e47ce58bc fix(approvals): restore queue targeting and plugin id prefixes 2026-03-30 07:37:50 +09:00
Peter Steinberger
7043705ef3 refactor: split MCP runtime and transport seams 2026-03-29 23:36:37 +01:00
Peter Steinberger
69eea2cb80 refactor: split approval auth delivery and rendering 2026-03-30 07:36:18 +09:00
Vincent Koc
c9eb31382e docs: add dedicated memory engine pages, replace ASCII diagram with Mermaid 2026-03-30 07:34:14 +09:00
Vincent Koc
67b381b928 style: format docs tables 2026-03-30 07:32:20 +09:00
Vincent Koc
143b4c54ba docs: simplify sessions/memory concept pages and fix QMD experimental label 2026-03-30 07:32:20 +09:00
Onur Solmaz
57069f2b2f gitignore: ignore docs/internal 2026-03-30 00:26:13 +02:00
Peter Steinberger
147c2c7389 fix: port safer bundle MCP naming onto latest main (#49505) (thanks @ziomancer) 2026-03-30 07:22:36 +09:00
Peter Steinberger
004bffa1c3 fix: finalize safer MCP tool naming (#49505) (thanks @ziomancer) 2026-03-30 07:22:36 +09:00
ziomancer
97bf38099a docs(mcp): document connection timeout and URL scheme validation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 07:22:36 +09:00
ziomancer
a74091eb98 docs(mcp): add CHANGELOG entries and MCP transport/namespacing docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 07:22:36 +09:00
ziomancer
14f78debd5 fix(mcp): allow colon in tool names and dispose transport on failed connect
The transcript validator regex rejected namespaced MCP tool names
(e.g., vigil-harbor:memory_status) because colon wasn't in the
allowed character set. Tool call blocks were silently dropped during
session repair, breaking tool-call/result pairing.

Also closes a resource leak: if client.connect() throws after the
transport is instantiated, the transport is now explicitly closed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 07:22:36 +09:00
ziomancer
414d7306d1 feat(mcp): add HTTP transport support and tool namespacing
MCP tools are now prefixed with their server name (e.g., vigil-harbor:memory_status)
to prevent collisions between tools from different MCP servers and built-in tools.

Adds SSE and StreamableHTTP transport support alongside existing stdio, enabling
connection to remote MCP servers via URL-based config with optional custom headers
and env var substitution. Includes config validation, session lifecycle management,
and 5 new tests for HTTP config edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 07:22:36 +09:00
Vincent Koc
102b7126c1 style: format session docs tables 2026-03-30 07:18:58 +09:00
Vincent Koc
2880b3d3ff docs: rewrite session management, session pruning, and session tools pages 2026-03-30 07:18:58 +09:00
Peter Steinberger
f8dc4305a5 refactor: share native approval delivery helpers 2026-03-30 07:16:33 +09:00
Mariano
5ef42fc856 Gateway: surface blocked ACP task outcomes (#57203) 2026-03-30 00:15:51 +02:00
Onur Solmaz
2da61e6553 [codex] Move internal development notes to maintainers (#57316)
* docs: move internal notes to maintainers

* docs: drop internal notes agent guidance
2026-03-30 00:15:08 +02:00
Peter Steinberger
d82d6ba0c4 fix: align openai fast mode with priority processing 2026-03-29 23:14:52 +01:00
Peter Steinberger
27519cf061 refactor: centralize node identity resolution 2026-03-29 23:14:22 +01:00
Vincent Koc
ace876b087 fix(gateway): keep startup model warmup static 2026-03-30 07:12:52 +09:00
Vincent Koc
bd89e07baa fix(agents): stop transient live-switch mismatches 2026-03-30 07:12:52 +09:00
Peter Steinberger
e01ca8cfc6 refactor(plugin-sdk): remove direct extension source leaks 2026-03-29 23:11:20 +01:00
Vincent Koc
40446ea27c docs: add cross-links to new memory-search page from reference docs 2026-03-30 07:10:33 +09:00
Vincent Koc
3584a893e8 docs: rewrite sessions/memory section -- compaction, memory, and new memory-search page 2026-03-30 07:10:01 +09:00
Peter Steinberger
6d9a7224aa refactor: unify approval command authorization 2026-03-30 07:06:29 +09:00
Peter Steinberger
6ca81f8ec7 test(gateway): decouple send coverage from telegram specifics 2026-03-29 22:59:32 +01:00
Peter Steinberger
168ab94eee refactor(config): pin runtime snapshot and drop ttl cache 2026-03-29 22:57:31 +01:00
Vincent Koc
22ffe7b1de docs: fix before_install finding field name, add MCP SSE transport docs, add acpx auto-install note 2026-03-30 06:53:35 +09:00
Vincent Koc
6d0abfa50c docs(exec): document denied approval output isolation behavior 2026-03-30 06:53:35 +09:00
Peter Steinberger
3ec000b995 refactor: align same-chat approval routing 2026-03-30 06:52:28 +09:00
Peter Steinberger
f16c176a4c fix: disambiguate legacy mac node identities 2026-03-29 22:47:15 +01:00
Peter Steinberger
2e0682d930 refactor: finish decoupling plugin sdk seams 2026-03-29 22:42:06 +01:00
Peter Steinberger
574d3c5213 fix: make same-chat approvals work across channels 2026-03-30 06:35:04 +09:00
Peter Steinberger
1ca01b738b fix: stabilize exec approval approver routing 2026-03-30 06:25:03 +09:00
Peter Steinberger
216afe275e test: harden packed manifest guardrails 2026-03-29 22:21:29 +01:00
Peter Steinberger
2f19b303c6 build: fix packaged plugin dependency ownership 2026-03-29 22:21:29 +01:00
Peter Steinberger
356adc98d5 test: align schema and redaction assertions 2026-03-29 22:21:29 +01:00
Peter Steinberger
b4badd7704 build: resolve plugin package contract path 2026-03-29 22:21:29 +01:00
Peter Steinberger
e45cc3890b refactor: unify sensitive URL config hints 2026-03-29 22:21:29 +01:00
Peter Steinberger
1318479a2c refactor: extract MCP transport helpers 2026-03-29 22:21:29 +01:00
Peter Steinberger
4635810385 fix: stabilize unit test planner scheduling 2026-03-30 06:17:06 +09:00
Peter Steinberger
9baa853797 test: isolate image read helper coverage 2026-03-29 22:10:37 +01:00
Peter Steinberger
421acd27e1 style: finalize exec formatter cleanup 2026-03-30 06:03:08 +09:00
Peter Steinberger
ddd2cbf03a style: resolve exec formatter follow-up 2026-03-30 06:03:08 +09:00
Peter Steinberger
bb10f60993 style: resolve exec formatter follow-up 2026-03-30 06:03:08 +09:00
Peter Steinberger
276ccd2583 fix(exec): default implicit target to auto 2026-03-30 06:03:08 +09:00
Peter Steinberger
d014f173f1 test: trim stale legacy coverage and repair mocks 2026-03-29 22:00:56 +01:00
Peter Steinberger
63e5c3349e refactor(config): drop obsolete legacy config aliases 2026-03-29 22:00:56 +01:00
Peter Steinberger
d9274444b7 fix: keep beta on newer prereleases 2026-03-30 05:37:01 +09:00
Peter Steinberger
aed87a608e fix: stabilize bundled plugin ci lanes 2026-03-30 05:35:53 +09:00
Peter Steinberger
694bc082a8 fix: resolve acpx MCP secret inputs 2026-03-30 05:30:32 +09:00
Peter Steinberger
35233bae96 refactor: decouple bundled plugin sdk surfaces 2026-03-29 21:20:46 +01:00
Peter Steinberger
5d4c4bb850 fix(exec): restore runtime-aware implicit host default 2026-03-29 21:18:41 +01:00
Onur Solmaz
96df794c12 [codex] Move internal experiment plans under docs-internal (#57262)
* docs: restore internal plan docs under docs-internal

* docs: move internal plans under docs/internal

* docs: restore deleted internal experiment notes

* docs: group internal notes by author

* docs: move Bob internal notes under osolmaz

* docs: move remaining internal notes under osolmaz

* Revert "docs: move remaining internal notes under osolmaz"

* docs: restore experiment architecture note

* docs: normalize osolmaz internal doc authorship

* docs: add identity metadata to internal docs

* docs: document internal development notes

* docs: use Peter in internal docs examples

* docs: move Mariano background task note to internal docs

* Revert "docs: use Peter in internal docs examples"

* docs: use real Peter note in internal docs readme

* docs: rename Mariano background task note

* docs: preserve others internal notes by default

* docs: allow BDFL override for internal notes
2026-03-29 22:18:26 +02:00
Peter Steinberger
f5f8ba6d35 fix: harden bundle MCP session runtime cache (#55090) (thanks @allan0509) 2026-03-30 05:10:32 +09:00
无忌
6477d783e8 Agents: cache bundle MCP runtime per session 2026-03-30 05:10:32 +09:00
Peter Steinberger
73477eee4c fix: harden ACP plugin tools bridge (#56867) (thanks @joe2643) 2026-03-30 05:09:59 +09:00
khhjoe
e24091413c fix: add curly braces for oxlint curly rule; copy postinstall script before pnpm install in Dockerfile 2026-03-30 05:09:59 +09:00
khhjoe
a8c189f463 fix(mcp): serialize result.content instead of wrapper object; warn on missing static assets 2026-03-30 05:09:59 +09:00
khhjoe
3151eb5b48 feat: auto-install acpx deps via npm postinstall on global install 2026-03-30 05:09:59 +09:00
khhjoe
5f628c0bf8 build: copy acpx mcp-proxy.mjs to dist in runtime-postbuild 2026-03-30 05:09:59 +09:00
khhjoe
415899984e feat(mcp): add plugin tools MCP server for ACP sessions
Standalone MCP server that exposes OpenClaw plugin-registered tools
(e.g. memory-lancedb's memory_recall, memory_store, memory_forget)
to ACP sessions running Claude Code via acpx's MCP proxy mechanism.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 05:09:59 +09:00
Peter Steinberger
3f5ed11266 fix: clear stalled model resolution lanes 2026-03-30 05:09:26 +09:00
Gustavo Madeira Santana
9b4f26e70a Plugins/CLI: add descriptor-backed lazy root command registration (#57165)
Merged via squash.

Prepared head SHA: ad1dee32eb
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 16:02:59 -04:00
Peter Steinberger
d330782ed1 fix(matrix): stop discovering runtime helper as plugin entry 2026-03-29 21:00:57 +01:00
Gustavo Madeira Santana
dc192d7b2f Build: mirror Matrix crypto WASM runtime deps (#57163)
Merged via squash.

Prepared head SHA: b3aeb9d08a
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 15:57:28 -04:00
Peter Steinberger
82f04ced27 refactor(plugin-sdk): drop legacy provider compat subpaths 2026-03-29 20:55:53 +01:00
Peter Steinberger
cce6d3bbb7 fix: resolve CI type and lint regressions 2026-03-30 04:51:33 +09:00
Peter Steinberger
855878b4f0 fix: stabilize serial test suite 2026-03-30 04:46:04 +09:00
George Zhang
b787669340 docs(plugins): add before_install payload example 2026-03-29 12:35:01 -07:00
George Zhang
2607191d04 refactor(plugins): centralize before_install context shaping 2026-03-29 12:35:01 -07:00
George Zhang
9a07fd83fb docs(plugins): describe before_install policy foundation 2026-03-29 12:35:01 -07:00
George Zhang
b5d48d311c test(plugins): cover before_install policy metadata 2026-03-29 12:35:01 -07:00
George Zhang
150faba8d1 plugins: enrich before_install policy context 2026-03-29 12:35:01 -07:00
George Zhang
9ea0b76f06 docs(plugins): document before_install hook 2026-03-29 12:35:01 -07:00
George Zhang
ac3951d731 test(plugins): cover before_install install flows 2026-03-29 12:35:01 -07:00
George Zhang
7cd9957f62 plugins: add before_install hook for install scanners 2026-03-29 12:35:01 -07:00
Robin Waslander
77555d6c85 fix(infra): classify SQLite transient errors as non-fatal in unhandled rejection handler (#57018)
Add isTransientSqliteError() covering SQLITE_CANTOPEN, SQLITE_BUSY,
SQLITE_LOCKED, and SQLITE_IOERR via named codes, numeric errcodes
(node:sqlite), and message-string fallback. Combine with existing
network transient check so both families are treated as non-fatal
in the global unhandled rejection handler.

Prevents crash loop under launchd on macOS when SQLite files are
temporarily unavailable.

Fixes #34678
2026-03-29 21:29:38 +02:00
Peter Steinberger
bfb0907777 fix: harden MCP SSE config redaction (#50396) (thanks @dhananjai1729) 2026-03-30 04:23:47 +09:00
dhananjai1729
2c6eb127d9 fix: redact sensitive query params in invalid URL error reasons
Extends the invalid-URL redaction to also scrub sensitive query parameters
(token, api_key, secret, access_token, etc.) using the same param list as
the valid-URL description path. Adds tests for both query param and
credential redaction in error reasons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 04:23:47 +09:00
dhananjai1729
4e03d899b3 fix: handle Headers instances in SSE fetch and redact invalid URLs
- Properly convert Headers instances to plain objects in eventSourceInit.fetch
  so SDK-generated headers (e.g. Accept: text/event-stream) are preserved
  while user-configured headers still take precedence.
- Redact potential credentials from invalid URLs in error reasons to prevent
  secret leakage in log output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 04:23:47 +09:00
dhananjai1729
62d0e12155 fix(mcp): user headers override SDK defaults & expand redaction list
Address Greptile P1/P2 review feedback:
- Fix header spread order so user-configured auth headers take precedence
  over SDK-internal headers in SSE eventSourceInit.fetch
- Add password, pass, auth, client_secret, refresh_token to the
  sensitive query-param redaction set in describeSseMcpServerLaunchConfig
- Add tests for redaction of all sensitive params and embedded credentials

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 04:23:47 +09:00
dhananjai1729
32b7c00f90 fix: apply SSE auth headers to initial GET, redact URL credentials, warn on malformed headers 2026-03-30 04:23:47 +09:00
dhananjai1729
6fda8b4e9a fix: use SDK Transport type to satisfy client.connect() signature 2026-03-30 04:23:47 +09:00
dhananjai1729
bf8303370e fix: address review feedback - fix env JSDoc, warn on dropped headers, await server close 2026-03-30 04:23:47 +09:00
dhananjai1729
d89bfed5cc feat(mcp): add SSE transport support for remote MCP servers 2026-03-30 04:23:47 +09:00
Peter Steinberger
fc5fdcb091 refactor(plugin-sdk): remove bundled provider setup shims 2026-03-29 20:23:20 +01:00
George Zhang
e133924047 [codex] harden clawhub plugin publishing and install (#56870)
* fix: harden clawhub plugin publishing and install

* fix(process): preserve windows shim exit success
2026-03-29 11:59:19 -07:00
Peter Steinberger
58dde4b016 test(contracts): hoist shared plugin mock ids 2026-03-29 19:54:27 +01:00
Marcus Castro
34648235a3 WhatsApp: use shared resolveReactionMessageId for context-aware reactions (#57226)
Wire the shared resolveReactionMessageId helper into the WhatsApp
channel adapter, matching the pattern already used by Telegram, Signal,
and Discord. The model can now react to the current inbound message
without explicitly providing a messageId.

Safety guards:
- Only falls back to context when the source is WhatsApp
- Suppresses fallback when targeting a different chat (normalized comparison)
- Throws ToolInputError (400) instead of plain Error (500) when messageId
  is missing, preserving gateway error mapping
2026-03-29 15:42:19 -03:00
Nimrod Gutman
f38b7291f9 fix(ios): mark activitykit import as preconcurrency (#57180)
* fix(ios): mark activitykit import as preconcurrency

* fix: note iOS ActivityKit preconcurrency build fix (#57180) (thanks @ngutman)
2026-03-29 21:24:25 +03:00
Peter Steinberger
5801506ce7 test(discord): fix hoisted configured-binding mocks 2026-03-29 19:17:08 +01:00
Peter Steinberger
8a6d1b9f1e test(contracts): avoid sync telegram vitest harness loads 2026-03-29 18:37:11 +01:00
Tyler Yust
798e5f9501 plugin-sdk: fix provider setup import cycles 2026-03-29 09:59:52 -07:00
Peter Steinberger
56640a6725 fix(plugin-sdk): break vllm setup recursion 2026-03-29 17:55:09 +01:00
Keith Elliott
2d2e386b94 fix(matrix): resolve crypto bootstrap failure and multi-extension idHint warning (#53298)
Merged via squash.

Prepared head SHA: 6f5813ffff
Co-authored-by: keithce <2086282+keithce@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 12:38:10 -04:00
Peter Steinberger
ba7911bd16 ci: extend long-running test lane timeouts 2026-03-29 16:48:50 +01:00
Peter Steinberger
354bc01f29 ci: raise test workflow timeouts 2026-03-29 16:00:51 +01:00
Peter Steinberger
637b4c8193 refactor: move remaining provider policy into plugins 2026-03-29 23:05:58 +09:00
Peter Steinberger
edc58a6864 refactor: generalize provider transport hooks 2026-03-29 23:05:58 +09:00
Peter Steinberger
8109195ad8 fix(plugin-sdk): avoid recursive bundled facade loads 2026-03-29 15:00:25 +01:00
Peter Steinberger
24d16c39ad refactor(plugin-sdk): remove source alias residue 2026-03-29 14:53:03 +01:00
Peter Steinberger
e6116769b4 build(plugin-sdk): verify dist facade exports 2026-03-29 14:53:03 +01:00
Peter Steinberger
2c9bc0bb78 chore(deps): bump workspace dependencies 2026-03-29 14:41:58 +01:00
Peter Steinberger
2dd29db464 fix: ease bundled browser plugin recovery 2026-03-29 22:39:30 +09:00
Peter Steinberger
f1af7d66d2 chore: bump version to 2026.3.29 2026-03-29 14:33:12 +01:00
Thomas M
0a01386756 fix: canonicalize session keys at write time (#30654) (thanks @thomasxm)
* fix: canonicalize session keys at write time to prevent orphaned sessions (#29683)

resolveSessionKey() uses hardcoded DEFAULT_AGENT_ID="main", but all read
paths canonicalize via cfg. When the configured default agent differs
(e.g. "ops" with mainKey "work"), writes produce "agent:main:main" while
reads look up "agent:ops:work", orphaning transcripts on every restart.

Fix all three write-path call sites by wrapping with
canonicalizeMainSessionAlias:
- initSessionState (auto-reply/reply/session.ts)
- runWebHeartbeatOnce (web/auto-reply/heartbeat-runner.ts)
- resolveCronAgentSessionKey (cron/isolated-agent/session-key.ts)

Add startup migration (migrateOrphanedSessionKeys) to rename existing
orphaned keys to canonical form, merging by most-recent updatedAt.

* fix: address review — track agent IDs in migration map, align snapshot key

P1: migrateOrphanedSessionKeys now tracks agentId alongside each store
path in a Map instead of inferring from the filesystem path. This
correctly handles custom session.store templates outside the default
agents/<id>/ layout.

P2: Pass the already-canonicalized sessionKey to getSessionSnapshot so
the heartbeat snapshot reads/restores use the same key as the write path.

* fix: log migration results at all early return points

migrateOrphanedSessionKeys runs before detectLegacyStateMigrations, so
it can canonicalize legacy keys (e.g. "main" → "agent:main:main") before
the legacy detector sees them. This caused the early return path to skip
logging, breaking doctor-state-migrations tests that assert log.info was
called.

Extract logMigrationResults helper and call it at every return point.

* fix: handle shared stores and ~ expansion in migration

P1: When session.store has no {agentId}, all agents resolve to the same
file. Track all agentIds per store path (Map<path, Set<id>>) and run
canonicalization once per agent. Skip cross-agent "agent:main:*"
remapping when "main" is a legitimate configured agent sharing the store,
to avoid merging its data into another agent's namespace.

P2: Use expandHomePrefix (environment-aware ~ resolution) instead of
os.homedir() in resolveStorePathFromTemplate, matching the runtime
resolveStorePath behavior for OPENCLAW_HOME/HOME overrides.

* fix: narrow cross-agent remap to provable orphan aliases only

Only remap agent:main:* keys where the suffix is a main session alias
("main" or the configured mainKey). Other agent:main:* keys — hooks,
subagents, cron sessions, per-sender keys — may be intentional
cross-agent references and must not be silently moved into another
agent's namespace.

* fix: run orphan-key session migration at gateway startup (#29683)

* fix: canonicalize cross-agent legacy main aliases in session keys (#29683)

* fix: guard shared-store migration against cross-agent legacy alias remap (#29683)

* refactor: split session-key migration out of pr 30654

---------

Co-authored-by: Your Name <your_email@example.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 18:59:25 +05:30
Peter Steinberger
e0f0a1aa1f docs: clarify browser allowlist troubleshooting 2026-03-29 22:19:22 +09:00
wangchunyue
2c8c4e45f1 fix: preserve fallback prompt on model fallback for new sessions (#55632) (thanks @openperf)
* fix(agents): preserve original task prompt on model fallback for new sessions

* fix(agents): use dynamic transcript check for sessionHasHistory on fallback retry

Address Greptile review feedback: replace the static !isNewSession flag
with a dynamic sessionFileHasContent() check that reads the on-disk
transcript before each fallback retry. This correctly handles the edge
case where the primary model completes at least one assistant-response
turn (flushing the user message to disk) before failing - the fallback
now sends the recovery prompt instead of duplicating the original body.

The !isNewSession short-circuit is kept as a fast path so existing
sessions skip the file read entirely.

* fix(agents): address security vulnerabilities in session fallback logic

Fixes three medium-severity security issues identified by Aisle Security Analysis on PR #55632:
- CWE-400: Unbounded session transcript read in sessionFileHasContent()
- CWE-400: Symlink-following in sessionFileHasContent()
- CWE-201: Sensitive prompt replay to a different fallback provider

* fix(agents): use JSONL parsing for session history detection (CWE-703)

Replace bounded byte-prefix substring matching in sessionFileHasContent()
with line-by-line JSONL record parsing. The previous approach could miss
an assistant message when the preceding user content exceeded the 256KB
read limit, causing a false negative that blocks cross-provider fallback
entirely.

* fix(agents): preserve fallback prompt across providers

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 18:35:45 +05:30
wangchunyue
fc3f6fa51f fix: preserve node exec cwd on remote hosts (#50961) (thanks @openperf)
* fix(gateway): skip local workdir resolution for remote node execution

* chore: add inline comment for non-obvious node workdir skip

* fix: preserve node exec cwd on remote hosts (#50961) (thanks @openperf)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 17:46:49 +05:30
Tak Hoffman
5f85c4e69f Docs: add runtime semantics guidance to AGENTS 2026-03-29 06:57:56 -05:00
Ayaan Zaidi
ee701d6bad build: bump Android version to 2026.3.29 2026-03-29 17:15:23 +05:30
Mariano
92d0b3a557 Gateway: abstract task registry storage (#56927)
Merged via squash.

Prepared head SHA: 8db9b860e8
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-29 12:58:06 +02:00
Mariano
17c36b5093 Gateway: track background task lifecycle (#52518)
Merged via squash.

Prepared head SHA: 7c4554204e
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-29 12:48:02 +02:00
Peter Steinberger
270d0c5158 fix: avoid telegram plugin self-recursive sdk imports 2026-03-29 11:32:29 +01:00
Luke
88ca0b2c3f fix(status): handle node-only hosts on current main (#56718)
* Status: handle node-only hosts

* Status: address follow-up review nits

* Changelog: note node-only status fix

* Status: lazy-load node-only helper
2026-03-29 21:12:08 +11:00
nanakotsai
571da81a35 fix: keep openai-codex on HTTP responses transport 2026-03-29 15:04:38 +05:30
Vincent Koc
e06069c8c2 fix(sandbox): add CJK fonts to browser image (#56905) 2026-03-29 18:21:51 +09:00
助爪
443295448c Track ACP sessions_spawn runs and emit ACP lifecycle events (#40885)
* Fix ACP sessions_spawn lifecycle tracking

* fix(tests): resolve leftover merge markers in sessions spawn lifecycle test

* fix(agents): clarify acp spawn cleanup semantics

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-29 18:20:10 +09:00
Vincent Koc
a7a89fb680 fix(ci): retry actionlint release downloads 2026-03-29 18:11:38 +09:00
xingjie zhou
81193336d0 fix(acpx): read ACPX_PINNED_VERSION from package.json instead of hard… (#49089)
* fix(acpx): read ACPX_PINNED_VERSION from package.json instead of hardcoding

The hardcoded ACPX_PINNED_VERSION ("0.1.16") falls out of sync with the bundled acpx version in package.json every release, causing ACP runtime to be marked unavailable due to version mismatch (see #43997).

* Validate and sanitize ACPX version retrieval

Add validation for acpx version from package.json
2026-03-29 18:05:55 +09:00
Frank Yang
5adc50ce6b docs(changelog): add slack status reactions entry 2026-03-29 16:57:47 +08:00
Vincent Koc
7c50138f62 fix(plugins): keep built cli metadata scans lightweight 2026-03-29 17:55:17 +09:00
Hsiao A
cea7162490 feat(slack): status reaction lifecycle for tool/thinking progress indicators (#56430)
Merged via squash.

Prepared head SHA: 1ba5df3e3b
Co-authored-by: hsiaoa <70124331+hsiaoa@users.noreply.github.com>
Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com>
Reviewed-by: @frankekn
2026-03-29 16:49:53 +08:00
Vincent Koc
e28fdb08b8 docs: add LINE ACP support and plugin requireApproval hook docs
- LINE: document ACP conversation binding support (#45826)
- Plugins: document requireApproval in before_tool_call hook semantics (#55339)
2026-03-29 17:45:26 +09:00
Vincent Koc
2899ce5198 Update CHANGELOG.md 2026-03-29 17:42:04 +09:00
Vincent Koc
af694def5b fix(agents): fail closed on silent turns (#52593)
* fix(agents): fail closed on silent turns

* fix(agents): suppress all silent turn emissions

* fix(agents): pass silent turns into embedded subscribe
2026-03-29 17:40:20 +09:00
Vincent Koc
f897aba69a docs: add missing feature docs for Matrix E2EE thumbnails, LINE media, and CJK memory
- Matrix: note encrypted thumbnail behavior in E2EE rooms (#54711)
- LINE: add outbound media section for image/video/audio sends (#45826)
- Memory: document CJK trigram tokenization and chunk sizing
2026-03-29 17:26:02 +09:00
Vincent Koc
3aac43e30b docs: remove stale MiniMax M2.5 refs and add image generation docs
After the M2.7-only catalog trim (#54487), update 10 docs files:
- Replace removed M2.5/VL-01 model references across FAQ, wizard,
  config reference, local-models, and provider pages
- Make local-models guide model-agnostic (generic LM Studio placeholder)
- Add image-01 generation section to minimax.md
- Leave third-party catalogs (Synthetic, Venice) unchanged
2026-03-29 17:26:02 +09:00
Vincent Koc
57882f0351 fix(web-search): localize shared search cache (#54040)
* fix(web-search): localize shared search cache

* docs(changelog): note localized web search cache

* test(web-search): assert module-local cache behavior

* Update CHANGELOG.md
2026-03-29 17:25:07 +09:00
Vignesh Natarajan
4d54376483 Tests: stabilize shard-2 queue and channel state 2026-03-29 01:12:58 -07:00
Vignesh Natarajan
9c185faba9 Agents: cover subagent memory tool policy 2026-03-29 01:12:58 -07:00
factnest365-ops
6c85c82ba3 fix: allow memory_search and memory_get in sub-agent sessions
Remove memory_search and memory_get from SUBAGENT_TOOL_DENY_ALWAYS.

These are read-only tools with no side effects that are essential for
multi-agent setups relying on shared memory for context retrieval.

Rationale:
- Read-only tools (memory_search, memory_get) have no side effects and
  cannot modify state, send messages, or affect external systems
- Other read-only tools (read, web_search, web_fetch) are already
  available to sub-agents by default
- Multi-agent deployments with shared knowledge depend on memory tools
  for context retrieval
- The workaround (tools.subagents.tools.alsoAllow) works but requires
  manual configuration that contradicts memorySearch.enabled: true

Fixes #55385
2026-03-29 01:12:58 -07:00
Peter Steinberger
341e617c84 docs(plugins): refresh bundled plugin runtime docs 2026-03-29 09:10:39 +01:00
Peter Steinberger
caeeecf399 refactor(test): centralize bundled channel test roots 2026-03-29 09:10:39 +01:00
Peter Steinberger
8e0ab35b0e refactor(plugins): decouple bundled plugin runtime loading 2026-03-29 09:10:38 +01:00
Vincent Koc
1738d540f4 fix(gateway/auth): local trusted-proxy fallback to require token auth (#54536)
* fix(auth): improve local request and trusted proxy handling

* fix(gateway): require token for local trusted-proxy fallback

* docs(changelog): credit trusted-proxy auth fix

* Update src/gateway/auth.ts

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

* fix(gateway): fail closed on forwarded local detection

* docs(gateway): clarify fail-closed local detection

* fix(gateway): harden trusted-proxy local fallback

* fix(gateway): align trusted-proxy loopback validation

* Update CHANGELOG.md

---------

Co-authored-by: “zhangning” <zhangning.2025@bytedance.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-29 17:05:00 +09:00
Vincent Koc
9777781001 fix(subagents): preserve requester agent for inline announces (#55998)
* fix(subagents): preserve requester agent on inline announce

* docs(changelog): remove maintainer follow-up entry
2026-03-29 17:00:05 +09:00
Vincent Koc
d6a4ec6a3d fix(telegram): sanitize invalid stream-order errors (#55999)
* fix(telegram): sanitize invalid stream order errors

* docs(changelog): remove maintainer follow-up entry
2026-03-29 16:59:15 +09:00
Vincent Koc
aec58d4cde fix(agents): repair btw reasoning and oauth snapshot refresh (#56001)
* fix(agents): repair btw reasoning and oauth snapshot refresh

* Update CHANGELOG.md

* test(agents): strengthen btw reasoning assertion
2026-03-29 16:58:49 +09:00
Peter Steinberger
f4d60478c9 test: reset plugin runtime state in optional tools tests 2026-03-29 08:52:12 +01:00
Ted Li
ebb919e311 fix: prefer transcript model in sessions list (#55628) (thanks @MonkeyLeeT)
* gateway: prefer transcript model in sessions list

* gateway: keep live subagent model in session rows

* gateway: prefer selected model until runtime refresh

* gateway: simplify session model identity selection

* gateway: avoid transcript model fallback on cost-only reads
2026-03-29 13:20:55 +05:30
Vignesh Natarajan
08b5206b19 chore(test): harden channel plugin registry against malformed runtime state 2026-03-29 00:47:39 -07:00
Vignesh Natarajan
8bdb518bde Memory/LanceDB: fix bundled runtime manifest lookup (#56623) 2026-03-29 00:37:46 -07:00
Peter Steinberger
c48e0f8e6a style: normalize import order and formatting 2026-03-29 16:33:22 +09:00
Peter Steinberger
04c976b43d refactor(markdown): share render-aware chunking 2026-03-29 16:33:22 +09:00
Peter Steinberger
c664b67796 fix: apply Mistral compat across proxy transports 2026-03-29 16:32:31 +09:00
Ayaan Zaidi
4a5885df3a fix(imessage): try all inbound echo ids 2026-03-29 13:00:01 +05:30
GodsBoy
bc9c074b2c fix(channels): use pinned channel registry for outbound adapter resolution
loadChannelOutboundAdapter (via createChannelRegistryLoader) was reading
from getActivePluginRegistry() — the unpinned active registry that gets
replaced whenever loadOpenClawPlugins() runs (config schema reads, plugin
status queries, tool listings, etc.).

After replacement, the active registry may omit channel entries or carry
them in setup mode without outbound adapters, causing:

  Outbound not configured for channel: telegram

The channel inbound path already uses the pinned registry
(getActivePluginChannelRegistry) which is frozen at gateway startup and
survives all subsequent registry replacements. This commit aligns the
outbound path to use the same pinned surface.

Adds a regression test that pins a registry with a telegram outbound
adapter, replaces the active registry with an empty one, then asserts
loadChannelOutboundAdapter still resolves the adapter.

Fixes #54745
Fixes #54013
2026-03-29 12:54:14 +05:30
Rohan Marr
b29e180ef4 fix: prevent self-chat dedupe false positives (#55359) (thanks @rmarr)
* fix(imessage): prevent self-chat dedupe false positives (#47830)

Move echo cache remember() to post-send only, add early return when
inbound message ID doesn't match cached IDs (prevents text-based
false positives in self-chat), and reduce text TTL from 5s to 3s.

Three targeted changes to fix silent user message loss in self-chat:

1. deliver.ts: Remove pre-send remember() call — cache only reflects
   successfully-delivered content, not pre-send full text.

2. echo-cache.ts: Skip text fallback when inbound has a valid message ID
   that doesn't match any cached outbound ID. In self-chat, sender == target
   so scopes collide; a user message with a fresh ID but matching text was
   incorrectly dropped as an echo.

3. echo-cache.ts: Reduce text TTL from 5000ms to 3000ms — agent echoes
   arrive within 1-2s, 5s was too wide.

Adds self-chat-dedupe.test.ts (7 tests) + updates deliver.test.ts.
BlueBubbles uses a different cache pattern — no changes needed there.

Closes #47830

* review(imessage): strip debug logs, bump echo TTL to 4s (#47830)

Bruce Phase 4 review changes:
- Remove all [IMSG-DEBUG] console.error calls from inbound-processing.ts
  and monitor-provider.ts (23 lines, left over from Phase 2 debug deploy)
- Bump SENT_MESSAGE_TEXT_TTL_MS from 3s to 4s in echo-cache.ts to give
  ~2s margin above the observed 2.2s echo arrival time under load
- Update TTL tests to reflect 4s TTL (expired at 5s, live at 3s)

* fix(imessage): add dedupe comments and canary/compat/TTL tests

* fix(imessage): address review feedback on echo cache, shadowing, and test IDs

* refactor(imessage): hoist inboundMessageId to eliminate duplicate computation (#47830)

* fix(imessage): unify self-chat echo matching

* fix: use inbound guid for self-chat echo matching (#55359) (thanks @rmarr)

---------

Co-authored-by: Rohan Marr <rmarr@users.noreply.github.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 12:51:17 +05:30
Vignesh Natarajan
1a0c3bf400 Memory: fix FTS-only branch compile on rebased main 2026-03-29 00:09:33 -07:00
Vignesh Natarajan
598f539be5 Memory: keep FTS-only indexing on reindex (#42714) 2026-03-29 00:06:49 -07:00
opriz
41c30f0c59 fix: populate FTS-only memory search without provider (#56473) (thanks @opriz)
* fix(memory): build FTS index when no embedding provider is available

* fix(memory): trigger full reindex on provider→FTS-only transition

* fix(memory): return FTS-only keyword hits at default threshold

* fix: keep FTS-only memory hits at default threshold (#56473) (thanks @opriz)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 12:22:35 +05:30
Vignesh Natarajan
4b137da582 Test: harden command queue test isolation (CI chore) 2026-03-28 23:43:03 -07:00
Vignesh Natarajan
e816d0968a Status: suppress false dual-stack loopback port warning (#53398) 2026-03-28 23:25:02 -07:00
Gustavo Madeira Santana
c7330eb716 Docs: audit Matrix CLI and setup docs 2026-03-29 01:48:18 -04:00
Gustavo Madeira Santana
efa4e3d83e Docs: audit Matrix channel docs 2026-03-29 01:48:14 -04:00
gfzhx
d458e1d05c fix(discord): do not bypass requireMention for configuredBinding channels 2026-03-29 11:17:15 +05:30
Jakub Rusz
7e7e45c2f3 feat(matrix): add draft streaming (edit-in-place partial replies) (#56387)
Merged via squash.

Prepared head SHA: 53e566bf30
Co-authored-by: jrusz <55534579+jrusz@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-29 01:43:02 -04:00
wangchunyue
dd61171f5b fix: prevent Telegram polling watchdog from dropping replies (#56343) (thanks @openperf)
* fix(telegram): prevent polling watchdog from aborting in-flight message delivery

The polling-stall watchdog only tracked getUpdates timestamps to detect
network stalls. When the agent takes >90s to process a message (common
with local/large models), getUpdates naturally pauses, and the watchdog
misidentifies this as a stall. It then calls fetchAbortController.abort(),
which cancels all in-flight Telegram API requests — including the
sendMessage call delivering the agent's reply. The message is silently
lost with no retry.

Track a separate lastApiActivityAt timestamp that is updated whenever
any Telegram API call (sendMessage, sendChatAction, etc.) completes
successfully. The watchdog now only triggers when both getUpdates AND
all other API activity have been silent beyond the threshold, proving
the network is genuinely stalled rather than just busy processing.

Update existing stall test to account for the new timestamp, and add a
regression test verifying that recent sendMessage activity suppresses
the watchdog.

Fixes #56065
Related: #53374, #54708

* fix(telegram): guard watchdog against in-flight API calls

* fix(telegram): bound watchdog API liveness

* fix: track newest watchdog API activity (#56343) (thanks @openperf)

* fix: note Telegram watchdog delivery fix (#56343) (thanks @openperf)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 11:11:28 +05:30
Shen Yiming
eee8e9679e fix(clawhub): sanitize archive temp filenames (openclaw#56779)
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: soimy <1550237+soimy@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-29 00:38:03 -05:00
Ayaan Zaidi
7a16a48198 fix: keep telegram plugin callbacks explicit 2026-03-29 10:56:36 +05:30
scoootscooob
5d81b64343 fix(exec): fail closed when sandbox is unavailable and harden deny followups (#56800)
* fix(exec): fail closed when sandbox is unavailable and harden deny followups

* docs(changelog): note exec fail-closed fix
2026-03-28 22:20:49 -07:00
Ayaan Zaidi
d5e59621a7 fix: keep telegram plugin fallback explicit (#45911) (thanks @suboss87) 2026-03-29 10:45:57 +05:30
Ayaan Zaidi
1791c7c304 fix: unify telegram exec approval auth 2026-03-29 10:45:57 +05:30
Subash Natarajan
aee7992629 fix(telegram): accept approval callbacks from forwarding target recipients
When approvals.exec.targets routes to a Telegram DM, the recipient
receives inline approval buttons but may not have explicit
channels.telegram.execApprovals configured. This adds a fallback
isTelegramExecApprovalTargetRecipient check so those DM recipients
can act on the buttons they were sent.

Includes accountId scoping for multi-bot deployments and 9 new tests.
2026-03-29 10:45:57 +05:30
Ayaan Zaidi
3a43401924 fix(ui): keep explicit ended steer targets 2026-03-29 10:27:07 +05:30
fuller-stack-dev
83808fe494 fix: wire control-ui steer and redirect (#54625) (thanks @fuller-stack-dev)
* feat(ui): wire /steer slash command to sessions.steer RPC

* feat(ui): wire /steer (soft inject) and /redirect (hard restart) slash commands

* test: use generic subagent names in steer/redirect tests

* fix(ui): exempt steer/redirect from busy-queue and guard sessions.list failures

* fix(ui): skip 'all' wildcard in steer/redirect target resolution

* test: register slash-command-executor test in vitest config

* fix(ui): restrict steer target to subagent keys and active sessions

Address two review issues in resolveSteerTarget:

P2: Replace resolveKillTargets with a dedicated resolveSteerSubagent
that matches only on subagent key suffix or label, not agent id.
This prevents false-positive targeting when the first word collides
with an agent id (e.g. "/steer main refine plan").

P1: Filter out ended sessions (endedAt set) so stale subagents with
reused names are not targeted.

* fix(ui): use shared generateUUID for steer idempotency key

* fix: restore telegram test to upstream state (merge artifact)

* fix(ui): track redirected run so Abort works and concurrent sends are blocked

* fix(ui): skip run tracking when /redirect targets a subagent session

* fix(ui): block idle steer runs

* fix(ui): dedupe steer slash command

* fix(ui): show pending steer state

* fix: wire control-ui steer and redirect (#54625) (thanks @fuller-stack-dev)

* fix: tighten steer target resolution (#54625) (thanks @fuller-stack-dev)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 10:15:58 +05:30
Ayaan Zaidi
e3faa99c6a fix(plugins): preserve gateway-bindable registry reuse
# Conflicts:
#	src/agents/runtime-plugins.test.ts
#	src/agents/runtime-plugins.ts
#	src/plugins/loader.ts
#	src/plugins/tools.ts
2026-03-29 09:59:06 +05:30
Vignesh Natarajan
bb9394e123 Changelog: note Control UI agents metadata/file fix 2026-03-28 21:22:59 -07:00
Vignesh Natarajan
5a0bd9036c Chore: regenerate protocol Swift agent summary models 2026-03-28 21:17:47 -07:00
Vignesh Natarajan
384a590e54 Agents UI: fix effective model and file hydration 2026-03-28 21:10:39 -07:00
Dewaldt Huysamen
27188fa39f fix: scope subagent registry reuse to tool loading (#56240) (thanks @GodsBoy)
* fix(plugins): reuse active registry for sub-agent tool resolution

* test(plugins): harden resolveRuntimePluginRegistry with per-field, caller-shape, and cold-start tests

Add 11 regression tests covering:
- R1: Per-field isolation (coreGatewayHandlers, includeSetupOnlyChannelPlugins,
  preferSetupRuntimeForChannelPlugins each independently prevent fallback;
  empty onlyPluginIds[] treated as non-gateway-scoped)
- R2: Caller-shape regression (tools.ts, memory-runtime.ts,
  channel-resolution.ts shapes fall back; web-search-providers.runtime.ts
  with onlyPluginIds does not)
- R3: Cold-start path (null active registry falls through to loadOpenClawPlugins)

Add debug logging to resolveRuntimePluginRegistry recording which exit path
was taken (no-options, cache-key-match, non-gateway-scoped fallback, fresh load).

* refactor: simplify plugin registry resolution tests and trim happy-path debug logs

* fix(plugins): address review comments on registry fallback

- Fix cold-start test assertion: loadOpenClawPlugins always activates
  the registry (shouldActivate defaults to true), so getActivePluginRegistry()
  is not null after the call. Updated assertion to match actual behavior.

- Add safety comment documenting why the non-gateway-scoped fallback is
  safe despite cache-key mismatch: single-gateway-per-process model means
  sub-agents share workspaceDir, config, and env with the gateway.

* test(plugins): restructure per-field isolation tests to avoid load timeouts

Test isGatewayScopedLoad directly instead of going through the full
resolveRuntimePluginRegistry path which triggers expensive plugin
discovery. This fixes the includeSetupOnlyChannelPlugins test timing
out in CI while providing more precise coverage of the predicate.

* fix(plugins): expand safety comment to address startup-scoped registry concern

* fix(plugins): scope subagent registry reuse to tool loading

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-29 09:36:09 +05:30
Vignesh Natarajan
6883f688e8 Docker setup: force BuildKit for local builds 2026-03-28 20:46:35 -07:00
yuanchao
ec7f19e2ef fix(kimi): preserve valid Anthropic-compatible toolCall arguments in malformed-args repair path (openclaw#54491)
Verified:
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-embedded-runner/run/attempt.test.ts

Co-authored-by: yuanaichi <7549002+yuanaichi@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 22:37:50 -05:00
Vignesh Natarajan
a2e4707cfe fix(agents): recover prefixed malformed tool-call JSON 2026-03-28 20:22:22 -07:00
Vignesh Natarajan
64da916590 fix(tui): stop hijacking j/k in model search 2026-03-28 19:48:00 -07:00
Tak Hoffman
dc64a86eb8 fix(tests): refresh mattermost monitor mocks 2026-03-28 21:37:22 -05:00
Vignesh Natarajan
61a0b02931 fix(tui): preserve optimistic user messages during active runs 2026-03-28 19:32:26 -07:00
Jackjin
a0407c7254 fix(line): preserve underscores inside words in stripMarkdown (#47465)
Fixes #46185.

Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm test -- extensions/line/src/markdown-to-line.test.ts src/tts/prepare-text.test.ts

Note: `pnpm check` currently fails on unchanged `extensions/microsoft/speech-provider.test.ts` lines 108 and 139 on the rebased base, outside this PR diff.
2026-03-28 21:31:09 -05:00
Tak Hoffman
e61ffa68f1 fix(tests): refresh generated schema contracts 2026-03-28 21:19:11 -05:00
Edward-Qiang-2024
1c8758fbd5 Fix: Correctly estimate CJK character token count in context pruner (openclaw#39985)
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test -- src/agents/pi-extensions/context-pruning.test.ts src/utils/cjk-chars.test.ts

Co-authored-by: Edward-Qiang-2024 <176464463+Edward-Qiang-2024@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 21:16:52 -05:00
Vignesh Natarajan
7cf87c4e53 chore(ci): refresh generated schema snapshots 2026-03-28 19:13:36 -07:00
Peter Steinberger
14832ff9f0 build: update appcast for 2026.3.28 2026-03-29 03:12:14 +01:00
Tak Hoffman
38d7e808d9 test: refresh bundled plugin metadata 2026-03-28 21:07:36 -05:00
Extra Small
69a0a0edc5 fix(tts): use Chinese voice for CJK text in edge-tts provider (openclaw#52355)
Verified:
- pnpm test -- extensions/microsoft/speech-provider.test.ts extensions/microsoft/tts.test.ts

Notes:
- Rebases and refactor-port completed onto current main.
- No required GitHub checks were reported for this branch at merge time.

Co-authored-by: Extra Small <littleshuai.bot@gmail.com>
2026-03-28 21:06:48 -05:00
Tak Hoffman
f1970b8aef fix(line): normalize canonical ACP targets (#56713) 2026-03-28 21:05:34 -05:00
Vignesh Natarajan
89881379dc fix(config): allow plugin channels in hooks mappings 2026-03-28 19:00:05 -07:00
Tak Hoffman
3ce48aff66 Memory: add configurable FTS5 tokenizer for CJK text support (openclaw#56707)
Verified:
- pnpm build
- pnpm check
- pnpm test -- extensions/memory-core/src/memory/manager-search.test.ts packages/memory-host-sdk/src/host/query-expansion.test.ts
- pnpm test -- extensions/memory-core/src/memory/index.test.ts -t "reindexes when extraPaths change"
- pnpm test -- src/config/schema.base.generated.test.ts
- pnpm test -- src/media-understanding/image.test.ts
- pnpm test

Co-authored-by: Mitsuyuki Osabe <24588751+carrotRakko@users.noreply.github.com>
2026-03-28 20:53:29 -05:00
Tak Hoffman
6f7ff545dd fix(line): add ACP binding parity (#56700)
* fix(line): support ACP current-conversation binding

* fix(line): add ACP binding routing parity

* docs(changelog): note LINE ACP parity

* fix(line): accept canonical ACP binding targets
2026-03-28 20:52:31 -05:00
Masato Hoshino
9449e54f4f feat(line): add outbound media support for image, video, and audio
pnpm install --frozen-lockfile
pnpm build
pnpm check
pnpm vitest run extensions/line/src/channel.sendPayload.test.ts extensions/line/src/send.test.ts extensions/line/src/outbound-media.test.ts

Co-authored-by: masatohoshino <246810661+masatohoshino@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 20:51:16 -05:00
jnuyao
f93ccc3443 fix(ui): prevent marked from auto-linking adjacent CJK characters (openclaw#48410)
Verified:
- ui: pnpm test -- --run src/ui/markdown.test.ts
- local full gate relaxed for this run; no required GitHub checks reported on the branch

Co-authored-by: jnuyao <2928523+jnuyao@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-28 20:49:29 -05:00
Peter Steinberger
f9b1079283 build: cut 2026.3.28 stable 2026-03-29 02:33:41 +01:00
Peter Steinberger
584e627d77 docs: add changelog for CJK memory chunking (#40271) 2026-03-29 10:22:43 +09:00
AaronLuo00
f8547fcae4 fix: guard fine-split against breaking UTF-16 surrogate pairs
When re-splitting CJK-heavy segments at chunking.tokens, check whether the
slice boundary falls on a high surrogate (0xD800–0xDBFF) and if so extend
by one code unit to keep the pair intact.  Prevents producing broken
surrogate halves for CJK Extension B+ characters (U+20000+).

Add test verifying no lone surrogates appear when splitting lines of
surrogate-pair characters with an odd token budget.

Addresses third-round Codex P2 review comment.
2026-03-29 10:22:43 +09:00
AaronLuo00
3b95aa8804 fix: address second-round review — Latin backward compat and emoji consistency
- Two-pass line splitting: first slice at maxChars (unchanged for Latin),
  then re-split only CJK-heavy segments at chunking.tokens. This preserves
  the original ~800-char segments for ASCII lines while keeping CJK chunks
  within the token budget.

- Narrow surrogate-pair adjustment to CJK Extension B+ range (D840–D87E)
  only, so emoji surrogate pairs are not affected. Mixed CJK+emoji text
  is now handled consistently regardless of composition.

- Add tests: emoji handling (2), Latin backward-compat long-line (1).

Addresses Codex P1 (oversized CJK segments) and P2s (Latin over-splitting,
emoji surrogate inconsistency).
2026-03-29 10:22:43 +09:00
AaronLuo00
a5147d4d88 fix: address bot review — surrogate-pair counting and CJK line splitting
- Use code-point length instead of UTF-16 length in estimateStringChars()
  so that CJK Extension B+ surrogate pairs (U+20000+) are counted as 1
  character, not 2 (fixes ~25% overestimate for rare characters).

- Change long-line split step from maxChars to chunking.tokens so that
  CJK lines are sliced into token-budget-sized segments instead of
  char-budget-sized segments that produce ~4x oversized chunks.

- Add tests for both fixes: surrogate-pair handling and long CJK line
  splitting.

Addresses review feedback from Greptile and Codex bots.
2026-03-29 10:22:43 +09:00
AaronLuo00
971ecabe80 fix(memory): account for CJK characters in QMD memory chunking
The QMD memory system uses a fixed 4:1 chars-to-tokens ratio for chunk
sizing, which severely underestimates CJK (Chinese/Japanese/Korean) text
where each character is roughly 1 token. This causes oversized chunks for
CJK users, degrading vector search quality and wasting context window space.

Changes:
- Add shared src/utils/cjk-chars.ts module with CJK-aware character
  counting (estimateStringChars) and token estimation helpers
- Update chunkMarkdown() in src/memory/internal.ts to use weighted
  character lengths for chunk boundary decisions and overlap calculation
- Replace hardcoded estimateTokensFromChars in the context report
  command with the shared utility
- Add 13 unit tests for the CJK estimation module and 5 new tests for
  CJK-aware memory chunking behavior

Backward compatible: pure ASCII/Latin text behavior is unchanged.

Closes #39965
Related: #40216
2026-03-29 10:22:43 +09:00
Vignesh Natarajan
7f46b03de0 fix: keep memory flush daily files append-only (#53725) (thanks @HPluseven) 2026-03-28 18:22:11 -07:00
Vignesh Natarajan
9d1498b2c2 Agents: add memory flush append regression 2026-03-28 18:22:11 -07:00
HPluseven
60b7613156 Agents: forward memory flush append guard 2026-03-28 18:22:11 -07:00
Vignesh Natarajan
e2d0b7c583 chore(test): harden mattermost slash-http module mocks 2026-03-28 18:21:19 -07:00
Peter Steinberger
72de33c976 chore: refresh plugin sdk api baseline 2026-03-29 02:16:37 +01:00
Peter Steinberger
148a65fe90 refactor: share webhook channel status helpers 2026-03-29 02:11:22 +01:00
Gustavo Madeira Santana
2afc655bd5 ACP: document Matrix bind-here support 2026-03-28 21:07:58 -04:00
Vignesh Natarajan
19e52a1ba2 fix(memory/qmd): honor embedInterval independent of update interval 2026-03-28 18:05:05 -07:00
karesansui
acbdafc4f4 fix: propagate webhook mode to health monitor snapshot
Webhook channels (LINE, Zalo, Nextcloud Talk, BlueBubbles) are
incorrectly flagged as stale-socket during quiet periods because
snapshot.mode is always undefined, making the mode !== "webhook"
guard in evaluateChannelHealth dead code.

Add mode: "webhook" to each webhook plugin's describeAccount and
propagate described.mode in getRuntimeSnapshot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 02:01:25 +01:00
Peter Steinberger
acca306665 fix: polish LINE status snapshot checks (#45701) (thanks @tamaosamu) 2026-03-29 00:57:04 +00:00
劉超
03941e2dbf fix(line): use configured field in collectStatusIssues instead of raw token
collectStatusIssues previously checked account.channelAccessToken directly,
but this field is stripped by projectSafeChannelAccountSnapshotFields for
security. This caused 'openclaw status' to always report WARN even when the
token is valid and the LINE provider starts successfully.

Use account.configured instead, which is already computed by
buildChannelAccountSnapshot and correctly reflects whether credentials
are present. This is consistent with how other channels (e.g. Telegram)
implement their status checks.

Fixes #45693
2026-03-29 00:57:04 +00:00
Peter Steinberger
b23ed7530b chore: ignore local tmp workspace 2026-03-29 00:51:03 +00:00
Peter Steinberger
5ebccf5e30 test: harden zalo webhook lifecycle tests 2026-03-29 00:48:02 +00:00
Peter Steinberger
9e1b524a00 fix: break mattermost runtime cycle 2026-03-29 00:43:58 +00:00
Peter Steinberger
fcc9fd1623 fix: land LINE timing-safe signature validation (#55663) (thanks @gavyngong) 2026-03-29 00:43:17 +00:00
gavyngong
7626d18c64 fix(line): eliminate timing side-channel in HMAC signature validation
Pad both buffers to equal length before constant-time comparison.

Key fix: call timingSafeEqual unconditionally and store the result
before the && length check, ensuring the constant-time comparison
always runs regardless of buffer lengths. This avoids JavaScript's
&& short-circuit evaluation which would skip timingSafeEqual on
length mismatches, preserving the timing side-channel.

Changes:
- Pad hash and signature buffers to maxLen before comparison
- Store timingSafeEqual result before combining with length check
- Add explanatory comment about the short-circuit avoidance
2026-03-29 00:43:17 +00:00
Peter Steinberger
92fb0caf35 fix: harden mac gateway attach smoke 2026-03-29 00:35:40 +00:00
Peter Steinberger
f9281d1b9d build: bump aws sdk and tsdown 2026-03-29 00:35:15 +00:00
Gustavo Madeira Santana
225d58b74a test: ignore stale isolated state dir in live env staging 2026-03-28 20:35:07 -04:00
Peter Steinberger
0e0945c5ed docs: reorder unreleased changelog fixes 2026-03-29 00:29:21 +00:00
Peter Steinberger
5cfb979766 docs: add missing CLAUDE symlinks 2026-03-29 09:29:04 +09:00
Peter Steinberger
5efed49208 fix: keep mac local gateway attached 2026-03-29 00:28:32 +00:00
Peter Steinberger
c9f1506d2f docs(xai): clarify x_search onboarding flow 2026-03-29 00:25:18 +00:00
Harold Hunt
fcee6fa047 Docs: add boundary AGENTS guides (#56647) 2026-03-28 20:22:03 -04:00
Peter Steinberger
03826b8075 fix(test): harden planner artifact cleanup and profile env fallback 2026-03-29 00:20:19 +00:00
Vignesh Natarajan
c3a0304f63 chore(test): fix stale web search audit coverage 2026-03-28 17:18:57 -07:00
Peter Steinberger
3d69ad8308 fix: preserve Teams Entra JWT fallback on legacy validator errors 2026-03-29 09:15:13 +09:00
Peter Steinberger
5872f860c9 feat(xai): add plugin-owned x_search onboarding 2026-03-29 00:12:37 +00:00
Gustavo Madeira Santana
ebb4794952 Tests: reuse paired provider contract aliases 2026-03-28 20:08:38 -04:00
Gustavo Madeira Santana
680c30bc5d Tests: shim config runtime for capability contracts 2026-03-28 20:02:28 -04:00
Peter Steinberger
3cbd3960f9 test: add minimax parallels smoke lane 2026-03-29 00:01:59 +00:00
Gustavo Madeira Santana
d0e0150129 Tests: retry scoped contract registry loads 2026-03-28 19:53:21 -04:00
Gustavo Madeira Santana
d3673fd53e Tests: isolate xAI contract lanes 2026-03-28 19:36:53 -04:00
Vignesh Natarajan
4e74e7e26c fix(memory): resolve slugified qmd search paths (#50313) 2026-03-28 16:26:38 -07:00
Gustavo Madeira Santana
5289e8f0fe Tests: lazy-load web search contract registries 2026-03-28 19:24:38 -04:00
Robin Waslander
3847ace25b fix(telegram): preserve forum topic routing for /new and /reset (#56654)
Build a topic-qualified routing target (telegram:<chatId>:topic:<threadId>)
for native commands in forum groups so /new and /reset stay scoped to
the active topic instead of falling back to General.

General topic (threadId=1) correctly falls through to the base chat
target since Telegram rejects message_thread_id=1 on sends.

Add regression tests for topic routing and General topic edge case.

Fixes #35963
2026-03-29 00:21:41 +01:00
Gustavo Madeira Santana
094524a549 Types: tighten Teams validator return type 2026-03-28 19:15:27 -04:00
Gustavo Madeira Santana
bd1c48e4d9 Tests: lazy-load extension contract registries 2026-03-28 19:09:49 -04:00
Brad Groux
dc382b09be fix(msteams): accept strict Bot Framework and Entra service tokens (#56631)
* msteams: log policy-based inbound drops at info level

* fix(msteams): validate Bot Framework and Entra service token issuers

---------

Co-authored-by: Brad Groux <bradgroux@users.noreply.github.com>
2026-03-28 18:04:00 -05:00
Gustavo Madeira Santana
0b8bc0e1b4 Tests: cap CI extension batch concurrency 2026-03-28 18:58:47 -04:00
frischeDaten
81432d6b7e fix(matrix): encrypt thumbnails in E2EE rooms using thumbnail_file (#54711)
Merged via squash.

Prepared head SHA: 92be0e1ac2
Co-authored-by: frischeDaten <5878058+frischeDaten@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-28 18:41:23 -04:00
Robin Waslander
468185d1b5 fix(agents): handle unhandled stop reasons gracefully instead of crashing (#56639)
Wrap the embedded agent stream to catch 'Unhandled stop reason: ...'
errors from the provider adapter and convert them into structured
assistant error messages instead of crashing the agent run.

Covers all unknown stop reasons so future provider additions don't
crash the runner. The wrapper becomes a harmless no-op once the
upstream dependency handles them natively.

Fixes #43607
2026-03-28 23:35:12 +01:00
Peter Steinberger
664680318e fix(release): validate built tarballs in workflows 2026-03-28 22:33:24 +00:00
Peter Steinberger
1249dad6c4 fix(release): skip lifecycle scripts in npm precheck 2026-03-28 22:29:47 +00:00
Peter Steinberger
587e18cd3f chore: prepare 2026.3.28-beta.1 release 2026-03-28 22:24:51 +00:00
Peter Steinberger
143fb34bf9 docs: remove stale code_execution secretref path 2026-03-28 22:10:22 +00:00
Peter Steinberger
45ecf5e2e9 fix(xai): narrow code execution config typing 2026-03-28 22:10:22 +00:00
Peter Steinberger
8a24cbf450 chore: bump version to 2026.3.28 2026-03-28 22:05:21 +00:00
OfflynAI
cb00d44ae4 fix(matrix): load crypto-nodejs via createRequire to fix __dirname in ESM (#54566)
Merged via squash.

Prepared head SHA: 61d99628f9
Co-authored-by: joelnishanth <140015627+joelnishanth@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-28 17:43:44 -04:00
Robin Waslander
2022dfd0a1 chore: backfill changelog entries for recent fixes (#56625)
Add missing changelog entries for PRs #56500, #56540, #56555, #56567,
#56573, #56587, #56595, #56612, #56620.
2026-03-28 22:42:27 +01:00
huntharo
216796f1e3 fix(xai): wire plugin-owned codeExecution config 2026-03-28 21:35:13 +00:00
huntharo
b7ab0ddb55 refactor(xai): move code_execution into plugin 2026-03-28 21:35:13 +00:00
Peter Steinberger
1617e0218f test(xai): add live x_search coverage 2026-03-28 21:35:13 +00:00
Peter Steinberger
6e0b67a2fd docs(secrets): sync credential surface matrix 2026-03-28 21:35:13 +00:00
Peter Steinberger
887d7584d6 refactor(plugins): expose bundled onboard helpers 2026-03-28 21:35:13 +00:00
Peter Steinberger
46c2928234 fix(plugins): stabilize provider contract loading 2026-03-28 21:35:13 +00:00
Peter Steinberger
dba1b31243 fix(xai): repair extension test boundaries 2026-03-28 21:35:13 +00:00
Peter Steinberger
1e424990a2 fix(xai): restore config-backed auth discovery 2026-03-28 21:35:13 +00:00
Peter Steinberger
2a950157b1 refactor(xai): move x_search into plugin 2026-03-28 21:35:13 +00:00
huntharo
396bf20cc6 Tools: add xAI-backed code_execution 2026-03-28 21:35:13 +00:00
huntharo
1c9684608a Docs: guide x_search toward exact-post stats lookups 2026-03-28 21:35:13 +00:00
huntharo
c8ed1638ea xAI: restore generic auth and x_search seams 2026-03-28 21:35:13 +00:00
huntharo
43143486eb Docs: refresh x_search secretref matrix 2026-03-28 21:35:13 +00:00
huntharo
0391e455bf Lint: drop stale model compat imports 2026-03-28 21:35:13 +00:00
huntharo
92fb4ad233 xAI: route x_search through public api seam 2026-03-28 21:35:13 +00:00
huntharo
4f3009f57e Tests: classify x_search secret target parity 2026-03-28 21:35:13 +00:00
huntharo
6e3b54430c Tests: keep extension onboarding coverage under extensions 2026-03-28 21:35:13 +00:00
huntharo
09e2ef965b Tests: fix rebased auth and runner type coverage 2026-03-28 21:35:13 +00:00
huntharo
b22f65992e Build: fix rebased provider secrets helper 2026-03-28 21:35:13 +00:00
huntharo
b918568b1e Rebase: reconcile xAI post-main conflicts 2026-03-28 21:35:13 +00:00
huntharo
fb989f0402 Tests: restore provider runtime contract wrapper 2026-03-28 21:35:13 +00:00
huntharo
df61660a26 xAI: centralize fallback auth resolution 2026-03-28 21:35:13 +00:00
huntharo
9dd08a49a4 xAI: reuse fallback auth for runtime and discovery 2026-03-28 21:35:13 +00:00
huntharo
800042a3d5 xAI: reuse plugin key for x_search 2026-03-28 21:35:13 +00:00
huntharo
8ca3710b90 xAI: strip unsupported payload fields 2026-03-28 21:35:13 +00:00
huntharo
fd748171b8 xAI: strip unsupported Responses reasoning params 2026-03-28 21:35:13 +00:00
huntharo
80a1ccc552 xAI: preserve session auth in embedded runs 2026-03-28 21:35:13 +00:00
huntharo
2765fdc2dd xAI: normalize stale Grok transport to Responses 2026-03-28 21:35:13 +00:00
huntharo
f0ce658fbb xAI: add auth resolution diagnostics 2026-03-28 21:35:13 +00:00
huntharo
d5fafbe3ce xAI: honor config-backed auth during provider bootstrap 2026-03-28 21:35:13 +00:00
huntharo
2d919cf63d xAI: reuse web search key for provider auth 2026-03-28 21:35:13 +00:00
huntharo
38e4b77e60 Tools: add x_search via xAI Responses 2026-03-28 21:35:13 +00:00
huntharo
5ed8ee6832 xAI: switch bundled provider defaults to Responses 2026-03-28 21:35:13 +00:00
Robin Waslander
4d6c8edd74 fix(telegram): skip empty text replies instead of crashing with GrammyError 400 (#56620)
Filter whitespace-only text chunks at the bot delivery fan-in before
they reach sendTelegramText(). Covers normal text replies, follow-up
text, and voice fallback text paths.

Media-only replies are unaffected. message_sent hook still fires with
success: false for suppressed empty replies.

Fixes #37278
2026-03-28 22:27:56 +01:00
Peter Steinberger
eec290e68d fix: support anthropic parallels smoke lanes 2026-03-28 21:27:39 +00:00
Devin Robison
703e68a749 Fix HTTP OpenAI-compatible routes missing operator.write scope checks (#56618)
* Fix HTTP OpenAI-compatible routes missing operator.write scope checks

* Update src/gateway/http-endpoint-helpers.ts

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

* Address Greptile feedback

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-28 15:22:21 -06:00
Robin Waslander
17479ceb43 fix(auto-reply): suppress JSON-wrapped NO_REPLY payloads before channel delivery (#56612)
Add shared isSilentReplyPayloadText() detector that catches both bare
NO_REPLY tokens and JSON {"action":"NO_REPLY"} envelopes. Apply at the
reply directive parser, reply normalizer, and embedded agent payload
builder so the control payload is stripped before any channel sees it.

Preserves media when text is only a silent control envelope.

Fixes #37727
2026-03-28 22:07:24 +01:00
Robin Waslander
ab2ef7bbfc fix(telegram): split long messages at word boundaries instead of mid-word (#56595)
Replace proportional text estimate with binary search for the largest
text prefix whose rendered Telegram HTML fits the character limit, then
split at the last whitespace boundary within that verified prefix.

Single words longer than the limit still hard-split (unavoidable).
Markdown formatting stays balanced across split points.

Fixes #36644
2026-03-28 21:24:59 +01:00
Robin Waslander
865160e572 fix(telegram): validate replyToMessageId before sending to Telegram API (#56587)
Add shared normalizeTelegramReplyToMessageId() that rejects non-numeric,
NaN, and mixed-content strings before they reach the Telegram Bot API.
Apply at all four API sinks: direct send, bot delivery, draft stream,
and bot helpers.

Prevents GrammyError 400 when non-numeric values from session metadata
slip through typed boundaries.

Fixes #37222
2026-03-28 20:47:10 +01:00
Robin Waslander
e69ea1acb3 fix(bluebubbles): guard debounce flush against null text (#56573)
Sanitize message text at the debounce enqueue boundary and add an
independent guard in combineDebounceEntries(). Prevents TypeError when
a queued entry has null text that reaches .trim() during flush.

Add regression test: enqueue null-text entry alongside valid message,
verify flush completes without error and valid message is delivered.

Fixes #35777
2026-03-28 20:22:05 +01:00
Peter Steinberger
756df2e955 test: tune gateway live probe skips 2026-03-28 19:13:47 +00:00
Peter Steinberger
914becee52 fix: isolate live test home from real config 2026-03-28 19:06:59 +00:00
Peter Steinberger
8ea4c4a6ba fix: tolerate npm stderr in Windows Parallels update smoke 2026-03-28 18:59:17 +00:00
Robin Waslander
d1b0f8e8e2 fix(google): resolve Gemini 3.1 models for all Google provider aliases (#56567)
The forward-compat resolver hardcoded 'google' as the provider ID for
template lookup, so alias providers (google-vertex, google-gemini-cli)
could not find matching templates. Pass the actual provider ID from the
runtime context and add a templateProviderId fallback for cross-provider
template resolution.

Also fix flash-lite prefix ordering — check 'gemini-3.1-flash-lite'
before 'gemini-3.1-flash' to prevent misclassification.

Add regression tests for pro, flash, and flash-lite across provider
aliases.

Fixes #36111
2026-03-28 19:59:14 +01:00
Robin Waslander
6be14ab388 fix(cli): defer zsh compdef registration until compinit is available (#56555)
The generated zsh completion script called compdef at source time,
which fails with 'command not found: compdef' when loaded before
compinit. Replace with a deferred registration that tries immediately,
and if compdef is not yet available, queues a self-removing precmd hook
that retries on first prompt.

Handles repeated sourcing (deduped hook entry) and shells that never
run compinit (completion simply never registers, matching zsh model).

Add real zsh integration test verifying no compdef error on source and
successful registration after compinit.

Fixes #14289
2026-03-28 19:35:32 +01:00
Tak Hoffman
f32f7d0809 Improve dashboard setup command copy UX (#56551) 2026-03-28 13:09:22 -05:00
Robin Waslander
31112d5985 fix(security): audit web search keys for all bundled providers (#56540)
hasWebSearchKey() was hardcoded to only check Brave and Perplexity
credentials. Replace with provider-aware check using
resolveBundledPluginWebSearchProviders() so Gemini, Grok/XAI, Kimi,
Moonshot, and OpenRouter credentials are recognized by the audit.

Add focused regression tests for each provider.

Fixes #34509
2026-03-28 18:55:38 +01:00
Peter Steinberger
02d4c1f2c3 refactor: derive channel metadata from plugin manifests 2026-03-28 17:17:10 +00:00
Frank Yang
c14b169a1b fix(acp): repair stale bindings after runtime exits (#56476)
* fix(acp): repair stale bindings after runtime exits

* fix(acp): narrow stale binding recovery

* fix(acp): preserve policy gating for stale sessions

* fix(acp): handle signal exits and canonical unbinds

* fix(acp): harden canonical stale-session recovery
2026-03-29 01:15:16 +08:00
Peter Steinberger
22de54d83d test: handle live model probe edge cases 2026-03-28 17:12:09 +00:00
Peter Steinberger
5194cf2019 refactor: load bundled provider catalogs dynamically 2026-03-28 16:57:36 +00:00
Tak Hoffman
54313a8730 fix(dev): rebuild dist after HEAD changes (#56510) 2026-03-28 11:49:09 -05:00
Robin Waslander
840b806c2f fix(docs): remove broken Xfinity SSL troubleshooting links from FAQ (#56500)
Remove circular self-link in English FAQ and dead anchor reference in
zh-CN FAQ. Both FAQ sections already contain the full workaround inline,
so the cross-references added no value and were never backed by a valid
target in troubleshooting.md.

Fixes #36970
2026-03-28 17:18:26 +01:00
Tak Hoffman
7a878164b0 ci: align bun shard counts with windows (#56429)
* ci: align bun shard counts with windows

* ci: retrigger stuck windows shard
2026-03-28 09:36:59 -05:00
Peter Steinberger
23772bb785 test: exclude topology fixtures from vitest collection 2026-03-28 13:49:16 +00:00
Peter Steinberger
f3ecd9ca9c test: guard ui session storage access in node runs 2026-03-28 13:38:21 +00:00
Tak Hoffman
3a34e6b65d Add reusable TypeScript topology analyzer for public surface usage 2026-03-28 08:37:26 -05:00
Peter Steinberger
5302aa8947 test: use safe storage helpers in app mount hooks 2026-03-28 13:24:04 +00:00
Saurabh Mishra
90e82fabb3 fix: display model name instead of ID in Telegram model selector (#56165) (#56175)
* fix: display model name instead of ID in Telegram model selector (#56165)

* fix(telegram): scope model display names by provider

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-03-28 09:23:09 -04:00
Peter Steinberger
e999f2aae3 test: silence lit dev-mode warnings in ui suite 2026-03-28 13:13:02 +00:00
Peter Steinberger
8c4cc61656 test: avoid raw localStorage access in chat view test 2026-03-28 13:10:27 +00:00
Peter Steinberger
bccbfdebfe fix: hydrate lazy tts provider config from source config 2026-03-28 12:56:27 +00:00
Peter Steinberger
3bb199aa43 refactor: lazy-load matrix setup bootstrap surfaces 2026-03-28 12:46:54 +00:00
Peter Steinberger
5df53a99b1 fix: set localstorage file for test planner workers 2026-03-28 12:46:54 +00:00
Tyler Yust
41cf93efff fix: include extension channels in subagent announce delivery path (#56348)
* fix: include extension channels in subagent announce delivery path

* test: cover extension announce delivery routes
2026-03-28 21:15:23 +09:00
Peter Steinberger
107969c725 test: silence warning filter stderr 2026-03-28 11:57:27 +00:00
Peter Steinberger
9b0b962f8c test: silence ui localstorage warning 2026-03-28 11:54:51 +00:00
Peter Steinberger
4757c32f63 test: silence planner fixture stderr 2026-03-28 11:53:14 +00:00
Peter Steinberger
241748ae60 test: align code region fence slices 2026-03-28 11:48:13 +00:00
Peter Steinberger
aa9454f270 fix: restore xai pricing cache fallback 2026-03-28 11:43:12 +00:00
Peter Steinberger
8061b792b2 test: repair focused unit lane drift 2026-03-28 11:41:06 +00:00
Peter Steinberger
aa33d585be fix: repair package contract and boundary drift 2026-03-28 11:40:40 +00:00
Peter Steinberger
f44d68a4f4 test: stabilize model auth label mocks 2026-03-28 11:40:40 +00:00
Peter Steinberger
c5a48a8c8a test: cover oauth profile store migration 2026-03-28 11:40:40 +00:00
Peter Steinberger
1c5a4d2a2b fix: stabilize implicit provider discovery merges 2026-03-28 11:40:40 +00:00
Peter Steinberger
e34a770b8a fix: keep provider discovery on mockable lazy runtime paths 2026-03-28 11:40:40 +00:00
Peter Steinberger
ff01d749fc fix: keep provider normalization on local sync paths 2026-03-28 11:40:13 +00:00
Peter Steinberger
cec1703734 fix: keep model selection on local normalization paths 2026-03-28 11:40:13 +00:00
Peter Steinberger
c1ae49e306 fix: keep cost lookup on sync pricing paths 2026-03-28 11:40:13 +00:00
Peter Steinberger
dec91c400d fix: keep status display on sync model metadata 2026-03-28 11:37:43 +00:00
Peter Steinberger
84d1781a3a fix: avoid status-time provider normalization recursion 2026-03-28 11:35:33 +00:00
Peter Steinberger
030d2e8b71 test: fix tts status helper temp-home prefs path 2026-03-28 11:35:33 +00:00
Peter Steinberger
0e11072b84 fix: avoid speech runtime import in status output 2026-03-28 11:35:33 +00:00
Peter Steinberger
85b3c1db30 fix: defer tts provider resolution until needed 2026-03-28 11:35:33 +00:00
Peter Steinberger
86dba6d906 fix: skip speech provider discovery on tts off path 2026-03-28 11:35:33 +00:00
Ayaan Zaidi
cfba0ab68f fix(process): wait for windows close state settlement 2026-03-28 16:55:15 +05:30
Ayaan Zaidi
0ebd7df9dc test(feishu): stabilize bot-menu lifecycle replay 2026-03-28 16:46:21 +05:30
Ayaan Zaidi
c3c1f9df54 fix(process): wait for windows exit code settlement 2026-03-28 16:37:29 +05:30
nikus-pan
bef4fa55f5 fix(model-fallback): add HTTP 410 to failover reason classification (#55201)
Merged via squash.

Prepared head SHA: 9c1780b739
Co-authored-by: nikus-pan <71585761+nikus-pan@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-28 14:03:20 +03:00
Ayaan Zaidi
b31cd35b36 test(plugin-sdk): align matrix runtime api guardrail 2026-03-28 16:20:33 +05:30
Ayaan Zaidi
8d8652257c test(providers): align active registry expectations 2026-03-28 16:15:56 +05:30
Ayaan Zaidi
c06dcf6b8b fix(plugins): preserve active capability providers 2026-03-28 15:46:24 +05:30
Ayaan Zaidi
8ea5c22985 fix(matrix): avoid heavy jiti runtime barrels 2026-03-28 15:35:05 +05:30
Ayaan Zaidi
a628d5d78b fix(irc): pin runtime barrel exports for jiti 2026-03-28 15:23:22 +05:30
Ayaan Zaidi
3145757f8f test: make minimax image path batch-stable 2026-03-28 15:07:53 +05:30
Peter Steinberger
6f6b55c072 fix: stabilize provider sdk runtime surfaces 2026-03-28 09:35:42 +00:00
Peter Steinberger
a955537a61 fix: slim provider sdk surfaces 2026-03-28 09:35:42 +00:00
Peter Steinberger
2212bd0d4a test: align runtime registry fixtures 2026-03-28 09:35:42 +00:00
Peter Steinberger
1d6ba41762 test: stabilize snapshot and typing helpers 2026-03-28 09:35:42 +00:00
Ayaan Zaidi
20aba8c518 fix(ci): restore extension test runtime deps and update voice-call expectations 2026-03-28 15:04:33 +05:30
Ayaan Zaidi
40a09cc582 fix(irc): avoid registry bootstrap in plugin sdk seam 2026-03-28 14:58:02 +05:30
Ayaan Zaidi
e0ba57e9c7 fix: preserve windows child exit codes 2026-03-28 14:55:20 +05:30
Ayaan Zaidi
7320973ab0 fix(provider-wizard): avoid hook-time model normalization 2026-03-28 14:46:12 +05:30
Ayaan Zaidi
ced88298d8 test: make media runtime seam mock bun-safe 2026-03-28 14:39:47 +05:30
Ayaan Zaidi
28074eeea3 fix: dedupe plugin sdk file-lock export 2026-03-28 14:35:24 +05:30
Mariano
0afd73c975 fix(daemon): surface probe close reasons (#56282)
Merged via squash.

Prepared head SHA: c356980aa4
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-28 10:04:56 +01:00
Ayaan Zaidi
a68bef42eb test: align code region fence slices 2026-03-28 14:34:01 +05:30
Ayaan Zaidi
5d3104e699 fix: regenerate swift protocol models 2026-03-28 14:29:41 +05:30
Kenny Xie
cb5afdf108 fix: prevent matrix-js-sdk plugin load crash (#56273) (thanks @aquaright1)
* Fix matrix-js-sdk multiple entrypoint crash on plugin load

* test(matrix): cover runtime bundle import regression

* fix: prevent matrix-js-sdk plugin load crash (#56273) (thanks @aquaright1)

* fix: widen matrix-js-sdk bundle import guard (#56273) (thanks @aquaright1)

---------

Co-authored-by: Kenny Xie <kennyxie@Mac-mini-von-Kenny.local>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-28 14:27:54 +05:30
Ayaan Zaidi
3c0cf26e16 test: align media runtime registry seam 2026-03-28 14:21:17 +05:30
Ayaan Zaidi
5f82741d0f test: align telegram thread binding seam coverage 2026-03-28 14:17:09 +05:30
Ayaan Zaidi
4ef2615d7d test: fix bedrock discovery seam typing 2026-03-28 14:09:25 +05:30
Lakshya Agarwal
4dfd2cd60c feat: add support for extra headers in Tavily API requests (#55335)
* feat: add support for extra headers in Tavily API requests

* test(tavily-client): add unit tests for X-Client-Source header in API calls

* fix(tavily): add client source attribution (#55335) (thanks @lakshyaag-tavily)

---------

Co-authored-by: Nimrod Gutman <nimrod.gutman@gmail.com>
2026-03-28 11:36:59 +03:00
Ayaan Zaidi
6777764a6b test: use bedrock plugin sdk seam in discovery tests 2026-03-28 14:05:49 +05:30
Peter Steinberger
a631604247 fix(ci): stabilize browser bundled integration tests 2026-03-28 08:34:18 +00:00
Peter Steinberger
a735a1a2d4 fix(text): handle fenced code fence termination 2026-03-28 08:34:18 +00:00
Ayaan Zaidi
c0a56ac1a1 test: use suite gateway hooks for channel mcp 2026-03-28 13:52:59 +05:30
Ayaan Zaidi
5224c5bbd5 test: isolate usage pricing cache state 2026-03-28 13:48:58 +05:30
Peter Steinberger
1c833b1eb5 test: align outbound telegram bootstrap mocks 2026-03-28 08:10:47 +00:00
Peter Steinberger
48b2eb2604 test: fix media and channel regression expectations 2026-03-28 08:10:47 +00:00
Peter Steinberger
7d000088a4 fix(ci): use process env for provider compat fallback 2026-03-28 08:10:47 +00:00
Peter Steinberger
f4fb45f1ee test: dedupe channel helper suites 2026-03-28 08:10:47 +00:00
Ayaan Zaidi
2181909f9a test: update image generation runtime seam 2026-03-28 13:39:55 +05:30
Ayaan Zaidi
0feeea0994 test: make media symlink fixture idempotent 2026-03-28 13:30:11 +05:30
Ayaan Zaidi
912bd1f5cc test: align media parse expectations 2026-03-28 13:29:14 +05:30
Peter Steinberger
bd4632b9c1 fix: mark buffered reply typing runs complete 2026-03-28 07:57:28 +00:00
Peter Steinberger
be38986141 fix: guard bundled channel runtime against TDZ imports 2026-03-28 07:57:28 +00:00
Ayaan Zaidi
04a40b2613 test: refresh base config schema snapshot 2026-03-28 13:25:37 +05:30
Ayaan Zaidi
8366c74ccd test: mock telegram conversation route seam 2026-03-28 13:17:57 +05:30
Peter Steinberger
d9e7178534 fix(ci): align embedded runner and bedrock typing drift 2026-03-28 07:33:19 +00:00
Peter Steinberger
71a3ad153a fix(ci): stabilize bundled capability contract loading 2026-03-28 07:33:19 +00:00
Peter Steinberger
f36354e401 test: dedupe pairing and channel contract suites 2026-03-28 07:31:40 +00:00
Peter Steinberger
e7c1fcba0c test: dedupe media utility suites 2026-03-28 07:31:40 +00:00
Peter Steinberger
155915e7dc test: dedupe routing and text suites 2026-03-28 07:31:40 +00:00
Peter Steinberger
30be04cd87 fix: include matrix runtime deps for bundled installs 2026-03-28 07:27:29 +00:00
Peter Steinberger
30bf4dd1ce test: isolate nextcloud talk from bundled channel imports 2026-03-28 07:18:07 +00:00
Peter Steinberger
2fd1a5274a fix: add getChat to telegram media test harness 2026-03-28 07:14:48 +00:00
Peter Steinberger
d69f20f451 fix: harden bundled channel runtime bootstrap 2026-03-28 07:10:05 +00:00
Peter Steinberger
f4cd06cb1a refactor: finish test cleanup off infra runtime 2026-03-28 06:59:32 +00:00
Peter Steinberger
5802d112da refactor: narrow telegram test mocks off infra runtime 2026-03-28 06:56:41 +00:00
Peter Steinberger
df4c9c5bd8 refactor: narrow test mocks off infra runtime 2026-03-28 06:54:03 +00:00
Peter Steinberger
61936938e9 refactor: move test harnesses off infra runtime 2026-03-28 06:52:06 +00:00
lixuankai
f0a57fad42 fix: isolate device chat defaults (#53752) (thanks @lixuankai)
* [feat]Multiple nodes session context isolated from each other

* feat(android): Multiple nodes session context isolated from each other

* feat(android): Multiple nodes session context isolated from each other

* feat(android): Multiple nodes session context isolated from each other

* fix(android): isolate device chat defaults

---------

Co-authored-by: lixuankai <lixuankai@oppo.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-28 12:19:47 +05:30
Peter Steinberger
7db10d2103 refactor: route discord channel through outbound runtime 2026-03-28 06:45:20 +00:00
Peter Steinberger
c65ec46490 refactor: trim remaining infra runtime residue 2026-03-28 06:41:56 +00:00
Peter Steinberger
922c90e9fa refactor: add approval runtime sdk seam 2026-03-28 06:33:07 +00:00
Peter Steinberger
0d98ce1065 refactor: add diagnostic and error runtime sdk seams 2026-03-28 06:26:38 +00:00
Peter Steinberger
70c2458861 refactor: add host and collection runtime sdk seams 2026-03-28 06:19:16 +00:00
Peter Steinberger
df204b1d8f fix: restore pi embedded runner transport typing 2026-03-28 06:18:35 +00:00
Peter Steinberger
eef4a7ae64 refactor(commands): drop provider default facade shims 2026-03-28 06:11:13 +00:00
Peter Steinberger
d042192c7c refactor(plugins): move provider policy hooks into plugins 2026-03-28 06:11:13 +00:00
Gustavo Madeira Santana
d042543539 Tests: share agents bind command harness 2026-03-28 02:09:43 -04:00
Gustavo Madeira Santana
5292622fec Tests: share partial module mock helper 2026-03-28 02:09:43 -04:00
Peter Steinberger
1db1c75a98 refactor: trim state persistence runtime seams 2026-03-28 06:08:18 +00:00
Peter Steinberger
7206ddea6f test: dedupe plugin contract suites 2026-03-28 06:04:51 +00:00
Peter Steinberger
48b2291b1e test: dedupe plugin provider runtime suites 2026-03-28 06:04:51 +00:00
Peter Steinberger
89bb2cf03e test: dedupe plugin bundle discovery suites 2026-03-28 06:04:50 +00:00
Peter Steinberger
69b54cbb1f test: align pi tool schema fixture type 2026-03-28 05:59:27 +00:00
Peter Steinberger
c222a44e6f refactor: add retry runtime sdk seam 2026-03-28 05:59:07 +00:00
Peter Steinberger
d42c2f6a17 test(pi-tools): use typebox schema fixture 2026-03-28 05:53:59 +00:00
Peter Steinberger
2c636fa3b8 docs(acp): clarify current conversation bind support 2026-03-28 05:53:34 +00:00
Peter Steinberger
e246efb288 fix(runtime): align channel runtime api seams 2026-03-28 05:53:32 +00:00
Peter Steinberger
06fba21a9d test(acp): cover persisted generic conversation binds 2026-03-28 05:53:07 +00:00
Peter Steinberger
83135c31c9 refactor(acp): extract generic current conversation binding store 2026-03-28 05:53:07 +00:00
Gustavo Madeira Santana
bde5bae69f Tests: preserve heartbeat-wake exports in mocks 2026-03-28 01:52:55 -04:00
Gustavo Madeira Santana
1e4241c34a Matrix: fix directory auth and credentials fallback 2026-03-28 01:52:55 -04:00
Gustavo Madeira Santana
b253ca70ef Tests: stabilize Matrix-related shared suites 2026-03-28 01:52:55 -04:00
Ayaan Zaidi
29b6e27c9e fix(android): auto-send voice turns on silence 2026-03-28 11:17:13 +05:30
Peter Steinberger
e7a61d13f0 fix: route signal runtime barrel off denied subpath 2026-03-28 05:44:33 +00:00
Peter Steinberger
a126d23f0d refactor: add fetch runtime sdk seam 2026-03-28 05:44:33 +00:00
Peter Steinberger
5b544c295a style(tests): normalize plugin runtime test formatting 2026-03-28 05:42:46 +00:00
Peter Steinberger
b236f39104 refactor(agents): generalize tool schema compat cleanup 2026-03-28 05:42:46 +00:00
Peter Steinberger
c7883fe892 refactor(plugins): register provider model id hooks 2026-03-28 05:42:46 +00:00
Peter Steinberger
49f693d06a refactor: widen webhook request guard sdk seam 2026-03-28 05:28:10 +00:00
Peter Steinberger
d5841f6412 refactor: centralize plugin API assembly 2026-03-28 05:24:25 +00:00
Peter Steinberger
6a556c6851 test(gateway): add live docker ACP bind coverage 2026-03-28 05:23:55 +00:00
Peter Steinberger
19e8e7190b fix(acp): avoid no-op gateway self-call after spawn 2026-03-28 05:23:55 +00:00
Peter Steinberger
b12f3ce6e5 fix(gateway): support synthetic chat origins 2026-03-28 05:23:55 +00:00
Tak Hoffman
26789db868 Fix TTS contract registry test context 2026-03-28 00:23:26 -05:00
Peter Steinberger
23f0486810 fix: stabilize plugin startup boundaries 2026-03-28 05:22:26 +00:00
Peter Steinberger
838013c87a refactor: expose webhook request guard sdk seam 2026-03-28 05:17:19 +00:00
Ayaan Zaidi
a7b8034a2b fix(android): use native tts in voice tab 2026-03-28 10:47:08 +05:30
Ayaan Zaidi
79fb980767 test(config): refresh base schema snapshot 2026-03-28 10:47:08 +05:30
Gustavo Madeira Santana
21c00165ef test: fix gateway handler and typing lease helper types 2026-03-28 01:11:24 -04:00
Gustavo Madeira Santana
e2a2492248 Secrets: fix Matrix default-account password activity 2026-03-28 01:08:33 -04:00
Peter Steinberger
38c65b4096 refactor: route slack prepare events through channel runtime 2026-03-28 05:06:20 +00:00
Peter Steinberger
f01f2ddc6d test(matrix): restore sdk mock ordering 2026-03-28 05:04:07 +00:00
Peter Steinberger
ccf54f263a refactor: route slack interactions through channel runtime 2026-03-28 05:03:22 +00:00
Ayaan Zaidi
16f8616d9d test(plugins): simplify typing pulse mock helper 2026-03-28 10:33:05 +05:30
Ayaan Zaidi
3a341355bf test(gateway): fill channels status handler options 2026-03-28 10:33:05 +05:30
Peter Steinberger
ab2bd34b66 refactor(xai): split provider compat facades
Co-authored-by: Harold Hunt <harold@pwrdrvr.com>
2026-03-28 05:02:41 +00:00
Peter Steinberger
c4e6fdf94d refactor(xai): move bundled xai runtime into plugin
Co-authored-by: Harold Hunt <harold@pwrdrvr.com>
2026-03-28 05:02:41 +00:00
Tak Hoffman
85064256a2 Refresh bundled plugin metadata snapshot 2026-03-28 00:00:14 -05:00
Peter Steinberger
02b8d47c6c test: align slots helper types 2026-03-28 04:58:53 +00:00
Peter Steinberger
6d3a6bda3d test: tighten typing lease mock helpers 2026-03-28 04:58:53 +00:00
Peter Steinberger
be31e7aa4c fix: unblock telegram typing and topic runtime builds 2026-03-28 04:58:34 +00:00
Peter Steinberger
ba02905c4f refactor: split mcp channel bridge internals 2026-03-28 04:58:34 +00:00
Ayaan Zaidi
fe679f0a90 fix(telegram): tighten reaction typings 2026-03-28 10:28:24 +05:30
Tak Hoffman
a790f63056 Fix typing lease background failure tests 2026-03-27 23:57:27 -05:00
Peter Steinberger
7d7883aa38 refactor: use temp-path sdk in discord voice manager 2026-03-28 04:56:53 +00:00
Tak Hoffman
0bcf076901 fix(regression): auto-enable channel status state 2026-03-27 23:56:29 -05:00
Peter Steinberger
dc87ffa46d fix(ci): guard telegram native command auth typing 2026-03-28 04:55:26 +00:00
Peter Steinberger
090a767754 fix: tighten telegram runtime type guards 2026-03-28 04:53:26 +00:00
Brad Groux
6b0e74000d fix(msteams): add blockStreaming config and progressive delivery (#56134)
- Add blockStreaming and blockStreamingCoalesceDefaults to MSTeams channel plugin (was the only channel missing it)
- Wire disableBlockStreaming flag in reply dispatcher from config
- Flush pending messages immediately during generation when blockStreaming is enabled
- Add comprehensive tests for schema validation and progressive flush behavior

Refs #56041
2026-03-27 23:53:24 -05:00
Peter Steinberger
4900890626 test: align macOS config audit expectations 2026-03-28 04:53:02 +00:00
Peter Steinberger
a70d9beb3a build: update macOS package dependencies 2026-03-28 04:53:02 +00:00
Tak Hoffman
3e8bad0d31 Refresh bundled plugin metadata snapshot 2026-03-27 23:52:32 -05:00
Tak Hoffman
b8012221d2 fix(regression): restore slots test helper typing 2026-03-27 23:52:08 -05:00
Tak Hoffman
ff348d2063 fix(regression): auto-enable gateway send selection 2026-03-27 23:51:28 -05:00
Peter Steinberger
222ba9f174 fix(ci): tighten telegram and typing test types 2026-03-28 04:49:21 +00:00
Gustavo Madeira Santana
470d6aee0f Gateway: keep auto-enabled plugin config through startup 2026-03-28 00:49:00 -04:00
Tak Hoffman
5167841ff8 fix(regression): auto-enable channels resolve selection 2026-03-27 23:48:54 -05:00
Tak Hoffman
897a6a6c5b fix(regression): auto-enable message channel selection 2026-03-27 23:47:56 -05:00
Tak Hoffman
384bdde514 fix(regression): persist auto-enabled directory config 2026-03-27 23:47:54 -05:00
Peter Steinberger
687d23ae8d test: restore extension boundary guardrails 2026-03-28 04:47:31 +00:00
Peter Steinberger
d29d56c090 build: update Peekaboo for macOS SDK compatibility 2026-03-28 04:47:31 +00:00
Tak Hoffman
46ab177743 fix(regression): persist auto-enabled channel auth config 2026-03-27 23:45:57 -05:00
Tak Hoffman
8539886cd8 fix(regression): auto-enable directory channel selection 2026-03-27 23:45:29 -05:00
Peter Steinberger
811685b95f test: dedupe plugin bundle boundary suites 2026-03-28 04:44:58 +00:00
Peter Steinberger
fc84dd398b test: dedupe plugin runtime registry suites 2026-03-28 04:43:29 +00:00
Peter Steinberger
25fea00bc7 test: dedupe plugin utility config suites 2026-03-28 04:43:29 +00:00
Tak Hoffman
c9d5d12183 fix(regression): auto-enable channel auth selection 2026-03-27 23:42:36 -05:00
Peter Steinberger
324c621ebe fix(ci): align telegram runtime and test drift 2026-03-28 04:41:23 +00:00
Tak Hoffman
7779205aa1 Keep matrix SDK external in bundle checks 2026-03-27 23:41:00 -05:00
Tak Hoffman
363038828f fix(regression): auto-enable gateway bootstrap snapshots 2026-03-27 23:40:51 -05:00
Peter Steinberger
0c729b6d30 test: dedupe plugin runtime utility suites 2026-03-28 04:40:08 +00:00
Peter Steinberger
12318d25ae test: dedupe plugin provider runtime status suites 2026-03-28 04:40:08 +00:00
Tak Hoffman
6fc949862a fix(regression): repair channel setup discovery test 2026-03-27 23:38:55 -05:00
Tak Hoffman
37ab1513e0 fix(regression): auto-enable channel setup discovery 2026-03-27 23:38:55 -05:00
Tak Hoffman
84af16e9c7 fix(regression): handle telegram command error envelopes 2026-03-27 23:36:37 -05:00
Gustavo Madeira Santana
b5958ce5fd Changelog: note plugin runtime fixes 2026-03-28 00:36:13 -04:00
Tak Hoffman
1b5043f47b fix(regression): auto-enable gateway plugin loads 2026-03-27 23:35:22 -05:00
Ayaan Zaidi
6949e17429 refactor(telegram): simplify transport typing 2026-03-28 10:05:11 +05:30
Gustavo Madeira Santana
59535e3414 Matrix: align default account secret handling 2026-03-28 00:34:48 -04:00
Tak Hoffman
411494faa8 fix(regression): guard malformed telegram reaction payloads 2026-03-27 23:34:09 -05:00
Tak Hoffman
1a7dc22995 fix(regression): remove duplicate media runtime config 2026-03-27 23:30:08 -05:00
Peter Steinberger
afdcf16528 docs: note grouped git sync workflow 2026-03-28 04:29:55 +00:00
Tak Hoffman
f672782f38 Stabilize slack interaction event mocks 2026-03-27 23:29:42 -05:00
Tak Hoffman
3ccc58ae29 Restore channel test module rebinding 2026-03-27 23:29:42 -05:00
Peter Steinberger
22c9be197e docs: tighten Mistral changelog wording 2026-03-28 04:29:22 +00:00
Peter Steinberger
244b70051e test: fix docker mcp stdio notification hook 2026-03-28 04:29:13 +00:00
Peter Steinberger
47b3bf8c89 test: drop unused capability test helper 2026-03-28 04:28:54 +00:00
Peter Steinberger
9155f3914a test: dedupe plugin provider helper suites 2026-03-28 04:28:54 +00:00
Peter Steinberger
7e921050e3 test: dedupe plugin lifecycle runtime suites 2026-03-28 04:28:54 +00:00
Peter Steinberger
04792e6c44 test: dedupe plugin bundle and discovery suites 2026-03-28 04:28:54 +00:00
Ayaan Zaidi
8465ddc1cc refactor(telegram): tighten helper field readers 2026-03-28 09:57:46 +05:30
Ayaan Zaidi
f9aa226d93 refactor(telegram): simplify action button parsing 2026-03-28 09:57:46 +05:30
Ayaan Zaidi
ed441b180b refactor(telegram): simplify message helper parsing 2026-03-28 09:57:46 +05:30
Tak Hoffman
d69664e107 fix(regression): preserve googlechat pairing account context 2026-03-27 23:25:30 -05:00
Gustavo Madeira Santana
286d6b388f Tests: remove stale runtime state setup 2026-03-28 00:25:14 -04:00
Gustavo Madeira Santana
cdf19111e5 Plugins: narrow loader testing helper surface 2026-03-28 00:25:14 -04:00
Tak Hoffman
742e0c8597 fix(regression): track outbound bootstrap by channel surface 2026-03-27 23:24:51 -05:00
Peter Steinberger
eca6b8f4e8 refactor: use temp-path sdk in discord voice message 2026-03-28 04:23:39 +00:00
Tak Hoffman
d6fafb8af9 Add collect-all test failure planning 2026-03-27 23:23:31 -05:00
Ayaan Zaidi
39829b5dc6 refactor(telegram): unify inline button capability parsing 2026-03-28 09:52:21 +05:30
Ayaan Zaidi
4a014083ff refactor(telegram): tighten api result typings 2026-03-28 09:52:21 +05:30
Ayaan Zaidi
9905f39e9d refactor(telegram): unify chat metadata parsing 2026-03-28 09:52:21 +05:30
Tak Hoffman
12488f45c2 fix(regression): preserve announce thread ids 2026-03-27 23:22:17 -05:00
Tak Hoffman
c0d4c07b88 fix(regression): scope plugin registry reuse by gateway methods 2026-03-27 23:22:10 -05:00
kakahu
158e7c517e fix(matrix): resolve env SecretRef fallback in clean() for channel startup (#54980)
Merged via squash.

Prepared head SHA: b71a86e68e
Co-authored-by: kakahu2015 <17962485+kakahu2015@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-28 00:21:57 -04:00
Peter Steinberger
9fa1674a56 refactor: use temp-path sdk in discord send 2026-03-28 04:20:20 +00:00
Gustavo Madeira Santana
392724ae57 Plugins: reuse shared bootstrap registry resolution 2026-03-28 00:19:33 -04:00
Gustavo Madeira Santana
ee7f5825c8 Plugins: share runtime registry resolution 2026-03-28 00:19:33 -04:00
Tak Hoffman
f811ce5052 fix(regression): preserve bluebubbles pairing account context 2026-03-27 23:18:10 -05:00
Tak Hoffman
c7e05f1f87 test(regression): cover mistral availability compat 2026-03-27 23:17:35 -05:00
Peter Steinberger
ee73342445 refactor: use channel runtime for imessage readiness 2026-03-28 04:16:35 +00:00
Tak Hoffman
a4c64d82f8 fix(regression): preserve telegram pairing account context 2026-03-27 23:15:23 -05:00
Peter Steinberger
bd28e6d444 refactor: move transport readiness onto channel runtime 2026-03-28 04:13:40 +00:00
Peter Steinberger
df4fd12225 test(matrix): mock chunkTextForOutbound in monitor registration test 2026-03-28 04:12:26 +00:00
Tak Hoffman
2aa09230c2 fix(regression): preserve feishu pairing account context 2026-03-27 23:12:18 -05:00
Ayaan Zaidi
efef319496 refactor(telegram): tighten chat action typings 2026-03-28 09:41:18 +05:30
Ayaan Zaidi
4018d04d56 refactor(telegram): simplify runtime handler typing 2026-03-28 09:41:18 +05:30
Ayaan Zaidi
3a2bf0aa1f refactor(telegram): share chat lookup types 2026-03-28 09:41:18 +05:30
Peter Steinberger
048a4e4f9e docs: clarify mcp server and client modes 2026-03-28 04:10:20 +00:00
Peter Steinberger
ec5877346c fix: harden mcp channel bridge smoke 2026-03-28 04:10:19 +00:00
Gustavo Madeira Santana
9b405f88d4 Plugins: reuse compatible runtime web search registries 2026-03-28 00:09:37 -04:00
Gustavo Madeira Santana
a00127bf5b Plugins: reuse compatible registries for runtime providers 2026-03-28 00:09:37 -04:00
Gustavo Madeira Santana
fd0aac297c Plugins: add runtime registry compatibility helper 2026-03-28 00:09:37 -04:00
Peter Steinberger
4beb231fd8 refactor: move heartbeat helpers onto channel runtime 2026-03-28 04:09:25 +00:00
Tak Hoffman
b9415ca24b fix(regression): preserve line pairing account context 2026-03-27 23:09:05 -05:00
Tak Hoffman
d50526dddc fix(regression): use active channel registry for generic bindings 2026-03-27 23:08:56 -05:00
Peter Steinberger
5e93419c31 fix: move Mistral compat into provider plugin 2026-03-28 04:08:37 +00:00
Tak Hoffman
fd48e4090a fix(regression): reject disabled channel auth stubs 2026-03-27 23:06:06 -05:00
Peter Steinberger
4e50548e46 fix: restore skill sourceInfo provenance handling 2026-03-28 04:05:18 +00:00
Tak Hoffman
102e313d55 fix(regression): refresh provider hook cache after config changes 2026-03-27 23:04:24 -05:00
Tak Hoffman
1e2e6fb613 fix(regression): allow auth-capable channel auto-pick without raw config 2026-03-27 23:03:52 -05:00
Peter Steinberger
578d02f40a test: dedupe plugin lifecycle registry suites 2026-03-28 04:02:35 +00:00
Peter Steinberger
e74f206a68 test: dedupe plugin provider runtime suites 2026-03-28 04:02:34 +00:00
Peter Steinberger
708ff9145e test: dedupe plugin utility config suites 2026-03-28 04:02:13 +00:00
Tak Hoffman
c5b1582d48 fix(regression): auto-enable web search provider loads 2026-03-27 23:00:49 -05:00
Nyanako
f652d9fd81 fix: preserve indentation when stripping reply directives (#55960) (thanks @Nanako0129)
* fix: preserve indentation when stripping reply directives

* fix: preserve word boundaries when stripping reply directives

* fix: drop separator space after leading reply directives

* fix: preserve indentation when stripping reply directives (#55960) (thanks @Nanako0129)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-28 09:28:45 +05:30
Peter Steinberger
0d0d46f5e9 refactor: route line media temp paths through temp-path sdk 2026-03-28 03:57:46 +00:00
Peter Steinberger
5acc4b5dc5 refactor: route acpx test temp paths through temp-path sdk 2026-03-28 03:56:29 +00:00
Tak Hoffman
ec122796f8 fix(regression): avoid loading memory runtime during shutdown 2026-03-27 22:55:40 -05:00
Peter Steinberger
c5c9640374 fix: harden config write auditing 2026-03-28 03:54:54 +00:00
Peter Steinberger
5853b1aab8 fix: replay skill source drift 2026-03-28 03:53:59 +00:00
Peter Steinberger
9058662d6f refactor: route signal event handler through channel runtime 2026-03-28 03:53:59 +00:00
Peter Steinberger
49968982a5 fix(plugin-sdk): avoid testing export drift 2026-03-28 03:53:38 +00:00
Peter Steinberger
dee2bde2f5 test(acp): cover generic conversation binds 2026-03-28 03:53:38 +00:00
Peter Steinberger
ec9f96cb2a refactor(plugin-sdk): align binding contract imports 2026-03-28 03:53:38 +00:00
Peter Steinberger
d0d4b73d25 refactor(acp): centralize conversation binding context 2026-03-28 03:53:38 +00:00
Tak Hoffman
09e35e69b2 fix(regression): auto-enable provider runtime loads 2026-03-27 22:53:32 -05:00
Peter Steinberger
cc9b2df97c test: stabilize telegram stalled-runner restart assertion 2026-03-28 03:51:16 +00:00
Ayaan Zaidi
921bb89b1a fix: add changelog for CJK MMR tokenize fix (#29396) (thanks @buyitsydney) 2026-03-28 09:19:52 +05:30
buyitsydney
4b69c6d3f1 fix(memory): add CJK/Kana/Hangul support to MMR tokenize() for diversity detection
The tokenize() function only matched [a-z0-9_]+ patterns, returning an
empty set for CJK-only text. This made Jaccard similarity always 0 (or
always 1 for two empty sets) for CJK content, effectively disabling MMR
diversity detection.

Add support for:
- CJK Unified Ideographs (U+4E00–U+9FFF, U+3400–U+4DBF)
- Hiragana (U+3040–U+309F) and Katakana (U+30A0–U+30FF)
- Hangul Syllables (U+AC00–U+D7AF) and Jamo (U+1100–U+11FF)

Characters are extracted as unigrams, and bigrams are generated only
from characters that are adjacent in the original text (no spurious
bigrams across ASCII boundaries).

Fixes #28000
2026-03-28 09:19:52 +05:30
chen-zhang-cs-code
92b8839488 fix: normalize unsupported Brave country filters (#55695) (thanks @chen-zhang-cs-code)
* fix(brave): normalize unsupported country filters

* fix: normalize unsupported Brave country filters (#55695) (thanks @chen-zhang-cs-code)

* fix: annotate Brave country enum source (#55695) (thanks @chen-zhang-cs-code)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-28 09:19:27 +05:30
Peter Steinberger
faae213ab7 refactor: route whatsapp monitor through channel runtime 2026-03-28 03:48:57 +00:00
Peter Steinberger
8147f5075b refactor: inline canonical skill source reads 2026-03-28 03:48:17 +00:00
Tak Hoffman
f4f492a410 fix(regression): scope channel setup reloads by channel registry 2026-03-27 22:46:47 -05:00
Peter Steinberger
ec6fba7d01 refactor: drop legacy skill source fallback 2026-03-28 03:45:56 +00:00
Peter Steinberger
24bb64b1c4 test: centralize canonical skill fixtures 2026-03-28 03:45:56 +00:00
Tak Hoffman
63e35b2d9d fix(regression): auto-enable memory runtime loads 2026-03-27 22:44:12 -05:00
Tak Hoffman
2ba5e7ebf9 fix(regression): align plugin inspect policy with auto-enabled config 2026-03-27 22:42:59 -05:00
Peter Steinberger
550c51bb6e refactor: route telegram bot deps through channel runtime 2026-03-28 03:42:37 +00:00
Tak Hoffman
2030c814ce fix(regression): auto-enable channel setup registry loads 2026-03-27 22:41:50 -05:00
Peter Steinberger
66c4c3bec8 test: align matrix runtime api allowlist 2026-03-28 03:40:51 +00:00
Tak Hoffman
36ac9224cc fix(regression): reload stale auto-enabled plugin tool registries 2026-03-27 22:40:24 -05:00
Tak Hoffman
e20823c741 fix(regression): auto-enable plugin status loads 2026-03-27 22:39:04 -05:00
Peter Steinberger
f3c8c27b3a fix: replay skill source fixture drift 2026-03-28 03:38:11 +00:00
Peter Steinberger
d83e3afc56 refactor: move slack system events onto channel runtime 2026-03-28 03:38:11 +00:00
Tak Hoffman
7918524229 fix(regression): reload stale preseeded cli channel registries 2026-03-27 22:37:58 -05:00
Tak Hoffman
cfd1e94e61 fix(regression): auto-enable plugin tool loads 2026-03-27 22:36:41 -05:00
Tak Hoffman
a6e597eda3 fix(regression): preserve plugin identity in hook test helpers 2026-03-27 22:34:09 -05:00
Tak Hoffman
8075641ce4 fix(regression): auto-enable plugin cli loads 2026-03-27 22:33:26 -05:00
Tak Hoffman
3f0b3a553a fix(regression): require channel scope in preseeded cli registry 2026-03-27 22:32:12 -05:00
Peter Steinberger
db2046f92f test: harden extension integration fixtures 2026-03-28 03:31:42 +00:00
Peter Steinberger
32fd469b2c test: align skill fixture source info 2026-03-28 03:31:42 +00:00
Tak Hoffman
ce7b3c94e0 fix(regression): merge aliased auth order provider keys 2026-03-27 22:31:07 -05:00
Peter Steinberger
b4c38c78f3 test: dedupe plugin provider runtime suites 2026-03-28 03:30:25 +00:00
Peter Steinberger
de173f0e3e test: dedupe plugin utility install suites 2026-03-28 03:30:25 +00:00
Peter Steinberger
1256943a46 test: dedupe plugin hook runner suites 2026-03-28 03:30:25 +00:00
Tak Hoffman
0946fdf625 fix(regression): widen preseeded cli plugin registry loads 2026-03-27 22:29:16 -05:00
Tak Hoffman
967702d928 test(regression): cover irc plugin-sdk facade exports 2026-03-27 22:28:04 -05:00
YTjungle
cb802afcbb fix(control-ui): size grouped chat bubbles by content 2026-03-27 22:27:47 -05:00
Peter Steinberger
2b450ab629 refactor: move discord system events onto channel runtime 2026-03-28 03:27:12 +00:00
Tak Hoffman
52def05ecd fix(regression): canonicalize auth order provider keys 2026-03-27 22:27:05 -05:00
Tak Hoffman
7bccf68794 fix(regression): preserve voice call timeout markers before hangup 2026-03-27 22:25:32 -05:00
Tak Hoffman
83adbc840c fix(regression): restore irc cold-runtime chunking 2026-03-27 22:24:27 -05:00
Peter Steinberger
71795c5323 refactor: move discord error formatting onto ssrf runtime 2026-03-28 03:22:16 +00:00
Tak Hoffman
3b8564a7c6 test(regression): cover setup and policy plugin-sdk facades 2026-03-27 22:20:40 -05:00
Peter Steinberger
c04ceb5cc2 refactor: route discord preflight activity through channel runtime 2026-03-28 03:19:50 +00:00
Peter Steinberger
8c277121d9 refactor: dedupe channel secret collectors 2026-03-28 03:18:54 +00:00
Peter Steinberger
07d386c2bb fix: dedupe voice call lifecycle cleanup 2026-03-28 03:18:54 +00:00
Peter Steinberger
0825ff9619 refactor: move discord duration formatting onto runtime env 2026-03-28 03:17:40 +00:00
Tak Hoffman
c1abf7c8c0 test(regression): cover bluebubbles plugin-sdk facade exports 2026-03-27 22:15:22 -05:00
Peter Steinberger
8ed25f95dd refactor: route discord activity through channel runtime 2026-03-28 03:15:03 +00:00
Tak Hoffman
08cd52b7c6 test(regression): cover cold-runtime plugin-sdk chunking exports 2026-03-27 22:12:39 -05:00
Peter Steinberger
277af32485 refactor: remove plugin sdk extension facade smells 2026-03-28 03:12:07 +00:00
Tak Hoffman
8c60e4e9f9 fix(regression): normalize image tool provider config aliases 2026-03-27 22:09:52 -05:00
Peter Steinberger
21136238ce test(discord): add acp bind flow integration coverage 2026-03-28 03:09:38 +00:00
Peter Steinberger
e11a74843e test: dedupe plugin hook merger suites 2026-03-28 03:08:10 +00:00
Peter Steinberger
218a711d5e test: dedupe plugin command and runtime helpers 2026-03-28 03:06:27 +00:00
Peter Steinberger
95acd74d7c test: dedupe plugin bundle and discovery helpers 2026-03-28 03:06:27 +00:00
Peter Steinberger
7a6f32a730 fix: replay skill source fixture drift 2026-03-28 03:06:06 +00:00
Peter Steinberger
12b7327e16 refactor: move secure random helpers onto core sdk 2026-03-28 03:06:06 +00:00
Neerav Makwana
b98a6c223d gateway: reuse session workspace for HTTP tool loading (#56101)
Merged via squash.

Prepared head SHA: f3006d77f7
Co-authored-by: neeravmakwana <261249544+neeravmakwana@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 23:01:43 -04:00
Tak Hoffman
ae2b1aef10 fix(regression): normalize model picker provider endpoint aliases 2026-03-27 22:01:39 -05:00
Peter Steinberger
969294f8c5 test: dedupe plugin install and packaging suites 2026-03-28 03:00:51 +00:00
Peter Steinberger
39f6fe9ab1 test: dedupe plugin runtime and provider suites 2026-03-28 03:00:51 +00:00
Peter Steinberger
b34b03dd9e refactor: move channel dedupe helpers onto core sdk 2026-03-28 02:58:45 +00:00
Peter Steinberger
024f2cf6e6 style: apply oxfmt drift 2026-03-28 02:55:07 +00:00
Peter Steinberger
67d0ecf5ec fix(ci): align skill fixture source info 2026-03-28 02:55:07 +00:00
Peter Steinberger
68416fdf83 refactor(acp): generalize message-channel binds 2026-03-28 02:53:54 +00:00
Peter Steinberger
491969efb0 refactor: route channel activity through channel runtime 2026-03-28 02:53:03 +00:00
Tak Hoffman
684a1565a9 fix(regression): align feishu send helper runtime usage 2026-03-27 21:52:06 -05:00
Peter Steinberger
c69a70714c test: harden contract registry fixtures 2026-03-28 02:49:49 +00:00
Peter Steinberger
c9c1e456d1 fix: replay skill source fixture drift 2026-03-28 02:48:35 +00:00
Peter Steinberger
00dcfa1b3d refactor: move channel backoff helpers onto runtime-env 2026-03-28 02:48:35 +00:00
Tak Hoffman
01e3dd3508 fix(regression): normalize provider aliases in context window guard 2026-03-27 21:47:59 -05:00
Tak Hoffman
4ec51f2d5f fix(regression): align msteams send helper runtime usage 2026-03-27 21:46:42 -05:00
Tak Hoffman
912a26e759 fix(regression): align mattermost send helper runtime usage 2026-03-27 21:45:10 -05:00
Peter Steinberger
b171e42117 refactor: move telegram timing helpers onto runtime-env 2026-03-28 02:43:29 +00:00
Peter Steinberger
71f37a59ca feat: add openclaw channel mcp bridge 2026-03-28 02:41:57 +00:00
Tak Hoffman
a65d603b31 fix(regression): align irc send helper runtime usage 2026-03-27 21:40:58 -05:00
Peter Steinberger
ea92003384 test: replay skill source fixture drift 2026-03-28 02:40:05 +00:00
Peter Steinberger
6a2c5b2b54 refactor: move telegram error formatting onto ssrf runtime 2026-03-28 02:38:02 +00:00
Tak Hoffman
33e64cfb64 fix(regression): align nextcloud-talk send helper runtime usage 2026-03-27 21:37:50 -05:00
Sid Uppal
295d1de8d9 fix(msteams): reset stream state after tool calls to prevent message loss (#56071)
* fix(msteams): reset stream state after preparePayload suppresses delivery

When an agent uses tools mid-response (text → tool calls → more text),
the stream controller's preparePayload would suppress fallback delivery
for ALL text segments because streamReceivedTokens stayed true. This
caused the second text segment to be silently lost or duplicated.

Fix: after preparePayload suppresses delivery for a streamed segment,
finalize the stream and reset streamReceivedTokens so subsequent
segments use fallback delivery.

Fixes openclaw/openclaw#56040

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(msteams): guard preparePayload against finalized stream re-suppression

When onPartialReply fires after the stream is finalized (post-tool
partial tokens), streamReceivedTokens gets set back to true but the
stream can't deliver. Add stream.isFinalized check so a finalized
stream never suppresses fallback delivery.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(msteams): await pending finalize in controller to prevent race

Store the fire-and-forget finalize promise from preparePayload and
await it in the controller's finalize() method. This ensures
markDispatchIdle waits for the in-flight stream finalization to
complete before context cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test(msteams): add edge case tests for multi-round and media payloads

Add tests for 3+ tool call rounds (text → tool → text → tool → text)
and media+text payloads after stream finalization, covering the full
contract of preparePayload across all input types and cycle counts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:36:37 -05:00
Peter Steinberger
4752aca926 test: dedupe plugin runtime registry suites 2026-03-28 02:34:39 +00:00
Peter Steinberger
a9be5421d0 test: dedupe plugin provider runtime suites 2026-03-28 02:34:39 +00:00
Peter Steinberger
0454612083 test: dedupe plugin bundle and discovery suites 2026-03-28 02:34:39 +00:00
Peter Steinberger
c18d315858 fix: replay skill source fixture drift 2026-03-28 02:34:11 +00:00
Peter Steinberger
6b4d097b25 refactor: route telegram env helpers through runtime-env 2026-03-28 02:34:11 +00:00
Tak Hoffman
d027b442af fix(regression): restore zalouser cold-runtime chunking 2026-03-27 21:33:14 -05:00
Peter Steinberger
05719648a1 test(line): isolate status probe fallback import state 2026-03-28 02:31:39 +00:00
Tak Hoffman
a3961d098a fix(regression): preserve mattermost reaction channel routing 2026-03-27 21:30:24 -05:00
Tak Hoffman
42ecfffbff fix(regression): restore signal cold-runtime chunking 2026-03-27 21:28:18 -05:00
Tak Hoffman
bd7375f84a fix: normalize image provider alias selection 2026-03-27 21:28:15 -05:00
Peter Steinberger
1bf8d69d95 refactor(msteams): share conversation store helpers 2026-03-28 02:26:48 +00:00
Peter Steinberger
4031bb1914 refactor: trim secret and ssrf helper runtime seams 2026-03-28 02:25:28 +00:00
Tak Hoffman
18fe752c48 fix(regression): restore googlechat cold-runtime chunking 2026-03-27 21:25:21 -05:00
Peter Steinberger
70a0ce2179 test: align skill fixture source info 2026-03-28 02:24:34 +00:00
Tak Hoffman
d519beb925 fix: normalize model scan provider filters 2026-03-27 21:23:51 -05:00
Tak Hoffman
3143cf86e8 fix(regression): restore whatsapp cold-runtime chunking 2026-03-27 21:23:18 -05:00
Tak Hoffman
e57342c7f2 fix(regression): restore msteams cold-runtime chunking 2026-03-27 21:21:40 -05:00
Peter Steinberger
3b9eb2cd1b refactor: trim bluebubbles runtime seams 2026-03-28 02:21:34 +00:00
Tak Hoffman
e5c4e89dc6 fix: normalize explicit context provider aliases 2026-03-27 21:21:32 -05:00
Tak Hoffman
fc542671eb fix: normalize history session provider lookup 2026-03-27 21:19:09 -05:00
Tak Hoffman
c0c32445ab fix(regression): restore feishu cold-runtime chunking 2026-03-27 21:17:17 -05:00
Tak Hoffman
5426bdf391 fix: normalize model catalog provider lookup 2026-03-27 21:16:44 -05:00
Peter Steinberger
46a44c5044 refactor: trim tlon runtime helper seams 2026-03-28 02:15:31 +00:00
Tak Hoffman
23d5bad3ae fix(regression): restore matrix cold-runtime chunking 2026-03-27 21:14:38 -05:00
Tak Hoffman
e83b1d7c43 fix: normalize live model provider aliases 2026-03-27 21:12:45 -05:00
Tak Hoffman
196d347153 fix(regression): restore mattermost cold-runtime chunking 2026-03-27 21:12:13 -05:00
Peter Steinberger
185668f5c5 refactor: trim extension helper runtime seams 2026-03-28 02:12:05 +00:00
Tak Hoffman
4cc8f8a1c6 fix: normalize models list provider filters 2026-03-27 21:10:35 -05:00
Tak Hoffman
e4538a2a70 fix(regression): classify toolcall content as tool output 2026-03-27 21:08:58 -05:00
Peter Steinberger
ce2444403e refactor: trim provider oauth runtime seams 2026-03-28 02:08:29 +00:00
Tak Hoffman
2638b566f1 fix(regression): canonicalize chat final session routing 2026-03-27 21:06:45 -05:00
Tak Hoffman
2877a7d8b2 fix: normalize status summary provider config lookup 2026-03-27 21:06:32 -05:00
Peter Steinberger
c1fb18189b test: dedupe plugin hook runner suites 2026-03-28 02:05:01 +00:00
Peter Steinberger
7d79134cee test: dedupe plugin runtime utility suites 2026-03-28 02:05:01 +00:00
Peter Steinberger
2926c25e10 fix: prefer freshest Teams DM reference (#54702) (thanks @gumclaw) 2026-03-28 02:04:51 +00:00
gumclaw
a717819f78 msteams: align memory store user resolution 2026-03-28 02:04:51 +00:00
gumclaw
28eb5ece14 msteams: prefer freshest personal conversation reference 2026-03-28 02:04:51 +00:00
Peter Steinberger
2c15960ac2 fix: replay skill fixture source drift 2026-03-28 02:04:31 +00:00
Peter Steinberger
e8866fc738 refactor: narrow provider runtime auth seams 2026-03-28 02:04:31 +00:00
Tak Hoffman
a0f48f099e fix(regression): canonicalize chat inject session routing 2026-03-27 21:04:16 -05:00
Tak Hoffman
7ccf4552ac fix: normalize provider catalog config lookup 2026-03-27 21:03:53 -05:00
Tak Hoffman
59a0411a78 fix(regression): canonicalize exec session routing 2026-03-27 21:02:03 -05:00
Tak Hoffman
fe295b15a5 fix: normalize provider catalog template lookup 2026-03-27 21:01:18 -05:00
Peter Steinberger
269f461b2e test: isolate zai probe target env in alias coverage 2026-03-28 02:00:53 +00:00
Tak Hoffman
8aace2b448 fix(regression): hydrate node tool event metadata 2026-03-27 21:00:46 -05:00
Peter Steinberger
72ba2b3653 chore: bump version metadata to 2026.3.27 2026-03-28 02:00:22 +00:00
Tak Hoffman
392c15aa73 fix: dedupe canonical providers in models status 2026-03-27 20:59:04 -05:00
Tak Hoffman
ee72081373 fix(regression): restore googlechat cold-runtime media send 2026-03-27 20:58:47 -05:00
Tak Hoffman
50c87c4682 fix: normalize catalog provider ids for probe model selection 2026-03-27 20:56:27 -05:00
Tak Hoffman
e890cde041 fix(regression): hydrate run-scoped tool event metadata 2026-03-27 20:56:04 -05:00
Peter Steinberger
c42ec81e37 feat(acp): add conversation binds for message channels 2026-03-28 01:54:25 +00:00
Tak Hoffman
067f8db4c9 fix(regression): preserve lifecycle session ownership metadata 2026-03-27 20:53:46 -05:00
Peter Steinberger
923b316ddc fix: harden parallels smoke verification 2026-03-28 01:51:18 +00:00
Tak Hoffman
a724246547 fix(regression): restore imessage cold-runtime chunking 2026-03-27 20:50:03 -05:00
Tak Hoffman
dd78b16cdc fix: normalize auth health provider aliases 2026-03-27 20:45:10 -05:00
Tak Hoffman
a265c59418 fix(regression): preserve transcript session ownership metadata 2026-03-27 20:43:56 -05:00
Tak Hoffman
9a57bdfdf1 fix(regression): preserve session tool event metadata 2026-03-27 20:42:38 -05:00
Gustavo Madeira Santana
86d8b06da9 Matrix: preserve strict DM SAS fallback 2026-03-27 21:42:12 -04:00
Tak Hoffman
724a9cfdba fix: preserve fallback provider capabilities under partial overrides 2026-03-27 20:40:53 -05:00
Peter Steinberger
43ba3ab6b5 refactor: scope provider runtime to enabled provider plugins 2026-03-28 01:40:30 +00:00
Peter Steinberger
1425259274 refactor: split bedrock provider stream helpers 2026-03-28 01:40:30 +00:00
Tak Hoffman
02bce20dd0 fix: prefer canonical cli backend config keys 2026-03-27 20:38:51 -05:00
Peter Steinberger
c364fc8428 test: dedupe plugin manifest and wizard suites 2026-03-28 01:38:12 +00:00
Peter Steinberger
fad42b19ee test: dedupe plugin core utility suites 2026-03-28 01:38:12 +00:00
Peter Steinberger
2accc0391a test: dedupe security utility suites 2026-03-28 01:38:12 +00:00
Tak Hoffman
87875430a8 fix(regression): preserve chat lifecycle subagent metadata 2026-03-27 20:37:22 -05:00
Tak Hoffman
7a1f64e86b fix: prefer profile auth in provider summaries 2026-03-27 20:36:06 -05:00
Tak Hoffman
9e16374898 fix(regression): restore signal cold-runtime status probing 2026-03-27 20:34:58 -05:00
Tak Hoffman
b9b84f2572 fix(regression): restore line cold-runtime status probing 2026-03-27 20:33:09 -05:00
Tak Hoffman
d11dc8feba fix: summarize plugin tool descriptions in catalog 2026-03-27 20:32:50 -05:00
Tak Hoffman
5a92655f5d fix: follow canonical skill source in status bundling 2026-03-27 20:30:17 -05:00
Tak Hoffman
ae9b9575c5 fix(regression): preserve gateway subagent session change metadata 2026-03-27 20:28:58 -05:00
Tak Hoffman
1cfea0af07 fix(regression): restore plugin sdk compat export 2026-03-27 20:27:53 -05:00
Tak Hoffman
f4a45071e3 fix: preserve session thread ids in agent send events 2026-03-27 20:24:35 -05:00
Tak Hoffman
7fadb4f7ff fix(regression): preserve subagent session ownership metadata 2026-03-27 20:24:15 -05:00
Tak Hoffman
5eb3ea3028 fix(regression): tolerate legacy skill source metadata 2026-03-27 20:24:15 -05:00
Tak Hoffman
39048e054d fix(regression): invalidate remote skill snapshots on disconnect 2026-03-27 20:24:15 -05:00
Tak Hoffman
2d75288738 fix(regression): export direct-dm plugin sdk subpath 2026-03-27 20:24:14 -05:00
Tak Hoffman
d2e25b03fe fix(regression): preserve external command auth context 2026-03-27 20:24:14 -05:00
Tak Hoffman
d604ce9950 fix(regression): preserve numeric session thread ids 2026-03-27 20:24:14 -05:00
Tak Hoffman
1efa81bcab fix(regression): restore imessage sdk facade targets 2026-03-27 20:24:14 -05:00
Tak Hoffman
8a687bdbd7 fix(regression): preserve spawned metadata across auto-reply reset 2026-03-27 20:24:14 -05:00
Tak Hoffman
3ec1df86fa fix(regression): restore slack probe fallback without runtime 2026-03-27 20:24:14 -05:00
Tak Hoffman
b598cdf968 fix(regression): preserve discord thread bindings for plugin commands 2026-03-27 20:24:14 -05:00
Tak Hoffman
b1eeca3b00 fix(regression): stop cross-channel plugin thread defaults 2026-03-27 20:24:14 -05:00
Tak Hoffman
835441233d fix(regression): support contracts surface in test planner 2026-03-27 20:24:14 -05:00
Tak Hoffman
9cb3ce8e1a fix(regression): restore typed provider compat tests 2026-03-27 20:24:14 -05:00
Tak Hoffman
803f60105b fix(regression): align provider flow docs with bundled compat 2026-03-27 20:24:14 -05:00
Tak Hoffman
27decb9649 fix(regression): route contract paths through test wrapper 2026-03-27 20:24:14 -05:00
Tak Hoffman
67fba9c5e1 fix(regression): align model picker with bundled compat 2026-03-27 20:24:14 -05:00
Tak Hoffman
e817b3cfbc fix(regression): align provider wizard with bundled compat 2026-03-27 20:24:14 -05:00
Peter Steinberger
50a2f67258 fix(ci): align skill fixture source info 2026-03-28 01:23:29 +00:00
Peter Steinberger
b81bf005b9 refactor: trim models-config test async wrappers 2026-03-28 01:21:56 +00:00
Tak Hoffman
1fee91e431 fix: preserve session thread ids in sessions changed events 2026-03-27 20:21:07 -05:00
Tak Hoffman
762afb1bf0 fix: preserve session thread ids in transcript event payloads 2026-03-27 20:21:07 -05:00
Tak Hoffman
07bbf50419 fix: preserve session route metadata in node event touches 2026-03-27 20:21:06 -05:00
Tak Hoffman
627d6c80f2 fix: preserve session thread ids in chat session snapshots 2026-03-27 20:21:06 -05:00
Tak Hoffman
53861607f6 fix: use origin thread metadata in tools effective context 2026-03-27 20:21:06 -05:00
Tak Hoffman
1b16a112e7 fix: keep numeric session thread ids in sessions list 2026-03-27 20:21:06 -05:00
Tak Hoffman
f0d5d7a33a fix: preserve session origin account metadata in announce routing 2026-03-27 20:21:06 -05:00
Tak Hoffman
59cd79d37f fix: use session origin thread metadata in chat routing 2026-03-27 20:21:06 -05:00
Tak Hoffman
a9e9c7cbfd fix: use session origin delivery metadata in outbound targets 2026-03-27 20:21:06 -05:00
Peter Steinberger
8222d3a83a refactor: make models-config mode resolution synchronous 2026-03-28 01:18:08 +00:00
Peter Steinberger
0ffd6b202f test: dedupe security audit and acl suites 2026-03-28 01:17:57 +00:00
Peter Steinberger
c8c669537f test: dedupe plugin contract and loader suites 2026-03-28 01:17:57 +00:00
Peter Steinberger
1adf08a19d fix: replay skill source fixture drift 2026-03-28 01:13:19 +00:00
Peter Steinberger
b9560f4685 docs: clarify legacy provider sdk compat barrels 2026-03-28 01:12:52 +00:00
Peter Steinberger
b643f92447 refactor: use main sdk barrels for model and whatsapp helpers 2026-03-28 01:10:44 +00:00
Peter Steinberger
883ff949c0 fix(ci): align skill fixture source info 2026-03-28 01:10:33 +00:00
Peter Steinberger
659fe82d31 refactor: use split provider config type in models-json test 2026-03-28 01:08:09 +00:00
Peter Steinberger
dc8486f5e6 refactor: use provider config type from split secret helper 2026-03-28 01:06:22 +00:00
ImLukeF
6c9126ec19 macOS: test gateway version normalization 2026-03-28 12:05:34 +11:00
huohua-dev
8545cbd358 fix(macos): strip "OpenClaw " prefix before parsing gateway version
`openclaw --version` outputs "OpenClaw 2026.x.y-z" but
readGatewayVersion() passed the full string to Semver.parse(),
which failed on the "OpenClaw " prefix. This caused the app to
fall back to reading package.json from a local source checkout
(~/Projects/openclaw), reporting a false version mismatch.

Strip the product name prefix before parsing so the installed
CLI version is correctly recognized.
2026-03-28 12:05:33 +11:00
Peter Steinberger
2de896524f refactor: route models-config internals through split helpers 2026-03-28 01:04:04 +00:00
Peter Steinberger
2d6f4bf6c6 refactor: split models-config provider normalization helper 2026-03-28 01:01:02 +00:00
Peter Steinberger
8ab8f2c461 fix: center skills detail modal 2026-03-28 01:00:44 +00:00
Peter Steinberger
4aa5526271 refactor: route plugin-sdk model and whatsapp facades through public barrels 2026-03-28 00:58:17 +00:00
Peter Steinberger
7a1dce307d refactor: split models-config provider policy helpers 2026-03-28 00:56:02 +00:00
Peter Steinberger
d38ec0c9c9 test: dedupe loader heartbeat and audit cases 2026-03-28 00:53:34 +00:00
Peter Steinberger
d69aedcd3e fix: replay skill source fixture drift 2026-03-28 00:52:45 +00:00
Peter Steinberger
5c52824d3e refactor: split models-config source-managed helpers 2026-03-28 00:52:20 +00:00
Peter Steinberger
7db79b04c6 refactor: split models-config provider discovery helpers 2026-03-28 00:48:30 +00:00
Peter Steinberger
fa4da0ce5d fix(ci): replay compaction and skills api drift 2026-03-28 00:47:11 +00:00
Peter Steinberger
6a039bca30 test: dedupe loader and audit suites 2026-03-28 00:46:53 +00:00
Peter Steinberger
b4fe0faf1b test: dedupe config and utility suites 2026-03-28 00:46:53 +00:00
Peter Steinberger
48eae5f327 test: isolate browser plugin cli integration 2026-03-28 00:45:57 +00:00
Peter Steinberger
8f06ed8ef5 fix: short-circuit disabled media runtime 2026-03-28 00:45:57 +00:00
Peter Steinberger
c8ad0bde08 refactor: split models-config provider secret helpers 2026-03-28 00:44:36 +00:00
Tak Hoffman
262e5c57c8 fix(ci): stabilize module-bound exact regressions (#56085)
* Adjust compaction identifier test for summary args

* Harden exec completion after child exit

* Handle SDK compaction and skill shape drift

* Stabilize Synology Chat module-bound tests

* Restore skill source compatibility shims

* Restore self-hosted provider discovery mocks
2026-03-27 19:44:15 -05:00
Peter Steinberger
ce21ef641a fix: replay compaction and skills api drift 2026-03-28 00:37:31 +00:00
Peter Steinberger
e1f300695a refactor: extract models-config api-key normalization helpers 2026-03-28 00:37:31 +00:00
Gustavo Madeira Santana
b6ead2dd3b fix(matrix): align outbound direct-room selection (#56076)
Merged via squash.

Prepared head SHA: bbd9afdd5c
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 20:35:55 -04:00
Peter Steinberger
6455606b90 refactor: extract models-config plugin catalog helpers 2026-03-28 00:33:52 +00:00
Peter Steinberger
1f24181495 fix(ci): replay compaction and skills api drift 2026-03-28 00:32:53 +00:00
Peter Steinberger
cb9c044025 refactor: extract models-config implicit provider orchestration 2026-03-28 00:30:50 +00:00
Peter Steinberger
0d12f1ab91 fix: replay compaction and skills api drift 2026-03-28 00:29:23 +00:00
Peter Steinberger
8056c5581b refactor: extract models-config provider auth helpers 2026-03-28 00:29:23 +00:00
qer
8c079a804c Plugins: clean up channel config on uninstall (#35915)
* Plugins: clean up channel config on uninstall

`openclaw plugins uninstall` only removed `plugins.*` entries but left
`channels.<id>` config behind, causing errors when the gateway
referenced a channel whose plugin no longer existed.

Now `removePluginFromConfig` also deletes the matching
`channels.<pluginId>` entry (exact match only), and the CLI
previews/reports the removal. Shared config keys like `defaults`
and `modelByChannel` are guarded from accidental removal.

* Plugins: sync uninstall preview with channel cleanup

* fix: clean up channel config on uninstall (#35915) (thanks @wbxl2000)

---------

Co-authored-by: George Zhang <georgezhangtj97@gmail.com>
2026-03-27 17:28:38 -07:00
Peter Steinberger
87792c9050 test: dedupe gateway network and transcript suites 2026-03-28 00:26:55 +00:00
Peter Steinberger
fef688fb7a test: dedupe utility and config suites 2026-03-28 00:26:55 +00:00
Peter Steinberger
d8f97358d7 refactor: extract models-config provider normalization helpers 2026-03-28 00:26:23 +00:00
Peter Steinberger
4c213a5de7 fix(ci): replay compaction and skills api drift 2026-03-28 00:24:28 +00:00
Gustavo Madeira Santana
238d369a77 Plugins: add nested discovery regression test 2026-03-27 20:24:14 -04:00
Peter Steinberger
7b12de591b refactor: route models-config provider statics through shared barrel 2026-03-28 00:23:13 +00:00
Peter Steinberger
94f87d7b11 refactor: keep shared provider model sdk generic 2026-03-28 00:20:13 +00:00
Peter Steinberger
542d62ba93 fix: replay compaction and skills api drift 2026-03-28 00:17:50 +00:00
Peter Steinberger
b8069c2bd1 refactor: trim provider model compat seams 2026-03-28 00:17:28 +00:00
Peter Steinberger
78160b5f88 fix: align discord registry and runtime test helpers 2026-03-28 00:13:44 +00:00
George Zhang
6b72de77ba Revert "Plugins: sync channel uninstall cleanup" 2026-03-27 17:12:57 -07:00
Peter Steinberger
96a4df49b9 fix(ci): align compaction and skills api drift 2026-03-28 00:12:14 +00:00
ZIHANXU
29674d75fb fix: load pierre themes without json module imports (#45869)
Merged via squash.

Prepared head SHA: dd456aa32b
Co-authored-by: NickHood1984 <124482724+NickHood1984@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 20:12:11 -04:00
Peter Steinberger
8e687613b6 refactor: move stream payload compat into provider seams 2026-03-28 00:10:39 +00:00
George Zhang
958e3a4c69 Plugins: sync channel uninstall cleanup 2026-03-27 17:10:32 -07:00
Peter Steinberger
1fc7a4e952 fix(ci): type capability provider manifest mock 2026-03-28 00:08:56 +00:00
Peter Steinberger
2d8351b3b4 fix: align anthropic and skills helpers with shared sdk 2026-03-28 00:08:52 +00:00
Peter Steinberger
79c56d417b docs(changelog): add missing security release notes 2026-03-28 00:08:25 +00:00
Tak Hoffman
3dbd81e610 fix(regression): restore bundled capability provider compat 2026-03-27 19:05:58 -05:00
Peter Steinberger
d0cd645b4a style: format exec approval prompt template 2026-03-28 00:05:32 +00:00
Peter Steinberger
dd640e3c41 refactor: add focused global singleton sdk seam 2026-03-28 00:05:32 +00:00
Peter Steinberger
de7bba14cc fix(ci): align compaction and skills api drift 2026-03-28 00:04:24 +00:00
Peter Steinberger
2a98464a28 test: dedupe outbound routing and queue suites 2026-03-28 00:02:09 +00:00
Peter Steinberger
0b013bdd94 test: dedupe exec approval and system run suites 2026-03-28 00:02:09 +00:00
Gustavo Madeira Santana
378803987c fix(diffs): stage bundled runtime deps after updates (#56077)
Merged via squash.

Prepared head SHA: 2a153451de
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 20:01:14 -04:00
Peter Steinberger
13316a9118 refactor: reuse shared model prefix helper in thinking 2026-03-28 00:00:08 +00:00
Tak Hoffman
7da92cc618 fix(regression): align doctor plugin status with runtime 2026-03-27 18:59:47 -05:00
Peter Steinberger
d343b11bf1 refactor: reuse shared openai model prefix helper 2026-03-27 23:58:42 +00:00
Peter Steinberger
4b5aa6fd0b fix: refresh skill fixtures for pi-coding-agent 2026-03-27 23:58:41 +00:00
Peter Steinberger
6ba0c434ba refactor: move plugin tool routing defaults into tool context 2026-03-27 23:58:04 +00:00
Peter Steinberger
44defeb71b fix: unify plugin tool thread defaults via delivery context 2026-03-27 23:58:04 +00:00
Peter Steinberger
1c412b1ac6 fix: resolve Telegram slash command bindings from sender peer 2026-03-27 23:58:04 +00:00
Tak Hoffman
ee2220ca08 fix(regression): align plugin status with runtime compat 2026-03-27 18:55:41 -05:00
Peter Steinberger
55b9ce1c9d fix(ci): use https for libsignal git dependency 2026-03-27 23:55:13 +00:00
Peter Steinberger
0b26e4d72a refactor: split shared provider catalog sdk helpers 2026-03-27 23:55:10 +00:00
Peter Steinberger
41eb7c5056 fix(ci): align compaction and skills api drift 2026-03-27 23:52:27 +00:00
Gustavo Madeira Santana
93f9ca00dd build: bump pi packages to 0.63.1 2026-03-27 19:51:10 -04:00
Peter Steinberger
e951838c33 docs: point sdk overview at provider model shared seam 2026-03-27 23:50:04 +00:00
Peter Steinberger
adb78fa5dd fix: note ollama think=false landing (#53200) (thanks @BruceMacD) 2026-03-27 23:49:33 +00:00
Bruce MacDonald
773c57b418 fix(ollama): send think=false for thinking models when thinking is off
Ollama thinking-capable models default to think=true when the parameter
is absent. When OpenClaw has thinking set to off, the request never
included think=false, so models continued generating thinking tokens
that were then discarded by the response parser, producing empty
responses.

Wire onPayload into the Ollama stream path so payload wrappers can
mutate the request body, and add an Ollama-specific wrapper that sets
top-level think=false when thinkingLevel is off.

Fixes #46680, #50702, #50712

Co-Authored-By: SnowSky1 <126348592+snowsky1@users.noreply.github.com>
2026-03-27 23:49:33 +00:00
Peter Steinberger
3b51e6471a test: switch provider model barrel straggler imports 2026-03-27 23:48:56 +00:00
Peter Steinberger
0e3f517881 fix(ci): refresh bundled plugin metadata baselines 2026-03-27 23:47:29 +00:00
Peter Steinberger
898f3fa591 style: format compact skill test import 2026-03-27 23:47:04 +00:00
Peter Steinberger
8d95351217 fix: update skill and compaction test fixtures 2026-03-27 23:47:04 +00:00
Peter Steinberger
b39a7e8073 fix: break plugin-sdk provider barrel recursion 2026-03-27 23:47:04 +00:00
Peter Steinberger
ac68494dae fix(ci): harden discord rate-limit helpers 2026-03-27 23:43:43 +00:00
Peter Steinberger
232a96a0dc test(browser): spy tmp-dir seam in pw download test 2026-03-27 23:40:35 +00:00
Peter Steinberger
25a988c211 fix(ci): narrow browser config refresh seam 2026-03-27 23:38:56 +00:00
Gustavo Madeira Santana
5ca8be7323 matrix: guard invalid HTML entity mention labels 2026-03-27 19:37:58 -04:00
Peter Steinberger
eef2f82986 test: dedupe infra utility suites 2026-03-27 23:33:08 +00:00
Peter Steinberger
36b9ec9418 fix(ci): narrow browser logger and schema seams 2026-03-27 23:29:59 +00:00
Peter Steinberger
fc5e5f1e8e fix: resolve loader and test fallout after sdk split 2026-03-27 23:27:55 +00:00
Peter Steinberger
4ca07559ab refactor: move provider seams behind plugin sdk surfaces 2026-03-27 23:26:26 +00:00
Peter Steinberger
4f0ad16a00 fix(ci): route browser tmp path through public temp-path seam 2026-03-27 23:24:57 +00:00
Peter Steinberger
6fec75f15d test(browser): isolate auth and download mocks 2026-03-27 23:20:43 +00:00
Peter Steinberger
c720fa83bb fix(browser): narrow browser support facades 2026-03-27 23:20:24 +00:00
Peter Steinberger
a27624437e fix(ci): align skills api drift and tui keybindings 2026-03-27 23:18:31 +00:00
Nick Ludlam
5d82534af7 fix(matrix): mentions should work with displayName labels (with help from Antigravity) (#55393)
Merged via squash.

Prepared head SHA: c6df37ce14
Co-authored-by: nickludlam <7568+nickludlam@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 19:16:33 -04:00
Gustavo Madeira Santana
04458c807c lockfile: restore microsoft-foundry importer 2026-03-27 19:10:13 -04:00
Peter Steinberger
8a788e2c0c test: dedupe infra and plugin-sdk utility suites 2026-03-27 23:08:57 +00:00
private-peter
0558f2470d fix(matrix): only use 2-member DM fallback when dm refresh fails (#54890)
Merged via squash.

Prepared head SHA: e32d220ef0
Co-authored-by: private-peter <251383182+private-peter@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 19:08:40 -04:00
Gustavo Madeira Santana
11952457af Docs: document skill source precedence 2026-03-27 19:05:04 -04:00
Gustavo Madeira Santana
1fc4d7259f Agents/TUI: align with current pi APIs 2026-03-27 19:05:04 -04:00
alberthild
c7fbd51890 fix(matrix): resolve reply context body and sender for quoted messages (#55056)
Merged via squash.

Prepared head SHA: 6fd580bb03
Co-authored-by: alberthild <3729342+alberthild@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 19:03:21 -04:00
Peter Steinberger
65ad45a37f test(browser): stabilize cli runtime seams 2026-03-27 22:59:11 +00:00
esrehmki
f7934d7024 fix(matrix): pass originalFilename to saveMediaBuffer and expose path via MEDIA tag (#55692)
Merged via squash.

Prepared head SHA: a68dc0841b
Co-authored-by: esrehmki <20036971+esrehmki@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-03-27 18:53:52 -04:00
Peter Steinberger
a74c50c861 test: align profile env bootstrap with lazy dotenv 2026-03-27 22:48:09 +00:00
Peter Steinberger
8908f6c23b fix(ci): pin patched path-to-regexp 2026-03-27 22:44:14 +00:00
Peter Steinberger
2fc386f0df test: dedupe plugin sdk helper suites 2026-03-27 22:43:38 +00:00
Peter Steinberger
90c50fd9d8 test: stabilize extension mocks for ci shards 2026-03-27 22:40:30 +00:00
Peter Steinberger
c52f89bd60 test: dedupe helper-heavy test suites 2026-03-27 22:35:27 +00:00
Tak Hoffman
0826fb4a00 fix(regression): export plugin-sdk huggingface facade 2026-03-27 17:34:26 -05:00
Peter Steinberger
d9d5688792 test: make extension no-test coverage dynamic 2026-03-27 22:29:35 +00:00
Peter Steinberger
a5cb9ec674 fix(ci): align skills api and trim status startup 2026-03-27 22:24:54 +00:00
Tak Hoffman
4d7c6519fc fix(regression): enable owning plugins for provider onboarding 2026-03-27 17:24:09 -05:00
Peter Steinberger
7d4fab3e73 test: debrand pairing and dm policy fixtures 2026-03-27 22:18:20 +00:00
Tak Hoffman
3106ad38f2 fix(regression): preserve ACP turns without admin provenance scope 2026-03-27 17:13:53 -05:00
Peter Steinberger
a1ab0d9886 test: debrand generic auth-choice placeholders 2026-03-27 22:10:07 +00:00
Peter Steinberger
a834832d26 test: debrand final helper placeholders 2026-03-27 22:06:29 +00:00
Peter Steinberger
634db43b3f test: debrand fallback and registry pin fixtures 2026-03-27 22:05:34 +00:00
Peter Steinberger
c815bddce7 test: debrand debounce and acp lifecycle fixtures 2026-03-27 22:03:51 +00:00
Peter Steinberger
b95a81498f test: debrand policy and registry fixtures 2026-03-27 22:03:15 +00:00
Tak Hoffman
12923eb612 fix(regression): align sdk config write account lookup 2026-03-27 17:02:33 -05:00
Peter Steinberger
d27b99c6af test: debrand helper fixture ids 2026-03-27 22:01:15 +00:00
Peter Steinberger
be67c0de1d test: debrand queue retry fixtures 2026-03-27 21:58:29 +00:00
Peter Steinberger
884247f8d8 test: debrand generic setup helper fixtures 2026-03-27 21:57:58 +00:00
Tak Hoffman
5f7f914796 fix(regression): restore external phone control commands 2026-03-27 16:57:16 -05:00
Peter Steinberger
2019b649af test: debrand generic session binding fixtures 2026-03-27 21:54:56 +00:00
Peter Steinberger
a9cc830ded test: debrand generic outbound channel fixtures 2026-03-27 21:53:19 +00:00
Peter Steinberger
a50455452d test: debrand plumbing labels and restore skill compat 2026-03-27 21:50:39 +00:00
Peter Steinberger
03e7e3cd27 test: debrand generic channel fixture names 2026-03-27 21:48:05 +00:00
Peter Steinberger
f09b449ab1 fix(ci): align skills source info and compaction args 2026-03-27 21:47:03 +00:00
Peter Steinberger
76d3c67a88 test: debrand session and allowlist placeholders 2026-03-27 21:45:29 +00:00
Peter Steinberger
adb20a9fa9 test: debrand generic formatting fixtures 2026-03-27 21:44:18 +00:00
Peter Steinberger
8ae90e16fc refactor: debrand core fixtures and align skill types 2026-03-27 21:43:03 +00:00
Tak Hoffman
a0cc684d02 fix(regression): restore modelstudio sdk facade exports 2026-03-27 16:39:18 -05:00
Peter Steinberger
992b30604d refactor: move extension-owned tests to extensions 2026-03-27 21:37:09 +00:00
Peter Steinberger
d506eea076 fix(ci): restore contract plugin-sdk source loading 2026-03-27 21:33:32 +00:00
Harold Hunt
7d18799bbe Hooks: pass inbound attachment arrays to plugins (#55452)
Merged via squash.

Prepared head SHA: 062f8d0513
Co-authored-by: huntharo <5617868+huntharo@users.noreply.github.com>
Reviewed-by: @huntharo
2026-03-27 17:23:24 -04:00
Tak Hoffman
9134dbd252 fix(regression): fail discord startup on reconnect exhaustion 2026-03-27 16:20:02 -05:00
Tak Hoffman
c125c33724 fix(regression): honor internal provider auth for directives 2026-03-27 16:15:09 -05:00
Tak Hoffman
f41cd12b54 fix(regression): restore modelstudio facade exports 2026-03-27 16:10:42 -05:00
Tak Hoffman
85cf23a9d6 fix(regression): allow external device pair approvals 2026-03-27 16:07:54 -05:00
Tak Hoffman
eacd5ac3ef fix(regression): restore external talk voice updates 2026-03-27 16:05:22 -05:00
Tak Hoffman
1a9abb13bd fix(regression): preserve CLI continuity across chat reset 2026-03-27 16:01:37 -05:00
Peter Steinberger
b50b9b16ab fix(ci): isolate discord session-key facade 2026-03-27 20:59:39 +00:00
Peter Steinberger
c813222671 fix(ci): support discord rate limit ctor drift 2026-03-27 20:54:23 +00:00
Tak Hoffman
f8edd09a2c fix(regression): invalidate stale legacy CLI sessions 2026-03-27 15:52:47 -05:00
Peter Steinberger
c73c050276 fix(ci): align compaction and skills tests with upstream agent API 2026-03-27 20:48:48 +00:00
Peter Steinberger
03333100ba fix(ci): align discord rate limit calls and telegram test imports 2026-03-27 20:48:21 +00:00
Peter Steinberger
cb5aefb790 fix: sync plugin sdk guardrails and test drift 2026-03-27 20:47:36 +00:00
Peter Steinberger
2bdbb189bd refactor: route plugin sdk facades through extension barrels 2026-03-27 20:47:36 +00:00
Tak Hoffman
fa56682b3c fix(regression): preserve CLI bindings across session reset 2026-03-27 15:47:16 -05:00
Tak Hoffman
9446ee8ea3 fix(regression): restore Telegram fallback probe coverage 2026-03-27 15:47:16 -05:00
Jacob Tomlinson
8eaa3417c3 Config: skip nonexistent Tavily web-search migration 2026-03-27 20:45:59 +00:00
Tak Hoffman
fc570934de fix(regression): refresh Telegram probe test imports 2026-03-27 15:42:03 -05:00
Tak Hoffman
7772395618 fix(regression): isolate Telegram runtime helper tests 2026-03-27 15:42:03 -05:00
Tak Hoffman
366c1d6b9e fix(regression): tighten Telegram runtime helper coverage 2026-03-27 15:42:03 -05:00
Peter Steinberger
fa05c351a1 fix(ci): align compaction and skills tests with upstream agent API 2026-03-27 20:41:10 +00:00
Jacob Tomlinson
20c7cbbf78 Telegram: tighten media SSRF policy (#56004)
* Telegram: tighten media SSRF policy

* Telegram: restrict media downloads to configured hosts

* Telegram: preserve custom media apiRoot hosts
2026-03-27 20:39:24 +00:00
Jacob Tomlinson
511093d4b3 Discord: apply component interaction policy gates (#56014)
* Discord: apply component interaction policy gates

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>

* Discord: pass carbon rate limit request

* Discord: reply to blocked component interactions

---------

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
2026-03-27 20:38:40 +00:00
Jacob Tomlinson
e403decb6e nextcloud-talk: throttle repeated webhook auth failures (#56007)
* nextcloud-talk: throttle repeated webhook auth failures

Co-authored-by: Brian Mendonca <208517100+bmendonca3@users.noreply.github.com>

* nextcloud-talk: scope webhook auth limiter per server

* nextcloud-talk: limit repeated webhook auth failures only

---------

Co-authored-by: Brian Mendonca <208517100+bmendonca3@users.noreply.github.com>
2026-03-27 20:37:55 +00:00
Jacob Tomlinson
355abe5eba Discord: enforce approver checks for text approvals (#56015)
* Discord: gate text approvals by approver policy

* Discord: require approvers for plugin text approvals

* Discord: preserve legacy text approval fallback
2026-03-27 20:37:15 +00:00
Jacob Tomlinson
be00fcfccb Gateway: align chat.send reset scope checks (#56009)
* Gateway: align chat.send reset scope checks

* Gateway: tighten chat.send reset regression test

* Gateway: honor internal provider reset scope
2026-03-27 20:36:31 +00:00
Jacob Tomlinson
aa66ae1fc7 Extensions: require admin for config write commands (#56002)
* Extensions: require admin for config write commands

* Tests: cover phone control disarm auth
2026-03-27 20:35:42 +00:00
Jacob Tomlinson
e64a881ae0 Channels: preserve routed group policy (#56011) 2026-03-27 20:33:47 +00:00
Tak Hoffman
77060aa9f9 fix(regression): preserve Telegram status fallback without runtime 2026-03-27 15:33:10 -05:00
Peter Steinberger
ae7d93adc4 fix(ci): restore green check after upstream API changes 2026-03-27 20:30:35 +00:00
Peter Steinberger
41901c19bf fix: restore green check after upstream API changes 2026-03-27 20:29:18 +00:00
Peter Steinberger
79e495a627 fix: add OpenAI version attribution header 2026-03-27 20:29:18 +00:00
Jacob Tomlinson
d61f8e5672 Net: block missing IPv6 special-use ranges (#56008)
* Net: block missing IPv6 special-use ranges

* Tests: refresh public IPv6 pinning fixtures
2026-03-27 20:28:25 +00:00
Jacob Tomlinson
85777e726c Voice Call: canonicalize Plivo V3 replay key (#56003)
Co-authored-by: zsx <git@zsxsoft.com>
2026-03-27 20:27:23 +00:00
Peter Steinberger
d73dbb6753 fix: restore provider auth and build checks 2026-03-27 20:20:31 +00:00
Peter Steinberger
c28e76c490 refactor: move provider model helpers into plugins 2026-03-27 20:20:31 +00:00
Peter Steinberger
5d3d54ee36 refactor: generate plugin sdk facades 2026-03-27 20:20:31 +00:00
Peter Steinberger
888be707cf fix(hooks): avoid repo-wide format churn 2026-03-27 20:19:53 +00:00
Tak Hoffman
4430805719 Allow inherited AWS config file paths 2026-03-27 15:16:19 -05:00
Tak Hoffman
8bcab7ec6f Allow manual remote URL after trust decline 2026-03-27 15:16:19 -05:00
Tak Hoffman
c3d45fbb19 Fallback to Jiti when bun is unavailable 2026-03-27 15:16:19 -05:00
Tak Hoffman
fa89d68e7a Fix compaction safeguard request auth lookup 2026-03-27 15:16:19 -05:00
Peter Steinberger
4505987b9c style(ui): format control views 2026-03-27 20:15:13 +00:00
Jacob Tomlinson
eb6a3fca26 telegram: use live runtime helpers in channel status 2026-03-27 20:14:33 +00:00
Peter Steinberger
953a438420 fix(discord): align rate-limit error callsites 2026-03-27 20:09:15 +00:00
Peter Steinberger
49dbf64ab1 fix(core): harden bundled provider runtime surfaces 2026-03-27 20:04:53 +00:00
Jacob Tomlinson
cf10183389 plugins: disable native jiti loading under bun 2026-03-27 20:02:59 +00:00
Jacob Tomlinson
3e4222e9d4 docs: fix duplicate testing heading 2026-03-27 19:50:09 +00:00
Vincent Koc
02e3061aa7 fix(discord): stop queued reconnect exhaustion crash (#55991) 2026-03-27 12:39:38 -07:00
Peter Steinberger
496a1a35bd fix(test): stabilize line and irc extension suites 2026-03-27 19:32:57 +00:00
Byungsker
1dae6cc617 docs(agent-loop): correct default timeoutSeconds from 600s to 172800s (48h) (#55419)
* docs(agent-loop): correct default timeoutSeconds from 600s to 172800s (48h)

The default was raised to 48 hours in PR #51874 (merged 2026-03-21) to
avoid cutting off long-running ACP sessions, but the docs were not
updated at the time. Closes #55380.

* docs: remove 'Use 0 to disable' per aisle security review
2026-03-27 12:31:24 -07:00
Jacob Tomlinson
16ed9bf147 config: fall back to jiti for channel config surfaces 2026-03-27 19:27:35 +00:00
Peter Steinberger
605c9306ab fix(ci): repair extension and discord test gates 2026-03-27 19:26:25 +00:00
Jacob Tomlinson
febcb01128 discord: fix Carbon RateLimitError calls 2026-03-27 19:16:36 +00:00
Jacob Tomlinson
4d7cc6bb4f gateway: restrict node pairing approvals (#55951)
* gateway: restrict node pairing approvals

* gateway: tighten node pairing scope checks

* gateway: harden node pairing reconnects

* agents: request elevated node pairing scopes

* agents: fix node pairing approval preflight scopes
2026-03-27 19:14:16 +00:00
Jacob Tomlinson
68ceaf7a5f zalo: gate image downloads before DM auth (#55979)
* zalo: gate image downloads before DM auth

* zalo: clarify pre-download auth sentinel
2026-03-27 19:12:26 +00:00
Jacob Tomlinson
9ec44fad39 Exec approvals: reject wrapper carrier allow-always targets (#55947)
* Exec approvals: reject wrapper carrier allow-always targets

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>

* Tests: add shell wrapper carrier follow-up assertion

---------

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
2026-03-27 19:07:47 +00:00
Nimrod Gutman
7ce2670043 fix(discord): update carbon beta (#55980)
* fix(discord): update carbon beta

* fix: update carbon beta (#55980) (thanks @ngutman)
2026-03-27 22:06:20 +03:00
Jacob Tomlinson
824e16f9dd fix(media): require fs access for dynamic local roots (#55946)
* fix(media): require fs access for dynamic local roots

* fix(media): tighten fs root expansion policy

* fix(media): align fs root expansion with effective policy
2026-03-27 19:06:02 +00:00
Jacob Tomlinson
c603123528 fix(gateway): require admin for persisted verbose defaults (#55916)
* fix(gateway): require admin for verbose persistence

* gateway: tighten verbose persistence follow-ups
2026-03-27 19:04:02 +00:00
joshavant
55cd272fe1 changelog correction
Signed-off-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-03-27 12:02:44 -07:00
Jacob Tomlinson
7a801cc451 Gateway: disconnect revoked device sessions (#55952)
* Gateway: disconnect revoked device sessions

* Gateway: normalize device disconnect targets

* Gateway: scope token revoke disconnects by role

* Gateway: respond before disconnecting sessions
2026-03-27 19:01:26 +00:00
Jacob Tomlinson
fef1b1918c SDK: break channel plugin import cycle 2026-03-27 19:00:57 +00:00
Jacob Tomlinson
80d1e8a11a fal: guard image fetches (#55948)
* fal: guard image fetches

* fal: isolate guarded fetch tests

* fal: trust configured relay hosts
2026-03-27 18:59:25 +00:00
Peter Steinberger
2f13758f42 test: stabilize extension ci mocks 2026-03-27 18:55:58 +00:00
Jacob Tomlinson
4ee4960de2 Pairing: forward caller scopes during approval (#55950)
* Pairing: require caller scopes on approvals

* Gateway: reject forbidden silent pairing results
2026-03-27 18:55:33 +00:00
Jacob Tomlinson
2e23d44491 tests(feishu): reload chat tool after mock reset 2026-03-27 18:53:39 +00:00
Jacob Tomlinson
6eb82fba3c Infra: block additional host exec env keys (#55977) 2026-03-27 18:50:37 +00:00
Jacob Tomlinson
fdbcfced84 Agents: enforce session status visibility (#55904)
* Agents: enforce session_status visibility

* Agents: preserve sandboxed session_status visibility checks
2026-03-27 18:49:24 +00:00
Jacob Tomlinson
b7b3c806b4 fix(compaction): guard legacy model registry auth lookup 2026-03-27 18:44:54 +00:00
Jacob Tomlinson
d6affb17d8 CLI: confirm discovered remote gateways before saving config (#55895)
* CLI: require trust confirmation for discovered remote gateways

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>

* CLI: clear discovery pin when remote URL changes

---------

Co-authored-by: nexrin <268879349+nexrin@users.noreply.github.com>
2026-03-27 18:43:42 +00:00
Peter Steinberger
c9d68fb9c2 fix: repair ci test and loader regressions 2026-03-27 18:41:47 +00:00
glitch
3cec3bd48b fix(memory): share embedding providers across plugin runtime splits (#55945)
Merged via squash.

Prepared head SHA: e913806211
Co-authored-by: glitch418x <189487110+glitch418x@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-27 21:40:19 +03:00
Jacob Tomlinson
78e2f3d66d Exec: tighten jq safe-bin env checks (#55905) 2026-03-27 18:37:31 +00:00
Jacob Tomlinson
c774db9a1f fix(compaction): pass summary headers before abort signal 2026-03-27 18:35:31 +00:00
Jacob Tomlinson
25210317b8 fix(skills): adapt skill source metadata API 2026-03-27 18:28:45 +00:00
Peter Steinberger
694619caaf fix(runtime): narrow discord binding targets after rebase 2026-03-27 18:15:40 +00:00
Peter Steinberger
6e107b8857 fix(runtime): stabilize provider and channel runtime tests 2026-03-27 18:15:40 +00:00
Peter Steinberger
52ef2ef790 fix(agents): align compaction and skill metadata APIs 2026-03-27 18:15:40 +00:00
Peter Steinberger
894f57a4ce style(ui): apply formatter output 2026-03-27 18:15:40 +00:00
Peter Steinberger
69e67a764d refactor(feishu): remove docx table lint suppressions 2026-03-27 18:15:40 +00:00
Jakub Rusz
8f44bd6426 fix(ollama): emit streaming events for text content during generation (#53891)
The Ollama stream function requested `stream: true` from the API but
accumulated all content chunks internally, emitting only a single `done`
event at the end. This prevented downstream consumers (block streaming
pipeline, typing indicators, draft stream) from receiving incremental
text updates during generation.

Emit the full `start → text_start → text_delta* → text_end → done`
event sequence matching the AssistantMessageEvent contract used by
Anthropic, OpenAI, and Google providers. Each `text_delta` carries both
the incremental `delta` and an accumulated `partial` snapshot.

Tool-call-only responses (no text content) continue to emit only the
`done` event, preserving backward compatibility.

---------

Signed-off-by: Jakub Rusz <jrusz@proton.me>
Co-authored-by: Claude <claude-opus-4-6> <noreply@anthropic.com>
Co-authored-by: Bruce MacDonald <brucewmacdonald@gmail.com>
2026-03-27 11:12:09 -07:00
Peter Steinberger
1086acf3c2 fix: repair latest-main ci gate 2026-03-27 17:57:23 +00:00
Radek Sienkiewicz
47ae562cc9 Docs: unify link audit entrypoint (#55912)
Merged via squash.

Prepared head SHA: 6b1ccb9f1f
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
2026-03-27 18:31:19 +01:00
Peter Steinberger
d35f37a58c style: format ui views 2026-03-27 17:23:40 +00:00
Peter Steinberger
6f7579803b refactor: route memory doctor suggestions through plugin metadata 2026-03-27 17:23:40 +00:00
Peter Steinberger
2d26f2d876 refactor: move legacy auth choice aliases into plugin manifests 2026-03-27 17:23:40 +00:00
Peter Steinberger
e25f634d50 refactor: move oauth profile repair metadata into providers 2026-03-27 17:23:40 +00:00
Peter Steinberger
570bfb655f refactor: route bundled provider catalog hooks through plugins 2026-03-27 17:23:40 +00:00
Peter Steinberger
910cb9f1af refactor: simplify provider auth storage setters 2026-03-27 17:23:40 +00:00
Peter Steinberger
67f609ea9a refactor: remove core provider model definitions compat 2026-03-27 17:23:40 +00:00
Peter Steinberger
3628451aa3 refactor: move provider default model refs into extension apis 2026-03-27 17:23:40 +00:00
Peter Steinberger
94780dde5d refactor: reduce provider auth storage boilerplate 2026-03-27 17:23:40 +00:00
Peter Steinberger
b568ccee7c refactor: route litellm sdk through public api barrel 2026-03-27 17:23:40 +00:00
Peter Steinberger
3702409427 refactor: trim remaining plugin sdk extension seams 2026-03-27 17:23:40 +00:00
Peter Steinberger
e599cb26de refactor: route provider catalogs through public api barrels 2026-03-27 17:23:40 +00:00
Altay
4693813503 fix: re-format exec-approval view 2026-03-27 19:51:36 +03:00
Altay
c352a018f1 chore: add lockfile entry for extensions 2026-03-27 19:50:46 +03:00
Peter Steinberger
ef1784d264 refactor: move bundled plugin policy into manifests 2026-03-27 16:40:27 +00:00
Peter Steinberger
ed055f44ae refactor: route plugin runtime through bundled seams 2026-03-27 16:40:27 +00:00
Peter Steinberger
e425056aa3 refactor: route plugin runtime media through sdk wrappers 2026-03-27 16:39:42 +00:00
Peter Steinberger
425032ed4d refactor: move public artifact metadata into plugins 2026-03-27 16:39:42 +00:00
Peter Steinberger
07df59287a refactor: share plugin capability provider resolution 2026-03-27 16:39:41 +00:00
Peter Steinberger
f1503bd5c7 refactor: route bundled capability providers through plugin runtime 2026-03-27 16:39:41 +00:00
Peter Steinberger
8d054e7892 test: move shared seams into contract suites 2026-03-27 16:33:53 +00:00
Peter Steinberger
09f2832670 test: split contract seams from unit lane 2026-03-27 16:28:23 +00:00
Peter Steinberger
89267f4273 test: finish reply seam cleanup 2026-03-27 16:22:27 +00:00
Radek Sienkiewicz
ce5b0577d4 docs: fix Browserless and broken doc links (#55881)
Merged via squash.

Prepared head SHA: 528d04e070
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
2026-03-27 17:11:57 +01:00
Peter Steinberger
8ff39007c4 test: remove moved core duplicates 2026-03-27 16:08:57 +00:00
Peter Steinberger
cd92549119 test: split extension-owned core coverage 2026-03-27 16:08:57 +00:00
Josh Avant
6ade9c474c feat(hooks): add async requireApproval to before_tool_call (#55339)
* Plugins: add native ask dialog for before_tool_call hooks

Extend the before_tool_call plugin hook with a requireApproval return field
that pauses agent execution and waits for real user approval via channels
(Telegram, Discord, /approve command) instead of relying on the agent to
cooperate with a soft block.

- Add requireApproval field to PluginHookBeforeToolCallResult with id, title,
  description, severity, timeout, and timeoutBehavior options
- Extend runModifyingHook merge callback to receive hook registration so
  mergers can stamp pluginId; always invoke merger even for the first result
- Make ExecApprovalManager generic so it can be reused for plugin approvals
- Add plugin.approval.request/waitDecision/resolve gateway methods with
  schemas, scope guards, and broadcast events
- Handle requireApproval in pi-tools via two-phase gateway RPC with fallback
  to soft block when the gateway is unavailable
- Extend the exec approval forwarder with plugin approval message builders
  and forwarding methods
- Update /approve command to fall back to plugin.approval.resolve when exec
  approval lookup fails
- Document before_tool_call requireApproval in hooks docs and unified
  /approve behavior in exec-approvals docs

* Plugins: simplify plugin approval code

- Extract mergeParamsWithApprovalOverrides helper to deduplicate param
  merge logic in before_tool_call hook handling
- Use idiomatic conditional spread syntax in toolContext construction
- Extract callApprovalMethod helper in /approve command to eliminate
  duplicated callGateway calls
- Simplify plugin approval schema by removing unnecessary Type.Union
  with Type.Null on optional fields
- Extract normalizeTrimmedString helper for turn source field trimming

* Tests: add plugin approval wiring and /approve fallback coverage

Fix 3 broken assertions expecting old "Exec approval" message text.
Add tests for the /approve command's exec→plugin fallback path,
plugin approval method registration and scope authorization, and
handler factory key verification.

* UI: wire plugin approval events into the exec approval overlay

Handle plugin.approval.requested and plugin.approval.resolved gateway
events by extending the existing exec approval queue with a kind
discriminator. Plugin approvals reuse the same overlay, queue management,
and expiry timer, with branched rendering for plugin-specific content
(title, description, severity). The decision handler routes resolve calls
to the correct gateway method based on kind.

* fix: read plugin approval fields from nested request payload

The gateway broadcasts plugin approval payloads with title, description,
severity, pluginId, agentId, and sessionKey nested inside the request
object (PluginApprovalRequestPayload), not at the top level. Fix the
parser to read from the correct location so the overlay actually appears.

* feat: invoke plugin onResolution callback after approval decision

Adds onResolution to the requireApproval type and invokes it after
the user resolves the approval dialog, enabling plugins to react to
allow-always vs allow-once decisions.

* docs: add onResolution callback to requireApproval hook documentation

* test: fix /approve assertion for unified approval response text

* docs: regenerate plugin SDK API baseline

* docs: add changelog entry for plugin approval hooks

* fix: harden plugin approval hook reliability

- Add APPROVAL_NOT_FOUND error code so /approve fallback uses structured
  matching instead of fragile string comparison
- Check block before requireApproval so higher-priority plugin blocks
  cannot be overridden by a lower-priority approval
- Race waitDecision against abort signal so users are not stuck waiting
  for the full approval timeout after cancelling a run
- Use null consistently for missing pluginDescription instead of
  converting to undefined
- Add comments explaining the +10s timeout buffer on gateway RPCs

* docs: document block > requireApproval precedence in hooks

* fix: address Phase 1 critical correctness issues for plugin approval hooks

- Fix timeout-allow param bug: return merged hook params instead of
  original params when timeoutBehavior is "allow", preventing security
  plugins from having their parameter rewrites silently discarded.

- Host-generate approval IDs: remove plugin-provided id field from the
  requireApproval type, gateway request, and protocol schema. Server
  always generates IDs via randomUUID() to prevent forged/predictable
  ID attacks.

- Define onResolution semantics: add PluginApprovalResolutions constants
  and PluginApprovalResolution type. onResolution callback now fires on
  every exit path (allow, deny, timeout, abort, gateway error, no-ID).
  Decision branching uses constants instead of hard-coded strings.

- Fix pre-existing test infrastructure issues: bypass CJS mock cache for
  getGlobalHookRunner global singleton, reset gateway mock between tests,
  fix hook merger priority ordering in block+requireApproval test.

* fix: tighten plugin approval schema and add kind-prefixed IDs

Harden the plugin approval request schema: restrict severity to
enum (info|warning|critical), cap timeoutMs at 600s, limit title
to 80 chars and description to 256 chars. Prefix plugin approval
IDs with `plugin:` so /approve routing can distinguish them from
exec approvals deterministically instead of relying on fallback.

* fix: address remaining PR feedback (Phases 1-3 source changes)

* chore: regenerate baselines and protocol artifacts

* fix: exclude requesting connection from approval-client availability check

hasExecApprovalClients() counted the backend connection that issued
the plugin.approval.request RPC as an approval client, preventing
the no-approval-route fast path from firing in headless setups and
causing 120s stalls. Pass the caller's connId so it is skipped.
Applied to both plugin and exec approval handlers.

* Approvals: complete Discord parity and compatibility fallback

* Hooks: make plugin approval onResolution non-blocking

* Hooks: freeze params after approval owner is selected

* Gateway: harden plugin approval request/decision flow

* Discord/Telegram: fix plugin approval delivery parity

* Approvals: fix Telegram plugin approval edge cases

* Auto-reply: enforce Telegram plugin approval approvers

* Approvals: harden Telegram and plugin resolve policies

* Agents: static-import gateway approval call and fix e2e mock loading

* Auto-reply: restore /approve Telegram import boundary

* Approvals: fail closed on no-route and neutralize Discord mentions

* docs: refresh generated config and plugin API baselines

---------

Co-authored-by: Václav Belák <vaclav.belak@gendigital.com>
2026-03-27 09:06:40 -07:00
Peter Steinberger
351a931a62 fix(ci): restore runtime-api guardrails 2026-03-27 15:56:54 +00:00
Sally O'Malley
df5b9ef0c6 update podman setup and docs (#55388)
* update podman setup and docs

Signed-off-by: sallyom <somalley@redhat.com>

* podman: persist runtime env defaults

Co-authored-by: albertxos <kickban3000@gmail.com>
Signed-off-by: sallyom <somalley@redhat.com>

* podman: harden env and path handling, other setup updates

Signed-off-by: sallyom <somalley@redhat.com>

* podman: allow symlinked home path components

Signed-off-by: sallyom <somalley@redhat.com>

* update podman docs

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: albertxos <kickban3000@gmail.com>
2026-03-27 11:47:35 -04:00
Ayaan Zaidi
5e8db468ff fix(agents): preserve embedded auth on HTTP fallback 2026-03-27 21:15:15 +05:30
Peter Steinberger
9098e948ac fix(ci): route extension tests through public test bridges 2026-03-27 15:20:01 +00:00
Peter Steinberger
833636f0b2 style(ui): normalize control-ui formatting 2026-03-27 15:15:40 +00:00
Peter Steinberger
6a0f9afc4e style: normalize test and sdk formatting 2026-03-27 15:15:04 +00:00
Peter Steinberger
8ddeada97d test: move extension-owned coverage into plugins 2026-03-27 15:11:33 +00:00
Peter Steinberger
97297049e7 fix(ci): restore boundary and test seams 2026-03-27 15:08:33 +00:00
Ayaan Zaidi
454f094c36 fix: avoid source imports in cli startup metadata build 2026-03-27 20:31:28 +05:30
junpei.o
be0e994cf0 feat(plugins): expose runId in agent hook context (#54265) 2026-03-27 10:47:13 -04:00
Peter Steinberger
87dddb818d fix(ci): restore plugin runtime boundaries 2026-03-27 14:38:40 +00:00
bottenbenny
f9b8499bf6 fix(chat): send button should use system theme variables (#55075)
Merged via squash.

Prepared head SHA: eb3e197874
Co-authored-by: bottenbenny <270688955+bottenbenny@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
2026-03-27 15:12:19 +01:00
Peter Steinberger
66a2e72bee fix: restore CI runtime seams 2026-03-27 14:07:01 +00:00
Ping
a6f5e57f46 fix(plugins): apply bundled allowlist compat in plugin status report (#55267)
* fix(plugins): apply bundled allowlist compat in plugin status report

`buildPluginStatusReport` (used by `openclaw plugins list` and
`openclaw doctor`) was calling `loadOpenClawPlugins` without applying
`withBundledPluginAllowlistCompat`. When `plugins.allow` is set, the
allowlist check in `resolveEffectiveEnableState` runs before the
bundled-default-enable check, causing all bundled plugins not explicitly
in the allowlist to be reported as "disabled".

The gateway runtime already applies this compat via
`providers.runtime.ts`, so the actual loaded state differs from what
CLI diagnostics report.

Apply the same `withBundledPluginAllowlistCompat` transform so the
status report matches gateway runtime behavior.

* add regression test for bundled allowlist compat wiring

Address review feedback: the previous mocks were identity stubs that
did not exercise the compat wiring. Now the mocks are spies, and a new
test verifies that:
1. loadPluginManifestRegistry is called to discover bundled plugin IDs
2. withBundledPluginAllowlistCompat receives only bundled IDs (not workspace)
3. loadOpenClawPlugins receives the compat-adjusted config

* scope compat to bundled providers only (address codex review)

Use resolveBundledProviderCompatPluginIds instead of injecting all
bundled plugin IDs. This matches the runtime compat surface in
providers.runtime.ts — non-provider bundled plugins (device-pair,
phone-control, etc.) are not auto-added to the allowlist, keeping
the status report consistent with gateway startup behavior.
2026-03-27 10:00:25 -04:00
Tak Hoffman
c2ca99aa0b fix(test-planner): keep exit fallback timer referenced 2026-03-27 09:00:09 -05:00
Val Alexander
bb932beeac Media: respect umask and clean failed downloads 2026-03-27 08:55:06 -05:00
Val Alexander
206c29514c Media: enforce file mode after writes 2026-03-27 08:55:06 -05:00
助爪
b1c982bb2d fix(agents): fail over and sanitize Codex server_error payloads (#42892)
Merged via squash.

Prepared head SHA: 6db9a5f02d
Co-authored-by: xaeon2026 <264572156+xaeon2026@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-27 16:51:39 +03:00
Peter Steinberger
546a1aad98 refactor: replace plugin-sdk dist env hacks with loader option 2026-03-27 13:46:17 +00:00
Peter Steinberger
ad89fa669c fix: unstick provider contract tests 2026-03-27 13:46:17 +00:00
Peter Steinberger
b5a8d5a230 fix: stabilize plugin-sdk test loading 2026-03-27 13:46:17 +00:00
Peter Steinberger
9d10a2e242 refactor: shrink remaining test seam reach-ins 2026-03-27 13:46:17 +00:00
Peter Steinberger
f217a10780 refactor: route more runtime-boundary tests through public seams 2026-03-27 13:46:17 +00:00
Peter Steinberger
9917f3b3a1 refactor: route ollama sdk through public barrels 2026-03-27 13:46:17 +00:00
Peter Steinberger
858b1dffb8 refactor: route zalouser payload mocks through test api 2026-03-27 13:46:17 +00:00
Peter Steinberger
b70b99d46d refactor: route telegram gateway test through test api 2026-03-27 13:46:17 +00:00
Peter Steinberger
e42b4afd39 refactor: route reply plumbing test through slack entry 2026-03-27 13:46:17 +00:00
Peter Steinberger
d0c77c9dfd refactor: narrow outbound payload runtime mock reach-ins 2026-03-27 13:46:17 +00:00
Peter Steinberger
169bf6adba refactor: route outbound payload tests through extension test seams 2026-03-27 13:46:17 +00:00
Peter Steinberger
c7b4c34e89 refactor: route provider test seams through extension barrels 2026-03-27 13:46:17 +00:00
Peter Steinberger
02d9f0d631 refactor: route exec approval surface test through extension entrypoints 2026-03-27 13:46:17 +00:00
Peter Steinberger
4019671331 refactor: add runtime-boundary plugin test seams 2026-03-27 13:46:17 +00:00
Peter Steinberger
c2b28753e7 refactor: route more test seams through public plugin APIs 2026-03-27 13:46:17 +00:00
Peter Steinberger
4d630b7e92 refactor: expose dm policy test seams 2026-03-27 13:46:17 +00:00
Peter Steinberger
a79ef1591a refactor: trim health test extension mocks 2026-03-27 13:46:17 +00:00
Peter Steinberger
45849eb757 refactor: remove remaining core extension reach-ins 2026-03-27 13:46:16 +00:00
Peter Steinberger
fc3246d8fd refactor: add public channel contract test seams 2026-03-27 13:46:16 +00:00
Peter Steinberger
04bde8c2b1 refactor: route session-binding contract tests through public seams 2026-03-27 13:46:16 +00:00
Peter Steinberger
8d39fe5a76 refactor: route channel contract tests through public barrels 2026-03-27 13:46:16 +00:00
Peter Steinberger
051d6f9342 refactor: remove onboarding gateway compat shims 2026-03-27 13:46:16 +00:00
Peter Steinberger
7999577ce1 refactor: route plugin sdk through extension barrels 2026-03-27 13:46:16 +00:00
Peter Steinberger
a10763e118 refactor: generate bundled channel seams 2026-03-27 13:46:16 +00:00
Peter Steinberger
9a775aa59c refactor: continue plugin seam cleanup 2026-03-27 13:46:16 +00:00
Tak Hoffman
4c8ed2ce46 test(planner): force real timers in executor fallback 2026-03-27 08:40:48 -05:00
Tak Hoffman
398af90a22 fix(ci): makin it green 2026-03-27 08:26:49 -05:00
lurebat
4cf783b7c1 fix(skills): honor OPENAI_BASE_URL in whisper api skill (#55597)
* fix(skills): honor OPENAI_BASE_URL in whisper api skill

* Update skills/openai-whisper-api/SKILL.md

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

---------

Co-authored-by: Asaf (via Bruh) <asaf@asafshq.win>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-27 09:23:26 -04:00
Tak Hoffman
45535ff433 dev: speed up local check loop 2026-03-27 07:56:41 -05:00
Peter Steinberger
bcfddcc768 refactor: pluginize litellm auth onboarding 2026-03-27 12:26:01 +00:00
Peter Steinberger
324cddee4c fix: resolve bundled plugins from running CLI 2026-03-27 12:26:01 +00:00
Peter Steinberger
ac2c2ac954 fix: stop test-parallel from waiting forever on child close 2026-03-27 12:20:51 +00:00
Peter Steinberger
f625a0b106 style: apply current oxfmt output to ui views 2026-03-27 12:03:25 +00:00
Peter Steinberger
7dd196ed74 fix: apply live model switches during active retries 2026-03-27 12:01:55 +00:00
Peter Steinberger
d746690be5 ci: align bun artifact upload with node24-safe action 2026-03-27 11:36:53 +00:00
Anmol Ahuja
c40884d306 Prefer non-user writeable paths (#54346)
* infra: trust system binary roots

* infra: isolate windows install root overrides

* infra: narrow windows reg lookup

* browser: restore windows executable comments

---------

Co-authored-by: Jacob Tomlinson <jtomlinson@nvidia.com>
2026-03-27 11:29:32 +00:00
mappel-nv
9d58f9e24f Replace killProcessTree references to shell-utils with process/kill-tree (#55213)
* Replace killProcessTree references to shell-utils with process/kill-tree

* Address grace timeout comment

* Align with existing process kill behavior

* bash: fail stop without pid

* bash: lazy-load kill tree on stop

---------

Co-authored-by: Jacob Tomlinson <jtomlinson@nvidia.com>
2026-03-27 11:25:56 +00:00
Shakker
d80b67124b docs: add changelog entries for #55732 and #55733 2026-03-27 11:09:11 +00:00
Shakker
c259ff7e01 docs: add changelog entry for #55730 2026-03-27 11:03:51 +00:00
Shakker
58cdcf74c7 fix(tui): validate activation slash commands 2026-03-27 11:03:26 +00:00
Shakker
14c63ca42a fix(tui): prune chat log system messages atomically 2026-03-27 11:03:26 +00:00
oliviareid-svg
32a3733dbe fix(google): strip empty required arrays from tool schemas for Gemini (#52106)
Merged via squash.

Prepared head SHA: 2ec59c1332
Co-authored-by: oliviareid-svg <269669958+oliviareid-svg@users.noreply.github.com>
Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com>
Reviewed-by: @altaywtf
2026-03-27 14:00:14 +03:00
Shakker
8fa62985b9 fix: preserve tui local auth with url overrides 2026-03-27 10:32:13 +00:00
Shakker
f1de00c107 fix: keep tui out of browser origin checks 2026-03-27 10:32:13 +00:00
Shakker
2b96569e2d fix: add dedicated tui gateway client auth 2026-03-27 10:32:13 +00:00
Shakker
3d609b112e fix: repair local control ui auth on quickstart reruns 2026-03-27 10:32:13 +00:00
Shakker
8270baa1d0 fix: allow local quickstart control ui without pairing 2026-03-27 10:32:13 +00:00
Shakker
37538072e6 fix: clarify no-daemon onboarding gateway checks 2026-03-27 10:32:13 +00:00
Shakker
e765daaed3 fix: show resolved gateway port in setup wizard 2026-03-27 10:32:13 +00:00
Jacob Tomlinson
4b9542716c Gateway: require verified scope for chat provenance (#55700)
* Gateway: require verified scope for chat provenance

* Gateway: clarify chat provenance auth gate
2026-03-27 10:13:34 +00:00
Jacob Tomlinson
83da3cfe31 infra: unwrap script wrapper approval targets (#55685)
* infra: unwrap script wrapper approvals

* infra: handle script short option values

* infra: gate script wrapper unwrapping by platform

* infra: narrow script wrapper option parsing
2026-03-27 10:05:35 +00:00
Jacob Tomlinson
cb5f7e201f gateway: cap concurrent pre-auth websocket upgrades (#55294)
* gateway: cap concurrent pre-auth websocket upgrades

* gateway: release pre-auth budget on failed upgrades

* gateway: scope pre-auth budgets to trusted client ip

* gateway: reject upgrades before ws handlers attach

* gateway: cap preauth budget for unknown client ip
2026-03-27 09:55:27 +00:00
Jacob Tomlinson
76411b2afc Agents: block protected gateway config writes (#55682)
* Agents: block protected gateway config writes

* Agents: tighten gateway config guard coverage

* Agents: guard migrated exec config aliases
2026-03-27 09:42:15 +00:00
Ted Li
4d5f762b7d fix: fall back from synthetic tool accounts (#55627) (thanks @MonkeyLeeT)
* feishu: fall back from synthetic tool accounts

* feishu: validate implicit tool accounts by config id

* fix: fall back from synthetic tool accounts (#55627) (thanks @MonkeyLeeT)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-27 15:09:36 +05:30
Jacob Tomlinson
2d80dbfeba fix(gateway): require read scope for models http (#55683) 2026-03-27 09:23:04 +00:00
Ayaan Zaidi
3a7cf5364f test(cleanup): isolate session lock queue coverage 2026-03-27 13:50:02 +05:30
Ayaan Zaidi
d6662e2aa7 fix(test): stabilize windows lock and cache paths 2026-03-27 13:29:15 +05:30
Peter Steinberger
a30dae3c71 fix: honor test planner cache paths by target platform 2026-03-27 07:53:57 +00:00
Peter Steinberger
1b77e6fd72 test: remove duplicate voice-call stdout assertion 2026-03-27 07:38:56 +00:00
Peter Steinberger
e1235ca7b4 test: fix voice-call cli stdout assertions 2026-03-27 07:37:41 +00:00
Ayaan Zaidi
53304ff704 test(voice-call): capture cli stdout 2026-03-27 13:01:32 +05:30
Peter Steinberger
9322481075 fix: route ollama helpers through plugin sdk 2026-03-27 07:26:41 +00:00
Ayaan Zaidi
ae72977076 fix(agents): restore ollama public seam 2026-03-27 12:46:34 +05:30
Tak Hoffman
23fae00fad Reduce script logging suppressions and Feishu any casts 2026-03-27 02:12:56 -05:00
Tak Hoffman
f5643544c2 Reduce lint suppressions in core tests and runtime 2026-03-27 02:11:26 -05:00
Peter Steinberger
7c00cc9d0a test: fix feishu batch insert test syntax 2026-03-27 07:10:55 +00:00
Peter Steinberger
854b71a4b0 test: fix feishu test typings 2026-03-27 07:10:55 +00:00
heavenlost
3cbd4de95c fix(gateway): restore loopback detail probes and identity fallback (#51087)
Merged via squash.

Prepared head SHA: f8a66ffde2
Co-authored-by: heavenlost <70937055+heavenlost@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
2026-03-27 08:09:41 +01:00
Tak Hoffman
6f92148da9 fix(test-planner): shrink local extension batches on constrained hosts 2026-03-27 01:59:46 -05:00
Ayaan Zaidi
cfddce4196 fix(feishu): restore tsgo test typing 2026-03-27 12:13:59 +05:30
Peter Steinberger
a3e73daa6b refactor: remove ollama legacy shims 2026-03-27 06:38:23 +00:00
Ayaan Zaidi
bd2c208689 refactor(mattermost): type config seams 2026-03-27 11:59:02 +05:30
Peter Steinberger
e58170ddc1 build: refresh plugin sdk api baseline 2026-03-27 06:26:21 +00:00
Ayaan Zaidi
f2b2b12af4 test(feishu): type bot interaction fixtures 2026-03-27 11:54:23 +05:30
Ayaan Zaidi
59a25978dd test(feishu): type reply lifecycle fixtures 2026-03-27 11:54:23 +05:30
Ayaan Zaidi
6ad50ce474 test(feishu): type tool harness fixtures 2026-03-27 11:54:23 +05:30
Ayaan Zaidi
1042710e3b test(feishu): type policy fixtures 2026-03-27 11:54:23 +05:30
Ayaan Zaidi
b23dc5073f test(feishu): type basic fixtures 2026-03-27 11:54:23 +05:30
Tak Hoffman
9a7c8e186e test(plugin-sdk): refresh matrix runtime api guardrail 2026-03-27 01:21:58 -05:00
Peter Steinberger
a8066ad96d fix: align skills and compaction api drift 2026-03-27 06:18:41 +00:00
Tak Hoffman
599d880c49 build: refresh plugin sdk api baseline 2026-03-27 01:12:57 -05:00
Peter Steinberger
a729eab6ee build: refresh plugin sdk api baseline 2026-03-27 06:06:37 +00:00
Peter Steinberger
f86765cba3 style: refresh ui formatting 2026-03-27 06:06:37 +00:00
Peter Steinberger
e3e9a56b02 fix: export matrix account helpers in runtime api 2026-03-27 06:06:37 +00:00
Peter Steinberger
8923fb8766 style: normalize ui formatting 2026-03-27 06:06:37 +00:00
Ayaan Zaidi
9db096a98f refactor(feishu): remove docx explicit-any escapes 2026-03-27 11:34:35 +05:30
Ayaan Zaidi
571d4d52e9 refactor(feishu): remove stale explicit-any escapes 2026-03-27 11:34:35 +05:30
Ayaan Zaidi
f248fc8f86 refactor(feishu): type runtime payload seams 2026-03-27 11:34:35 +05:30
Ayaan Zaidi
9ce2dbe9aa refactor(feishu): type sender drive and typing helpers 2026-03-27 11:34:35 +05:30
Tak Hoffman
f799cd6a14 test(agents): align compaction safeguard auth mock 2026-03-27 01:03:05 -05:00
Tak Hoffman
55ab98dc40 fix(agents): adapt compaction and skill source types 2026-03-27 01:02:22 -05:00
Tak Hoffman
417024f9ad style(ui): apply formatting cleanup 2026-03-27 00:57:46 -05:00
Tak Hoffman
716c93f624 docs(plugin-sdk): refresh generated API baseline 2026-03-27 00:53:50 -05:00
Tak Hoffman
2b586b423a test(google): spy on image-generation auth surface 2026-03-27 00:51:38 -05:00
Tak Hoffman
04d01984ef fix(build): make bundled runtime-deps staging incremental 2026-03-27 00:51:38 -05:00
Ayaan Zaidi
bd4ecbfe49 refactor(feishu): type docx and media sdk seams 2026-03-27 11:15:07 +05:30
Peter Steinberger
2f979e9be0 test: fix memory-core host type import 2026-03-27 05:38:58 +00:00
Peter Steinberger
4c27c90fc2 refactor: finish moving provider runtime into extensions 2026-03-27 05:38:58 +00:00
Peter Steinberger
25879be46a test: stub summary transport in tts tests 2026-03-27 05:38:58 +00:00
Peter Steinberger
64bf80d4d5 refactor: move provider runtime into extensions 2026-03-27 05:38:58 +00:00
Peter Steinberger
53a3922e1c style: normalize ui slash executor formatting 2026-03-27 05:38:22 +00:00
Saurabh
8ab6891d97 fix: remove dead .status assignment and add 503 error test 2026-03-27 05:38:22 +00:00
Saurabh
517ae6ea14 fix: trigger model fallback on HTTP 503 Service Unavailable (#55178) 2026-03-27 05:38:22 +00:00
lurebat
5d91b68af3 fix(whatsapp): exclude quoted message mentionedJids from mention detection (#52711)
Merged via squash.

Prepared head SHA: 2ac325f615
Co-authored-by: lurebat <154669821+lurebat@users.noreply.github.com>
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Reviewed-by: @mcaxtr
2026-03-27 02:35:49 -03:00
Ayaan Zaidi
85d5e4360d fix(skills): use skill sourceInfo 2026-03-27 10:59:07 +05:30
Ayaan Zaidi
39fae14c72 fix(agents): adapt compaction request auth 2026-03-27 10:59:07 +05:30
Ayaan Zaidi
9368f834c0 style(ui): format view templates 2026-03-27 10:59:07 +05:30
Ayaan Zaidi
f804da9444 test(mattermost): remove double-cast test helpers 2026-03-27 10:59:07 +05:30
Tak Hoffman
75534f7a47 docs(config): refresh bundled channel config metadata 2026-03-27 00:25:43 -05:00
Peter Steinberger
ee12f24760 fix: restore ci compatibility 2026-03-27 05:23:50 +00:00
Tak Hoffman
c13d6dbf55 docs: define YOLO mode for main verification 2026-03-27 00:11:15 -05:00
Tak Hoffman
ed2798417e check: restore bundled channel config metadata gate 2026-03-27 00:11:15 -05:00
Peter Steinberger
fbd8990e78 fix: resolve rebase fallout in runtime tests 2026-03-27 05:07:50 +00:00
Peter Steinberger
ffa2a47c58 test: stabilize slow contract and integration suites 2026-03-27 05:07:50 +00:00
Peter Steinberger
a9e241dacb fix: align runtime types with upstream changes 2026-03-27 05:07:50 +00:00
Peter Steinberger
a1f995053e refactor: migrate more boundary parsing to zod 2026-03-27 05:07:50 +00:00
Ayaan Zaidi
137a56194e test(mattermost): remove untyped mocks 2026-03-27 10:31:08 +05:30
Ayaan Zaidi
e2d0a808e0 refactor(mattermost): type action params 2026-03-27 10:31:08 +05:30
Marcus Castro
38adeb888c fix: align Skill consumers with sourceInfo → source rename 2026-03-27 01:49:58 -03:00
Marcus Castro
2942df6b9f fix: align compaction call sites with upstream API drift 2026-03-27 01:49:58 -03:00
Peter Steinberger
8cf1e46a94 style: format ui sources 2026-03-27 04:45:17 +00:00
Peter Steinberger
3557bce827 fix: adapt to upstream agent api changes 2026-03-27 04:45:17 +00:00
Peter Steinberger
a4b77ad33f refactor: shortcut bundled provider contract fixtures 2026-03-27 04:44:43 +00:00
Peter Steinberger
17203d0af9 fix: stabilize codex oauth refresh tests 2026-03-27 04:44:43 +00:00
Peter Steinberger
4629ab3d8a refactor: move model picker logic into flow module 2026-03-27 04:44:43 +00:00
Ayaan Zaidi
06820b6daf refactor(mattermost): define post schema once 2026-03-27 10:08:29 +05:30
Ayaan Zaidi
ca9659ffb0 style(mattermost): format websocket monitor 2026-03-27 10:04:23 +05:30
Ayaan Zaidi
51d851e092 fix(skills): use skill sourceInfo 2026-03-27 09:57:02 +05:30
Ayaan Zaidi
b1d853d88b fix(agents): adapt compaction request auth 2026-03-27 09:56:35 +05:30
Ayaan Zaidi
8b13710c09 refactor(plugin-sdk): expose zod subpath 2026-03-27 09:55:47 +05:30
Peter Steinberger
70184d0a5e fix: compaction API drift + Skill sourceInfo→source migration
- compaction.ts: drop removed 'headers' param from generateSummary call
- compaction.retry.test.ts: align test call with new generateSummary signature
- compaction-safeguard.ts: replace getApiKeyAndHeaders with getApiKey (upstream removed)
- Migrate all Skill sourceInfo.source → flat source field across agents, cli, security
- Update 6 test files to match new Skill shape
2026-03-27 04:23:39 +00:00
Peter Steinberger
d8a1808bd6 fix: nextcloud-talk + mattermost type errors
- nextcloud-talk setup-core: cast input to NextcloudSetupInput before accessing .secret/.secretFile/.baseUrl
- mattermost monitor-websocket: add intermediate 'as unknown' for ZodRecord→ZodType<MattermostPost> cast
2026-03-27 04:23:39 +00:00
Tak Hoffman
2b55708f40 docs(config): refresh generated baselines 2026-03-26 23:19:57 -05:00
Tak Hoffman
5eee793669 test(config): align legacy routing snapshot expectation 2026-03-26 23:17:43 -05:00
huntharo
4262abe05d test: lower prepare unit-fast batch target 2026-03-27 00:12:49 -04:00
Peter Steinberger
eebce9e9c7 refactor: move memory host into sdk package 2026-03-27 04:12:04 +00:00
Peter Steinberger
490b2f881c fix(ci): restore codex oauth refresh fallback 2026-03-27 04:08:12 +00:00
Ayaan Zaidi
b99b16d71e test: activate openrouter media registry in vision test 2026-03-27 09:36:25 +05:30
Ayaan Zaidi
513df9fdb0 build: refresh bundled plugin metadata 2026-03-27 09:36:25 +05:30
Neerav Makwana
4604d252b2 Media: allow active OpenRouter image models 2026-03-27 09:36:25 +05:30
Tak Hoffman
962cc740a0 fix: keep session settings in sessions list 2026-03-26 22:48:39 -05:00
Peter Steinberger
ef56d79a6a refactor: collapse zod setup validators 2026-03-27 03:48:15 +00:00
Tak Hoffman
e25965ed4a fix: keep session thread ids in sessions list 2026-03-26 22:43:05 -05:00
Tak Hoffman
a2cc0630d2 fix: use session origin provider in subagent queue routing 2026-03-26 22:42:45 -05:00
Tak Hoffman
6e5bc5647a fix: use session origin provider in chat delivery routing 2026-03-26 22:42:45 -05:00
Tak Hoffman
8e4115947b fix: use session origin provider for reset overrides 2026-03-26 22:42:45 -05:00
Tak Hoffman
5783c2e070 fix: use session origin provider in status queue settings 2026-03-26 22:42:45 -05:00
Tak Hoffman
bd5fe92c94 fix: use session origin provider in sessions list 2026-03-26 22:42:45 -05:00
Peter Steinberger
e6c5ce136e refactor: share zod setup validators across channels 2026-03-27 03:41:40 +00:00
Peter Steinberger
1d1f36adff refactor: parse extension event payloads with zod 2026-03-27 03:41:40 +00:00
Peter Steinberger
35b132884c refactor: add zod helpers for json file readers 2026-03-27 03:41:40 +00:00
Tak Hoffman
5b4669632a Avoid stale sessions_send reply carryover 2026-03-26 22:40:50 -05:00
Tak Hoffman
6ef6e80abe Avoid stale subagent reply carryover 2026-03-26 22:40:50 -05:00
Tak Hoffman
d81b5fc792 Fix status subagent ownership parity 2026-03-26 22:40:50 -05:00
SnowSky1
7016659dbe fix: land bundled plugin doctor migration (#55054) (thanks @SnowSky1)
* fix(doctor): migrate legacy bundled plugin load paths

* fix(doctor): preserve unknown plugin path entries

* fix: derive bundled plugin legacy paths from actual directory names

* fix: land bundled plugin doctor migration (#55054) (thanks @SnowSky1)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-03-27 09:05:34 +05:30
Peter Steinberger
a9b982c954 refactor: remove memory-core engine barrel 2026-03-27 03:35:00 +00:00
Tak Hoffman
3e121edf20 fix(msteams): normalize memory store conversation ids 2026-03-26 22:29:46 -05:00
Peter Steinberger
be6b841334 fix: align skill and compaction API usage 2026-03-27 03:27:51 +00:00
Tak Hoffman
0235cca58a fix: preserve explicit reset transcript paths 2026-03-26 22:27:09 -05:00
Peter Steinberger
b15d9eb565 fix(agents): restore compaction headers typing 2026-03-27 03:19:59 +00:00
Peter Steinberger
158c4c1f4f fix(repo): restore gate after upstream drift 2026-03-27 02:58:14 +00:00
Peter Steinberger
f6de4cd766 refactor: remove memory-core runtime barrel 2026-03-27 02:54:23 +00:00
Peter Steinberger
c9ab095099 refactor: deduplicate plugin config schemas 2026-03-27 02:53:08 +00:00
Peter Steinberger
a331270f8a fix: restore green build after upstream API drift 2026-03-27 02:49:53 +00:00
Peter Steinberger
bd6c7969ea refactor: extract memory host sdk package 2026-03-27 02:49:33 +00:00
Peter Steinberger
dff3ca2018 fix: stabilize ci after deps refresh 2026-03-27 02:44:05 +00:00
Kinfey
c959ac3a25 fix: WSL2 Ollama networking and provider discovery diagnostics (#55435)
- Fix Ollama stream handling for WSL2 environments
- Update undici global dispatcher for WSL2 networking compatibility
- Adjust provider discovery configuration
- Add WSL2 networking tests
2026-03-26 21:41:05 -05:00
Peter Steinberger
9df9bd436e refactor(config): dedupe legacy migration metadata 2026-03-27 02:37:47 +00:00
Peter Steinberger
66e7e29219 refactor(config): simplify version and allowed-value resolution 2026-03-27 02:37:17 +00:00
Peter Steinberger
417b3dd5e0 refactor: move channel prefer-over metadata into manifests 2026-03-27 02:36:56 +00:00
Peter Steinberger
b96f5d94db chore(telegram): downgrade default network logs 2026-03-27 02:29:32 +00:00
Peter Steinberger
465f830bcd fix(config): support uri formats in schema validation 2026-03-27 02:29:32 +00:00
Peter Steinberger
0b94382930 fix(plugins): prefer runtime version for host compatibility 2026-03-27 02:29:32 +00:00
Peter Steinberger
dd098596cf refactor: collapse bundled channel metadata into plugin manifests 2026-03-27 02:29:19 +00:00
Peter Steinberger
ea60bc01b9 refactor(config): drop stale legacy migrations 2026-03-27 02:29:08 +00:00
Peter Steinberger
10527ff8a3 build: refresh deps and vitest cache lanes 2026-03-27 02:26:07 +00:00
Peter Steinberger
b49accc273 test: add websocket replay planning coverage 2026-03-27 02:16:01 +00:00
Peter Steinberger
86bac4ee2a refactor: split openai websocket message conversion 2026-03-27 02:16:01 +00:00
Vincent Koc
fa2a318f40 Align ACPX built-in agent registry with latest acpx (#55476)
* Add Cursor CLI to ACP allowedAgents

- acpx: add cursor to ACPX_BUILTIN_AGENT_COMMANDS (agent acp)
- docs: add cursor to acp-agents harness list and allowedAgents example

Fixes #28321

Made-with: Cursor

* ACP Cursor: add to acp-router skill, system-prompt, and schema help

- acp-router SKILL: add Cursor to description, intent, agentId mapping,
  harness aliases, and built-in adapter commands (agent acp)
- system-prompt: add cursor to ACP harness example
- schema.help: add cursor to runtime.acp.agent example

Fixes #28321

Made-with: Cursor

* fix(acpx): align built-in agent registry with latest acpx

---------

Co-authored-by: Rob MacDonald <rob@robmacdonald.com>
2026-03-26 19:15:17 -07:00
Peter Steinberger
e9f54ca815 docs: update parallels smoke guidance 2026-03-27 02:15:15 +00:00
Peter Steinberger
1ff1679984 test: harden parallels windows smoke polling 2026-03-27 02:15:15 +00:00
Tak Hoffman
3e5d86384e fix: initialize reset transcript files 2026-03-26 21:12:36 -05:00
Peter Steinberger
77d15841d7 refactor: move manifest legacy migration into doctor 2026-03-27 02:09:58 +00:00
Tak Hoffman
5404b0eaa6 fix(msteams): preserve timezone on memory upsert 2026-03-26 21:06:16 -05:00
Tak Hoffman
708b9339a5 fix: assign reset transcript paths to new sessions 2026-03-26 21:05:21 -05:00
Peter Steinberger
14b3360c22 chore: bump versions to 2026.3.26 2026-03-27 02:03:22 +00:00
Peter Steinberger
7a35bca2ec refactor: make memory embedding adapters generic 2026-03-27 02:02:24 +00:00
Peter Steinberger
42be3fb059 refactor: collapse manifest contract mirrors 2026-03-27 02:01:59 +00:00
Peter Steinberger
b666ce692f refactor: extract openai ws replay helpers 2026-03-27 02:00:51 +00:00
Peter Steinberger
60a8dd95de refactor: split compaction safeguard quality helpers 2026-03-27 02:00:09 +00:00
Peter Steinberger
40bd36e35d refactor: move channel config metadata into plugin-owned manifests 2026-03-27 01:59:30 +00:00
Tak Hoffman
e1ff753790 fix: persist create transcript paths in session entries 2026-03-26 20:54:15 -05:00
Peter Steinberger
ca01595699 refactor: split tool display exec parsing 2026-03-27 01:50:32 +00:00
Peter Steinberger
d7b61228e2 fix: tighten openai ws reasoning replay (#53856) 2026-03-27 01:49:55 +00:00
Peter Steinberger
ad21d84940 docs: add apply_patch changelog note 2026-03-27 01:46:30 +00:00
Peter Steinberger
ab6ddf7245 refactor: slim plugin sdk provider entrypoints 2026-03-27 01:45:53 +00:00
Peter Steinberger
485bfe95ed fix: clean bundled contract metadata follow-ups 2026-03-27 01:45:53 +00:00
Peter Steinberger
ba7804df50 refactor: derive bundled contracts from extension manifests 2026-03-27 01:45:52 +00:00
Peter Steinberger
b75be09144 refactor: split subagent announce delivery helpers 2026-03-27 01:44:59 +00:00
Tak Hoffman
320c0d65a1 fix: keep session settings in chat lifecycle events 2026-03-26 20:38:35 -05:00
Peter Steinberger
3fdd7c9e00 refactor: split compaction hooks 2026-03-27 01:36:13 +00:00
Peter Steinberger
f862685ed8 refactor: split subagent registry lifecycle 2026-03-27 01:33:13 +00:00
MiloStack
b33ad4d7cb fix: guarantee heartbeat timer re-arm with try/finally (#52270)
Merged via squash.

Prepared head SHA: cd0bcc2fb8
Co-authored-by: MiloStack <265805734+MiloStack@users.noreply.github.com>
Co-authored-by: grp06 <1573959+grp06@users.noreply.github.com>
Reviewed-by: @grp06
2026-03-26 18:29:33 -07:00
Peter Steinberger
cfbef8035d refactor: split subagent run manager 2026-03-27 01:26:07 +00:00
Peter Steinberger
18dc98b00e refactor: split embedded run auth controller 2026-03-27 01:21:10 +00:00
Peter Steinberger
f3b152e0d9 refactor: split channel setup into shared flow modules 2026-03-27 01:17:39 +00:00
Peter Steinberger
7d6d642cb8 refactor: move doctor orchestration into flow contributions 2026-03-27 01:17:39 +00:00
Peter Steinberger
23aded30d8 refactor: add provider and search flow contributions 2026-03-27 01:17:39 +00:00
Peter Steinberger
5e35e6a95f fix: lazy-load zca-js at the zalouser runtime boundary 2026-03-27 01:14:42 +00:00
Peter Steinberger
b9c60fd37a fix: default and gate apply_patch like write 2026-03-27 01:14:42 +00:00
Tak Hoffman
c326083ad8 fix: keep session settings in gateway live events 2026-03-26 20:11:13 -05:00
Tak Hoffman
48ff617169 fix: keep fast mode in gateway session rows 2026-03-26 20:11:13 -05:00
Peter Steinberger
ba60154826 fix: unify upload-file message actions 2026-03-27 01:04:01 +00:00
Peter Steinberger
046a950877 refactor: split agent command execution helpers 2026-03-27 01:02:40 +00:00
Peter Steinberger
6fd1725a06 fix: preserve ws reasoning replay (#53856) (thanks @xujingchen1996) 2026-03-26 17:53:59 -07:00
scoootscooob
cc359d4c9d fix: use runtime model and per-agent thinking defaults in status (thanks @scoootscooob, @xaeon2026, @ysfbsf) (#55425)
Merged via squash.

Prepared head SHA: 061d7c7ac0
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Co-authored-by: scoootscooob <167050519+scoootscooob@users.noreply.github.com>
Reviewed-by: @scoootscooob
2026-03-26 17:49:21 -07:00
Peter Steinberger
c9556c257e docs: clarify memory plugin adapter ids 2026-03-27 00:47:01 +00:00
Peter Steinberger
dbf78de7c6 refactor: move memory engine behind plugin adapters 2026-03-27 00:47:01 +00:00
Peter Steinberger
aed6283faa refactor: split embedded run setup helpers 2026-03-27 00:46:01 +00:00
Peter Steinberger
d4da878d64 fix: preserve plugin sdk api baseline source link 2026-03-27 00:44:24 +00:00
Peter Steinberger
7de494fcec test: stabilize discord monitor ci isolation 2026-03-27 00:44:24 +00:00
Peter Steinberger
89e6b91b89 fix: decouple moonshot stream wrappers from provider runtime 2026-03-27 00:40:51 +00:00
Peter Steinberger
770c462c47 refactor: split subagent registry helpers 2026-03-27 00:35:41 +00:00
R. Desmond
fa0835dd32 test(agents): cover undersized model dispatch guard (#55369) 2026-03-26 20:30:20 -04:00
Peter Steinberger
5a98a1dbe2 refactor: split embedded run helpers 2026-03-27 00:27:49 +00:00
Peter Steinberger
540b98b23f refactor: split subagent announce output helpers 2026-03-27 00:23:32 +00:00
Peter Steinberger
0d9e4f20d5 refactor: split embedded attempt helpers 2026-03-27 00:23:32 +00:00
Peter Steinberger
48ae976333 refactor: split cli runner pipeline 2026-03-27 00:19:24 +00:00
Peter Steinberger
4329c93f85 test: wire discord monitor runtime seams 2026-03-27 00:05:49 +00:00
Craig Allan-McWilliams
984f98be95 Fix: treat HTTP 500 as a transient failover error (#55332)
HTTP 500 (Internal Server Error) was not triggering model fallback,
causing agents to fail outright instead of trying the next candidate.
This is inconsistent with TRANSIENT_HTTP_ERROR_CODES which already
includes 500. Aligns the direct status check with that constant.

Co-authored-by: Craig McWilliams <craigamcw@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:05:15 -04:00
Peter Steinberger
da845ce598 test: align discord lifecycle shutdown expectations 2026-03-27 00:03:00 +00:00
Peter Steinberger
2f43c6b334 refactor: split discord monitor startup and lifecycle 2026-03-27 00:03:00 +00:00
rustam
e3cd209889 chore: remove stale README-header.png packlist entry (#55325) 2026-03-26 16:59:36 -07:00
Coy Geek
8e285d112d fix(cr-mbx-feishu-encryptkey-config-redaction-bypass): apply security fix (#53414)
Generated by staged fix workflow.
2026-03-26 19:58:37 -04:00
Saurabh
afc649255c fix: match guild-level entries in Discord exec allowlist (#55175) 2026-03-26 16:56:58 -07:00
Peter Steinberger
bdeb7d859b test: stabilize discord monitor ci mocks 2026-03-26 23:54:59 +00:00
Marko Jak
b8ff152a98 fix(cli): isolate claude MCP config 2026-03-26 16:52:17 -07:00
Peter Steinberger
85b169c453 fix: clamp copilot auth refresh overflow (#55360) (thanks @michael-abdo) 2026-03-26 16:48:06 -07:00
Peter Steinberger
f0c1057f68 fix: restore reveal-to-edit raw config flow 2026-03-26 23:45:10 +00:00
Peter Steinberger
d25c4fd6c5 test: tighten discord lifecycle gateway mocks 2026-03-26 23:44:43 +00:00
Peter Steinberger
4726593d6d test: refresh planner batching expectations 2026-03-26 23:44:43 +00:00
Peter Steinberger
96a44d979d fix: route memory test harness through plugin sdk 2026-03-26 23:44:43 +00:00
felear2022
623f4d3056 fix: use stream-json output for Claude CLI backend to prevent watchdog timeouts
The Claude CLI backend uses `--output-format json`, which produces no
stdout until the entire request completes. When session context is large
(100K+ tokens) or API response is slow, the no-output watchdog timer
(max 180s for resume sessions) kills the process before it finishes,
resulting in "CLI produced no output for 180s and was terminated" errors.

Switch to `--output-format stream-json --verbose` so Claude CLI emits
NDJSON events throughout processing (init, assistant, rate_limit, result).
Each event resets the watchdog timer, which is the intended behavior —
the watchdog detects truly stuck processes, not slow-but-progressing ones.

Changes:
- cli-backends.ts: `json` → `stream-json --verbose`, `output: "jsonl"`
- helpers.ts: teach parseCliJsonl to extract text from Claude's
  `{"type":"result","result":"..."}` NDJSON line

Note: `--verbose` is required for stream-json in `-p` (print) mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:39:15 -07:00
kenantan32
4ad7d51c01 fix: preserve CLI session ID in text output mode
When the CLI backend output mode is "text", sessionId was hardcoded to
undefined. This caused the fallback chain to store the OpenClaw internal
UUID as the CLI session ID. On resume, --resume was called with the
wrong UUID, resulting in "No conversation found with session ID".

Return resolvedSessionId instead of undefined so the correct CLI session
ID is persisted and resume works correctly.
2026-03-26 16:34:02 -07:00
Peter Steinberger
e46e655451 test: restore memory test seams (#55324) (thanks @joelnishanth) 2026-03-26 16:33:43 -07:00
joelnishanth
5b85d0efa4 discord: fix stale-socket reconnect crash from uncaught reconnect-exhausted error 2026-03-26 16:33:43 -07:00
dhi13man
9f8c4efa9b fix(agents): use claude-cli backend in tools-disabled regression test
codex-cli lacks systemPromptArg, so the system prompt is never
serialized into argv — making the not-toContain assertion pass
vacuously even on pre-fix code. Switch to claude-cli which defines
systemPromptArg ("--append-system-prompt") and add a positive
assertion that the user-supplied prompt IS present in argv.

Co-Authored-By: Dhiman's Agentic Suite <dhiman.seal@hotmail.com>
2026-03-26 16:30:19 -07:00
dhi13man
99f0ea8d43 fix(agents): remove unconditional "Tools are disabled" prompt injection in CLI runner
`runCliAgent()` unconditionally appended "Tools are disabled in this
session. Do not call tools." to `extraSystemPrompt` for every CLI
backend session. The intent was to prevent the LLM from calling
OpenClaw's embedded API tools (since CLI backends manage their own
tools natively). However, CLI agents like Claude Code interpret this
text as a blanket prohibition on ALL tools, including their own native
Bash, Read, and Write tools.

This caused silent failures across cron jobs, group chats, and DM
sessions when using any CLI backend: the agent would see the injected
text in the system prompt and refuse to execute tools, returning text
responses instead. Cron jobs reported `lastStatus: "ok"` despite the
agent failing to run scripts.

The fix removes the hardcoded string entirely. CLI backends already
receive `tools: []` (no OpenClaw embedded tools in the API call), so
the text was redundant at best.

Closes #44135

Co-Authored-By: Dhiman's Agentic Suite <dhiman.seal@hotmail.com>
2026-03-26 16:30:19 -07:00
Peter Steinberger
16565020a1 refactor: finish browser test path cleanup 2026-03-26 23:28:46 +00:00
Peter Steinberger
0ef2a9c8b5 refactor: remove core browser test duplicates 2026-03-26 23:28:34 +00:00
Peter Steinberger
9a7ceceffa refactor: move browser tests into plugin 2026-03-26 23:26:37 +00:00
Peter Steinberger
22348914cf refactor: centralize discord gateway ownership 2026-03-26 23:25:27 +00:00
Peter Steinberger
01bcbcf8d5 refactor: require legacy config migration on read 2026-03-26 23:23:47 +00:00
Peter Steinberger
cad83db8b2 refactor: move memory engine into memory plugin 2026-03-26 23:20:35 +00:00
Peter Steinberger
0e182dd3e1 refactor: share top-level setup dm policies 2026-03-26 23:20:26 +00:00
Peter Steinberger
abf95c5f99 refactor: share build copy script helpers 2026-03-26 23:20:26 +00:00
Peter Steinberger
0106b0488a refactor: share config section card rendering 2026-03-26 23:20:26 +00:00
Peter Steinberger
4890656d9d refactor: share matrix state file path helper 2026-03-26 23:20:26 +00:00
Peter Steinberger
bfad32aa16 refactor: share directory config listers 2026-03-26 23:20:26 +00:00
Peter Steinberger
4151b48d6c style(browser): format profiles service test 2026-03-26 23:18:57 +00:00
Peter Steinberger
8eeccb116d test(planner): refresh extension batch expectations 2026-03-26 23:16:22 +00:00
4010 changed files with 212401 additions and 89255 deletions

View File

@@ -45,6 +45,7 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- The macOS smoke should include a dashboard load phase after gateway health: resolve the tokenized URL with `openclaw dashboard --no-open`, verify the served HTML contains the Control UI title/root shell, then open Safari and require an established localhost TCP connection from Safari to the gateway port.
- `prlctl exec` is fine for deterministic repo commands, but use the guest Terminal or `prlctl enter` when installer parity or shell-sensitive behavior matters.
- Multi-word `openclaw agent --message ...` checks should go through a guest shell wrapper (`guest_current_user_sh` / `guest_current_user_cli` or `/bin/sh -lc ...`), not raw `prlctl exec ... node openclaw.mjs ...`, or the message can be split into extra argv tokens and Commander reports `too many arguments for 'agent'`.
- When ref-mode onboarding stores `OPENAI_API_KEY` as an env secret ref, the post-onboard agent verification should also export `OPENAI_API_KEY` for the guest command. The gateway can still reject with pairing-required and fall back to embedded execution, and that fallback needs the env-backed credential available in the shell.
- On the fresh Tahoe snapshot, `brew` exists but `node` may be missing from PATH in noninteractive exec. Use `/opt/homebrew/bin/node` when needed.
- Fresh host-served tgz installs should install as guest root with `HOME=/var/root`, then run onboarding as the desktop user via `prlctl exec --current-user`.
- Root-installed tgz smoke can log plugin blocks for world-writable `extensions/*`; do not treat that as an onboarding or gateway failure unless plugin loading is the task.
@@ -59,6 +60,9 @@ Use this skill for Parallels guest workflows and smoke interpretation. Do not lo
- Multi-word `openclaw agent --message ...` checks should call `& $openclaw ...` inside PowerShell, not `Start-Process ... -ArgumentList` against `openclaw.cmd`, or Commander can see split argv and throw `too many arguments for 'agent'`.
- Windows installer/tgz phases now retry once after guest-ready recheck; keep new Windows smoke steps idempotent so a transport-flake retry is safe.
- Windows global `npm install -g` phases can stay quiet for a minute or more even when healthy; inspect the phase log before calling it hung, and only treat it as a regression once the retry wrapper or timeout trips.
- Fresh Windows ref-mode onboard should use the same background PowerShell runner plus done-file/log-drain pattern as the npm-update helper, including startup materialization checks, host-side timeouts on short poll `prlctl exec` calls, and retry-on-poll-failure behavior for transient transport flakes.
- Fresh Windows ref-mode agent verification should set `OPENAI_API_KEY` in the PowerShell environment before invoking `openclaw.cmd agent`, for the same pairing-required fallback reason as macOS.
- The Windows upgrade smoke lane should restart the managed gateway after `upgrade.install-main` and before `upgrade.onboard-ref`, or the old process can keep the previous gateway token and fail `gateway-health` with `unauthorized: gateway token mismatch`.
- Keep onboarding and status output ASCII-clean in logs; fancy punctuation becomes mojibake in current capture paths.
- If you hit an older run with `rc=255` plus an empty `fresh.install-main.log` or `upgrade.install-main.log`, treat it as a likely `prlctl exec` transport drop after guest start-up, not immediate proof of an npm/package failure.

View File

@@ -17,7 +17,7 @@ Use this skill for release and publish-time workflow. Keep ordinary development
## Keep release channel naming aligned
- `stable`: tagged releases only, with npm dist-tag `latest`
- `stable`: tagged releases only, published to npm `latest` and then mirrored onto npm `beta` unless `beta` already points at a newer prerelease
- `beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
- `dev`: moving head on `main`

View File

@@ -66,17 +66,19 @@ jobs:
run: pnpm canvas:a2ui:bundle
- name: Upload A2UI bundle artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: canvas-a2ui-bundle
path: src/canvas-host/a2ui/
include-hidden-files: true
retention-days: 1
bun-checks:
name: ${{ matrix.check_name }}
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) }}

View File

@@ -35,7 +35,6 @@ jobs:
has_changed_extensions: ${{ steps.manifest.outputs.has_changed_extensions }}
changed_extensions_matrix: ${{ steps.manifest.outputs.changed_extensions_matrix }}
run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }}
run_release_check: ${{ steps.manifest.outputs.run_release_check }}
run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }}
checks_fast_matrix: ${{ steps.manifest.outputs.checks_fast_matrix }}
run_checks: ${{ steps.manifest.outputs.run_checks }}
@@ -278,40 +277,12 @@ jobs:
include-hidden-files: true
retention-days: 1
# Validate npm pack contents after build (only on push to main, not PRs).
release-check:
needs: [preflight, build-artifacts]
if: needs.preflight.outputs.run_release_check == 'true'
runs-on: blacksmith-16vcpu-ubuntu-2404
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
use-sticky-disk: "false"
- name: Download dist artifact
uses: actions/download-artifact@v8
with:
name: dist-build
path: dist/
- name: Check release contents
run: pnpm release:check
checks-fast:
name: ${{ matrix.check_name }}
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) }}
@@ -339,6 +310,7 @@ jobs:
pnpm test:extensions
;;
contracts|contracts-protocol)
pnpm build
pnpm test:contracts
pnpm protocol:check
;;
@@ -353,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) }}
@@ -432,8 +404,6 @@ jobs:
node openclaw.mjs --help
node openclaw.mjs status --json --timeout 1
pnpm test:build:singleton
node scripts/stage-bundled-plugin-runtime-deps.mjs
node --import tsx scripts/release-check.ts
;;
*)
echo "Unsupported checks task: $TASK" >&2
@@ -446,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) }}
@@ -518,6 +488,51 @@ jobs:
continue-on-error: true
run: pnpm run lint:plugins:no-extension-imports
- name: Run no-random-messaging guard
id: no_random_messaging
continue-on-error: true
run: pnpm run lint:tmp:no-random-messaging
- name: Run channel-agnostic boundary guard
id: channel_agnostic_boundaries
continue-on-error: true
run: pnpm run lint:tmp:channel-agnostic-boundaries
- name: Run no-raw-channel-fetch guard
id: no_raw_channel_fetch
continue-on-error: true
run: pnpm run lint:tmp:no-raw-channel-fetch
- name: Run ingress owner guard
id: ingress_owner
continue-on-error: true
run: pnpm run lint:agent:ingress-owner
- name: Run no-register-http-handler guard
id: no_register_http_handler
continue-on-error: true
run: pnpm run lint:plugins:no-register-http-handler
- name: Run no-monolithic plugin-sdk entry import guard
id: no_monolithic_plugin_sdk_entry_imports
continue-on-error: true
run: pnpm run lint:plugins:no-monolithic-plugin-sdk-entry-imports
- name: Run no-extension-src-imports guard
id: no_extension_src_imports
continue-on-error: true
run: pnpm run lint:plugins:no-extension-src-imports
- name: Run no-extension-test-core-imports guard
id: no_extension_test_core_imports
continue-on-error: true
run: pnpm run lint:plugins:no-extension-test-core-imports
- name: Run plugin-sdk subpaths exported guard
id: plugin_sdk_subpaths_exported
continue-on-error: true
run: pnpm run lint:plugins:plugin-sdk-subpaths-exported
- name: Run web search provider boundary guard
id: web_search_provider_boundary
continue-on-error: true
@@ -533,6 +548,11 @@ jobs:
continue-on-error: true
run: pnpm run lint:extensions:no-plugin-sdk-internal
- name: Run extension relative-outside-package guard
id: extension_relative_outside_package_boundary
continue-on-error: true
run: pnpm run lint:extensions:no-relative-outside-package
- name: Enforce safe external URL opening policy
id: no_raw_window_open
continue-on-error: true
@@ -543,16 +563,6 @@ jobs:
continue-on-error: true
run: pnpm test:gateway:watch-regression
- name: Check config docs drift statefile
id: config_docs_drift
continue-on-error: true
run: pnpm config:docs:check
- name: Check plugin SDK API baseline drift
id: plugin_sdk_api_drift
continue-on-error: true
run: pnpm plugin-sdk:api:check
- name: Upload gateway watch regression artifacts
if: always()
uses: actions/upload-artifact@v7
@@ -565,24 +575,40 @@ jobs:
if: always()
env:
PLUGIN_EXTENSION_BOUNDARY_OUTCOME: ${{ steps.plugin_extension_boundary.outcome }}
NO_RANDOM_MESSAGING_OUTCOME: ${{ steps.no_random_messaging.outcome }}
CHANNEL_AGNOSTIC_BOUNDARIES_OUTCOME: ${{ steps.channel_agnostic_boundaries.outcome }}
NO_RAW_CHANNEL_FETCH_OUTCOME: ${{ steps.no_raw_channel_fetch.outcome }}
INGRESS_OWNER_OUTCOME: ${{ steps.ingress_owner.outcome }}
NO_REGISTER_HTTP_HANDLER_OUTCOME: ${{ steps.no_register_http_handler.outcome }}
NO_MONOLITHIC_PLUGIN_SDK_ENTRY_IMPORTS_OUTCOME: ${{ steps.no_monolithic_plugin_sdk_entry_imports.outcome }}
NO_EXTENSION_SRC_IMPORTS_OUTCOME: ${{ steps.no_extension_src_imports.outcome }}
NO_EXTENSION_TEST_CORE_IMPORTS_OUTCOME: ${{ steps.no_extension_test_core_imports.outcome }}
PLUGIN_SDK_SUBPATHS_EXPORTED_OUTCOME: ${{ steps.plugin_sdk_subpaths_exported.outcome }}
WEB_SEARCH_PROVIDER_BOUNDARY_OUTCOME: ${{ steps.web_search_provider_boundary.outcome }}
EXTENSION_SRC_OUTSIDE_PLUGIN_SDK_BOUNDARY_OUTCOME: ${{ steps.extension_src_outside_plugin_sdk_boundary.outcome }}
EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME: ${{ steps.extension_plugin_sdk_internal_boundary.outcome }}
EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME: ${{ steps.extension_relative_outside_package_boundary.outcome }}
NO_RAW_WINDOW_OPEN_OUTCOME: ${{ steps.no_raw_window_open.outcome }}
GATEWAY_WATCH_REGRESSION_OUTCOME: ${{ steps.gateway_watch_regression.outcome }}
CONFIG_DOCS_DRIFT_OUTCOME: ${{ steps.config_docs_drift.outcome }}
PLUGIN_SDK_API_DRIFT_OUTCOME: ${{ steps.plugin_sdk_api_drift.outcome }}
run: |
failures=0
for result in \
"plugin-extension-boundary|$PLUGIN_EXTENSION_BOUNDARY_OUTCOME" \
"lint:tmp:no-random-messaging|$NO_RANDOM_MESSAGING_OUTCOME" \
"lint:tmp:channel-agnostic-boundaries|$CHANNEL_AGNOSTIC_BOUNDARIES_OUTCOME" \
"lint:tmp:no-raw-channel-fetch|$NO_RAW_CHANNEL_FETCH_OUTCOME" \
"lint:agent:ingress-owner|$INGRESS_OWNER_OUTCOME" \
"lint:plugins:no-register-http-handler|$NO_REGISTER_HTTP_HANDLER_OUTCOME" \
"lint:plugins:no-monolithic-plugin-sdk-entry-imports|$NO_MONOLITHIC_PLUGIN_SDK_ENTRY_IMPORTS_OUTCOME" \
"lint:plugins:no-extension-src-imports|$NO_EXTENSION_SRC_IMPORTS_OUTCOME" \
"lint:plugins:no-extension-test-core-imports|$NO_EXTENSION_TEST_CORE_IMPORTS_OUTCOME" \
"lint:plugins:plugin-sdk-subpaths-exported|$PLUGIN_SDK_SUBPATHS_EXPORTED_OUTCOME" \
"web-search-provider-boundary|$WEB_SEARCH_PROVIDER_BOUNDARY_OUTCOME" \
"extension-src-outside-plugin-sdk-boundary|$EXTENSION_SRC_OUTSIDE_PLUGIN_SDK_BOUNDARY_OUTCOME" \
"extension-plugin-sdk-internal-boundary|$EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME" \
"extension-relative-outside-package-boundary|$EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME" \
"lint:ui:no-raw-window-open|$NO_RAW_WINDOW_OPEN_OUTCOME" \
"gateway-watch-regression|$GATEWAY_WATCH_REGRESSION_OUTCOME" \
"config-docs-drift|$CONFIG_DOCS_DRIFT_OUTCOME" \
"plugin-sdk-api-drift|$PLUGIN_SDK_API_DRIFT_OUTCOME"; do
"gateway-watch-regression|$GATEWAY_WATCH_REGRESSION_OUTCOME"; do
name="${result%%|*}"
outcome="${result#*|}"
if [ "$outcome" != "success" ]; then
@@ -690,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.

View File

@@ -58,6 +58,12 @@ jobs:
RELEASE_TAG: ${{ inputs.tag }}
run: gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null
- name: Build
run: pnpm build
- name: Build Control UI
run: pnpm ui:build
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}

View File

@@ -52,19 +52,6 @@ jobs:
install-bun: "false"
use-sticky-disk: "false"
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
run: |
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
# Fetch the full main ref so merge-base ancestry checks keep working
# for older tagged commits that are still contained in main.
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
pnpm release:openclaw:npm:check
- name: Ensure version is not already published
env:
PREFLIGHT_ONLY: ${{ inputs.preflight_only }}
@@ -89,6 +76,22 @@ jobs:
- name: Build
run: pnpm build
- name: Build Control UI
run: pnpm ui:build
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
RELEASE_MAIN_REF: origin/main
run: |
set -euo pipefail
RELEASE_SHA=$(git rev-parse HEAD)
export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF
# Fetch the full main ref so merge-base ancestry checks keep working
# for older tagged commits that are still contained in main.
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
pnpm release:openclaw:npm:check
- name: Verify release contents
run: pnpm release:check
@@ -142,6 +145,24 @@ jobs:
install-bun: "false"
use-sticky-disk: "false"
- name: Ensure version is not already published
run: |
set -euo pipefail
PACKAGE_VERSION=$(node -p "require('./package.json').version")
if npm view "openclaw@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
echo "openclaw@${PACKAGE_VERSION} is already published on npm."
exit 1
fi
echo "Publishing openclaw@${PACKAGE_VERSION}"
- name: Build
run: pnpm build
- name: Build Control UI
run: pnpm ui:build
- name: Validate release tag and package metadata
env:
RELEASE_TAG: ${{ inputs.tag }}
@@ -155,17 +176,5 @@ jobs:
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main
pnpm release:openclaw:npm:check
- name: Ensure version is not already published
run: |
set -euo pipefail
PACKAGE_VERSION=$(node -p "require('./package.json').version")
if npm view "openclaw@${PACKAGE_VERSION}" version >/dev/null 2>&1; then
echo "openclaw@${PACKAGE_VERSION} is already published on npm."
exit 1
fi
echo "Publishing openclaw@${PACKAGE_VERSION}"
- name: Publish
run: bash scripts/openclaw-npm-publish.sh --publish

View File

@@ -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

4
.gitignore vendored
View File

@@ -85,6 +85,7 @@ apps/ios/*.mobileprovision
# Local untracked files
.local/
docs/.local/
docs/internal/
tmp/
IDENTITY.md
USER.md
@@ -137,3 +138,6 @@ docs/superpowers
# Deprecated changelog fragment workflow
changelog/fragments/
# Local scratch workspace
.tmp/

View File

@@ -1,6 +1,11 @@
{
"globs": ["docs/**/*.md", "docs/**/*.mdx", "README.md"],
"ignores": ["docs/zh-CN/**", "docs/.i18n/**", "docs/reference/templates/**", "**/.local/**"],
"ignores": [
"docs/zh-CN/**",
"docs/.i18n/**",
"docs/reference/templates/**",
"**/.local/**"
],
"config": {
"default": true,

View File

@@ -1,2 +1,3 @@
**/node_modules/
**/.runtime-deps-*/
docs/.generated/

View File

@@ -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,17 +9,59 @@
- 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:
- 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:
- bundled-plugin-tree `AGENTS.md`
- `src/plugin-sdk/AGENTS.md`
- `src/channels/AGENTS.md`
- `src/plugins/AGENTS.md`
- `src/gateway/protocol/AGENTS.md`
- Plugin and extension boundary:
- 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 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`
- Definition files: `src/channels/plugins/types.plugin.ts`, `src/channels/plugins/types.core.ts`, `src/channels/plugins/types.adapters.ts`, `src/plugin-sdk/core.ts`, `src/plugin-sdk/channel-contract.ts`
- Rule: `src/channels/**` is core implementation. If plugin authors need a new seam, add it to the Plugin SDK instead of telling them to import channel internals.
- Provider/model boundary:
- Public docs: `docs/plugins/sdk-provider-plugins.md`, `docs/concepts/model-providers.md`, `docs/plugins/architecture.md`
- Definition files: `src/plugins/types.ts`, `src/plugin-sdk/provider-entry.ts`, `src/plugin-sdk/provider-auth.ts`, `src/plugin-sdk/provider-catalog-shared.ts`, `src/plugin-sdk/provider-model-shared.ts`
- Rule: core owns the generic inference loop; provider plugins own provider-specific behavior through registration and typed hooks. Do not solve provider needs by reaching into unrelated core internals.
- Rule: avoid ad hoc reads of `plugins.entries.<id>.config` from unrelated core code. If core needs plugin-owned auth/config behavior, add or use a generic seam (`resolveSyntheticAuth`, public SDK/helper facades, manifest metadata, plugin auto-enable hooks) and honor plugin disablement plus SecretRef semantics.
- Rule: vendor-owned tools and settings belong in the owning plugin. Do not add provider-specific tool config, secret collection, or runtime enablement to core `tools.*` surfaces unless the tool is intentionally core-owned.
- Gateway protocol boundary:
- Public docs: `docs/gateway/protocol.md`, `docs/gateway/bridge-protocol.md`, `docs/concepts/architecture.md`
- Definition files: `src/gateway/protocol/schema.ts`, `src/gateway/protocol/schema/*.ts`, `src/gateway/protocol/index.ts`
- 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/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 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)
@@ -60,7 +102,8 @@
- Runtime baseline: Node **22+** (keep Node + Bun paths working).
- Install deps: `pnpm install`
- If deps are missing (for example `node_modules` missing, `vitest not found`, or `command not found`), run the repos package-manager install command (prefer lockfile/README-defined PM), then rerun the exact requested command once. Apply this to test/build/lint/typecheck/dev commands; if retry still fails, report the command and first actionable error.
- Pre-commit hooks: `prek install` (runs same checks as CI)
- Pre-commit hooks: `prek install`. The hook runs the repo verification flow, including `pnpm check`.
- `FAST_COMMIT=1` skips the repo-wide `pnpm format` and `pnpm check` inside the pre-commit hook only. Use it when you intentionally want a faster commit path and are running equivalent targeted verification manually. It does not change CI and does not change what `pnpm check` itself does.
- Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches).
- Prefer Bun for TypeScript execution (scripts, dev, tests): `bun <file.ts>` / `bunx <tool>`.
- Run CLI in dev: `pnpm openclaw ...` (bun) or `pnpm dev`.
@@ -71,16 +114,28 @@
- Lint/format: `pnpm check`
- Format check: `pnpm format` (oxfmt --check)
- Format fix: `pnpm format:fix` (oxfmt --write)
- Terminology:
- "gate" means a verification command or command set that must be green for the decision you are making.
- A local dev gate is the fast default loop, usually `pnpm check` plus any scoped test you actually need.
- A landing gate is the broader bar before pushing `main`, usually `pnpm check`, `pnpm test`, and `pnpm build` when the touched surface can affect build output, packaging, lazy-loading/module boundaries, or published surfaces.
- A CI gate is whatever the relevant workflow enforces for that lane (for example `check`, `check-additional`, `build-smoke`, or release validation).
- Local dev gate: prefer `pnpm check` for the normal edit loop. It keeps the repo-architecture policy guards out of the default local loop.
- CI architecture gate: `check-additional` enforces architecture and boundary policy guards that are intentionally kept out of the default local loop.
- Formatting gate: the pre-commit hook runs `pnpm format` before `pnpm check`. If you want a formatting-only preflight locally, run `pnpm format` explicitly.
- If you need a fast commit loop, `FAST_COMMIT=1 git commit ...` skips the hooks repo-wide `pnpm format` and `pnpm check`; use that only when you are deliberately covering the touched surface some other way.
- Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage`
- Generated baseline artifacts live together under `docs/.generated/`.
- Config schema drift uses `pnpm config:docs:gen` / `pnpm config:docs:check`.
- Plugin SDK API drift uses `pnpm plugin-sdk:api:gen` / `pnpm plugin-sdk:api:check`.
- If you change config schema/help or the public Plugin SDK surface, update the matching baseline artifact and keep the two drift-check flows adjacent in scripts/workflows/docs guidance rather than inventing a third pattern.
- For narrowly scoped changes, prefer narrowly scoped tests that directly validate the touched behavior. If no meaningful scoped test exists, say so explicitly and use the next most direct validation available.
- Preferred landing bar for pushes to `main`: `pnpm check` and `pnpm test`, with a green result when feasible.
- Verification modes for work on `main`:
- Default mode: `main` is relatively stable. Count pre-commit hook coverage when it already verified the current tree, avoid rerunning the exact same checks just for ceremony, and prefer keeping CI/main green before landing.
- Fast-commit mode: `main` is moving fast and you intentionally optimize for shorter commit loops. Prefer explicit local verification close to the final landing point, and it is acceptable to use `--no-verify` for intermediate or catch-up commits after equivalent checks have already run locally.
- Preferred landing bar for pushes to `main`: in Default mode, favor `pnpm check` and `pnpm test` near the final rebase/push point when feasible. In fast-commit mode, verify the touched surface locally near landing without insisting every intermediate commit replay the full hook.
- Scoped tests prove the change itself. `pnpm test` remains the default `main` landing bar; scoped tests do not replace full-suite gates by default.
- Hard gate: if the change can affect build output, packaging, lazy-loading/module boundaries, or published surfaces, `pnpm build` MUST be run and MUST pass before pushing `main`.
- Default rule: do not commit or push with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface.
- Default rule: do not land changes with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface. Fast-commit mode changes how verification is sequenced; it does not lower the requirement to validate and clean up the touched surface before final landing.
- For narrowly scoped changes, if unrelated failures already exist on latest `origin/main`, state that clearly, report the scoped tests you ran, and ask before broadening scope into unrelated fixes or landing despite those failures.
- Do not use scoped tests as permission to ignore plausibly related failures.
@@ -88,11 +143,19 @@
- Language: TypeScript (ESM). Prefer strict typing; avoid `any`.
- Formatting/linting via Oxlint and Oxfmt.
- Never add `@ts-nocheck` and do not disable `no-explicit-any`; fix root causes and update Oxlint/Oxfmt config only when required.
- 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.
@@ -122,6 +185,7 @@
- Keep Vitest on `forks` only. Do not introduce or reintroduce any non-`forks` Vitest pool or alternate execution mode in configs, wrapper scripts, or default test commands without explicit approval in this chat. This includes `threads`, `vmThreads`, `vmForks`, and any future/nonstandard pool variant.
- If local Vitest runs cause memory pressure, the wrapper now derives budgets from host capabilities (CPU, memory band, current load). For a conservative explicit override during land/gate runs, use `OPENCLAW_TEST_PROFILE=serial OPENCLAW_TEST_SERIAL_GATEWAY=1 pnpm test`.
- Live tests (real keys): `OPENCLAW_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- `pnpm test:live` defaults quiet now. Keep `[live]` progress; suppress profile/gateway chatter. Full logs: `OPENCLAW_LIVE_TEST_QUIET=0 pnpm test:live`.
- Full kit + whats covered: `docs/help/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Changelog placement: in the active version block, append new entries to the end of the target section (`### Changes` or `### Fixes`); do not insert new entries at the top of a section.
@@ -196,6 +260,7 @@
- Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default.
- **Multi-agent safety:** do **not** create/apply/drop `git stash` entries unless explicitly requested (this includes `git pull --rebase --autostash`). Assume other agents may be working; keep unrelated WIP untouched and avoid cross-cutting state changes.
- **Multi-agent safety:** when the user says "push", you may `git pull --rebase` to integrate latest changes (never discard other agents' work). When the user says "commit", scope to your changes only. When the user says "commit all", commit everything in grouped chunks.
- **Multi-agent safety:** prefer grouped `commit` / `pull --rebase` / `push` cycles for related work instead of many tiny syncs.
- **Multi-agent safety:** do **not** create/remove/modify `git worktree` checkouts (or edit `.worktrees/*`) unless explicitly requested.
- **Multi-agent safety:** do **not** switch branches / check out a different branch unless explicitly requested.
- **Multi-agent safety:** running multiple agents is OK as long as each agent has its own session.

File diff suppressed because it is too large Load Diff

View File

@@ -94,6 +94,7 @@ Welcome to the lobster tank! 🦞
- `pnpm test:extension --list` to see valid extension ids
- If you changed shared plugin or channel surfaces, run `pnpm test:contracts`
- For targeted shared-surface work, use `pnpm test:contracts:channels` or `pnpm test:contracts:plugins`
- These commands also cover the shared seam/smoke files that the default unit lane skips
- If you changed broader runtime behavior, still run the relevant wider lanes (`pnpm test:extensions`, `pnpm test:channels`, or `pnpm test`) before asking for review
- If you have access to Codex, run `codex review --base origin/main` locally before opening or updating your PR. Treat this as the current highest standard of AI review, even if GitHub Codex review also runs.
- Do not submit refactor-only PRs unless a maintainer explicitly requested that refactor for an active fix or deliverable.

View File

@@ -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.
@@ -61,8 +64,9 @@ WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
COPY ui/package.json ./ui/package.json
COPY patches ./patches
COPY scripts/postinstall-bundled-plugins.mjs ./scripts/postinstall-bundled-plugins.mjs
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 +77,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 +118,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 +153,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

View File

@@ -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 \

View File

@@ -101,7 +101,7 @@ OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boun
- If multiple users need OpenClaw, use one VPS (or host/OS user boundary) per user.
- For advanced setups, multiple gateways on one machine are possible, but only with strict isolation and are not the recommended default.
- Exec behavior is host-first by default: `agents.defaults.sandbox.mode` defaults to `off`.
- `tools.exec.host` defaults to `sandbox` as a routing preference, but if sandbox runtime is not active for the session, exec runs on the gateway host.
- `tools.exec.host` defaults to `auto`: sandbox when sandbox runtime is active for the session, otherwise gateway.
- Implicit exec calls (no explicit host in the tool call) follow the same behavior.
- This is expected in OpenClaw's one-user trusted-operator model. If you need isolation, enable sandbox mode (`non-main`/`all`) and keep strict tool policy.

View File

@@ -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 Chromes 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>

View File

@@ -65,8 +65,8 @@ android {
applicationId = "ai.openclaw.app"
minSdk = 31
targetSdk = 36
versionCode = 2026032500
versionName = "2026.3.25"
versionCode = 2026033000
versionName = "2026.3.30"
ndk {
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")

View File

@@ -24,7 +24,6 @@ import ai.openclaw.app.voice.TalkModeManager
import ai.openclaw.app.voice.VoiceConversationEntry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -34,7 +33,6 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject
@@ -195,7 +193,12 @@ class NodeRuntime(
private val _pendingGatewayTrust = MutableStateFlow<GatewayTrustPrompt?>(null)
val pendingGatewayTrust: StateFlow<GatewayTrustPrompt?> = _pendingGatewayTrust.asStateFlow()
private val _mainSessionKey = MutableStateFlow("main")
private fun resolveNodeMainSessionKey(agentId: String? = gatewayDefaultAgentId): String {
val deviceId = identityStore.loadOrCreate().deviceId
return buildNodeMainSessionKey(deviceId, agentId)
}
private val _mainSessionKey = MutableStateFlow(resolveNodeMainSessionKey())
val mainSessionKey: StateFlow<String> = _mainSessionKey.asStateFlow()
private val cameraHudSeq = AtomicLong(0)
@@ -243,7 +246,7 @@ class NodeRuntime(
_serverName.value = name
_remoteAddress.value = remote
_seamColorArgb.value = DEFAULT_SEAM_COLOR_ARGB
applyMainSessionKey(mainSessionKey)
syncMainSessionKey(resolveAgentIdFromMainSessionKey(mainSessionKey))
updateStatus()
micCapture.onGatewayConnectionChanged(true)
scope.launch {
@@ -259,9 +262,6 @@ class NodeRuntime(
_serverName.value = null
_remoteAddress.value = null
_seamColorArgb.value = DEFAULT_SEAM_COLOR_ARGB
if (!isCanonicalMainSessionKey(_mainSessionKey.value)) {
_mainSessionKey.value = "main"
}
chat.applyMainSessionKey(resolveMainSessionKey())
chat.onDisconnected(message)
updateStatus()
@@ -320,9 +320,11 @@ class NodeRuntime(
session = operatorSession,
json = json,
supportsChatSubscribe = false,
)
).also {
it.applyMainSessionKey(_mainSessionKey.value)
}
private val voiceReplySpeakerLazy: Lazy<TalkModeManager> = lazy {
// Reuse the existing TalkMode speech engine (ElevenLabs + deterministic system-TTS fallback)
// Reuse the existing TalkMode speech engine for native Android TTS playback
// without enabling the legacy talk capture loop.
TalkModeManager(
context = appContext,
@@ -404,13 +406,12 @@ class NodeRuntime(
)
}
private fun applyMainSessionKey(candidate: String?) {
val trimmed = normalizeMainKey(candidate) ?: return
if (isCanonicalMainSessionKey(_mainSessionKey.value)) return
if (_mainSessionKey.value == trimmed) return
_mainSessionKey.value = trimmed
talkMode.setMainSessionKey(trimmed)
chat.applyMainSessionKey(trimmed)
private fun syncMainSessionKey(agentId: String?) {
val resolvedKey = resolveNodeMainSessionKey(agentId)
if (_mainSessionKey.value == resolvedKey) return
_mainSessionKey.value = resolvedKey
talkMode.setMainSessionKey(resolvedKey)
chat.applyMainSessionKey(resolvedKey)
updateHomeCanvasState()
}
@@ -960,9 +961,7 @@ class NodeRuntime(
val config = root?.get("config").asObjectOrNull()
val ui = config?.get("ui").asObjectOrNull()
val raw = ui?.get("seamColor").asStringOrNull()?.trim()
val sessionCfg = config?.get("session").asObjectOrNull()
val mainKey = normalizeMainKey(sessionCfg?.get("mainKey").asStringOrNull())
applyMainSessionKey(mainKey)
syncMainSessionKey(gatewayDefaultAgentId)
val parsed = parseHexColorArgb(raw)
_seamColorArgb.value = parsed ?: DEFAULT_SEAM_COLOR_ARGB
@@ -995,7 +994,7 @@ class NodeRuntime(
gatewayDefaultAgentId = defaultAgentId.ifEmpty { null }
gatewayAgents = agents
applyMainSessionKey(mainKey)
syncMainSessionKey(resolveAgentIdFromMainSessionKey(mainKey) ?: gatewayDefaultAgentId)
updateHomeCanvasState()
} catch (_: Throwable) {
// ignore

View File

@@ -11,3 +11,14 @@ internal fun isCanonicalMainSessionKey(raw: String?): Boolean {
if (trimmed == "global") return true
return trimmed.startsWith("agent:")
}
internal fun resolveAgentIdFromMainSessionKey(raw: String?): String? {
val trimmed = raw?.trim().orEmpty()
if (!trimmed.startsWith("agent:")) return null
return trimmed.removePrefix("agent:").substringBefore(':').trim().ifEmpty { null }
}
internal fun buildNodeMainSessionKey(deviceId: String, agentId: String?): String {
val resolvedAgentId = agentId?.trim().orEmpty().ifEmpty { "main" }
return "agent:$resolvedAgentId:node-${deviceId.take(12)}"
}

View File

@@ -24,6 +24,7 @@ class ChatController(
private val json: Json,
private val supportsChatSubscribe: Boolean,
) {
private var appliedMainSessionKey = "main"
private val _sessionKey = MutableStateFlow("main")
val sessionKey: StateFlow<String> = _sessionKey.asStateFlow()
@@ -73,7 +74,7 @@ class ChatController(
}
fun load(sessionKey: String) {
val key = sessionKey.trim().ifEmpty { "main" }
val key = normalizeRequestedSessionKey(sessionKey)
_sessionKey.value = key
scope.launch { bootstrap(forceHealth = true, refreshSessions = true) }
}
@@ -81,9 +82,15 @@ class ChatController(
fun applyMainSessionKey(mainSessionKey: String) {
val trimmed = mainSessionKey.trim()
if (trimmed.isEmpty()) return
if (_sessionKey.value == trimmed) return
if (_sessionKey.value != "main") return
_sessionKey.value = trimmed
val nextState =
applyMainSessionKey(
currentSessionKey = normalizeRequestedSessionKey(_sessionKey.value),
appliedMainSessionKey = appliedMainSessionKey,
nextMainSessionKey = trimmed,
)
appliedMainSessionKey = nextState.appliedMainSessionKey
if (_sessionKey.value == nextState.currentSessionKey) return
_sessionKey.value = nextState.currentSessionKey
scope.launch { bootstrap(forceHealth = true, refreshSessions = true) }
}
@@ -102,7 +109,7 @@ class ChatController(
}
fun switchSession(sessionKey: String) {
val key = sessionKey.trim()
val key = normalizeRequestedSessionKey(sessionKey)
if (key.isEmpty()) return
if (key == _sessionKey.value) return
_sessionKey.value = key
@@ -111,6 +118,13 @@ class ChatController(
scope.launch { bootstrap(forceHealth = true, refreshSessions = false) }
}
private fun normalizeRequestedSessionKey(sessionKey: String): String {
val key = sessionKey.trim()
if (key.isEmpty()) return appliedMainSessionKey
if (key == "main" && appliedMainSessionKey != "main") return appliedMainSessionKey
return key
}
fun sendMessage(
message: String,
thinkingLevel: String,
@@ -532,6 +546,28 @@ class ChatController(
}
}
internal data class MainSessionState(
val currentSessionKey: String,
val appliedMainSessionKey: String,
)
internal fun applyMainSessionKey(
currentSessionKey: String,
appliedMainSessionKey: String,
nextMainSessionKey: String,
): MainSessionState {
if (currentSessionKey == appliedMainSessionKey) {
return MainSessionState(
currentSessionKey = nextMainSessionKey,
appliedMainSessionKey = nextMainSessionKey,
)
}
return MainSessionState(
currentSessionKey = currentSessionKey,
appliedMainSessionKey = nextMainSessionKey,
)
}
internal fun reconcileMessageIds(previous: List<ChatMessage>, incoming: List<ChatMessage>): List<ChatMessage> {
if (previous.isEmpty() || incoming.isEmpty()) return incoming

View File

@@ -61,7 +61,7 @@ fun ChatSheetContent(viewModel: MainViewModel) {
val pendingToolCalls by viewModel.chatPendingToolCalls.collectAsState()
val sessions by viewModel.chatSessions.collectAsState()
LaunchedEffect(mainSessionKey) {
LaunchedEffect(Unit) {
viewModel.loadChat(mainSessionKey)
}

View File

@@ -52,6 +52,7 @@ class MicCaptureManager(
private const val speechMinSessionMs = 30_000L
private const val speechCompleteSilenceMs = 1_500L
private const val speechPossibleSilenceMs = 900L
private const val transcriptIdleFlushMs = 1_600L
private const val maxConversationEntries = 40
private const val pendingRunTimeoutMs = 45_000L
}
@@ -87,8 +88,7 @@ class MicCaptureManager(
val isSending: StateFlow<Boolean> = _isSending
private val messageQueue = ArrayDeque<String>()
private val sessionSegments = mutableListOf<String>()
private var lastFinalSegment: String? = null
private var flushedPartialTranscript: String? = null
private var pendingRunId: String? = null
private var pendingAssistantEntryId: String? = null
private var gatewayConnected = false
@@ -96,6 +96,7 @@ class MicCaptureManager(
private var recognizer: SpeechRecognizer? = null
private var restartJob: Job? = null
private var drainJob: Job? = null
private var transcriptFlushJob: Job? = null
private var pendingRunTimeoutJob: Job? = null
private var stopRequested = false
@@ -115,10 +116,9 @@ class MicCaptureManager(
stop()
// Capture any partial transcript that didn't get a final result from the recognizer
val partial = _liveTranscript.value?.trim().orEmpty()
if (partial.isNotEmpty() && sessionSegments.isEmpty()) {
sessionSegments.add(partial)
if (partial.isNotEmpty()) {
queueRecognizedMessage(partial)
}
flushSessionToQueue()
drainJob = null
_micCooldown.value = false
sendQueuedIfIdle()
@@ -132,6 +132,11 @@ class MicCaptureManager(
sendQueuedIfIdle()
return
}
pendingRunTimeoutJob?.cancel()
pendingRunTimeoutJob = null
pendingRunId = null
pendingAssistantEntryId = null
_isSending.value = false
if (messageQueue.isNotEmpty()) {
_statusText.value = queuedWaitingStatus()
}
@@ -210,6 +215,8 @@ class MicCaptureManager(
stopRequested = true
restartJob?.cancel()
restartJob = null
transcriptFlushJob?.cancel()
transcriptFlushJob = null
_isListening.value = false
_statusText.value = if (_isSending.value) "Mic off · sending…" else "Mic off"
_inputLevel.value = 0f
@@ -263,17 +270,10 @@ class MicCaptureManager(
}
}
private fun flushSessionToQueue() {
// Add sentence-ending punctuation between recognizer segments to avoid run-on text
val message = sessionSegments.joinToString(". ") { segment ->
val trimmed = segment.trimEnd()
if (trimmed.isNotEmpty() && trimmed.last() in ".!?,;:") trimmed else trimmed
}.trim().let { if (it.isNotEmpty() && it.last() !in ".!?") "$it." else it }
sessionSegments.clear()
private fun queueRecognizedMessage(text: String) {
val message = text.trim()
_liveTranscript.value = null
lastFinalSegment = null
if (message.isEmpty()) return
appendConversation(
role = VoiceConversationRole.User,
text = message,
@@ -282,6 +282,20 @@ class MicCaptureManager(
publishQueue()
}
private fun scheduleTranscriptFlush(expectedText: String) {
transcriptFlushJob?.cancel()
transcriptFlushJob =
scope.launch {
delay(transcriptIdleFlushMs)
if (!_micEnabled.value || _isSending.value) return@launch
val current = _liveTranscript.value?.trim().orEmpty()
if (current.isEmpty() || current != expectedText) return@launch
flushedPartialTranscript = current
queueRecognizedMessage(current)
sendQueuedIfIdle()
}
}
private fun publishQueue() {
_queuedMessages.value = messageQueue.toList()
}
@@ -436,19 +450,12 @@ class MicCaptureManager(
}
}
private fun onFinalTranscript(text: String) {
val trimmed = text.trim()
if (trimmed.isEmpty()) return
_liveTranscript.value = trimmed
if (lastFinalSegment == trimmed) return
lastFinalSegment = trimmed
sessionSegments.add(trimmed)
}
private fun disableMic(status: String) {
stopRequested = true
restartJob?.cancel()
restartJob = null
transcriptFlushJob?.cancel()
transcriptFlushJob = null
_micEnabled.value = false
_isListening.value = false
_inputLevel.value = 0f
@@ -546,11 +553,18 @@ class MicCaptureManager(
}
override fun onResults(results: Bundle?) {
transcriptFlushJob?.cancel()
transcriptFlushJob = null
val text = results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).orEmpty().firstOrNull()
if (!text.isNullOrBlank()) {
onFinalTranscript(text)
// Don't auto-send on silence — accumulate transcript.
// Send happens when mic is toggled off (setMicEnabled(false)).
val trimmed = text.trim()
if (trimmed != flushedPartialTranscript) {
queueRecognizedMessage(trimmed)
sendQueuedIfIdle()
} else {
flushedPartialTranscript = null
_liveTranscript.value = null
}
}
scheduleRestart()
}
@@ -558,7 +572,9 @@ class MicCaptureManager(
override fun onPartialResults(partialResults: Bundle?) {
val text = partialResults?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).orEmpty().firstOrNull()
if (!text.isNullOrBlank()) {
_liveTranscript.value = text.trim()
val trimmed = text.trim()
_liveTranscript.value = trimmed
scheduleTranscriptFlush(trimmed)
}
}

View File

@@ -7,7 +7,6 @@ import android.content.pm.PackageManager
import android.media.AudioAttributes
import android.media.AudioFocusRequest
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -15,12 +14,12 @@ import android.os.SystemClock
import android.speech.RecognitionListener
import android.speech.RecognizerIntent
import android.speech.SpeechRecognizer
import android.util.Base64
import android.util.Log
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import androidx.core.content.ContextCompat
import ai.openclaw.app.gateway.GatewaySession
import ai.openclaw.app.isCanonicalMainSessionKey
import java.io.File
import java.util.Locale
import java.util.UUID
import java.util.concurrent.atomic.AtomicLong
import kotlinx.coroutines.CancellationException
@@ -86,8 +85,6 @@ class TalkModeManager(
private var lastSpokenText: String? = null
private var lastInterruptedAtSeconds: Double? = null
private var currentVoiceId: String? = null
private var currentModelId: String? = null
// Interrupt-on-speech is disabled by default: starting a SpeechRecognizer during
// TTS creates an audio session conflict on some OEMs. Can be enabled via gateway talk config.
private var interruptOnSpeech: Boolean = false
@@ -104,8 +101,10 @@ class TalkModeManager(
private val playbackGeneration = AtomicLong(0L)
private var ttsJob: Job? = null
private val playerLock = Any()
private var player: MediaPlayer? = null
private val ttsLock = Any()
private var textToSpeech: TextToSpeech? = null
private var textToSpeechInit: CompletableDeferred<TextToSpeech>? = null
@Volatile private var currentUtteranceId: String? = null
@Volatile private var finalizeInFlight = false
private var listenWatchdogJob: Job? = null
@@ -131,7 +130,6 @@ class TalkModeManager(
fun setMainSessionKey(sessionKey: String?) {
val trimmed = sessionKey?.trim().orEmpty()
if (trimmed.isEmpty()) return
if (isCanonicalMainSessionKey(mainSessionKey)) return
mainSessionKey = trimmed
}
@@ -340,6 +338,7 @@ class TalkModeManager(
recognizer?.destroy()
recognizer = null
}
shutdownTextToSpeech()
}
private fun startListeningInternal(markListening: Boolean) {
@@ -647,19 +646,6 @@ class TalkModeManager(
val cleaned = parsed.stripped.trim()
if (cleaned.isEmpty()) return
_lastAssistantText.value = cleaned
val requestedVoice = directive?.voiceId?.trim()?.takeIf { it.isNotEmpty() }
if (directive?.voiceId != null) {
if (directive.once != true) {
currentVoiceId = requestedVoice
}
}
if (directive?.modelId != null) {
if (directive.once != true) {
currentModelId = directive.modelId?.trim()?.takeIf { it.isNotEmpty() }
}
}
ensurePlaybackActive(playbackToken)
_statusText.value = "Speaking…"
@@ -670,147 +656,98 @@ class TalkModeManager(
try {
val ttsStarted = SystemClock.elapsedRealtime()
val speech = requestTalkSpeak(cleaned, directive)
playGatewaySpeech(speech, playbackToken)
Log.d(tag, "talk.speak ok durMs=${SystemClock.elapsedRealtime() - ttsStarted} provider=${speech.provider}")
speakWithSystemTts(cleaned, directive, playbackToken)
Log.d(tag, "system tts ok durMs=${SystemClock.elapsedRealtime() - ttsStarted}")
} catch (err: Throwable) {
if (isPlaybackCancelled(err, playbackToken)) {
Log.d(tag, "assistant speech cancelled")
return
}
_statusText.value = "Speak failed: ${err.message ?: err::class.simpleName}"
Log.w(tag, "talk.speak failed: ${err.message ?: err::class.simpleName}")
Log.w(tag, "system tts failed: ${err.message ?: err::class.simpleName}")
} finally {
_isSpeaking.value = false
}
}
private data class GatewayTalkSpeech(
val audioBase64: String,
val provider: String,
val outputFormat: String?,
val mimeType: String?,
val fileExtension: String?,
)
private suspend fun requestTalkSpeak(text: String, directive: TalkDirective?): GatewayTalkSpeech {
val modelId =
directive?.modelId?.trim()?.takeIf { it.isNotEmpty() } ?: currentModelId?.trim()?.takeIf { it.isNotEmpty() }
val voiceId =
directive?.voiceId?.trim()?.takeIf { it.isNotEmpty() } ?: currentVoiceId?.trim()?.takeIf { it.isNotEmpty() }
val params =
buildJsonObject {
put("text", JsonPrimitive(text))
voiceId?.let { put("voiceId", JsonPrimitive(it)) }
modelId?.let { put("modelId", JsonPrimitive(it)) }
TalkModeRuntime.resolveSpeed(directive?.speed, directive?.rateWpm)?.let {
put("speed", JsonPrimitive(it))
}
TalkModeRuntime.validatedStability(directive?.stability, modelId)?.let {
put("stability", JsonPrimitive(it))
}
TalkModeRuntime.validatedUnit(directive?.similarity)?.let {
put("similarity", JsonPrimitive(it))
}
TalkModeRuntime.validatedUnit(directive?.style)?.let {
put("style", JsonPrimitive(it))
}
directive?.speakerBoost?.let { put("speakerBoost", JsonPrimitive(it)) }
TalkModeRuntime.validatedSeed(directive?.seed)?.let { put("seed", JsonPrimitive(it)) }
TalkModeRuntime.validatedNormalize(directive?.normalize)?.let {
put("normalize", JsonPrimitive(it))
}
TalkModeRuntime.validatedLanguage(directive?.language)?.let {
put("language", JsonPrimitive(it))
}
directive?.outputFormat?.trim()?.takeIf { it.isNotEmpty() }?.let {
put("outputFormat", JsonPrimitive(it))
}
}
val res = session.request("talk.speak", params.toString())
val root = json.parseToJsonElement(res).asObjectOrNull() ?: error("talk.speak returned invalid JSON")
val audioBase64 = root["audioBase64"].asStringOrNull()?.trim().orEmpty()
val provider = root["provider"].asStringOrNull()?.trim().orEmpty()
if (audioBase64.isEmpty()) {
error("talk.speak missing audioBase64")
}
if (provider.isEmpty()) {
error("talk.speak missing provider")
}
return GatewayTalkSpeech(
audioBase64 = audioBase64,
provider = provider,
outputFormat = root["outputFormat"].asStringOrNull()?.trim(),
mimeType = root["mimeType"].asStringOrNull()?.trim(),
fileExtension = root["fileExtension"].asStringOrNull()?.trim(),
)
}
private suspend fun playGatewaySpeech(speech: GatewayTalkSpeech, playbackToken: Long) {
private suspend fun speakWithSystemTts(text: String, directive: TalkDirective?, playbackToken: Long) {
ensurePlaybackActive(playbackToken)
cleanupPlayer()
ensurePlaybackActive(playbackToken)
val audioBytes =
try {
Base64.decode(speech.audioBase64, Base64.DEFAULT)
} catch (err: IllegalArgumentException) {
throw IllegalStateException("talk.speak returned invalid audio", err)
val engine = ensureTextToSpeech()
val utteranceId = UUID.randomUUID().toString()
val finished = CompletableDeferred<Unit>()
withContext(Dispatchers.Main) {
ensurePlaybackActive(playbackToken)
synchronized(ttsLock) {
currentUtteranceId = utteranceId
engine.stop()
}
val suffix = resolveGatewayAudioSuffix(speech)
val tempFile =
withContext(Dispatchers.IO) { File.createTempFile("tts_", suffix, context.cacheDir) }
try {
withContext(Dispatchers.IO) { tempFile.writeBytes(audioBytes) }
val player = MediaPlayer()
synchronized(playerLock) {
this.player = player
val locale =
TalkModeRuntime.validatedLanguage(directive?.language)?.let { Locale.forLanguageTag(it) }
if (locale != null) {
val localeResult = engine.setLanguage(locale)
if (
localeResult == TextToSpeech.LANG_MISSING_DATA ||
localeResult == TextToSpeech.LANG_NOT_SUPPORTED
) {
throw IllegalStateException("Language unavailable on this device")
}
}
val finished = CompletableDeferred<Unit>()
player.setAudioAttributes(
engine.setSpeechRate((TalkModeRuntime.resolveSpeed(directive?.speed, directive?.rateWpm) ?: 1.0).toFloat())
engine.setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_MEDIA)
.build(),
)
player.setOnCompletionListener { finished.complete(Unit) }
player.setOnErrorListener { _, what, extra ->
finished.completeExceptionally(IllegalStateException("MediaPlayer error what=$what extra=$extra"))
true
engine.setOnUtteranceProgressListener(
object : UtteranceProgressListener() {
override fun onStart(utteranceId: String?) = Unit
override fun onDone(utteranceId: String?) {
if (utteranceId == currentUtteranceId) {
finished.complete(Unit)
}
}
@Suppress("OVERRIDE_DEPRECATION")
@Deprecated("Deprecated in Java")
override fun onError(utteranceId: String?) {
if (utteranceId == currentUtteranceId) {
finished.completeExceptionally(IllegalStateException("TextToSpeech playback failed"))
}
}
override fun onError(utteranceId: String?, errorCode: Int) {
if (utteranceId == currentUtteranceId) {
finished.completeExceptionally(IllegalStateException("TextToSpeech playback failed ($errorCode)"))
}
}
override fun onStop(utteranceId: String?, interrupted: Boolean) {
if (utteranceId == currentUtteranceId) {
finished.completeExceptionally(CancellationException("assistant speech cancelled"))
}
}
},
)
val result = engine.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceId)
if (result != TextToSpeech.SUCCESS) {
throw IllegalStateException("TextToSpeech start failed")
}
player.setDataSource(tempFile.absolutePath)
withContext(Dispatchers.IO) { player.prepare() }
ensurePlaybackActive(playbackToken)
player.start()
}
try {
finished.await()
ensurePlaybackActive(playbackToken)
} finally {
try {
cleanupPlayer(player)
} catch (_: Throwable) {}
tempFile.delete()
synchronized(ttsLock) {
if (currentUtteranceId == utteranceId) {
currentUtteranceId = null
}
}
}
}
private fun resolveGatewayAudioSuffix(speech: GatewayTalkSpeech): String {
val extension = speech.fileExtension?.trim()
if (!extension.isNullOrEmpty()) {
return if (extension.startsWith(".")) extension else ".$extension"
}
val mimeType = speech.mimeType?.trim()?.lowercase()
if (mimeType == "audio/mpeg") return ".mp3"
if (mimeType == "audio/ogg") return ".ogg"
if (mimeType == "audio/wav") return ".wav"
if (mimeType == "audio/webm") return ".webm"
val outputFormat = speech.outputFormat?.trim()?.lowercase().orEmpty()
if (outputFormat == "mp3" || outputFormat.startsWith("mp3_") || outputFormat.endsWith("-mp3")) return ".mp3"
if (outputFormat == "opus" || outputFormat.startsWith("opus_")) return ".ogg"
if (outputFormat.endsWith("-wav")) return ".wav"
if (outputFormat.endsWith("-webm")) return ".webm"
return ".audio"
}
fun stopTts() {
stopSpeaking(resetInterrupt = true)
_isSpeaking.value = false
@@ -819,19 +756,14 @@ class TalkModeManager(
private fun stopSpeaking(resetInterrupt: Boolean = true) {
if (!_isSpeaking.value) {
cleanupPlayer()
stopTextToSpeechPlayback()
abandonAudioFocus()
return
}
if (resetInterrupt) {
val currentMs = synchronized(playerLock) {
try {
player?.currentPosition?.toDouble() ?: 0.0
} catch (_: IllegalStateException) { 0.0 }
}
lastInterruptedAtSeconds = currentMs / 1000.0
lastInterruptedAtSeconds = null
}
cleanupPlayer()
stopTextToSpeechPlayback()
_isSpeaking.value = false
abandonAudioFocus()
}
@@ -871,15 +803,79 @@ class TalkModeManager(
audioFocusRequest = null
}
private fun cleanupPlayer(expectedPlayer: MediaPlayer? = null) {
synchronized(playerLock) {
val p = player ?: return
if (expectedPlayer != null && p !== expectedPlayer) return
player = null
try {
p.stop()
} catch (_: IllegalStateException) {}
p.release()
private suspend fun ensureTextToSpeech(): TextToSpeech {
val existing = synchronized(ttsLock) { textToSpeech }
if (existing != null) {
return existing
}
val deferred: CompletableDeferred<TextToSpeech>
val created: Boolean
synchronized(ttsLock) {
val ready = textToSpeech
if (ready != null) {
deferred = CompletableDeferred<TextToSpeech>().also { it.complete(ready) }
created = false
} else {
val pending = textToSpeechInit
if (pending != null) {
deferred = pending
created = false
} else {
deferred = CompletableDeferred<TextToSpeech>()
textToSpeechInit = deferred
created = true
}
}
}
if (!created) {
return deferred.await()
}
withContext(Dispatchers.Main) {
synchronized(ttsLock) {
textToSpeech?.let {
textToSpeechInit = null
deferred.complete(it)
return@withContext
}
}
var engine: TextToSpeech? = null
engine = TextToSpeech(context) { status ->
if (status == TextToSpeech.SUCCESS) {
val initialized = engine ?: run {
deferred.completeExceptionally(IllegalStateException("TextToSpeech init failed"))
return@TextToSpeech
}
synchronized(ttsLock) {
textToSpeech = initialized
textToSpeechInit = null
}
deferred.complete(initialized)
} else {
synchronized(ttsLock) {
textToSpeechInit = null
}
engine?.shutdown()
deferred.completeExceptionally(IllegalStateException("TextToSpeech init failed ($status)"))
}
}
}
return deferred.await()
}
private fun stopTextToSpeechPlayback() {
synchronized(ttsLock) {
currentUtteranceId = null
textToSpeech?.stop()
}
}
private fun shutdownTextToSpeech() {
synchronized(ttsLock) {
currentUtteranceId = null
textToSpeech?.stop()
textToSpeech?.shutdown()
textToSpeech = null
textToSpeechInit = null
}
}
@@ -913,9 +909,6 @@ class TalkModeManager(
val res = session.request("talk.config", "{}")
val root = json.parseToJsonElement(res).asObjectOrNull()
val parsed = TalkModeGatewayConfigParser.parse(root?.get("config").asObjectOrNull())
if (!isCanonicalMainSessionKey(mainSessionKey)) {
mainSessionKey = parsed.mainSessionKey
}
silenceWindowMs = parsed.silenceTimeoutMs
parsed.interruptOnSpeech?.let { interruptOnSpeech = it }
configLoaded = true
@@ -944,32 +937,6 @@ class TalkModeManager(
return null
}
fun validatedUnit(value: Double?): Double? {
if (value == null) return null
if (value < 0 || value > 1) return null
return value
}
fun validatedStability(value: Double?, modelId: String?): Double? {
if (value == null) return null
val normalized = modelId?.trim()?.lowercase()
if (normalized == "eleven_v3") {
return if (value == 0.0 || value == 0.5 || value == 1.0) value else null
}
return validatedUnit(value)
}
fun validatedSeed(value: Long?): Long? {
if (value == null) return null
if (value < 0 || value > 4294967295L) return null
return value
}
fun validatedNormalize(value: String?): String? {
val normalized = value?.trim()?.lowercase() ?: return null
return if (normalized in listOf("auto", "on", "off")) normalized else null
}
fun validatedLanguage(value: String?): String? {
val normalized = value?.trim()?.lowercase() ?: return null
if (normalized.length != 2) return null

View File

@@ -0,0 +1,20 @@
package ai.openclaw.app
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
class SessionKeyTest {
@Test
fun buildNodeMainSessionKeyUsesStableDeviceScopedSuffix() {
val key = buildNodeMainSessionKey(deviceId = "1234567890abcdef", agentId = "ops")
assertEquals("agent:ops:node-1234567890ab", key)
}
@Test
fun resolveAgentIdFromMainSessionKeyParsesCanonicalAgentKey() {
assertEquals("ops", resolveAgentIdFromMainSessionKey("agent:ops:main"))
assertNull(resolveAgentIdFromMainSessionKey("global"))
}
}

View File

@@ -0,0 +1,32 @@
package ai.openclaw.app.chat
import org.junit.Assert.assertEquals
import org.junit.Test
class ChatControllerSessionPolicyTest {
@Test
fun applyMainSessionKeyMovesCurrentSessionWhenStillOnDefault() {
val state =
applyMainSessionKey(
currentSessionKey = "main",
appliedMainSessionKey = "main",
nextMainSessionKey = "agent:ops:node-device",
)
assertEquals("agent:ops:node-device", state.currentSessionKey)
assertEquals("agent:ops:node-device", state.appliedMainSessionKey)
}
@Test
fun applyMainSessionKeyKeepsUserSelectedSession() {
val state =
applyMainSessionKey(
currentSessionKey = "custom",
appliedMainSessionKey = "agent:ops:node-old",
nextMainSessionKey = "agent:ops:node-new",
)
assertEquals("custom", state.currentSessionKey)
assertEquals("agent:ops:node-new", state.appliedMainSessionKey)
}
}

View File

@@ -1,8 +1,8 @@
// Shared iOS version defaults.
// Generated overrides live in build/Version.xcconfig (git-ignored).
OPENCLAW_GATEWAY_VERSION = 2026.3.25
OPENCLAW_MARKETING_VERSION = 2026.3.25
OPENCLAW_BUILD_VERSION = 202603250
OPENCLAW_GATEWAY_VERSION = 2026.3.30
OPENCLAW_MARKETING_VERSION = 2026.3.30
OPENCLAW_BUILD_VERSION = 2026033000
#include? "../build/Version.xcconfig"

View File

@@ -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.22-beta.1` becomes:
- `CFBundleShortVersionString = 2026.3.22`
- `CFBundleVersion = next TestFlight build number for 2026.3.22`
- A root version like `2026.3.30-beta.1` becomes:
- `CFBundleShortVersionString = 2026.3.30`
- `CFBundleVersion = next TestFlight build number for 2026.3.30`
Required env for beta builds:

View File

@@ -1,4 +1,4 @@
import ActivityKit
@preconcurrency import ActivityKit
import Foundation
import os

View File

@@ -43,7 +43,7 @@
"location" : "https://github.com/steipete/Peekaboo.git",
"state" : {
"branch" : "main",
"revision" : "bace59f90bb276f1c6fb613acfda3935ec4a7a90"
"revision" : "8659b70d386d02f831e277386b3216023ccc707e"
}
},
{
@@ -96,8 +96,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-subprocess.git",
"state" : {
"revision" : "ba5888ad7758cbcbe7abebac37860b1652af2d9c",
"version" : "0.3.0"
"revision" : "13d087685b95d64d6aac9b94500d347bbe84c39b",
"version" : "0.4.0"
}
},
{

View File

@@ -16,9 +16,9 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/orchetect/MenuBarExtraAccess", exact: "1.2.2"),
.package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.1.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.8.0"),
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.8.1"),
.package(url: "https://github.com/swiftlang/swift-subprocess.git", from: "0.4.0"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.10.1"),
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.9.0"),
.package(url: "https://github.com/steipete/Peekaboo.git", branch: "main"),
.package(path: "../shared/OpenClawKit"),
.package(path: "../../Swabble"),

View File

@@ -768,10 +768,8 @@ struct DebugSettings: View {
}
private func loadSessionStorePath() {
let url = self.configURL()
let parsed = OpenClawConfigFile.loadDict()
guard
let data = try? Data(contentsOf: url),
let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let session = parsed["session"] as? [String: Any],
let path = session["store"] as? String
else {
@@ -783,28 +781,14 @@ struct DebugSettings: View {
private func saveSessionStorePath() {
let trimmed = self.sessionStorePath.trimmingCharacters(in: .whitespacesAndNewlines)
var root: [String: Any] = [:]
let url = self.configURL()
if let data = try? Data(contentsOf: url),
let parsed = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
{
root = parsed
}
var root = OpenClawConfigFile.loadDict()
var session = root["session"] as? [String: Any] ?? [:]
session["store"] = trimmed.isEmpty ? SessionLoader.defaultStorePath : trimmed
root["session"] = session
do {
let data = try JSONSerialization.data(withJSONObject: root, options: [.prettyPrinted, .sortedKeys])
try FileManager().createDirectory(
at: url.deletingLastPathComponent(),
withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic])
self.sessionStoreSaveError = nil
} catch {
self.sessionStoreSaveError = error.localizedDescription
}
OpenClawConfigFile.saveDict(root)
self.sessionStoreSaveError = nil
}
private var bindingOverride: Binding<String> {
@@ -828,10 +812,6 @@ struct DebugSettings: View {
private var canRestartGateway: Bool {
self.state.connectionMode == .local
}
private func configURL() -> URL {
OpenClawPaths.configURL
}
}
extension DebugSettings {

View File

@@ -193,7 +193,7 @@ enum GatewayEnvironment {
let port = self.gatewayPort()
if let gatewayBin {
let bind = self.preferredGatewayBind() ?? "loopback"
let cmd = [gatewayBin, "gateway-daemon", "--port", "\(port)", "--bind", bind]
let cmd = [gatewayBin, "gateway", "--port", "\(port)", "--bind", bind]
return GatewayCommandResolution(status: status, command: cmd)
}
@@ -201,7 +201,7 @@ enum GatewayEnvironment {
case let .success(resolvedRuntime) = runtime
{
let bind = self.preferredGatewayBind() ?? "loopback"
let cmd = [resolvedRuntime.path, entry, "gateway-daemon", "--port", "\(port)", "--bind", bind]
let cmd = [resolvedRuntime.path, entry, "gateway", "--port", "\(port)", "--bind", bind]
return GatewayCommandResolution(status: status, command: cmd)
}
@@ -291,6 +291,17 @@ enum GatewayEnvironment {
// MARK: - Internals
/// Exposed for tests so CLI version output normalization stays local to gateway checks.
static func normalizeGatewayVersionOutput(_ raw: String?) -> String? {
guard var normalized = raw?.trimmingCharacters(in: .whitespacesAndNewlines), !normalized.isEmpty else {
return nil
}
if normalized.lowercased().hasPrefix("openclaw ") {
normalized = String(normalized.dropFirst("openclaw ".count))
}
return normalized
}
private static func readGatewayVersion(binary: String) -> Semver? {
let start = Date()
let process = Process()
@@ -317,9 +328,8 @@ enum GatewayEnvironment {
bin=\(binary, privacy: .public)
""")
}
let raw = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines)
return Semver.parse(raw)
let raw = String(data: data, encoding: .utf8)
return Semver.parse(self.normalizeGatewayVersionOutput(raw))
} catch {
let elapsedMs = Int(Date().timeIntervalSince(start) * 1000)
self.logger.error(

View File

@@ -44,6 +44,7 @@ final class GatewayProcessManager {
private var logRefreshTask: Task<Void, Never>?
#if DEBUG
private var testingConnection: GatewayConnection?
private var testingSkipControlChannelRefresh = false
#endif
private let logger = Logger(subsystem: "ai.openclaw", category: "gateway.process")
@@ -364,6 +365,11 @@ final class GatewayProcessManager {
}
private func refreshControlChannelIfNeeded(reason: String) {
#if DEBUG
if self.testingSkipControlChannelRefresh {
return
}
#endif
switch ControlChannel.shared.state {
case .connected, .connecting:
return
@@ -421,6 +427,10 @@ extension GatewayProcessManager {
self.testingConnection = connection
}
func setTestingSkipControlChannelRefresh(_ skip: Bool) {
self.testingSkipControlChannelRefresh = skip
}
func setTestingDesiredActive(_ active: Bool) {
self.desiredActive = active
}
@@ -428,5 +438,9 @@ extension GatewayProcessManager {
func setTestingLastFailureReason(_ reason: String?) {
self.lastFailureReason = reason
}
func _testAttachExistingGatewayIfAvailable() async -> Bool {
await self.attachExistingGatewayIfAvailable()
}
}
#endif

View File

@@ -18,6 +18,7 @@ enum HostEnvSecurityPolicy {
"ENV",
"GIT_EXTERNAL_DIFF",
"GIT_EXEC_PATH",
"GIT_TEMPLATE_DIR",
"SHELL",
"SHELLOPTS",
"PS4",
@@ -79,7 +80,8 @@ enum HostEnvSecurityPolicy {
"GEM_PATH",
"BUNDLE_GEMFILE",
"COMPOSER_HOME",
"XDG_CONFIG_HOME"
"XDG_CONFIG_HOME",
"AWS_CONFIG_FILE"
]
static let blockedOverridePrefixes: [String] = [

View File

@@ -44,6 +44,7 @@ enum OpenClawConfigFile {
let previousData = try? Data(contentsOf: url)
let previousRoot = previousData.flatMap { self.parseConfigData($0) }
let previousBytes = previousData?.count
let previousAttributes = try? FileManager().attributesOfItem(atPath: url.path)
let hadMetaBefore = self.hasMeta(previousRoot)
let gatewayModeBefore = self.gatewayMode(previousRoot)
@@ -57,6 +58,7 @@ enum OpenClawConfigFile {
withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic])
let nextBytes = data.count
let nextAttributes = try? FileManager().attributesOfItem(atPath: url.path)
let gatewayModeAfter = self.gatewayMode(output)
let suspicious = self.configWriteSuspiciousReasons(
existsBefore: previousData != nil,
@@ -74,6 +76,18 @@ enum OpenClawConfigFile {
"existsBefore": previousData != nil,
"previousBytes": previousBytes ?? NSNull(),
"nextBytes": nextBytes,
"previousDev": self.fileSystemNumber(previousAttributes?[.systemNumber]) ?? NSNull(),
"nextDev": self.fileSystemNumber(nextAttributes?[.systemNumber]) ?? NSNull(),
"previousIno": self.fileSystemNumber(previousAttributes?[.systemFileNumber]) ?? NSNull(),
"nextIno": self.fileSystemNumber(nextAttributes?[.systemFileNumber]) ?? NSNull(),
"previousMode": self.posixMode(previousAttributes?[.posixPermissions]) ?? NSNull(),
"nextMode": self.posixMode(nextAttributes?[.posixPermissions]) ?? NSNull(),
"previousNlink": self.fileAttributeInt(previousAttributes?[.referenceCount]) ?? NSNull(),
"nextNlink": self.fileAttributeInt(nextAttributes?[.referenceCount]) ?? NSNull(),
"previousUid": self.fileAttributeInt(previousAttributes?[.ownerAccountID]) ?? NSNull(),
"nextUid": self.fileAttributeInt(nextAttributes?[.ownerAccountID]) ?? NSNull(),
"previousGid": self.fileAttributeInt(previousAttributes?[.groupOwnerAccountID]) ?? NSNull(),
"nextGid": self.fileAttributeInt(nextAttributes?[.groupOwnerAccountID]) ?? NSNull(),
"hasMetaBefore": hadMetaBefore,
"hasMetaAfter": self.hasMeta(output),
"gatewayModeBefore": gatewayModeBefore ?? NSNull(),
@@ -384,6 +398,23 @@ enum OpenClawConfigFile {
return date.timeIntervalSince1970 * 1000
}
private static func fileAttributeInt(_ value: Any?) -> Int? {
if let number = value as? NSNumber { return number.intValue }
if let number = value as? Int { return number }
return nil
}
private static func fileSystemNumber(_ value: Any?) -> String? {
if let number = value as? NSNumber { return number.stringValue }
if let number = value as? Int { return String(number) }
return nil
}
private static func posixMode(_ value: Any?) -> Int? {
guard let mode = self.fileAttributeInt(value) else { return nil }
return mode & 0o777
}
private static func configFingerprint(
data: Data,
root: [String: Any]?,
@@ -396,6 +427,12 @@ enum OpenClawConfigFile {
"bytes": data.count,
"mtimeMs": self.fileTimestampMs(attributes?[.modificationDate]) ?? NSNull(),
"ctimeMs": self.fileTimestampMs(attributes?[.creationDate]) ?? NSNull(),
"dev": self.fileSystemNumber(attributes?[.systemNumber]) ?? NSNull(),
"ino": self.fileSystemNumber(attributes?[.systemFileNumber]) ?? NSNull(),
"mode": self.posixMode(attributes?[.posixPermissions]) ?? NSNull(),
"nlink": self.fileAttributeInt(attributes?[.referenceCount]) ?? NSNull(),
"uid": self.fileAttributeInt(attributes?[.ownerAccountID]) ?? NSNull(),
"gid": self.fileAttributeInt(attributes?[.groupOwnerAccountID]) ?? NSNull(),
"hasMeta": self.hasMeta(root),
"gatewayMode": self.gatewayMode(root) ?? NSNull(),
"observedAt": observedAt,
@@ -408,6 +445,12 @@ enum OpenClawConfigFile {
(left["bytes"] as? Int) == (right["bytes"] as? Int) &&
(left["mtimeMs"] as? Double) == (right["mtimeMs"] as? Double) &&
(left["ctimeMs"] as? Double) == (right["ctimeMs"] as? Double) &&
(left["dev"] as? String) == (right["dev"] as? String) &&
(left["ino"] as? String) == (right["ino"] as? String) &&
(left["mode"] as? Int) == (right["mode"] as? Int) &&
(left["nlink"] as? Int) == (right["nlink"] as? Int) &&
(left["uid"] as? Int) == (right["uid"] as? Int) &&
(left["gid"] as? Int) == (right["gid"] as? Int) &&
(left["hasMeta"] as? Bool) == (right["hasMeta"] as? Bool) &&
(left["gatewayMode"] as? String) == (right["gatewayMode"] as? String)
}
@@ -509,6 +552,12 @@ enum OpenClawConfigFile {
"bytes": current["bytes"] ?? NSNull(),
"mtimeMs": current["mtimeMs"] ?? NSNull(),
"ctimeMs": current["ctimeMs"] ?? NSNull(),
"dev": current["dev"] ?? NSNull(),
"ino": current["ino"] ?? NSNull(),
"mode": current["mode"] ?? NSNull(),
"nlink": current["nlink"] ?? NSNull(),
"uid": current["uid"] ?? NSNull(),
"gid": current["gid"] ?? NSNull(),
"hasMeta": current["hasMeta"] ?? false,
"gatewayMode": current["gatewayMode"] ?? NSNull(),
"suspicious": suspicious,
@@ -516,11 +565,23 @@ enum OpenClawConfigFile {
"lastKnownGoodBytes": lastKnownGood?["bytes"] ?? NSNull(),
"lastKnownGoodMtimeMs": lastKnownGood?["mtimeMs"] ?? NSNull(),
"lastKnownGoodCtimeMs": lastKnownGood?["ctimeMs"] ?? NSNull(),
"lastKnownGoodDev": lastKnownGood?["dev"] ?? NSNull(),
"lastKnownGoodIno": lastKnownGood?["ino"] ?? NSNull(),
"lastKnownGoodMode": lastKnownGood?["mode"] ?? NSNull(),
"lastKnownGoodNlink": lastKnownGood?["nlink"] ?? NSNull(),
"lastKnownGoodUid": lastKnownGood?["uid"] ?? NSNull(),
"lastKnownGoodGid": lastKnownGood?["gid"] ?? NSNull(),
"lastKnownGoodGatewayMode": lastKnownGood?["gatewayMode"] ?? NSNull(),
"backupHash": backup?["hash"] ?? NSNull(),
"backupBytes": backup?["bytes"] ?? NSNull(),
"backupMtimeMs": backup?["mtimeMs"] ?? NSNull(),
"backupCtimeMs": backup?["ctimeMs"] ?? NSNull(),
"backupDev": backup?["dev"] ?? NSNull(),
"backupIno": backup?["ino"] ?? NSNull(),
"backupMode": backup?["mode"] ?? NSNull(),
"backupNlink": backup?["nlink"] ?? NSNull(),
"backupUid": backup?["uid"] ?? NSNull(),
"backupGid": backup?["gid"] ?? NSNull(),
"backupGatewayMode": backup?["gatewayMode"] ?? NSNull(),
"clobberedPath": clobberedPath ?? NSNull(),
])

View File

@@ -23,6 +23,9 @@ actor PortGuardian {
private var records: [Record] = []
private let logger = Logger(subsystem: "ai.openclaw", category: "portguard")
#if DEBUG
private var testingDescriptors: [Int: Descriptor] = [:]
#endif
private nonisolated static let appSupportDir: URL = {
let base = FileManager().urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
return base.appendingPathComponent("OpenClaw", isDirectory: true)
@@ -130,6 +133,11 @@ actor PortGuardian {
}
func describe(port: Int) async -> Descriptor? {
#if DEBUG
if let descriptor = self.testingDescriptors[port] {
return descriptor
}
#endif
guard let listener = await self.listeners(on: port).first else { return nil }
let path = Self.executablePath(for: listener.pid)
return Descriptor(pid: listener.pid, command: listener.command, executablePath: path)
@@ -368,8 +376,12 @@ actor PortGuardian {
if port == GatewayEnvironment.gatewayPort() { return true }
return false
case .local:
// The gateway daemon may listen as `openclaw` or as its runtime (`node`, `bun`, etc).
if full.contains("gateway-daemon") { return true }
// Preserve both the legacy hidden alias and the current service process title.
if full.contains("gateway-daemon") || full.contains("openclaw-gateway")
|| cmd.contains("openclaw-gateway")
{
return true
}
// If args are unavailable, treat a CLI listener as expected.
if cmd.contains("openclaw"), full == cmd { return true }
return false
@@ -402,6 +414,18 @@ actor PortGuardian {
}
}
#if DEBUG
extension PortGuardian {
func setTestingDescriptor(_ descriptor: Descriptor?, forPort port: Int) {
if let descriptor {
self.testingDescriptors[port] = descriptor
} else {
self.testingDescriptors.removeValue(forKey: port)
}
}
}
#endif
#if DEBUG
extension PortGuardian {
static func _testParseListeners(_ text: String) -> [(

View File

@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2026.3.25</string>
<string>2026.3.30</string>
<key>CFBundleVersion</key>
<string>202603250</string>
<string>2026033000</string>
<key>CFBundleIconFile</key>
<string>OpenClaw</string>
<key>CFBundleURLTypes</key>

View File

@@ -9,6 +9,7 @@ public enum ErrorCode: String, Codable, Sendable {
case notPaired = "NOT_PAIRED"
case agentTimeout = "AGENT_TIMEOUT"
case invalidRequest = "INVALID_REQUEST"
case approvalNotFound = "APPROVAL_NOT_FOUND"
case unavailable = "UNAVAILABLE"
}
@@ -2234,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
}
}
@@ -3434,6 +3443,90 @@ public struct ExecApprovalResolveParams: Codable, Sendable {
}
}
public struct PluginApprovalRequestParams: Codable, Sendable {
public let pluginid: String?
public let title: String
public let description: String
public let severity: String?
public let toolname: String?
public let toolcallid: String?
public let agentid: String?
public let sessionkey: String?
public let turnsourcechannel: String?
public let turnsourceto: String?
public let turnsourceaccountid: String?
public let turnsourcethreadid: AnyCodable?
public let timeoutms: Int?
public let twophase: Bool?
public init(
pluginid: String?,
title: String,
description: String,
severity: String?,
toolname: String?,
toolcallid: String?,
agentid: String?,
sessionkey: String?,
turnsourcechannel: String?,
turnsourceto: String?,
turnsourceaccountid: String?,
turnsourcethreadid: AnyCodable?,
timeoutms: Int?,
twophase: Bool?)
{
self.pluginid = pluginid
self.title = title
self.description = description
self.severity = severity
self.toolname = toolname
self.toolcallid = toolcallid
self.agentid = agentid
self.sessionkey = sessionkey
self.turnsourcechannel = turnsourcechannel
self.turnsourceto = turnsourceto
self.turnsourceaccountid = turnsourceaccountid
self.turnsourcethreadid = turnsourcethreadid
self.timeoutms = timeoutms
self.twophase = twophase
}
private enum CodingKeys: String, CodingKey {
case pluginid = "pluginId"
case title
case description
case severity
case toolname = "toolName"
case toolcallid = "toolCallId"
case agentid = "agentId"
case sessionkey = "sessionKey"
case turnsourcechannel = "turnSourceChannel"
case turnsourceto = "turnSourceTo"
case turnsourceaccountid = "turnSourceAccountId"
case turnsourcethreadid = "turnSourceThreadId"
case timeoutms = "timeoutMs"
case twophase = "twoPhase"
}
}
public struct PluginApprovalResolveParams: Codable, Sendable {
public let id: String
public let decision: String
public init(
id: String,
decision: String)
{
self.id = id
self.decision = decision
}
private enum CodingKeys: String, CodingKey {
case id
case decision
}
}
public struct DevicePairListParams: Codable, Sendable {}
public struct DevicePairApproveParams: Codable, Sendable {
@@ -3637,6 +3730,10 @@ public struct ChatSendParams: Codable, Sendable {
public let message: String
public let thinking: String?
public let deliver: Bool?
public let originatingchannel: String?
public let originatingto: String?
public let originatingaccountid: String?
public let originatingthreadid: String?
public let attachments: [AnyCodable]?
public let timeoutms: Int?
public let systeminputprovenance: [String: AnyCodable]?
@@ -3648,6 +3745,10 @@ public struct ChatSendParams: Codable, Sendable {
message: String,
thinking: String?,
deliver: Bool?,
originatingchannel: String?,
originatingto: String?,
originatingaccountid: String?,
originatingthreadid: String?,
attachments: [AnyCodable]?,
timeoutms: Int?,
systeminputprovenance: [String: AnyCodable]?,
@@ -3658,6 +3759,10 @@ public struct ChatSendParams: Codable, Sendable {
self.message = message
self.thinking = thinking
self.deliver = deliver
self.originatingchannel = originatingchannel
self.originatingto = originatingto
self.originatingaccountid = originatingaccountid
self.originatingthreadid = originatingthreadid
self.attachments = attachments
self.timeoutms = timeoutms
self.systeminputprovenance = systeminputprovenance
@@ -3670,6 +3775,10 @@ public struct ChatSendParams: Codable, Sendable {
case message
case thinking
case deliver
case originatingchannel = "originatingChannel"
case originatingto = "originatingTo"
case originatingaccountid = "originatingAccountId"
case originatingthreadid = "originatingThreadId"
case attachments
case timeoutms = "timeoutMs"
case systeminputprovenance = "systemInputProvenance"

View File

@@ -19,6 +19,15 @@ struct GatewayEnvironmentTests {
#expect(Semver.parse("invalid") == nil)
#expect(Semver.parse("1.2") == nil)
#expect(Semver.parse("1.2.x") == nil)
// Product-prefixed output from `openclaw --version` should NOT parse as semver
// (the prefix must be stripped by the caller, not the parser).
#expect(Semver.parse("OpenClaw 2026.3.23-1") == nil)
}
@Test func `gateway version output strips product prefix before parsing`() {
let normalized = GatewayEnvironment.normalizeGatewayVersionOutput(" OpenClaw 2026.3.23-1 \n")
#expect(normalized == "2026.3.23-1")
#expect(Semver.parse(normalized) == Semver(major: 2026, minor: 3, patch: 23))
}
@Test func `semver compatibility requires same major and not older`() {

View File

@@ -7,7 +7,7 @@ struct GatewayLaunchAgentManagerTests {
let url = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [
"ProgramArguments": ["openclaw", "gateway-daemon", "--port", "18789", "--bind", "loopback"],
"ProgramArguments": ["openclaw", "gateway", "--port", "18789", "--bind", "loopback"],
"EnvironmentVariables": [
"OPENCLAW_GATEWAY_TOKEN": " secret ",
"OPENCLAW_GATEWAY_PASSWORD": "pw",
@@ -28,7 +28,7 @@ struct GatewayLaunchAgentManagerTests {
let url = FileManager().temporaryDirectory
.appendingPathComponent("openclaw-launchd-\(UUID().uuidString).plist")
let plist: [String: Any] = [
"ProgramArguments": ["openclaw", "gateway-daemon", "--port", "18789"],
"ProgramArguments": ["openclaw", "gateway", "--port", "18789"],
]
let data = try PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0)
try data.write(to: url, options: [.atomic])

View File

@@ -35,4 +35,92 @@ struct GatewayProcessManagerTests {
#expect(ready)
#expect(manager.lastFailureReason == nil)
}
@Test func `attaches to existing gateway without spawning launchd`() async throws {
let healthData = Data(
"""
{
"ok": true,
"ts": 1,
"durationMs": 0,
"channels": {
"telegram": {
"configured": true,
"linked": true,
"authAgeMs": 60000
}
},
"channelOrder": ["telegram"],
"channelLabels": {
"telegram": "Telegram"
},
"heartbeatSeconds": 30,
"sessions": {
"path": "/tmp/sessions",
"count": 1,
"recent": []
}
}
""".utf8)
let session = GatewayTestWebSocketSession(
taskFactory: {
GatewayTestWebSocketTask(
sendHook: { task, message, sendIndex in
guard sendIndex > 0 else { return }
guard let id = GatewayWebSocketTestSupport.requestID(from: message) else { return }
let json = """
{
"type": "res",
"id": "\(id)",
"ok": true,
"payload": \(String(decoding: healthData, as: UTF8.self))
}
"""
task.emitReceiveSuccess(.data(Data(json.utf8)))
})
})
let url = try #require(URL(string: "ws://example.invalid"))
let connection = GatewayConnection(
configProvider: { (url: url, token: nil, password: nil) },
sessionBox: WebSocketSessionBox(session: session))
let port = GatewayEnvironment.gatewayPort()
let descriptor = PortGuardian.Descriptor(
pid: 4242,
command: "openclaw-gateway",
executablePath: "/tmp/openclaw-gateway")
let manager = GatewayProcessManager.shared
await PortGuardian.shared.setTestingDescriptor(descriptor, forPort: port)
manager.setTestingConnection(connection)
manager.setTestingSkipControlChannelRefresh(true)
manager.setTestingLastFailureReason("stale")
func cleanup() async {
await PortGuardian.shared.setTestingDescriptor(nil, forPort: port)
manager.setTestingConnection(nil)
manager.setTestingSkipControlChannelRefresh(false)
manager.setTestingDesiredActive(false)
manager.setTestingLastFailureReason(nil)
}
do {
let attached = await manager._testAttachExistingGatewayIfAvailable()
#expect(attached)
#expect(manager.lastFailureReason == nil)
guard case let .attachedExisting(statusDetails) = manager.status else {
Issue.record("expected attachedExisting status")
await cleanup()
return
}
let details = try #require(statusDetails)
#expect(details.contains("port \(port)"))
#expect(details.contains("Telegram linked"))
#expect(details.contains("auth 1m"))
#expect(details.contains("pid 4242 openclaw-gateway @ /tmp/openclaw-gateway"))
await cleanup()
} catch {
await cleanup()
throw error
}
}
}

View File

@@ -167,6 +167,11 @@ struct LowCoverageHelperTests {
fullCommand: "python server.py",
port: 18789, mode: .local) == false)
#expect(PortGuardian._testIsExpected(
command: "node",
fullCommand: "openclaw-gateway",
port: 18789, mode: .local) == true)
#expect(PortGuardian._testIsExpected(
command: "node",
fullCommand: "node /path/to/gateway-daemon",

View File

@@ -3,17 +3,19 @@ import Testing
@testable import OpenClaw
@Suite(.serialized) struct NodeServiceManagerTests {
@Test func `builds node service commands with current CLI shape`() throws {
let tmp = try makeTempDirForTests()
CommandResolver.setProjectRoot(tmp.path)
@Test func `builds node service commands with current CLI shape`() async throws {
try await TestIsolation.withUserDefaultsValues(["openclaw.gatewayProjectRootPath": nil]) {
let tmp = try makeTempDirForTests()
CommandResolver.setProjectRoot(tmp.path)
let openclawPath = tmp.appendingPathComponent("node_modules/.bin/openclaw")
try makeExecutableForTests(at: openclawPath)
let openclawPath = tmp.appendingPathComponent("node_modules/.bin/openclaw")
try makeExecutableForTests(at: openclawPath)
let start = NodeServiceManager._testServiceCommand(["start"])
#expect(start == [openclawPath.path, "node", "start", "--json"])
let start = NodeServiceManager._testServiceCommand(["start"])
#expect(start == [openclawPath.path, "node", "start", "--json"])
let stop = NodeServiceManager._testServiceCommand(["stop"])
#expect(stop == [openclawPath.path, "node", "stop", "--json"])
let stop = NodeServiceManager._testServiceCommand(["stop"])
#expect(stop == [openclawPath.path, "node", "stop", "--json"])
}
}
}

View File

@@ -133,6 +133,10 @@ struct OpenClawConfigFileTests {
#expect(auditRoot?["event"] as? String == "config.write")
#expect(auditRoot?["result"] as? String == "success")
#expect(auditRoot?["configPath"] as? String == configPath.path)
#expect(auditRoot?["previousMode"] is NSNull)
#expect(auditRoot?["nextMode"] is NSNumber)
#expect(auditRoot?["previousIno"] is NSNull)
#expect(auditRoot?["nextIno"] as? String != nil)
}
}
@@ -188,6 +192,10 @@ struct OpenClawConfigFileTests {
let auditRoot = try JSONSerialization.jsonObject(with: Data(observeLine.utf8)) as? [String: Any]
#expect(auditRoot?["source"] as? String == "macos-openclaw-config-file")
#expect(auditRoot?["configPath"] as? String == configPath.path)
#expect(auditRoot?["mode"] is NSNumber)
#expect(auditRoot?["ino"] as? String != nil)
#expect(auditRoot?["lastKnownGoodMode"] is NSNumber)
#expect(auditRoot?["backupMode"] is NSNull)
let suspicious = auditRoot?["suspicious"] as? [String] ?? []
#expect(suspicious.contains("gateway-mode-missing-vs-last-good"))
#expect(suspicious.contains("update-channel-only-root"))

View File

@@ -9,6 +9,7 @@ public enum ErrorCode: String, Codable, Sendable {
case notPaired = "NOT_PAIRED"
case agentTimeout = "AGENT_TIMEOUT"
case invalidRequest = "INVALID_REQUEST"
case approvalNotFound = "APPROVAL_NOT_FOUND"
case unavailable = "UNAVAILABLE"
}
@@ -2234,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
}
}
@@ -3434,6 +3443,90 @@ public struct ExecApprovalResolveParams: Codable, Sendable {
}
}
public struct PluginApprovalRequestParams: Codable, Sendable {
public let pluginid: String?
public let title: String
public let description: String
public let severity: String?
public let toolname: String?
public let toolcallid: String?
public let agentid: String?
public let sessionkey: String?
public let turnsourcechannel: String?
public let turnsourceto: String?
public let turnsourceaccountid: String?
public let turnsourcethreadid: AnyCodable?
public let timeoutms: Int?
public let twophase: Bool?
public init(
pluginid: String?,
title: String,
description: String,
severity: String?,
toolname: String?,
toolcallid: String?,
agentid: String?,
sessionkey: String?,
turnsourcechannel: String?,
turnsourceto: String?,
turnsourceaccountid: String?,
turnsourcethreadid: AnyCodable?,
timeoutms: Int?,
twophase: Bool?)
{
self.pluginid = pluginid
self.title = title
self.description = description
self.severity = severity
self.toolname = toolname
self.toolcallid = toolcallid
self.agentid = agentid
self.sessionkey = sessionkey
self.turnsourcechannel = turnsourcechannel
self.turnsourceto = turnsourceto
self.turnsourceaccountid = turnsourceaccountid
self.turnsourcethreadid = turnsourcethreadid
self.timeoutms = timeoutms
self.twophase = twophase
}
private enum CodingKeys: String, CodingKey {
case pluginid = "pluginId"
case title
case description
case severity
case toolname = "toolName"
case toolcallid = "toolCallId"
case agentid = "agentId"
case sessionkey = "sessionKey"
case turnsourcechannel = "turnSourceChannel"
case turnsourceto = "turnSourceTo"
case turnsourceaccountid = "turnSourceAccountId"
case turnsourcethreadid = "turnSourceThreadId"
case timeoutms = "timeoutMs"
case twophase = "twoPhase"
}
}
public struct PluginApprovalResolveParams: Codable, Sendable {
public let id: String
public let decision: String
public init(
id: String,
decision: String)
{
self.id = id
self.decision = decision
}
private enum CodingKeys: String, CodingKey {
case id
case decision
}
}
public struct DevicePairListParams: Codable, Sendable {}
public struct DevicePairApproveParams: Codable, Sendable {
@@ -3637,6 +3730,10 @@ public struct ChatSendParams: Codable, Sendable {
public let message: String
public let thinking: String?
public let deliver: Bool?
public let originatingchannel: String?
public let originatingto: String?
public let originatingaccountid: String?
public let originatingthreadid: String?
public let attachments: [AnyCodable]?
public let timeoutms: Int?
public let systeminputprovenance: [String: AnyCodable]?
@@ -3648,6 +3745,10 @@ public struct ChatSendParams: Codable, Sendable {
message: String,
thinking: String?,
deliver: Bool?,
originatingchannel: String?,
originatingto: String?,
originatingaccountid: String?,
originatingthreadid: String?,
attachments: [AnyCodable]?,
timeoutms: Int?,
systeminputprovenance: [String: AnyCodable]?,
@@ -3658,6 +3759,10 @@ public struct ChatSendParams: Codable, Sendable {
self.message = message
self.thinking = thinking
self.deliver = deliver
self.originatingchannel = originatingchannel
self.originatingto = originatingto
self.originatingaccountid = originatingaccountid
self.originatingthreadid = originatingthreadid
self.attachments = attachments
self.timeoutms = timeoutms
self.systeminputprovenance = systeminputprovenance
@@ -3670,6 +3775,10 @@ public struct ChatSendParams: Codable, Sendable {
case message
case thinking
case deliver
case originatingchannel = "originatingChannel"
case originatingto = "originatingTo"
case originatingaccountid = "originatingAccountId"
case originatingthreadid = "originatingThreadId"
case attachments
case timeoutms = "timeoutMs"
case systeminputprovenance = "systemInputProvenance"

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
{"generatedBy":"scripts/generate-config-doc-baseline.ts","recordType":"meta","totalPaths":5531}
{"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}
@@ -467,7 +471,7 @@
{"recordType":"path","path":"agents.list.*.reasoningDefault","kind":"core","type":"string","required":false,"enumValues":["on","off","stream"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent Reasoning Default","help":"Optional per-agent default reasoning visibility (on|off|stream). Applies when no per-message or session reasoning override is set.","hasChildren":false}
{"recordType":"path","path":"agents.list.*.runtime","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent Runtime","help":"Optional runtime descriptor for this agent. Use embedded for default OpenClaw execution or acp for external ACP harness defaults.","hasChildren":true}
{"recordType":"path","path":"agents.list.*.runtime.acp","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Runtime","help":"ACP runtime defaults for this agent when runtime.type=acp. Binding-level ACP overrides still take precedence per conversation.","hasChildren":true}
{"recordType":"path","path":"agents.list.*.runtime.acp.agent","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Harness Agent","help":"Optional ACP harness agent id to use for this OpenClaw agent (for example codex, claude).","hasChildren":false}
{"recordType":"path","path":"agents.list.*.runtime.acp.agent","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Harness Agent","help":"Optional ACP harness agent id to use for this OpenClaw agent (for example codex, claude, cursor, gemini, openclaw).","hasChildren":false}
{"recordType":"path","path":"agents.list.*.runtime.acp.backend","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Backend","help":"Optional ACP backend override for this agent's ACP sessions (falls back to global acp.backend).","hasChildren":false}
{"recordType":"path","path":"agents.list.*.runtime.acp.cwd","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Working Directory","help":"Optional default working directory for this agent's ACP sessions.","hasChildren":false}
{"recordType":"path","path":"agents.list.*.runtime.acp.mode","kind":"core","type":"string","required":false,"enumValues":["persistent","oneshot"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Agent ACP Mode","help":"Optional ACP session mode default for this agent (persistent or oneshot).","hasChildren":false}
@@ -638,7 +642,7 @@
{"recordType":"path","path":"agents.list.*.tools.sandbox.tools.deny","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"agents.list.*.tools.sandbox.tools.deny.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"agents.list.*.workspace","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"approvals","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approvals","help":"Approval routing controls for forwarding exec approval requests to chat destinations outside the originating session. Keep this disabled unless operators need explicit out-of-band approval visibility.","hasChildren":true}
{"recordType":"path","path":"approvals","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approvals","help":"Approval routing controls for forwarding exec and plugin approval requests to chat destinations outside the originating session. Keep these disabled unless operators need explicit out-of-band approval visibility.","hasChildren":true}
{"recordType":"path","path":"approvals.exec","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Exec Approval Forwarding","help":"Groups exec-approval forwarding behavior including enablement, routing mode, filters, and explicit targets. Configure here when approval prompts must reach operational channels instead of only the origin thread.","hasChildren":true}
{"recordType":"path","path":"approvals.exec.agentFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for forwarded approvals, for example `[\"primary\", \"ops-agent\"]`. Use this to limit forwarding blast radius and avoid notifying channels for unrelated agents.","hasChildren":true}
{"recordType":"path","path":"approvals.exec.agentFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -652,6 +656,19 @@
{"recordType":"path","path":"approvals.exec.targets.*.channel","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Channel","help":"Channel/provider ID used for forwarded approval delivery, such as discord, slack, or a plugin channel id. Use valid channel IDs only so approvals do not silently fail due to unknown routes.","hasChildren":false}
{"recordType":"path","path":"approvals.exec.targets.*.threadId","kind":"core","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Thread ID","help":"Optional thread/topic target for channels that support threaded delivery of forwarded approvals. Use this to keep approval traffic contained in operational threads instead of main channels.","hasChildren":false}
{"recordType":"path","path":"approvals.exec.targets.*.to","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Approval Target Destination","help":"Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider). Verify semantics per provider because destination format differs across channel integrations.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding","help":"Groups plugin-approval forwarding behavior including enablement, routing mode, filters, and explicit targets. Independent of exec approval forwarding. Configure here when plugin approval prompts must reach operational channels.","hasChildren":true}
{"recordType":"path","path":"approvals.plugin.agentFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Agent Filter","help":"Optional allowlist of agent IDs eligible for forwarded plugin approvals, for example `[\"primary\", \"ops-agent\"]`. Use this to limit forwarding blast radius.","hasChildren":true}
{"recordType":"path","path":"approvals.plugin.agentFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"approvals.plugin.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Forward Plugin Approvals","help":"Enables forwarding of plugin approval requests to configured delivery destinations (default: false). Independent of approvals.exec.enabled.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin.mode","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding Mode","help":"Controls where plugin approval prompts are sent: \"session\" uses origin chat, \"targets\" uses configured targets, and \"both\" sends to both paths.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin.sessionFilter","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["storage"],"label":"Plugin Approval Session Filter","help":"Optional session-key filters matched as substring or regex-style patterns, for example `[\"discord:\", \"^agent:ops:\"]`. Use narrow patterns so only intended approval contexts are forwarded.","hasChildren":true}
{"recordType":"path","path":"approvals.plugin.sessionFilter.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"approvals.plugin.targets","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Forwarding Targets","help":"Explicit delivery targets used when plugin approval forwarding mode includes targets, each with channel and destination details.","hasChildren":true}
{"recordType":"path","path":"approvals.plugin.targets.*","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"approvals.plugin.targets.*.accountId","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Account ID","help":"Optional account selector for multi-account channel setups when plugin approvals must route through a specific account context.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin.targets.*.channel","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Channel","help":"Channel/provider ID used for forwarded plugin approval delivery, such as discord, slack, or a plugin channel id.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin.targets.*.threadId","kind":"core","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Thread ID","help":"Optional thread/topic target for channels that support threaded delivery of forwarded plugin approvals.","hasChildren":false}
{"recordType":"path","path":"approvals.plugin.targets.*.to","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Approval Target Destination","help":"Destination identifier inside the target channel (channel ID, user ID, or thread root depending on provider).","hasChildren":false}
{"recordType":"path","path":"audio","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Audio","help":"Global audio ingestion settings used before higher-level tools process speech or media content. Configure this when you need deterministic transcription behavior for voice notes and clips.","hasChildren":true}
{"recordType":"path","path":"audio.transcription","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["media"],"label":"Audio Transcription","help":"Command-based transcription settings for converting audio files into text before agent handling. Keep a simple, deterministic command path here so failures are easy to diagnose in logs.","hasChildren":true}
{"recordType":"path","path":"audio.transcription.command","kind":"core","type":"array","required":true,"deprecated":false,"sensitive":false,"tags":["media"],"label":"Audio Transcription Command","help":"Executable + args used to transcribe audio (first token must be a safe binary/path), for example `[\"whisper-cli\", \"--model\", \"small\", \"{input}\"]`. Prefer a pinned command so runtime environments behave consistently.","hasChildren":true}
@@ -734,7 +751,7 @@
{"recordType":"path","path":"canvasHost.liveReload","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["reliability"],"label":"Canvas Host Live Reload","help":"Enables automatic live-reload behavior for canvas assets during development workflows. Keep disabled in production-like environments where deterministic output is preferred.","hasChildren":false}
{"recordType":"path","path":"canvasHost.port","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Canvas Host Port","help":"TCP port used by the canvas host HTTP server when canvas hosting is enabled. Choose a non-conflicting port and align firewall/proxy policy accordingly.","hasChildren":false}
{"recordType":"path","path":"canvasHost.root","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Canvas Host Root Directory","help":"Filesystem root directory served by canvas host for canvas content and static assets. Use a dedicated directory and avoid broad repo roots for least-privilege file exposure.","hasChildren":false}
{"recordType":"path","path":"channels","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Channels","help":"Channel provider configurations plus shared defaults that control access policies, heartbeat visibility, and per-surface behavior. Keep defaults centralized and override per provider only where required.","hasChildren":true}
{"recordType":"path","path":"channels","kind":"core","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Channels","help":"Channel provider configurations plus shared defaults that control access policies, heartbeat visibility, and per-surface behavior. Keep defaults centralized and override per provider only where required.","hasChildren":true}
{"recordType":"path","path":"channels.bluebubbles","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"BlueBubbles","help":"iMessage via the BlueBubbles mac app + REST API.","hasChildren":true}
{"recordType":"path","path":"channels.bluebubbles.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.bluebubbles.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1287,7 +1304,7 @@
{"recordType":"path","path":"channels.feishu.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.appId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.appSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.appSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.appSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.appSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.appSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1308,7 +1325,7 @@
{"recordType":"path","path":"channels.feishu.accounts.*.dms.*.systemPrompt","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.domain","kind":"channel","type":"string","required":false,"enumValues":["feishu","lark"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.encryptKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.encryptKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.encryptKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.encryptKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.encryptKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1361,7 +1378,7 @@
{"recordType":"path","path":"channels.feishu.accounts.*.tools.wiki","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.topicSessionMode","kind":"channel","type":"string","required":false,"enumValues":["disabled","enabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.typingIndicator","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.verificationToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.verificationToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.accounts.*.verificationToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.verificationToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.accounts.*.verificationToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1373,7 +1390,7 @@
{"recordType":"path","path":"channels.feishu.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.appId","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.appSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.appSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.appSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.appSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.appSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1400,7 +1417,7 @@
{"recordType":"path","path":"channels.feishu.dynamicAgentCreation.maxAgents","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.dynamicAgentCreation.workspaceTemplate","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.encryptKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.encryptKey","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.encryptKey.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.encryptKey.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.encryptKey.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1452,14 +1469,14 @@
{"recordType":"path","path":"channels.feishu.tools.wiki","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.topicSessionMode","kind":"channel","type":"string","required":false,"enumValues":["disabled","enabled"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.typingIndicator","kind":"channel","type":"boolean","required":true,"defaultValue":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.verificationToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.verificationToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.feishu.verificationToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.verificationToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.verificationToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.webhookHost","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.webhookPath","kind":"channel","type":"string","required":true,"defaultValue":"/feishu/events","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.feishu.webhookPort","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.googlechat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Google Chat","help":"Google Workspace Chat app via HTTP webhooks.","hasChildren":true}
{"recordType":"path","path":"channels.googlechat","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Google Chat","help":"Google Workspace Chat app with HTTP webhook.","hasChildren":true}
{"recordType":"path","path":"channels.googlechat.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.googlechat.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.googlechat.accounts.*.actions","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1855,13 +1872,13 @@
{"recordType":"path","path":"channels.irc.textChunkLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.irc.tls","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.irc.username","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"LINE","help":"LINE Messaging API bot for Japan/Taiwan/Thailand markets.","hasChildren":true}
{"recordType":"path","path":"channels.line","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"LINE","help":"LINE Messaging API webhook bot.","hasChildren":true}
{"recordType":"path","path":"channels.line.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.line.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.line.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.line.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.channelAccessToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.channelSecret","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.channelAccessToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["access","auth","channels","network","security"],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.channelSecret","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.dmPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","allowlist","pairing","disabled"],"defaultValue":"pairing","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.accounts.*.groupAllowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -1879,13 +1896,19 @@
{"recordType":"path","path":"channels.line.accounts.*.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"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":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}
{"recordType":"path","path":"channels.line.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.channelAccessToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.channelSecret","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.channelAccessToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["access","auth","channels","network","security"],"hasChildren":false}
{"recordType":"path","path":"channels.line.channelSecret","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":false}
{"recordType":"path","path":"channels.line.defaultAccount","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.dmPolicy","kind":"channel","type":"string","required":true,"enumValues":["open","allowlist","pairing","disabled"],"defaultValue":"pairing","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.line.enabled","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1904,11 +1927,20 @@
{"recordType":"path","path":"channels.line.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"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":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}
{"recordType":"path","path":"channels.matrix.accessToken","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.accessToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["access","auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.matrix.accessToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.accessToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.accessToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.accounts","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.matrix.accounts.*","kind":"channel","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.ackReaction","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1921,7 +1953,7 @@
{"recordType":"path","path":"channels.matrix.actions.profile","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.actions.reactions","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.actions.verification","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":["access","channels","network"],"label":"Matrix Allow Bot Messages","help":"Allow messages from other configured Matrix bot accounts to trigger replies (default: false). Set \"mentions\" to only accept bot messages that visibly mention this bot.","hasChildren":false}
{"recordType":"path","path":"channels.matrix.allowBots","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.allowlistOnly","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.autoJoin","kind":"channel","type":"string","required":false,"enumValues":["always","allowlist","off"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1967,7 +1999,7 @@
{"recordType":"path","path":"channels.matrix.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.mediaMaxMb","kind":"channel","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.password","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.matrix.password","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.matrix.password.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.password.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.matrix.password.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -1995,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}
@@ -2018,7 +2051,7 @@
{"recordType":"path","path":"channels.mattermost.accounts.*.blockStreamingCoalesce.idleMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.blockStreamingCoalesce.maxChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.blockStreamingCoalesce.minChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.accounts.*.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.accounts.*.botToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.botToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.accounts.*.botToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2061,26 +2094,26 @@
{"recordType":"path","path":"channels.mattermost.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Base URL","help":"Base URL for your Mattermost server (e.g., https://chat.example.com).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.baseUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreaming","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreamingCoalesce","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.blockStreamingCoalesce.idleMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreamingCoalesce.maxChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.blockStreamingCoalesce.minChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"label":"Mattermost Bot Token","help":"Bot token from Mattermost System Console -> Integrations -> Bot Accounts.","hasChildren":true}
{"recordType":"path","path":"channels.mattermost.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.botToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.botToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.botToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.capabilities","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.capabilities.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.chatmode","kind":"channel","type":"string","required":false,"enumValues":["oncall","onmessage","onchar"],"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Chat Mode","help":"Reply to channel messages on mention (\"oncall\"), on trigger chars (\">\" or \"!\") (\"onchar\"), or on every message (\"onmessage\").","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.chatmode","kind":"channel","type":"string","required":false,"enumValues":["oncall","onmessage","onchar"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.chunkMode","kind":"channel","type":"string","required":false,"enumValues":["length","newline"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.commands","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.commands.callbackPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.commands.callbackUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.commands.native","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.commands.nativeSkills","kind":"channel","type":["boolean","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.configWrites","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Config Writes","help":"Allow Mattermost to write config in response to channel events/commands (default: true).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.configWrites","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dangerouslyAllowNameMatching","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.defaultAccount","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.dmChannelRetry","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -2100,10 +2133,10 @@
{"recordType":"path","path":"channels.mattermost.markdown","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.markdown.tables","kind":"channel","type":"string","required":false,"enumValues":["off","bullets","code"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.name","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.oncharPrefixes","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Onchar Prefixes","help":"Trigger prefixes for onchar mode (default: [\">\", \"!\"]).","hasChildren":true}
{"recordType":"path","path":"channels.mattermost.oncharPrefixes","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.mattermost.oncharPrefixes.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.replyToMode","kind":"channel","type":"string","required":false,"enumValues":["off","first","all"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Mattermost Require Mention","help":"Require @mention in channels before responding (default: true).","hasChildren":false}
{"recordType":"path","path":"channels.mattermost.requireMention","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.mattermost.textChunkLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["channels","network"],"label":"Microsoft Teams","help":"Teams SDK; enterprise support.","hasChildren":true}
@@ -2114,6 +2147,7 @@
{"recordType":"path","path":"channels.msteams.appPassword.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.appPassword.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.appPassword.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.blockStreaming","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.blockStreamingCoalesce","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.msteams.blockStreamingCoalesce.idleMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.msteams.blockStreamingCoalesce.maxChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2207,7 +2241,7 @@
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.apiPassword.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2219,11 +2253,11 @@
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.blockStreamingCoalesce.idleMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.blockStreamingCoalesce.maxChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.blockStreamingCoalesce.minChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.botSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.chunkMode","kind":"channel","type":"string","required":false,"enumValues":["length","newline"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.accounts.*.dmPolicy","kind":"channel","type":"string","required":true,"enumValues":["pairing","allowlist","open","disabled"],"defaultValue":"pairing","deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2264,7 +2298,7 @@
{"recordType":"path","path":"channels.nextcloud-talk.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.allowFrom.*","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.allowPrivateNetwork","kind":"channel","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.apiPassword.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -2276,11 +2310,11 @@
{"recordType":"path","path":"channels.nextcloud-talk.blockStreamingCoalesce.idleMs","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.blockStreamingCoalesce.maxChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.blockStreamingCoalesce.minChars","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.botSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.botSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.nextcloud-talk.botSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.botSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.botSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.botSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.botSecretFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security","storage"],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.chunkMode","kind":"channel","type":"string","required":false,"enumValues":["length","newline"],"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.defaultAccount","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.nextcloud-talk.dmHistoryLimit","kind":"channel","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3324,7 +3358,7 @@
{"recordType":"path","path":"channels.zalo.accounts.*","kind":"channel","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.botToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.botToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.botToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3341,14 +3375,14 @@
{"recordType":"path","path":"channels.zalo.accounts.*.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.accounts.*.webhookUrl","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.allowFrom","kind":"channel","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.allowFrom.*","kind":"channel","type":["number","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.botToken","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.botToken.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.botToken.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.botToken.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3366,7 +3400,7 @@
{"recordType":"path","path":"channels.zalo.responsePrefix","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.tokenFile","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.webhookPath","kind":"channel","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.webhookSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.webhookSecret","kind":"channel","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","channels","network","security"],"hasChildren":true}
{"recordType":"path","path":"channels.zalo.webhookSecret.id","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.webhookSecret.provider","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"channels.zalo.webhookSecret.source","kind":"channel","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -3937,6 +3971,8 @@
{"recordType":"path","path":"models.providers.*.models.*.compat.thinkingFormat","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"models.providers.*.models.*.compat.toolCallArgumentsEncoding","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"models.providers.*.models.*.compat.toolSchemaProfile","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"models.providers.*.models.*.compat.unsupportedToolSchemaKeywords","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"models.providers.*.models.*.compat.unsupportedToolSchemaKeywords.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"models.providers.*.models.*.contextWindow","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"models.providers.*.models.*.cost","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"models.providers.*.models.*.cost.cacheRead","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -4313,6 +4349,15 @@
{"recordType":"path","path":"plugins.entries.line.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.line.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.line.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.litellm","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/litellm-provider","help":"OpenClaw LiteLLM provider plugin (plugin: litellm)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.litellm.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/litellm-provider Config","help":"Plugin-defined config payload for litellm.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.litellm.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable @openclaw/litellm-provider","hasChildren":false}
{"recordType":"path","path":"plugins.entries.litellm.hooks","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Hook Policy","help":"Per-plugin typed hook policy controls for core-enforced safety gates. Use this to constrain high-impact hook categories without disabling the entire plugin.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.litellm.hooks.allowPromptInjection","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Prompt Injection Hooks","help":"Controls whether this plugin may mutate prompts through typed hooks. Set false to block `before_prompt_build` and ignore prompt-mutating fields from legacy `before_agent_start`, while preserving legacy `modelOverride` and `providerOverride` behavior.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.litellm.subagent","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Plugin Subagent Policy","help":"Per-plugin subagent runtime controls for model override trust and allowlists. Keep this unset unless a plugin must explicitly steer subagent model selection.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.litellm.subagent.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Plugin Subagent Allowed Models","help":"Allowed override targets for trusted plugin subagent runs as canonical \"provider/model\" refs. Use \"*\" only when you intentionally allow any model.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.litellm.subagent.allowedModels.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.litellm.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.llm-task","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"LLM Task","help":"Generic JSON-only LLM tool for structured tasks callable from workflows. (plugin: llm-task)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.llm-task.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"LLM Task Config","help":"Plugin-defined config payload for llm-task.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.llm-task.config.allowedModels","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
@@ -4539,6 +4584,7 @@
{"recordType":"path","path":"plugins.entries.openshell.config.gateway","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Gateway Name","help":"Optional OpenShell gateway name passed as --gateway.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.openshell.config.gatewayEndpoint","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Gateway Endpoint","help":"Optional OpenShell gateway endpoint passed as --gateway-endpoint.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.openshell.config.gpu","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"GPU","help":"Request GPU resources when creating the sandbox.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.openshell.config.mode","kind":"plugin","type":"string","required":false,"enumValues":["mirror","remote"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Mode","help":"Sandbox mode. Use mirror for the default local-workspace flow or remote for a fully remote workspace.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.openshell.config.policy","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Policy File","help":"Optional path to a custom OpenShell sandbox policy YAML.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.openshell.config.providers","kind":"plugin","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Providers","help":"Provider names to attach when a sandbox is created.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.openshell.config.providers.*","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -4739,7 +4785,7 @@
{"recordType":"path","path":"plugins.entries.voice-call.config.outbound.notifyHangupDelaySec","kind":"plugin","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Notify Hangup Delay (sec)","hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.plivo","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"plugins.entries.voice-call.config.plivo.authId","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.plivo.authToken","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.plivo.authToken","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.provider","kind":"plugin","type":"string","required":false,"enumValues":["telnyx","twilio","plivo","mock"],"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Provider","help":"Use twilio, telnyx, or mock for dev/no-network.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.publicUrl","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Public Webhook URL","hasChildren":false}
{"recordType":"path","path":"plugins.entries.voice-call.config.responseModel","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Response Model","hasChildren":false}
@@ -4869,6 +4915,11 @@
{"recordType":"path","path":"plugins.entries.whatsapp.subagent.allowModelOverride","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access"],"label":"Allow Plugin Subagent Model Override","help":"Explicitly allows this plugin to request provider/model overrides in background subagent runs. Keep false unless the plugin is trusted to steer model selection.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xai-plugin","help":"OpenClaw xAI plugin (plugin: xai)","hasChildren":true}
{"recordType":"path","path":"plugins.entries.xai.config","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"@openclaw/xai-plugin Config","help":"Plugin-defined config payload for xai.","hasChildren":true}
{"recordType":"path","path":"plugins.entries.xai.config.codeExecution","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"plugins.entries.xai.config.codeExecution.enabled","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Enable Code Execution","help":"Enable the code_execution tool for remote xAI sandbox analysis.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai.config.codeExecution.maxTurns","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Code Execution Max Turns","help":"Optional max internal tool turns xAI may use for code_execution.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai.config.codeExecution.model","kind":"plugin","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models"],"label":"Code Execution Model","help":"xAI model override for code_execution.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai.config.codeExecution.timeoutSeconds","kind":"plugin","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance"],"label":"Code Execution Timeout","help":"Timeout in seconds for code_execution requests.","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai.config.webSearch","kind":"plugin","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"plugins.entries.xai.config.webSearch.apiKey","kind":"plugin","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security"],"label":"Grok Search API Key","help":"xAI API key for Grok web search (fallback: XAI_API_KEY env var).","hasChildren":false}
{"recordType":"path","path":"plugins.entries.xai.config.webSearch.inlineCitations","kind":"plugin","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Inline Citations","help":"Include inline markdown citations in Grok responses.","hasChildren":false}
@@ -5122,7 +5173,7 @@
{"recordType":"path","path":"tools.exec.applyPatch","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"tools.exec.applyPatch.allowModels","kind":"core","type":"array","required":false,"deprecated":false,"sensitive":false,"tags":["access","tools"],"label":"apply_patch Model Allowlist","help":"Optional allowlist of model ids (e.g. \"gpt-5.2\" or \"openai/gpt-5.2\").","hasChildren":true}
{"recordType":"path","path":"tools.exec.applyPatch.allowModels.*","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.exec.applyPatch.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable apply_patch","help":"Experimental. Enables apply_patch for OpenAI models when allowed by tool policy.","hasChildren":false}
{"recordType":"path","path":"tools.exec.applyPatch.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable apply_patch","help":"Enable or disable apply_patch for OpenAI and OpenAI Codex models when allowed by tool policy (default: true).","hasChildren":false}
{"recordType":"path","path":"tools.exec.applyPatch.workspaceOnly","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["access","advanced","security","tools"],"label":"apply_patch Workspace-Only","help":"Restrict apply_patch paths to the workspace directory (default: true). Set false to allow writing outside the workspace (dangerous).","hasChildren":false}
{"recordType":"path","path":"tools.exec.ask","kind":"core","type":"string","required":false,"enumValues":["off","on-miss","always"],"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Exec Ask","help":"Approval strategy for when exec commands require human confirmation before running. Use stricter ask behavior in shared channels and lower-friction settings in private operator contexts.","hasChildren":false}
{"recordType":"path","path":"tools.exec.backgroundMs","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
@@ -5502,6 +5553,17 @@
{"recordType":"path","path":"tools.web.search.perplexity.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.search.provider","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Web Search Provider","help":"Search provider id. Auto-detected from available API keys if omitted.","hasChildren":false}
{"recordType":"path","path":"tools.web.search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"Web Search Timeout (sec)","help":"Timeout in seconds for web_search requests.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":true}
{"recordType":"path","path":"tools.web.x_search.apiKey","kind":"core","type":["object","string"],"required":false,"deprecated":false,"sensitive":true,"tags":["auth","security","tools"],"label":"xAI API Key","help":"xAI API key for X search (fallback: XAI_API_KEY env var).","hasChildren":true}
{"recordType":"path","path":"tools.web.x_search.apiKey.id","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.apiKey.provider","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.apiKey.source","kind":"core","type":"string","required":true,"deprecated":false,"sensitive":false,"tags":[],"hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.cacheTtlMinutes","kind":"core","type":"number","required":false,"deprecated":false,"sensitive":false,"tags":["performance","storage","tools"],"label":"X Search Cache TTL (min)","help":"Cache TTL in minutes for x_search results.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.enabled","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"Enable X Search Tool","help":"Enable the x_search tool (requires XAI_API_KEY or tools.web.x_search.apiKey).","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.inlineCitations","kind":"core","type":"boolean","required":false,"deprecated":false,"sensitive":false,"tags":["tools"],"label":"X Search Inline Citations","help":"Keep inline citations from xAI in x_search responses when available (default: false).","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.maxTurns","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"X Search Max Turns","help":"Optional max internal search/tool turns xAI may use per x_search request. Omit to let xAI choose.","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.model","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["models","tools"],"label":"X Search Model","help":"Model to use for X search (default: \"grok-4-1-fast-non-reasoning\").","hasChildren":false}
{"recordType":"path","path":"tools.web.x_search.timeoutSeconds","kind":"core","type":"integer","required":false,"deprecated":false,"sensitive":false,"tags":["performance","tools"],"label":"X Search Timeout (sec)","help":"Timeout in seconds for x_search requests.","hasChildren":false}
{"recordType":"path","path":"ui","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"UI","help":"UI presentation settings for accenting and assistant identity shown in control surfaces. Use this for branding and readability customization without changing runtime behavior.","hasChildren":true}
{"recordType":"path","path":"ui.assistant","kind":"core","type":"object","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Assistant Appearance","help":"Assistant display identity settings for name and avatar shown in UI surfaces. Keep these values aligned with your operator-facing persona and support expectations.","hasChildren":true}
{"recordType":"path","path":"ui.assistant.avatar","kind":"core","type":"string","required":false,"deprecated":false,"sensitive":false,"tags":["advanced"],"label":"Assistant Avatar","help":"Assistant avatar image source used in UI surfaces (URL, path, or data URI depending on runtime support). Use trusted assets and consistent branding dimensions for clean rendering.","hasChildren":false}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -47,6 +47,10 @@
"source": "Quick Start",
"target": "快速开始"
},
{
"source": "Diffs",
"target": "Diffs"
},
{
"source": "Capability Cookbook",
"target": "能力扩展手册"

View File

@@ -129,7 +129,7 @@ Example `package.json`:
}
```
Each entry points to a hook directory containing `HOOK.md` and `handler.ts` (or `index.ts`).
Each entry points to a hook directory containing `HOOK.md` and a handler file. The loader tries `handler.ts`, `handler.js`, `index.ts`, `index.js` in order.
Hook packs can ship dependencies; they will be installed under `~/.openclaw/hooks/<id>`.
Each `openclaw.hooks` entry must stay inside the package directory after symlink
resolution; entries that escape are rejected.
@@ -236,6 +236,9 @@ Each event includes:
sessionId?: string,
// Agent bootstrap events (agent:bootstrap):
bootstrapFiles?: WorkspaceBootstrapFile[],
sessionKey?: string, // routing session key
sessionId?: string, // internal session UUID
agentId?: string, // resolved agent ID
// Message events (see Message Events section for full details):
from?: string, // message:received
to?: string, // message:sent
@@ -265,6 +268,25 @@ Triggered when agent commands are issued:
Internal hook payloads emit these as `type: "session"` with `action: "compact:before"` / `action: "compact:after"`; listeners subscribe with the combined keys above.
Specific handler registration uses the literal key format `${type}:${action}`. For these events, register `session:compact:before` and `session:compact:after`.
`session:compact:before` context fields:
- `sessionId`: internal session UUID
- `missingSessionKey`: true when no session key was available
- `messageCount`: number of messages before compaction
- `tokenCount`: token count before compaction (may be absent)
- `messageCountOriginal`: message count from the full untruncated session history
- `tokenCountOriginal`: token count of the full original history (may be absent)
`session:compact:after` context fields (in addition to `sessionId` and `missingSessionKey`):
- `messageCount`: message count after compaction
- `tokenCount`: token count after compaction (may be absent)
- `compactedCount`: number of messages that were compacted/removed
- `summaryLength`: character length of the generated compaction summary
- `tokensBefore`: token count from before compaction (for delta calculation)
- `tokensAfter`: token count after compaction
- `firstKeptEntryId`: ID of the first message entry retained after compaction
### Agent Events
- **`agent:bootstrap`**: Before workspace bootstrap files are injected (hooks may mutate `context.bootstrapFiles`)
@@ -293,12 +315,16 @@ Session events include rich context about the session and changes:
label?: string | null, // Human-readable session label
// AI model configuration
model?: string | null, // Model override (e.g., "claude-opus-4-5")
model?: string | null, // Model override (e.g., "claude-sonnet-4-6")
thinkingLevel?: string | null, // Thinking level ("off"|"low"|"med"|"high")
verboseLevel?: string | null, // Verbose output level
reasoningLevel?: string | null, // Reasoning mode override
elevatedLevel?: string | null, // Elevated mode override
responseUsage?: "off" | "tokens" | "full" | null, // Usage display mode
responseUsage?: "off" | "tokens" | "full" | "on" | null, // Usage display mode ("on" is backwards-compat alias for "full")
fastMode?: boolean | null, // Fast/turbo mode toggle
spawnedWorkspaceDir?: string | null, // Workspace dir override for spawned subagents
subagentRole?: "orchestrator" | "leaf" | null, // Subagent role assignment
subagentControlScope?: "children" | "none" | null, // Scope of subagent control
// Tool execution settings
execHost?: string | null, // Exec host (sandbox|gateway|node)
@@ -318,7 +344,7 @@ Session events include rich context about the session and changes:
}
```
**Security note:** Only privileged clients (including the Control UI) can trigger `session:patch` events. Standard WebChat clients are blocked from patching sessions (see PR #20800), so the hook will not fire from those connections.
**Security note:** Only privileged clients (including the Control UI) can trigger `session:patch` events. Standard WebChat clients are blocked from patching sessions, so the hook will not fire from those connections.
See `SessionsPatchParamsSchema` in `src/gateway/protocol/schema/sessions.ts` for the complete type definition.
@@ -459,17 +485,206 @@ These hooks are not event-stream listeners; they let plugins synchronously adjus
### Plugin Hook Events
#### before_tool_call
Runs before each tool call. Plugins can modify parameters, block the call, or request user approval.
Return fields:
- **`params`**: Override tool parameters (merged with original params)
- **`block`**: Set to `true` to block the tool call
- **`blockReason`**: Reason shown to the agent when blocked
- **`requireApproval`**: Pause execution and wait for user approval via channels
The `requireApproval` field triggers native platform approval (Telegram buttons, Discord components, `/approve` command) instead of relying on the agent to cooperate:
```typescript
{
requireApproval: {
title: "Sensitive operation",
description: "This tool call modifies production data",
severity: "warning", // "info" | "warning" | "critical"
timeoutMs: 120000, // default: 120s
timeoutBehavior: "deny", // "allow" | "deny" (default)
onResolution: async (decision) => {
// Called after the user resolves: "allow-once", "allow-always", "deny", "timeout", or "cancelled"
},
}
}
```
The `onResolution` callback is invoked with the final decision string after the approval resolves, times out, or is cancelled. It runs in-process within the plugin (not sent to the gateway). Use it to persist decisions, update caches, or perform cleanup.
The `pluginId` field is stamped automatically by the hook runner from the plugin registration. When multiple plugins return `requireApproval`, the first one (highest priority) wins.
`block` takes precedence over `requireApproval`: if the merged hook result has both `block: true` and a `requireApproval` field, the tool call is blocked immediately without triggering the approval flow. This ensures a higher-priority plugin's block cannot be overridden by a lower-priority plugin's approval request.
If the gateway is unavailable or does not support plugin approvals, the tool call falls back to a soft block using the `description` as the block reason.
#### before_install
Runs after the built-in install security scan and before installation continues. OpenClaw fires this hook for interactive skill installs as well as plugin bundle, package, and single-file installs.
Return fields:
- **`findings`**: Additional scan findings to surface as warnings
- **`block`**: Set to `true` to block the install
- **`blockReason`**: Human-readable reason shown when blocked
Event fields:
- **`targetType`**: Install target category (`skill` or `plugin`)
- **`targetName`**: Human-readable skill name or plugin id for the install target
- **`sourcePath`**: Absolute path to the install target content being scanned
- **`sourcePathKind`**: Whether the scanned content is a `file` or `directory`
- **`origin`**: Normalized install origin when available (for example `openclaw-bundled`, `openclaw-workspace`, `plugin-bundle`, `plugin-package`, or `plugin-file`)
- **`request`**: Provenance for the install request, including `kind`, `mode`, and optional `requestedSpecifier`
- **`builtinScan`**: Structured result of the built-in scanner, including `status`, summary counts, findings, and optional `error`
- **`skill`**: Skill install metadata when `targetType` is `skill`, including `installId` and the selected `installSpec`
- **`plugin`**: Plugin install metadata when `targetType` is `plugin`, including the canonical `pluginId`, normalized `contentType`, optional `packageName` / `manifestId` / `version`, and `extensions`
Example event (plugin package install):
```json
{
"targetType": "plugin",
"targetName": "acme-audit",
"sourcePath": "/var/folders/.../openclaw-plugin-acme-audit/package",
"sourcePathKind": "directory",
"origin": "plugin-package",
"request": {
"kind": "plugin-npm",
"mode": "install",
"requestedSpecifier": "@acme/openclaw-plugin-audit@1.4.2"
},
"builtinScan": {
"status": "ok",
"scannedFiles": 12,
"critical": 0,
"warn": 1,
"info": 0,
"findings": [
{
"severity": "warn",
"ruleId": "network_fetch",
"file": "dist/index.js",
"line": 88,
"message": "Dynamic network fetch detected during install review."
}
]
},
"plugin": {
"pluginId": "acme-audit",
"contentType": "package",
"packageName": "@acme/openclaw-plugin-audit",
"manifestId": "acme-audit",
"version": "1.4.2",
"extensions": ["./dist/index.js"]
}
}
```
Skill installs use the same event shape with `targetType: "skill"` and a `skill` object instead of `plugin`.
Decision semantics:
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_install`: `{ block: false }` is treated as no decision.
Use this hook for external security scanners, policy engines, or enterprise approval gates that need to audit install sources before they are installed.
#### Compaction lifecycle
Compaction lifecycle hooks exposed through the plugin hook runner:
- **`before_compaction`**: Runs before compaction with count/token metadata
- **`after_compaction`**: Runs after compaction with compaction summary metadata
### Complete Plugin Hook Reference
All 27 hooks registered via the Plugin SDK. Hooks marked **sequential** run in priority order and can modify results; **parallel** hooks are fire-and-forget.
#### Model and prompt hooks
| Hook | When | Execution | Returns |
| ---------------------- | -------------------------------------------- | ---------- | ---------------------------------------------------------- |
| `before_model_resolve` | Before model/provider lookup | Sequential | `{ modelOverride?, providerOverride? }` |
| `before_prompt_build` | After model resolved, session messages ready | Sequential | `{ systemPrompt?, prependContext?, appendSystemContext? }` |
| `before_agent_start` | Legacy combined hook (prefer the two above) | Sequential | Union of both result shapes |
| `llm_input` | Immediately before the LLM API call | Parallel | `void` |
| `llm_output` | Immediately after LLM response received | Parallel | `void` |
#### Agent lifecycle hooks
| Hook | When | Execution | Returns |
| ------------------- | ---------------------------------------------- | --------- | ------- |
| `agent_end` | After agent run completes (success or failure) | Parallel | `void` |
| `before_reset` | When `/new` or `/reset` clears a session | Parallel | `void` |
| `before_compaction` | Before compaction summarizes history | Parallel | `void` |
| `after_compaction` | After compaction completes | Parallel | `void` |
#### Session lifecycle hooks
| Hook | When | Execution | Returns |
| --------------- | ------------------------- | --------- | ------- |
| `session_start` | When a new session begins | Parallel | `void` |
| `session_end` | When a session ends | Parallel | `void` |
#### Message flow hooks
| Hook | When | Execution | Returns |
| ---------------------- | ------------------------------------------------- | -------------------- | ----------------------------- |
| `inbound_claim` | Before command/agent dispatch; first-claim wins | Sequential | `{ handled: boolean }` |
| `message_received` | After an inbound message is received | Parallel | `void` |
| `before_dispatch` | After commands parsed, before model dispatch | Sequential | `{ handled: boolean, text? }` |
| `message_sending` | Before an outbound message is delivered | Sequential | `{ content?, cancel? }` |
| `message_sent` | After an outbound message is delivered | Parallel | `void` |
| `before_message_write` | Before a message is written to session transcript | **Sync**, sequential | `{ block?, message? }` |
#### Tool execution hooks
| Hook | When | Execution | Returns |
| --------------------- | --------------------------------------------- | -------------------- | ----------------------------------------------------- |
| `before_tool_call` | Before each tool call | Sequential | `{ params?, block?, blockReason?, requireApproval? }` |
| `after_tool_call` | After a tool call completes | Parallel | `void` |
| `tool_result_persist` | Before a tool result is written to transcript | **Sync**, sequential | `{ message? }` |
#### Subagent hooks
| Hook | When | Execution | Returns |
| -------------------------- | ------------------------------------------ | ---------- | --------------------------------- |
| `subagent_spawning` | Before a subagent session is created | Sequential | `{ status, threadBindingReady? }` |
| `subagent_delivery_target` | After spawning, to resolve delivery target | Sequential | `{ origin? }` |
| `subagent_spawned` | After a subagent is fully spawned | Parallel | `void` |
| `subagent_ended` | When a subagent session terminates | Parallel | `void` |
#### Gateway hooks
| Hook | When | Execution | Returns |
| --------------- | ------------------------------------------ | --------- | ------- |
| `gateway_start` | After the gateway process is fully started | Parallel | `void` |
| `gateway_stop` | When the gateway is shutting down | Parallel | `void` |
#### Install hooks
| Hook | When | Execution | Returns |
| ---------------- | ----------------------------------------------------- | ---------- | ------------------------------------- |
| `before_install` | After built-in security scan, before install proceeds | Sequential | `{ findings?, block?, blockReason? }` |
<Note>
Two hooks (`tool_result_persist` and `before_message_write`) are **synchronous only** — they must not return a Promise. Returning a Promise from these hooks is caught at runtime and the result is discarded with a warning.
</Note>
For full handler signatures and context types, see [Plugin Architecture](/plugins/architecture).
### Future Events
Planned event types:
The following event types are planned for the internal hook event stream.
Note that `session_start` and `session_end` already exist as [Plugin Hook API](/plugins/architecture#provider-runtime-hooks) hooks
but are not yet available as internal hook event keys in `HOOK.md` metadata:
- **`session:start`**: When a new session begins
- **`session:end`**: When a session ends
- **`session:start`**: When a new session begins (planned for internal hook stream; available as plugin hook `session_start`)
- **`session:end`**: When a session ends (planned for internal hook stream; available as plugin hook `session_end`)
- **`agent:error`**: When an agent encounters an error
## Creating Custom Hooks
@@ -885,8 +1100,8 @@ metadata: { "openclaw": { "events": ["command"] } } # General - more overhead
The gateway logs hook loading at startup:
```
Registered hook: session-memory -> command:new
```text
Registered hook: session-memory -> command:new, command:reset
Registered hook: bootstrap-extra-files -> agent:bootstrap
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup

View File

@@ -212,6 +212,60 @@ Per-group configuration:
- Uses `allowFrom` and `groupAllowFrom` to determine command authorization.
- Authorized senders can run control commands even without mentioning in groups.
## ACP conversation bindings
BlueBubbles chats can be turned into durable ACP workspaces without changing the transport layer.
Fast operator flow:
- Run `/acp spawn codex --bind here` inside the DM or allowed group chat.
- Future messages in that same BlueBubbles conversation route to the spawned ACP session.
- `/new` and `/reset` reset the same bound ACP session in place.
- `/acp close` closes the ACP session and removes the binding.
Configured persistent bindings are also supported through top-level `bindings[]` entries with `type: "acp"` and `match.channel: "bluebubbles"`.
`match.peer.id` can use any supported BlueBubbles target form:
- normalized DM handle such as `+15555550123` or `user@example.com`
- `chat_id:<id>`
- `chat_guid:<guid>`
- `chat_identifier:<identifier>`
For stable group bindings, prefer `chat_id:*` or `chat_identifier:*`.
Example:
```json5
{
agents: {
list: [
{
id: "codex",
runtime: {
type: "acp",
acp: { agent: "codex", backend: "acpx", mode: "persistent" },
},
},
],
},
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "bluebubbles",
accountId: "default",
peer: { kind: "dm", id: "+15555550123" },
},
acp: { label: "codex-imessage" },
},
],
}
```
See [ACP Agents](/tools/acp-agents) for shared ACP binding behavior.
## Typing + read receipts
- **Typing indicators**: Sent automatically before and during response generation.
@@ -266,8 +320,9 @@ Available actions:
- **addParticipant**: Add someone to a group (`chatGuid`, `address`)
- **removeParticipant**: Remove someone from a group (`chatGuid`, `address`)
- **leaveGroup**: Leave a group chat (`chatGuid`)
- **sendAttachment**: Send media/files (`to`, `buffer`, `filename`, `asVoice`)
- **upload-file**: Send media/files (`to`, `buffer`, `filename`, `asVoice`)
- Voice memos: set `asVoice: true` with **MP3** or **CAF** audio to send as an iMessage voice message. BlueBubbles converts MP3 → CAF when sending voice memos.
- Legacy alias: `sendAttachment` still works, but `upload-file` is the canonical action name.
### Message IDs (short vs full)

View File

@@ -103,7 +103,7 @@ openclaw config set channels.discord.enabled true --strict-json
openclaw gateway
```
If OpenClaw is already running as a background service, use `openclaw gateway restart` instead.
If OpenClaw is already running as a background service, restart it via the OpenClaw Mac app or by stopping and restarting the `openclaw gateway run` process.
</Step>
@@ -750,9 +750,13 @@ Default slash command settings:
Notes:
- `/acp spawn codex --bind here` binds the current Discord channel or thread in place and keeps future messages routed to the same ACP session.
- That can still mean "start a fresh Codex ACP session", but it does not create a new Discord thread by itself. The existing channel stays the chat surface.
- Codex may still run in its own `cwd` or backend workspace on disk. That workspace is runtime state, not a Discord thread.
- Thread messages can inherit the parent channel ACP binding.
- In a bound channel or thread, `/new` and `/reset` reset the same ACP session in place.
- Temporary thread bindings still work and can override target resolution while active.
- `spawnAcpSessions` is only required when OpenClaw needs to create/bind a child thread via `--thread auto|here`. It is not required for `/acp spawn ... --bind here` in the current channel.
See [ACP Agents](/tools/acp-agents) for binding behavior details.
@@ -944,11 +948,15 @@ Default slash command settings:
Config path:
- `channels.discord.execApprovals.enabled`
- `channels.discord.execApprovals.approvers`
- `channels.discord.execApprovals.approvers` (optional; falls back to owner IDs inferred from `allowFrom` and explicit DM `defaultTo` when possible)
- `channels.discord.execApprovals.target` (`dm` | `channel` | `both`, default: `dm`)
- `agentFilter`, `sessionFilter`, `cleanupAfterResolve`
When `target` is `channel` or `both`, the approval prompt is visible in the channel. Only configured approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.
Discord becomes an approval client when `enabled: true` and at least one approver can be resolved, either from `execApprovals.approvers` or from the account's existing owner config (`allowFrom`, legacy `dm.allowFrom`, or explicit DM `defaultTo`).
When `target` is `channel` or `both`, the approval prompt is visible in the channel. Only resolved approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.
Discord also renders the shared approval buttons used by other chat channels. The native Discord adapter mainly adds approver DM routing and channel fanout.
Gateway auth for this handler uses the same shared credential resolution contract as other Gateway clients:
@@ -957,7 +965,7 @@ Default slash command settings:
- remote-mode support via `gateway.remote.*` when applicable
- URL overrides are override-safe: CLI overrides do not reuse implicit credentials, and env overrides use env credentials only
If approvals fail with unknown approval IDs, verify approver list and feature enablement.
Exec approvals expire after 30 minutes by default. If approvals fail with unknown approval IDs, verify approver resolution and feature enablement.
Related docs: [Exec approvals](/tools/exec-approvals)
@@ -988,7 +996,7 @@ Default gate behavior:
## Components v2 UI
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept `components` for custom UI (advanced; requires Carbon component instances), while legacy `embeds` remain available but are not recommended.
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept `components` for custom UI (advanced; requires constructing a component payload via the discord tool), while legacy `embeds` remain available but are not recommended.
- `channels.discord.ui.components.accentColor` sets the accent color used by Discord component containers (hex).
- Set per account with `channels.discord.accounts.<id>.ui.components.accentColor`.

View File

@@ -81,7 +81,7 @@ Lark (global) tenants should use [https://open.larksuite.com/app](https://open.l
2. Fill in the app name + description
3. Choose an app icon
![Create enterprise app](../images/feishu-step2-create-app.png)
![Create enterprise app](/images/feishu-step2-create-app.png)
### 3. Copy credentials
@@ -92,7 +92,7 @@ From **Credentials & Basic Info**, copy:
**Important:** keep the App Secret private.
![Get credentials](../images/feishu-step3-credentials.png)
![Get credentials](/images/feishu-step3-credentials.png)
### 4. Configure permissions
@@ -126,7 +126,7 @@ On **Permissions**, click **Batch import** and paste:
}
```
![Configure permissions](../images/feishu-step4-permissions.png)
![Configure permissions](/images/feishu-step4-permissions.png)
### 5. Enable bot capability
@@ -135,7 +135,7 @@ In **App Capability** > **Bot**:
1. Enable bot capability
2. Set the bot name
![Enable bot capability](../images/feishu-step5-bot-capability.png)
![Enable bot capability](/images/feishu-step5-bot-capability.png)
### 6. Configure event subscription
@@ -151,7 +151,7 @@ In **Event Subscription**:
⚠️ If the gateway is not running, the long-connection setup may fail to save.
![Configure event subscription](../images/feishu-step6-event-subscription.png)
![Configure event subscription](/images/feishu-step6-event-subscription.png)
### 7. Publish the app
@@ -206,7 +206,7 @@ When using webhook mode, set both `channels.feishu.verificationToken` and `chann
The screenshot below shows where to find the **Verification Token**. The **Encrypt Key** is listed in the same **Encryption** section.
![Verification Token location](../images/feishu-verification-token.png)
![Verification Token location](/images/feishu-verification-token.png)
### Configure via environment variables
@@ -395,6 +395,8 @@ In addition to allowing the group itself, **all messages** in that group are gat
---
<a id="get-groupuser-ids"></a>
## Get group/user IDs
### Group IDs (chat_id)

View File

@@ -201,6 +201,7 @@ Notes:
- Default webhook path is `/googlechat` if `webhookPath` isnt set.
- `dangerouslyAllowNameMatching` re-enables mutable email principal matching for allowlists (break-glass compatibility mode).
- Reactions are available via the `reactions` tool and `channels action` when `actions.reactions` is enabled.
- Message actions expose `send` for text and `upload-file` for explicit attachment sends. `upload-file` accepts `media` / `filePath` / `path` plus optional `message`, `filename`, and thread targeting.
- `typingIndicator` supports `none`, `message` (default), and `reaction` (reaction requires user OAuth).
- Attachments are downloaded through the Chat API and stored in the media pipeline (size capped by `mediaMaxMb`).

View File

@@ -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)
@@ -54,6 +54,8 @@ If you want...
- Direct chats use the main session (or per-sender if configured).
- Heartbeats are skipped for group sessions.
<a id="pattern-personal-dms-public-groups-single-agent"></a>
## Pattern: personal DMs + public groups (single agent)
Yes — this works well if your “personal” traffic is **DMs** and your “public” traffic is **groups**.
@@ -187,7 +189,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.

View File

@@ -184,6 +184,58 @@ imsg send <handle> "test"
</Tab>
</Tabs>
## ACP conversation bindings
Legacy iMessage chats can also be bound to ACP sessions.
Fast operator flow:
- Run `/acp spawn codex --bind here` inside the DM or allowed group chat.
- Future messages in that same iMessage conversation route to the spawned ACP session.
- `/new` and `/reset` reset the same bound ACP session in place.
- `/acp close` closes the ACP session and removes the binding.
Configured persistent bindings are supported through top-level `bindings[]` entries with `type: "acp"` and `match.channel: "imessage"`.
`match.peer.id` can use:
- normalized DM handle such as `+15555550123` or `user@example.com`
- `chat_id:<id>` (recommended for stable group bindings)
- `chat_guid:<guid>`
- `chat_identifier:<identifier>`
Example:
```json5
{
agents: {
list: [
{
id: "codex",
runtime: {
type: "acp",
acp: { agent: "codex", backend: "acpx", mode: "persistent" },
},
},
],
},
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "imessage",
accountId: "default",
peer: { kind: "group", id: "chat_id:123" },
},
acp: { label: "codex-group" },
},
],
}
```
See [ACP Agents](/tools/acp-agents) for shared ACP binding behavior.
## Deployment patterns
<AccordionGroup>

View File

@@ -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

View File

@@ -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

View File

@@ -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.
@@ -470,6 +510,23 @@ Matrix supports native Matrix threads for both automatic replies and message-too
- Top-level Matrix room/DM `/focus` creates a new Matrix thread and binds it to the target session when `threadBindings.spawnSubagentSessions=true`.
- Running `/focus` or `/acp spawn --thread here` inside an existing Matrix thread binds that current thread instead.
## ACP conversation bindings
Matrix rooms, DMs, and existing Matrix threads can be turned into durable ACP workspaces without changing the chat surface.
Fast operator flow:
- Run `/acp spawn codex --bind here` inside the Matrix DM, room, or existing thread you want to keep using.
- In a top-level Matrix DM or room, the current DM/room stays the chat surface and future messages route to the spawned ACP session.
- Inside an existing Matrix thread, `--bind here` binds that current thread in place.
- `/new` and `/reset` reset the same bound ACP session in place.
- `/acp close` closes the ACP session and removes the binding.
Notes:
- `--bind here` does not create a child Matrix thread.
- `threadBindings.spawnAcpSessions` is only required for `/acp spawn --thread auto|here`, where OpenClaw needs to create or bind a child Matrix thread.
### Thread Binding Config
Matrix inherits global defaults from `session.threadBindings`, and also supports per-channel overrides:
@@ -644,8 +701,8 @@ Live directory lookup uses the logged-in Matrix account:
- `homeserver`: homeserver URL, for example `https://matrix.example.org`.
- `allowPrivateNetwork`: allow this Matrix account to connect to private/internal homeservers. Enable this when the homeserver resolves to `localhost`, a LAN/Tailscale IP, or an internal host such as `matrix-synapse`.
- `userId`: full Matrix user ID, for example `@bot:example.org`.
- `accessToken`: access token for token-based auth.
- `password`: password for password-based login.
- `accessToken`: access token for token-based auth. Plaintext values and SecretRef values are supported for `channels.matrix.accessToken` and `channels.matrix.accounts.<id>.accessToken` across env/file/exec providers. See [Secrets Management](/gateway/secrets).
- `password`: password for password-based login. Plaintext values and SecretRef values are supported.
- `deviceId`: explicit Matrix device ID.
- `deviceName`: device display name for password login.
- `avatarUrl`: stored self-avatar URL for profile sync and `set-profile` updates.
@@ -656,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`).
@@ -666,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`).

View File

@@ -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,

View File

@@ -11,7 +11,7 @@ title: "Microsoft Teams"
Updated: 2026-01-21
Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards.
Status: text + DM attachments are supported; channel/group file sending requires `sharePointSiteId` + Graph permissions (see [Sending files in group chats](#sending-files-in-group-chats)). Polls are sent via Adaptive Cards. Message actions expose explicit `upload-file` for file-first sends.
## Plugin required
@@ -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:
@@ -527,6 +527,7 @@ Teams recently introduced two channel UI styles over the same underlying data mo
- **DMs:** Images and file attachments work via Teams bot file APIs.
- **Channels/groups:** Attachments live in M365 storage (SharePoint/OneDrive). The webhook payload only includes an HTML stub, not the actual file bytes. **Graph API permissions are required** to download channel attachments.
- For explicit file-first sends, use `action=upload-file` with `media` / `filePath` / `path`; optional `message` becomes the accompanying text/comment, and `filename` overrides the uploaded name.
Without Graph permissions, channel messages with images will be received as text-only (the image content is not accessible to the bot).
By default, OpenClaw only downloads media from Microsoft/Teams hostnames. Override with `channels.msteams.mediaAllowHosts` (use `["*"]` to allow any host).

View File

@@ -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,

View File

@@ -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.

View File

@@ -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)

View File

@@ -806,21 +806,23 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \
Config path:
- `channels.telegram.execApprovals.enabled`
- `channels.telegram.execApprovals.approvers`
- `channels.telegram.execApprovals.approvers` (optional; falls back to numeric owner IDs inferred from `allowFrom` and direct `defaultTo` when possible)
- `channels.telegram.execApprovals.target` (`dm` | `channel` | `both`, default: `dm`)
- `agentFilter`, `sessionFilter`
Approvers must be numeric Telegram user IDs. When `enabled` is false or `approvers` is empty, Telegram does not act as an exec approval client. Approval requests fall back to other configured approval routes or the exec approval fallback policy.
Approvers must be numeric Telegram user IDs. Telegram becomes an exec approval client when `enabled` is true and at least one approver can be resolved, either from `execApprovals.approvers` or from the account's numeric owner config (`allowFrom` and direct-message `defaultTo`). Approval requests otherwise fall back to other configured approval routes or the exec approval fallback policy.
Telegram also renders the shared approval buttons used by other chat channels. The native Telegram adapter mainly adds approver DM routing, channel/topic fanout, and typing hints before delivery.
Delivery rules:
- `target: "dm"` sends approval prompts only to configured approver DMs
- `target: "dm"` sends approval prompts only to resolved approver DMs
- `target: "channel"` sends the prompt back to the originating Telegram chat/topic
- `target: "both"` sends to approver DMs and the originating chat/topic
Only configured approvers can approve or deny. Non-approvers cannot use `/approve` and cannot use Telegram approval buttons.
Only resolved approvers can approve or deny. Non-approvers cannot use `/approve` and cannot use Telegram approval buttons.
Channel delivery shows the command text in the chat, so only enable `channel` or `both` in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for both the approval prompt and the post-approval follow-up.
Channel delivery shows the command text in the chat, so only enable `channel` or `both` in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for both the approval prompt and the post-approval follow-up. Exec approvals expire after 30 minutes by default.
Inline approval buttons also depend on `channels.telegram.capabilities.inlineButtons` allowing the target surface (`dm`, `group`, or `all`).
@@ -932,7 +934,7 @@ Primary reference:
- top-level `bindings[]` with `type: "acp"` and canonical topic id `chatId:topic:topicId` in `match.peer.id`: persistent ACP topic binding fields (see [ACP Agents](/tools/acp-agents#channel-specific-settings)).
- `channels.telegram.direct.<id>.topics.<threadId>.agentId`: route DM topics to a specific agent (same behavior as forum topics).
- `channels.telegram.execApprovals.enabled`: enable Telegram as a chat-based exec approval client for this account.
- `channels.telegram.execApprovals.approvers`: Telegram user IDs allowed to approve or deny exec requests. Required when exec approvals are enabled.
- `channels.telegram.execApprovals.approvers`: Telegram user IDs allowed to approve or deny exec requests. Optional when `channels.telegram.allowFrom` or a direct `channels.telegram.defaultTo` already identifies the owner.
- `channels.telegram.execApprovals.target`: `dm | channel | both` (default: `dm`). `channel` and `both` preserve the originating Telegram topic when present.
- `channels.telegram.execApprovals.agentFilter`: optional agent ID filter for forwarded approval prompts.
- `channels.telegram.execApprovals.sessionFilter`: optional session key filter (substring or regex) for forwarded approval prompts.

View File

@@ -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)

View File

@@ -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)

View File

@@ -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:

View File

@@ -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.

View File

@@ -17,6 +17,10 @@ over WebSocket. It keeps ACP sessions mapped to Gateway session keys.
runtime. It focuses on session routing, prompt delivery, and basic streaming
updates.
If you want an external MCP client to talk directly to OpenClaw channel
conversations instead of hosting an ACP harness session, use
[`openclaw mcp serve`](/cli/mcp) instead.
## Compatibility Matrix
| ACP area | Status | Notes |
@@ -143,6 +147,10 @@ Per-session `mcpServers` are not supported in bridge mode. If an ACP client
sends them during `newSession` or `loadSession`, the bridge returns a clear
error instead of silently ignoring them.
If you want ACPX-backed sessions to see OpenClaw plugin tools, enable the
gateway-side ACPX plugin bridge instead of trying to pass per-session
`mcpServers`. See [ACP Agents](/tools/acp-agents#plugin-tools-mcp-bridge).
## Use from `acpx` (Codex, Claude, other ACP clients)
If you want a coding agent such as Codex or Claude Code to talk to your

View File

@@ -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:

View File

@@ -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"
---

View File

@@ -15,6 +15,11 @@ Note: The **Model** section now includes a multi-select for the
Tip: `openclaw config` without a subcommand opens the same wizard. Use
`openclaw config get|set|unset` for non-interactive edits.
For web search, `openclaw configure --section web` lets you choose a provider
and configure its credentials. If you choose **Grok**, configure can also show
a separate follow-up step to enable `x_search` with the same `XAI_API_KEY` and
pick an `x_search` model. Other web-search providers do not show that step.
Related:
- Gateway configuration reference: [Configuration](/gateway/configuration)
@@ -32,5 +37,6 @@ Notes:
```bash
openclaw configure
openclaw configure --section web
openclaw configure --section model --section channels
```

View File

@@ -27,6 +27,7 @@ This page describes the current CLI behavior. If commands change, update this do
- [`agent`](/cli/agent)
- [`agents`](/cli/agents)
- [`acp`](/cli/acp)
- [`mcp`](/cli/mcp)
- [`status`](/cli/status)
- [`health`](/cli/health)
- [`sessions`](/cli/sessions)
@@ -63,6 +64,7 @@ This page describes the current CLI behavior. If commands change, update this do
- `--dev`: isolate state under `~/.openclaw-dev` and shift default ports.
- `--profile <name>`: isolate state under `~/.openclaw-<name>`.
- `--container <name>`: target a named container for execution.
- `--no-color`: disable ANSI colors.
- `--update`: shorthand for `openclaw update` (source installs only).
- `-V`, `--version`, `-v`: print version and exit.
@@ -154,10 +156,21 @@ openclaw [--dev] [--profile <name>] <command>
list
add
delete
bindings
bind
unbind
set-identity
acp
mcp
status
health
sessions
cleanup
tasks
list
show
notify
cancel
gateway
call
health
@@ -191,7 +204,7 @@ openclaw [--dev] [--profile <name>] <command>
fallbacks list|add|remove|clear
image-fallbacks list|add|remove|clear
scan
auth add|setup-token|paste-token
auth add|login|login-github-copilot|setup-token|paste-token
auth order get|set|clear
sandbox
list
@@ -347,7 +360,18 @@ Options:
- `--non-interactive`
- `--mode <local|remote>`
- `--flow <quickstart|advanced|manual>` (manual is an alias for advanced)
- `--auth-choice <setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ollama|ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|mistral-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|opencode-go|custom-api-key|skip>`
- `--auth-choice <choice>` where `<choice>` is one of:
`setup-token`, `token`, `chutes`, `deepseek-api-key`, `openai-codex`, `openai-api-key`,
`openrouter-api-key`, `kilocode-api-key`, `litellm-api-key`, `ai-gateway-api-key`,
`cloudflare-ai-gateway-api-key`, `moonshot-api-key`, `moonshot-api-key-cn`,
`kimi-code-api-key`, `synthetic-api-key`, `venice-api-key`, `together-api-key`,
`huggingface-api-key`, `apiKey`, `gemini-api-key`, `google-gemini-cli`, `zai-api-key`,
`zai-coding-global`, `zai-coding-cn`, `zai-global`, `zai-cn`, `xiaomi-api-key`,
`minimax-global-oauth`, `minimax-global-api`, `minimax-cn-oauth`, `minimax-cn-api`,
`opencode-zen`, `opencode-go`, `github-copilot`, `copilot-proxy`, `xai-api-key`,
`mistral-api-key`, `volcengine-api-key`, `byteplus-api-key`, `qianfan-api-key`,
`modelstudio-standard-api-key-cn`, `modelstudio-standard-api-key`,
`modelstudio-api-key-cn`, `modelstudio-api-key`, `custom-api-key`, `skip`
- `--token-provider <id>` (non-interactive; used with `--auth-choice token`)
- `--token <token>` (non-interactive; used with `--auth-choice token`)
- `--token-profile-id <id>` (non-interactive; default: `<provider>:manual`)
@@ -365,8 +389,8 @@ Options:
- `--minimax-api-key <key>`
- `--opencode-zen-api-key <key>`
- `--opencode-go-api-key <key>`
- `--custom-base-url <url>` (non-interactive; used with `--auth-choice custom-api-key` or `--auth-choice ollama`)
- `--custom-model-id <id>` (non-interactive; used with `--auth-choice custom-api-key` or `--auth-choice ollama`)
- `--custom-base-url <url>` (non-interactive; used with `--auth-choice custom-api-key`)
- `--custom-model-id <id>` (non-interactive; used with `--auth-choice custom-api-key`)
- `--custom-api-key <key>` (non-interactive; optional; used with `--auth-choice custom-api-key`; falls back to `CUSTOM_API_KEY` when omitted)
- `--custom-provider-id <id>` (non-interactive; optional custom provider id)
- `--custom-compatibility <openai|anthropic>` (non-interactive; optional; default `openai`)
@@ -385,8 +409,11 @@ Options:
- `--daemon-runtime <node|bun>`
- `--skip-channels`
- `--skip-skills`
- `--skip-search`
- `--skip-health`
- `--skip-ui`
- `--cloudflare-ai-gateway-account-id <id>`
- `--cloudflare-ai-gateway-gateway-id <id>`
- `--node-manager <npm|pnpm|bun>` (pnpm recommended; bun not recommended for Gateway runtime)
- `--json`
@@ -427,6 +454,9 @@ Options:
- `--yes`: accept defaults without prompting (headless).
- `--non-interactive`: skip prompts; apply safe migrations only.
- `--deep`: scan system services for extra gateway installs.
- `--repair` (alias: `--fix`): attempt automatic repairs for detected issues.
- `--force`: force repairs even when not strictly needed.
- `--generate-gateway-token`: generate a new gateway auth token.
## Channel helpers
@@ -580,15 +610,19 @@ Run one agent turn via the Gateway (or `--local` embedded).
Required:
- `--message <text>`
- `-m, --message <text>`
Options:
- `--to <dest>` (for session key and optional delivery)
- `-t, --to <dest>` (for session key and optional delivery)
- `--session-id <id>`
- `--thinking <off|minimal|low|medium|high|xhigh>` (GPT-5.2 + Codex models only)
- `--verbose <on|full|off>`
- `--channel <whatsapp|telegram|discord|slack|mattermost|signal|imessage|msteams>`
- `--agent <id>` (agent id; overrides routing bindings)
- `--thinking <off|minimal|low|medium|high|xhigh>` (provider support varies; not model-gated at CLI level)
- `--verbose <on|off>`
- `--channel <channel>` (delivery channel; omit to use the main session channel)
- `--reply-to <target>` (delivery target override, separate from session routing)
- `--reply-channel <channel>` (delivery channel override)
- `--reply-account <id>` (delivery account id override)
- `--local`
- `--deliver`
- `--json`
@@ -722,6 +756,12 @@ Options:
- `--verbose`
- `--store <path>`
- `--active <minutes>`
- `--agent <id>` (filter sessions by agent)
- `--all-agents` (show sessions across all agents)
Subcommands:
- `sessions cleanup` — remove expired or orphaned sessions
## Reset / Uninstall
@@ -759,6 +799,15 @@ Notes:
- `--non-interactive` requires `--yes` and explicit scopes (or `--all`).
### `tasks`
List and manage task runs across agents.
- `tasks list` — show active and recent task runs
- `tasks show <id>` — show details for a specific task run
- `tasks notify <id>` — send a notification for a task run
- `tasks cancel <id>` — cancel a running task
## Gateway
### `gateway`
@@ -816,10 +865,16 @@ Notes:
Tail Gateway file logs via RPC.
Notes:
Options:
- TTY sessions render a colorized, structured view; non-TTY falls back to plain text.
- `--json` emits line-delimited JSON (one log event per line).
- `--limit <n>`: maximum number of log lines to return
- `--max-bytes <n>`: maximum bytes to read from the log file
- `--follow`: follow the log file (tail -f style)
- `--interval <ms>`: polling interval in ms when following
- `--local-time`: display timestamps in local time
- `--json`: emit line-delimited JSON
- `--plain`: disable structured formatting
- `--no-color`: disable ANSI colors
Examples:
@@ -876,9 +931,10 @@ Anthropic Claude CLI migration:
```bash
openclaw models auth login --provider anthropic --method cli --set-default
openclaw onboard --auth-choice anthropic-cli
```
Note: `--auth-choice anthropic-cli` is a deprecated legacy alias. Use `models auth login` instead.
### `models` (root)
`openclaw models` is an alias for `models status`.
@@ -966,11 +1022,13 @@ Options:
- `--set-image`
- `--json`
### `models auth add|setup-token|paste-token`
### `models auth add|login|login-github-copilot|setup-token|paste-token`
Options:
- `add`: interactive auth helper
- `login`: `--provider <name>`, `--method <method>`, `--set-default`
- `login-github-copilot`: GitHub Copilot OAuth login flow
- `setup-token`: `--provider <name>` (default `anthropic`), `--yes`
- `paste-token`: `--provider <name>`, `--profile-id <id>`, `--expires-in <duration>`
@@ -1071,7 +1129,6 @@ Subcommands:
- `nodes reject <requestId>`
- `nodes rename --node <id|name|ip> --name <displayName>`
- `nodes invoke --node <id|name|ip> --command <command> [--params <json>] [--invoke-timeout <ms>] [--idempotency-key <key>]`
- `nodes run --node <id|name|ip> [--cwd <path>] [--env KEY=VAL] [--command-timeout <ms>] [--needs-screen-recording] [--invoke-timeout <ms>] <command...>` (mac node or headless node host)
- `nodes notify --node <id|name|ip> [--title <text>] [--body <text>] [--sound <name>] [--priority <passive|active|timeSensitive>] [--delivery <system|overlay|auto>] [--invoke-timeout <ms>]` (mac only)
Camera:

467
docs/cli/mcp.md Normal file
View File

@@ -0,0 +1,467 @@
---
summary: "Expose OpenClaw channel conversations over MCP and manage saved MCP server definitions"
read_when:
- Connecting Codex, Claude Code, or another MCP client to OpenClaw-backed channels
- Running `openclaw mcp serve`
- Managing OpenClaw-saved MCP server definitions
title: "mcp"
---
# mcp
`openclaw mcp` has two jobs:
- run OpenClaw as an MCP server with `openclaw mcp serve`
- manage OpenClaw-owned outbound MCP server definitions with `list`, `show`,
`set`, and `unset`
In other words:
- `serve` is OpenClaw acting as an MCP server
- `list` / `show` / `set` / `unset` is OpenClaw acting as an MCP client-side
registry for other MCP servers its runtimes may consume later
Use [`openclaw acp`](/cli/acp) when OpenClaw should host a coding harness
session itself and route that runtime through ACP.
## OpenClaw as an MCP server
This is the `openclaw mcp serve` path.
## When to use `serve`
Use `openclaw mcp serve` when:
- Codex, Claude Code, or another MCP client should talk directly to
OpenClaw-backed channel conversations
- you already have a local or remote OpenClaw Gateway with routed sessions
- you want one MCP server that works across OpenClaw's channel backends instead
of running separate per-channel bridges
Use [`openclaw acp`](/cli/acp) instead when OpenClaw should host the coding
runtime itself and keep the agent session inside OpenClaw.
## How it works
`openclaw mcp serve` starts a stdio MCP server. The MCP client owns that
process. While the client keeps the stdio session open, the bridge connects to a
local or remote OpenClaw Gateway over WebSocket and exposes routed channel
conversations over MCP.
Lifecycle:
1. the MCP client spawns `openclaw mcp serve`
2. the bridge connects to Gateway
3. routed sessions become MCP conversations and transcript/history tools
4. live events are queued in memory while the bridge is connected
5. if Claude channel mode is enabled, the same session can also receive
Claude-specific push notifications
Important behavior:
- live queue state starts when the bridge connects
- older transcript history is read with `messages_read`
- Claude push notifications only exist while the MCP session is alive
- when the client disconnects, the bridge exits and the live queue is gone
## Choose a client mode
Use the same bridge in two different ways:
- Generic MCP clients: standard MCP tools only. Use `conversations_list`,
`messages_read`, `events_poll`, `events_wait`, `messages_send`, and the
approval tools.
- Claude Code: standard MCP tools plus the Claude-specific channel adapter.
Enable `--claude-channel-mode on` or leave the default `auto`.
Today, `auto` behaves the same as `on`. There is no client capability detection
yet.
## What `serve` exposes
The bridge uses existing Gateway session route metadata to expose channel-backed
conversations. A conversation appears when OpenClaw already has session state
with a known route such as:
- `channel`
- recipient or destination metadata
- optional `accountId`
- optional `threadId`
This gives MCP clients one place to:
- list recent routed conversations
- read recent transcript history
- wait for new inbound events
- send a reply back through the same route
- see approval requests that arrive while the bridge is connected
## Usage
```bash
# Local Gateway
openclaw mcp serve
# Remote Gateway
openclaw mcp serve --url wss://gateway-host:18789 --token-file ~/.openclaw/gateway.token
# Remote Gateway with password auth
openclaw mcp serve --url wss://gateway-host:18789 --password-file ~/.openclaw/gateway.password
# Enable verbose bridge logs
openclaw mcp serve --verbose
# Disable Claude-specific push notifications
openclaw mcp serve --claude-channel-mode off
```
## Bridge tools
The current bridge exposes these MCP tools:
- `conversations_list`
- `conversation_get`
- `messages_read`
- `attachments_fetch`
- `events_poll`
- `events_wait`
- `messages_send`
- `permissions_list_open`
- `permissions_respond`
### `conversations_list`
Lists recent session-backed conversations that already have route metadata in
Gateway session state.
Useful filters:
- `limit`
- `search`
- `channel`
- `includeDerivedTitles`
- `includeLastMessage`
### `conversation_get`
Returns one conversation by `session_key`.
### `messages_read`
Reads recent transcript messages for one session-backed conversation.
### `attachments_fetch`
Extracts non-text message content blocks from one transcript message. This is a
metadata view over transcript content, not a standalone durable attachment blob
store.
### `events_poll`
Reads queued live events since a numeric cursor.
### `events_wait`
Long-polls until the next matching queued event arrives or a timeout expires.
Use this when a generic MCP client needs near-real-time delivery without a
Claude-specific push protocol.
### `messages_send`
Sends text back through the same route already recorded on the session.
Current behavior:
- requires an existing conversation route
- uses the session's channel, recipient, account id, and thread id
- sends text only
### `permissions_list_open`
Lists pending exec/plugin approval requests the bridge has observed since it
connected to the Gateway.
### `permissions_respond`
Resolves one pending exec/plugin approval request with:
- `allow-once`
- `allow-always`
- `deny`
## Event model
The bridge keeps an in-memory event queue while it is connected.
Current event types:
- `message`
- `exec_approval_requested`
- `exec_approval_resolved`
- `plugin_approval_requested`
- `plugin_approval_resolved`
- `claude_permission_request`
Important limits:
- the queue is live-only; it starts when the MCP bridge starts
- `events_poll` and `events_wait` do not replay older Gateway history by
themselves
- durable backlog should be read with `messages_read`
## Claude channel notifications
The bridge can also expose Claude-specific channel notifications. This is the
OpenClaw equivalent of a Claude Code channel adapter: standard MCP tools remain
available, but live inbound messages can also arrive as Claude-specific MCP
notifications.
Flags:
- `--claude-channel-mode off`: standard MCP tools only
- `--claude-channel-mode on`: enable Claude channel notifications
- `--claude-channel-mode auto`: current default; same bridge behavior as `on`
When Claude channel mode is enabled, the server advertises Claude experimental
capabilities and can emit:
- `notifications/claude/channel`
- `notifications/claude/channel/permission`
Current bridge behavior:
- inbound `user` transcript messages are forwarded as
`notifications/claude/channel`
- Claude permission requests received over MCP are tracked in-memory
- if the linked conversation later sends `yes abcde` or `no abcde`, the bridge
converts that to `notifications/claude/channel/permission`
- these notifications are live-session only; if the MCP client disconnects,
there is no push target
This is intentionally client-specific. Generic MCP clients should rely on the
standard polling tools.
## MCP client config
Example stdio client config:
```json
{
"mcpServers": {
"openclaw": {
"command": "openclaw",
"args": [
"mcp",
"serve",
"--url",
"wss://gateway-host:18789",
"--token-file",
"/path/to/gateway.token"
]
}
}
}
```
For most generic MCP clients, start with the standard tool surface and ignore
Claude mode. Turn Claude mode on only for clients that actually understand the
Claude-specific notification methods.
## Options
`openclaw mcp serve` supports:
- `--url <url>`: Gateway WebSocket URL
- `--token <token>`: Gateway token
- `--token-file <path>`: read token from file
- `--password <password>`: Gateway password
- `--password-file <path>`: read password from file
- `--claude-channel-mode <auto|on|off>`: Claude notification mode
- `-v`, `--verbose`: verbose logs on stderr
Prefer `--token-file` or `--password-file` over inline secrets when possible.
## Security and trust boundary
The bridge does not invent routing. It only exposes conversations that Gateway
already knows how to route.
That means:
- sender allowlists, pairing, and channel-level trust still belong to the
underlying OpenClaw channel configuration
- `messages_send` can only reply through an existing stored route
- approval state is live/in-memory only for the current bridge session
- bridge auth should use the same Gateway token or password controls you would
trust for any other remote Gateway client
If a conversation is missing from `conversations_list`, the usual cause is not
MCP configuration. It is missing or incomplete route metadata in the underlying
Gateway session.
## Testing
OpenClaw ships a deterministic Docker smoke for this bridge:
```bash
pnpm test:docker:mcp-channels
```
That smoke:
- starts a seeded Gateway container
- starts a second container that spawns `openclaw mcp serve`
- verifies conversation discovery, transcript reads, attachment metadata reads,
live event queue behavior, and outbound send routing
- validates Claude-style channel and permission notifications over the real
stdio MCP bridge
This is the fastest way to prove the bridge works without wiring a real
Telegram, Discord, or iMessage account into the test run.
For broader testing context, see [Testing](/help/testing).
## Troubleshooting
### No conversations returned
Usually means the Gateway session is not already routable. Confirm that the
underlying session has stored channel/provider, recipient, and optional
account/thread route metadata.
### `events_poll` or `events_wait` misses older messages
Expected. The live queue starts when the bridge connects. Read older transcript
history with `messages_read`.
### Claude notifications do not show up
Check all of these:
- the client kept the stdio MCP session open
- `--claude-channel-mode` is `on` or `auto`
- the client actually understands the Claude-specific notification methods
- the inbound message happened after the bridge connected
### Approvals are missing
`permissions_list_open` only shows approval requests observed while the bridge
was connected. It is not a durable approval history API.
## OpenClaw as an MCP client registry
This is the `openclaw mcp list`, `show`, `set`, and `unset` path.
These commands do not expose OpenClaw over MCP. They manage OpenClaw-owned MCP
server definitions under `mcp.servers` in OpenClaw config.
Those saved definitions are for runtimes that OpenClaw launches or configures
later, such as embedded Pi and other runtime adapters. OpenClaw stores the
definitions centrally so those runtimes do not need to keep their own duplicate
MCP server lists.
Important behavior:
- these commands only read or write OpenClaw config
- they do not connect to the target MCP server
- they do not validate whether the command, URL, or remote transport is
reachable right now
- runtime adapters decide which transport shapes they actually support at
execution time
## Saved MCP server definitions
OpenClaw also stores a lightweight MCP server registry in config for surfaces
that want OpenClaw-managed MCP definitions.
Commands:
- `openclaw mcp list`
- `openclaw mcp show [name]`
- `openclaw mcp set <name> <json>`
- `openclaw mcp unset <name>`
Examples:
```bash
openclaw mcp list
openclaw mcp show context7 --json
openclaw mcp set context7 '{"command":"uvx","args":["context7-mcp"]}'
openclaw mcp set docs '{"url":"https://mcp.example.com"}'
openclaw mcp unset context7
```
Example config shape:
```json
{
"mcp": {
"servers": {
"context7": {
"command": "uvx",
"args": ["context7-mcp"]
},
"docs": {
"url": "https://mcp.example.com"
}
}
}
}
```
### Stdio transport
Launches a local child process and communicates over stdin/stdout.
| Field | Description |
| -------------------------- | --------------------------------- |
| `command` | Executable to spawn (required) |
| `args` | Array of command-line arguments |
| `env` | Extra environment variables |
| `cwd` / `workingDirectory` | Working directory for the process |
### SSE / HTTP transport
Connects to a remote MCP server over HTTP Server-Sent Events.
| Field | Description |
| --------- | ---------------------------------------------------------------- |
| `url` | HTTP or HTTPS URL of the remote server (required) |
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
Example:
```json
{
"mcp": {
"servers": {
"remote-tools": {
"url": "https://mcp.example.com",
"headers": {
"Authorization": "Bearer <token>"
}
}
}
}
}
```
Sensitive values in `url` (userinfo) and `headers` are redacted in logs and
status output.
These commands manage saved config only. They do not start the channel bridge,
open a live MCP client session, or prove the target server is reachable.
## Current limits
This page documents the bridge as shipped today.
Current limits:
- conversation discovery depends on existing Gateway session route metadata
- no generic push protocol beyond the Claude-specific adapter
- no message edit or react tools yet
- HTTP/SSE transport connects to a single remote server; no multiplexed upstream yet
- `permissions_list_open` only includes approvals observed while the bridge is
connected

View File

@@ -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

View File

@@ -37,13 +37,10 @@ openclaw nodes status --last-connected 24h
Use `--connected` to only show currently-connected nodes. Use `--last-connected <duration>` to
filter to nodes that connected within a duration (e.g. `24h`, `7d`).
## Invoke / run
## Invoke
```bash
openclaw nodes invoke --node <id|name|ip> --command <command> --params <json>
openclaw nodes run --node <id|name|ip> <command...>
openclaw nodes run --raw "git status"
openclaw nodes run --agent main --node <id|name|ip> --raw "git status"
```
Invoke flags:
@@ -51,25 +48,8 @@ Invoke flags:
- `--params <json>`: JSON object string (default `{}`).
- `--invoke-timeout <ms>`: node invoke timeout (default `15000`).
- `--idempotency-key <key>`: optional idempotency key.
- `system.run` and `system.run.prepare` are blocked here; use the `exec` tool with `host=node` for shell execution.
### Exec-style defaults
`nodes run` mirrors the models exec behavior (defaults + approvals):
- Reads `tools.exec.*` (plus `agents.list[].tools.exec.*` overrides).
- Uses exec approvals (`exec.approval.request`) before invoking `system.run`.
- `--node` can be omitted when `tools.exec.node` is set.
- Requires a node that advertises `system.run` (macOS companion app or headless node host).
Flags:
- `--cwd <path>`: working directory.
- `--env <key=val>`: env override (repeatable). Note: node hosts ignore `PATH` overrides (and `tools.exec.pathPrepend` is not applied to node hosts).
- `--command-timeout <ms>`: command timeout.
- `--invoke-timeout <ms>`: node invoke timeout (default `30000`).
- `--needs-screen-recording`: require screen recording permission.
- `--raw <command>`: run a shell string (`/bin/sh -lc` or `cmd.exe /c`).
In allowlist mode on Windows node hosts, `cmd.exe /c` shell-wrapper runs require approval
(allowlist entry alone does not auto-allow the wrapper form).
- `--agent <id>`: agent-scoped approvals/allowlists (defaults to configured agent).
- `--ask <off|on-miss|always>`, `--security <deny|allowlist|full>`: overrides.
For shell execution on a node, use the `exec` tool with `host=node` instead of `openclaw nodes run`.
The `nodes` CLI is now capability-focused: direct RPC via `nodes invoke`, plus pairing, camera,
screen, location, canvas, and notifications.

View File

@@ -140,6 +140,9 @@ Flow notes:
- `quickstart`: minimal prompts, auto-generates a gateway token.
- `manual`: full prompts for port/bind/auth (alias of `advanced`).
- In the web-search step, choosing **Grok** can trigger a separate follow-up
prompt to enable `x_search` with the same `XAI_API_KEY` and optionally pick
an `x_search` model. Other web-search providers do not show that prompt.
- Local onboarding DM scope behavior: [CLI Setup Reference](/start/wizard-cli-reference#outputs-and-internals).
- Fastest first chat: `openclaw dashboard` (Control UI, no channel setup).
- Custom Provider: connect any OpenAI or Anthropic compatible endpoint,

View File

@@ -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`.

View File

@@ -87,6 +87,7 @@ These run inside the agent loop or gateway pipeline:
- **`agent_end`**: inspect the final message list and run metadata after completion.
- **`before_compaction` / `after_compaction`**: observe or annotate compaction cycles.
- **`before_tool_call` / `after_tool_call`**: intercept tool params/results.
- **`before_install`**: inspect built-in scan findings and optionally block skill or plugin installs.
- **`tool_result_persist`**: synchronously transform tool results before they are written to the session transcript.
- **`message_received` / `message_sending` / `message_sent`**: inbound + outbound message hooks.
- **`session_start` / `session_end`**: session lifecycle boundaries.
@@ -96,6 +97,8 @@ Hook decision rules for outbound/tool guards:
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_tool_call`: `{ block: false }` is a no-op and does not clear a prior block.
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
- `before_install`: `{ block: false }` is a no-op and does not clear a prior block.
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
- `message_sending`: `{ cancel: false }` is a no-op and does not clear a prior cancel.
@@ -145,7 +148,7 @@ See [Plugin hooks](/plugins/architecture#provider-runtime-hooks) for the hook AP
## Timeouts
- `agent.wait` default: 30s (just the wait). `timeoutMs` param overrides.
- Agent runtime: `agents.defaults.timeoutSeconds` default 600s; enforced in `runEmbeddedPiAgent` abort timer.
- Agent runtime: `agents.defaults.timeoutSeconds` default 172800s (48 hours); enforced in `runEmbeddedPiAgent` abort timer.
## Where things can end early

View File

@@ -1,123 +1,86 @@
---
summary: "Context window + compaction: how OpenClaw keeps sessions under model limits"
summary: "How OpenClaw summarizes long conversations to stay within model limits"
read_when:
- You want to understand auto-compaction and /compact
- You are debugging long sessions hitting context limits
title: "Compaction"
---
# Context Window & Compaction
# Compaction
Every model has a **context window** (max tokens it can see). Long-running chats accumulate messages and tool results; once the window is tight, OpenClaw **compacts** older history to stay within limits.
Every model has a context window -- the maximum number of tokens it can process.
When a conversation approaches that limit, OpenClaw **compacts** older messages
into a summary so the chat can continue.
## What compaction is
## How it works
Compaction **summarizes older conversation** into a compact summary entry and keeps recent messages intact. The summary is stored in the session history, so future requests use:
1. Older conversation turns are summarized into a compact entry.
2. The summary is saved in the session transcript.
3. Recent messages are kept intact.
- The compaction summary
- Recent messages after the compaction point
The full conversation history stays on disk. Compaction only changes what the
model sees on the next turn.
Compaction **persists** in the sessions JSONL history.
## Auto-compaction
## Configuration
Auto-compaction is on by default. It runs when the session nears the context
limit, or when the model returns a context-overflow error (in which case
OpenClaw compacts and retries).
Use the `agents.defaults.compaction` setting in your `openclaw.json` to configure compaction behavior (mode, target tokens, etc.).
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). You can override this with `identifierPolicy: "off"` or provide custom text with `identifierPolicy: "custom"` and `identifierInstructions`.
You can optionally specify a different model for compaction summarization via `agents.defaults.compaction.model`. This is useful when your primary model is a local or small model and you want compaction summaries produced by a more capable model. The override accepts any `provider/model-id` string:
```json
{
"agents": {
"defaults": {
"compaction": {
"model": "openrouter/anthropic/claude-sonnet-4-6"
}
}
}
}
```
This also works with local models, for example a second Ollama model dedicated to summarization or a fine-tuned compaction specialist:
```json
{
"agents": {
"defaults": {
"compaction": {
"model": "ollama/llama3.1:8b"
}
}
}
}
```
When unset, compaction uses the agent's primary model.
## Auto-compaction (default on)
When a session nears or exceeds the models context window, OpenClaw triggers auto-compaction and may retry the original request using the compacted context.
Youll see:
- `🧹 Auto-compaction complete` in verbose mode
- `/status` showing `🧹 Compactions: <count>`
Before compaction, OpenClaw can run a **silent memory flush** turn to store
durable notes to disk. See [Memory](/concepts/memory) for details and config.
<Info>
Before compacting, OpenClaw automatically reminds the agent to save important
notes to [memory](/concepts/memory) files. This prevents context loss.
</Info>
## Manual compaction
Use `/compact` (optionally with instructions) to force a compaction pass:
Type `/compact` in any chat to force a compaction. Add instructions to guide
the summary:
```
/compact Focus on decisions and open questions
/compact Focus on the API design decisions
```
## Context window source
## Using a different model
Context window is model-specific. OpenClaw uses the model definition from the configured provider catalog to determine limits.
By default, compaction uses your agent's primary model. You can use a more
capable model for better summaries:
```json5
{
agents: {
defaults: {
compaction: {
model: "openrouter/anthropic/claude-sonnet-4-6",
},
},
},
}
```
## Compaction vs pruning
- **Compaction**: summarises and **persists** in JSONL.
- **Session pruning**: trims old **tool results** only, **in-memory**, per request.
| | Compaction | Pruning |
| ---------------- | ----------------------------- | -------------------------------- |
| **What it does** | Summarizes older conversation | Trims old tool results |
| **Saved?** | Yes (in session transcript) | No (in-memory only, per request) |
| **Scope** | Entire conversation | Tool results only |
See [/concepts/session-pruning](/concepts/session-pruning) for pruning details.
[Session pruning](/concepts/session-pruning) is a lighter-weight complement that
trims tool output without summarizing.
## OpenAI server-side compaction
## Troubleshooting
OpenClaw also supports OpenAI Responses server-side compaction hints for
compatible direct OpenAI models. This is separate from local OpenClaw
compaction and can run alongside it.
**Compacting too often?** The model's context window may be small, or tool
outputs may be large. Try enabling
[session pruning](/concepts/session-pruning).
- Local compaction: OpenClaw summarizes and persists into session JSONL.
- Server-side compaction: OpenAI compacts context on the provider side when
`store` + `context_management` are enabled.
**Context feels stale after compaction?** Use `/compact Focus on <topic>` to
guide the summary, or enable the [memory flush](/concepts/memory) so notes
survive.
See [OpenAI provider](/providers/openai) for model params and overrides.
**Need a clean slate?** `/new` starts a fresh session without compacting.
## Custom context engines
Compaction behavior is owned by the active
[context engine](/concepts/context-engine). The legacy engine uses the built-in
summarization described above. Plugin engines (selected via
`plugins.slots.contextEngine`) can implement any compaction strategy — DAG
summaries, vector retrieval, incremental condensation, etc.
When a plugin engine sets `ownsCompaction: true`, OpenClaw delegates all
compaction decisions to the engine and does not run built-in auto-compaction.
When `ownsCompaction` is `false` or unset, OpenClaw may still use Pi's
built-in in-attempt auto-compaction, but the active engine's `compact()` method
still handles `/compact` and overflow recovery. There is no automatic fallback
to the legacy engine's compaction path.
If you are building a non-owning context engine, implement `compact()` by
calling `delegateCompactionToRuntime(...)` from `openclaw/plugin-sdk/core`.
## Tips
- Use `/compact` when sessions feel stale or context is bloated.
- Large tool outputs are already truncated; pruning can further reduce tool-result buildup.
- If you need a fresh slate, `/new` or `/reset` starts a new session id.
For advanced configuration (reserve tokens, identifier preservation, custom
context engines, OpenAI server-side compaction), see the
[Session Management Deep Dive](/reference/session-management-compaction).

View File

@@ -0,0 +1,105 @@
---
title: "Builtin Memory Engine"
summary: "The default SQLite-based memory backend with keyword, vector, and hybrid search"
read_when:
- You want to understand the default memory backend
- You want to configure embedding providers or hybrid search
---
# Builtin Memory Engine
The builtin engine is the default memory backend. It stores your memory index in
a per-agent SQLite database and needs no extra dependencies to get started.
## What it provides
- **Keyword search** via FTS5 full-text indexing (BM25 scoring).
- **Vector search** via embeddings from any supported provider.
- **Hybrid search** that combines both for best results.
- **CJK support** via trigram tokenization for Chinese, Japanese, and Korean.
- **sqlite-vec acceleration** for in-database vector queries (optional).
## Getting started
If you have an API key for OpenAI, Gemini, Voyage, or Mistral, the builtin
engine auto-detects it and enables vector search. No config needed.
To set a provider explicitly:
```json5
{
agents: {
defaults: {
memorySearch: {
provider: "openai",
},
},
},
}
```
Without an embedding provider, only keyword search is available.
## Supported embedding providers
| Provider | ID | Auto-detected | Notes |
| -------- | --------- | ------------- | ----------------------------------- |
| OpenAI | `openai` | Yes | Default: `text-embedding-3-small` |
| Gemini | `gemini` | Yes | Supports multimodal (image + audio) |
| Voyage | `voyage` | Yes | |
| Mistral | `mistral` | Yes | |
| Ollama | `ollama` | No | Local, set explicitly |
| Local | `local` | Yes (first) | GGUF model, ~0.6 GB download |
Auto-detection picks the first provider whose API key can be resolved, in the
order shown. Set `memorySearch.provider` to override.
## How indexing works
OpenClaw indexes `MEMORY.md` and `memory/*.md` into chunks (~400 tokens with
80-token overlap) and stores them in a per-agent SQLite database.
- **Index location:** `~/.openclaw/memory/<agentId>.sqlite`
- **File watching:** changes to memory files trigger a debounced reindex (1.5s).
- **Auto-reindex:** when the embedding provider, model, or chunking config
changes, the entire index is rebuilt automatically.
- **Reindex on demand:** `openclaw memory index --force`
<Info>
You can also index Markdown files outside the workspace with
`memorySearch.extraPaths`. See the
[configuration reference](/reference/memory-config#additional-memory-paths).
</Info>
## When to use
The builtin engine is the right choice for most users:
- Works out of the box with no extra dependencies.
- Handles keyword and vector search well.
- Supports all embedding providers.
- Hybrid search combines the best of both retrieval approaches.
Consider switching to [QMD](/concepts/memory-qmd) if you need reranking, query
expansion, or want to index directories outside the workspace.
Consider [Honcho](/concepts/memory-honcho) if you want cross-session memory with
automatic user modeling.
## Troubleshooting
**Memory search disabled?** Check `openclaw memory status`. If no provider is
detected, set one explicitly or add an API key.
**Stale results?** Run `openclaw memory index --force` to rebuild. The watcher
may miss changes in rare edge cases.
**sqlite-vec not loading?** OpenClaw falls back to in-process cosine similarity
automatically. Check logs for the specific load error.
## Configuration
For embedding provider setup, hybrid search tuning (weights, MMR, temporal
decay), batch indexing, multimodal memory, sqlite-vec, extra paths, and all
other config knobs, see the
[Memory configuration reference](/reference/memory-config).

View File

@@ -0,0 +1,140 @@
---
title: "Honcho Memory"
summary: "AI-native cross-session memory via the Honcho plugin"
read_when:
- You want persistent memory that works across sessions and channels
- You want AI-powered recall and user modeling
---
# Honcho Memory
[Honcho](https://honcho.dev) adds AI-native memory to OpenClaw. It persists
conversations to a dedicated service and builds user and agent models over time,
giving your agent cross-session context that goes beyond workspace Markdown
files.
## What it provides
- **Cross-session memory** -- conversations are persisted after every turn, so
context carries across session resets, compaction, and channel switches.
- **User modeling** -- Honcho maintains a profile for each user (preferences,
facts, communication style) and for the agent (personality, learned
behaviors).
- **Semantic search** -- search over observations from past conversations, not
just the current session.
- **Multi-agent awareness** -- parent agents automatically track spawned
sub-agents, with parents added as observers in child sessions.
## Available tools
Honcho registers tools that the agent can use during conversation:
**Data retrieval (fast, no LLM call):**
| Tool | What it does |
| --------------------------- | ------------------------------------------------------ |
| `honcho_context` | Full user representation across sessions |
| `honcho_search_conclusions` | Semantic search over stored conclusions |
| `honcho_search_messages` | Find messages across sessions (filter by sender, date) |
| `honcho_session` | Current session history and summary |
**Q&A (LLM-powered):**
| Tool | What it does |
| ------------ | ------------------------------------------------------------------------- |
| `honcho_ask` | Ask about the user. `depth='quick'` for facts, `'thorough'` for synthesis |
## Getting started
Install the plugin and run setup:
```bash
openclaw plugins install @honcho-ai/openclaw-honcho
openclaw honcho setup
openclaw gateway --force
```
The setup command prompts for your API credentials, writes the config, and
optionally migrates existing workspace memory files.
<Info>
Honcho can run entirely locally (self-hosted) or via the managed API at
`api.honcho.dev`. No external dependencies are required for the self-hosted
option.
</Info>
## Configuration
Settings live under `plugins.entries["openclaw-honcho"].config`:
```json5
{
plugins: {
entries: {
"openclaw-honcho": {
config: {
apiKey: "your-api-key", // omit for self-hosted
workspaceId: "openclaw", // memory isolation
baseUrl: "https://api.honcho.dev",
},
},
},
},
}
```
For self-hosted instances, point `baseUrl` to your local server (for example
`http://localhost:8000`) and omit the API key.
## Migrating existing memory
If you have existing workspace memory files (`USER.md`, `MEMORY.md`,
`IDENTITY.md`, `memory/`, `canvas/`), `openclaw honcho setup` detects and
offers to migrate them.
<Info>
Migration is non-destructive -- files are uploaded to Honcho. Originals are
never deleted or moved.
</Info>
## How it works
After every AI turn, the conversation is persisted to Honcho. Both user and
agent messages are observed, allowing Honcho to build and refine its models over
time.
During conversation, Honcho tools query the service in the `before_prompt_build`
phase, injecting relevant context before the model sees the prompt. This ensures
accurate turn boundaries and relevant recall.
## Honcho vs builtin memory
| | Builtin / QMD | Honcho |
| ----------------- | ---------------------------- | ----------------------------------- |
| **Storage** | Workspace Markdown files | Dedicated service (local or hosted) |
| **Cross-session** | Via memory files | Automatic, built-in |
| **User modeling** | Manual (write to MEMORY.md) | Automatic profiles |
| **Search** | Vector + keyword (hybrid) | Semantic over observations |
| **Multi-agent** | Not tracked | Parent/child awareness |
| **Dependencies** | None (builtin) or QMD binary | Plugin install |
Honcho and the builtin memory system can work together. When QMD is configured,
additional tools become available for searching local Markdown files alongside
Honcho's cross-session memory.
## CLI commands
```bash
openclaw honcho setup # Configure API key and migrate files
openclaw honcho status # Check connection status
openclaw honcho ask <question> # Query Honcho about the user
openclaw honcho search <query> [-k N] [-d D] # Semantic search over memory
```
## Further reading
- [Plugin source code](https://github.com/plastic-labs/openclaw-honcho)
- [Honcho documentation](https://docs.honcho.dev)
- [Honcho OpenClaw integration guide](https://docs.honcho.dev/v3/guides/integrations/openclaw)
- [Memory](/concepts/memory) -- OpenClaw memory overview
- [Context Engines](/concepts/context-engine) -- how plugin context engines work

157
docs/concepts/memory-qmd.md Normal file
View File

@@ -0,0 +1,157 @@
---
title: "QMD Memory Engine"
summary: "Local-first search sidecar with BM25, vectors, reranking, and query expansion"
read_when:
- You want to set up QMD as your memory backend
- You want advanced memory features like reranking or extra indexed paths
---
# QMD Memory Engine
[QMD](https://github.com/tobi/qmd) is a local-first search sidecar that runs
alongside OpenClaw. It combines BM25, vector search, and reranking in a single
binary, and can index content beyond your workspace memory files.
## What it adds over builtin
- **Reranking and query expansion** for better recall.
- **Index extra directories** -- project docs, team notes, anything on disk.
- **Index session transcripts** -- recall earlier conversations.
- **Fully local** -- runs via Bun + node-llama-cpp, auto-downloads GGUF models.
- **Automatic fallback** -- if QMD is unavailable, OpenClaw falls back to the
builtin engine seamlessly.
## Getting started
### Prerequisites
- Install QMD: `bun install -g https://github.com/tobi/qmd`
- SQLite build that allows extensions (`brew install sqlite` on macOS).
- QMD must be on the gateway's `PATH`.
- macOS and Linux work out of the box. Windows is best supported via WSL2.
### Enable
```json5
{
memory: {
backend: "qmd",
},
}
```
OpenClaw creates a self-contained QMD home under
`~/.openclaw/agents/<agentId>/qmd/` and manages the sidecar lifecycle
automatically -- collections, updates, and embedding runs are handled for you.
## How the sidecar works
- OpenClaw creates collections from your workspace memory files and any
configured `memory.qmd.paths`, then runs `qmd update` + `qmd embed` on boot
and periodically (default every 5 minutes).
- Boot refresh runs in the background so chat startup is not blocked.
- Searches use the configured `searchMode` (default: `search`; also supports
`vsearch` and `query`). If a mode fails, OpenClaw retries with `qmd query`.
- If QMD fails entirely, OpenClaw falls back to the builtin SQLite engine.
<Info>
The first search may be slow -- QMD auto-downloads GGUF models (~2 GB) for
reranking and query expansion on the first `qmd query` run.
</Info>
## Indexing extra paths
Point QMD at additional directories to make them searchable:
```json5
{
memory: {
backend: "qmd",
qmd: {
paths: [{ name: "docs", path: "~/notes", pattern: "**/*.md" }],
},
},
}
```
Snippets from extra paths appear as `qmd/<collection>/<relative-path>` in
search results. `memory_get` understands this prefix and reads from the correct
collection root.
## Indexing session transcripts
Enable session indexing to recall earlier conversations:
```json5
{
memory: {
backend: "qmd",
qmd: {
sessions: { enabled: true },
},
},
}
```
Transcripts are exported as sanitized User/Assistant turns into a dedicated QMD
collection under `~/.openclaw/agents/<id>/qmd/sessions/`.
## Search scope
By default, QMD search results are only surfaced in DM sessions (not groups or
channels). Configure `memory.qmd.scope` to change this:
```json5
{
memory: {
qmd: {
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }],
},
},
},
}
```
When scope denies a search, OpenClaw logs a warning with the derived channel and
chat type so empty results are easier to debug.
## Citations
When `memory.citations` is `auto` or `on`, search snippets include a
`Source: <path#line>` footer. Set `memory.citations = "off"` to omit the footer
while still passing the path to the agent internally.
## When to use
Choose QMD when you need:
- Reranking for higher-quality results.
- To search project docs or notes outside the workspace.
- To recall past session conversations.
- Fully local search with no API keys.
For simpler setups, the [builtin engine](/concepts/memory-builtin) works well
with no extra dependencies.
## Troubleshooting
**QMD not found?** Ensure the binary is on the gateway's `PATH`. If OpenClaw
runs as a service, create a symlink:
`sudo ln -s ~/.bun/bin/qmd /usr/local/bin/qmd`.
**First search very slow?** QMD downloads GGUF models on first use. Pre-warm
with `qmd query "test"` using the same XDG dirs OpenClaw uses.
**Search times out?** Increase `memory.qmd.limits.timeoutMs` (default: 4000ms).
Set to `120000` for slower hardware.
**Empty results in group chats?** Check `memory.qmd.scope` -- the default only
allows DM sessions.
## Configuration
For the full config surface (`memory.qmd.*`), search modes, update intervals,
scope rules, and all other knobs, see the
[Memory configuration reference](/reference/memory-config).

View File

@@ -0,0 +1,141 @@
---
title: "Memory Search"
summary: "How memory search finds relevant notes using embeddings and hybrid retrieval"
read_when:
- You want to understand how memory_search works
- You want to choose an embedding provider
- You want to tune search quality
---
# Memory Search
`memory_search` finds relevant notes from your memory files, even when the
wording differs from the original text. It works by indexing memory into small
chunks and searching them using embeddings, keywords, or both.
## Quick start
If you have an OpenAI, Gemini, Voyage, or Mistral API key configured, memory
search works automatically. To set a provider explicitly:
```json5
{
agents: {
defaults: {
memorySearch: {
provider: "openai", // or "gemini", "local", "ollama", etc.
},
},
},
}
```
For local embeddings with no API key, use `provider: "local"` (requires
node-llama-cpp).
## Supported providers
| Provider | ID | Needs API key | Notes |
| -------- | --------- | ------------- | ----------------------------- |
| OpenAI | `openai` | Yes | Auto-detected, fast |
| Gemini | `gemini` | Yes | Supports image/audio indexing |
| Voyage | `voyage` | Yes | Auto-detected |
| Mistral | `mistral` | Yes | Auto-detected |
| Ollama | `ollama` | No | Local, must set explicitly |
| Local | `local` | No | GGUF model, ~0.6 GB download |
## How search works
OpenClaw runs two retrieval paths in parallel and merges the results:
```mermaid
flowchart LR
Q["Query"] --> E["Embedding"]
Q --> T["Tokenize"]
E --> VS["Vector Search"]
T --> BM["BM25 Search"]
VS --> M["Weighted Merge"]
BM --> M
M --> R["Top Results"]
```
- **Vector search** finds notes with similar meaning ("gateway host" matches
"the machine running OpenClaw").
- **BM25 keyword search** finds exact matches (IDs, error strings, config
keys).
If only one path is available (no embeddings or no FTS), the other runs alone.
## Improving search quality
Two optional features help when you have a large note history:
### Temporal decay
Old notes gradually lose ranking weight so recent information surfaces first.
With the default half-life of 30 days, a note from last month scores at 50% of
its original weight. Evergreen files like `MEMORY.md` are never decayed.
<Tip>
Enable temporal decay if your agent has months of daily notes and stale
information keeps outranking recent context.
</Tip>
### MMR (diversity)
Reduces redundant results. If five notes all mention the same router config, MMR
ensures the top results cover different topics instead of repeating.
<Tip>
Enable MMR if `memory_search` keeps returning near-duplicate snippets from
different daily notes.
</Tip>
### Enable both
```json5
{
agents: {
defaults: {
memorySearch: {
query: {
hybrid: {
mmr: { enabled: true },
temporalDecay: { enabled: true },
},
},
},
},
},
}
```
## Multimodal memory
With Gemini Embedding 2, you can index images and audio files alongside
Markdown. Search queries remain text, but they match against visual and audio
content. See the [Memory configuration reference](/reference/memory-config) for
setup.
## Session memory search
You can optionally index session transcripts so `memory_search` can recall
earlier conversations. This is opt-in via
`memorySearch.experimental.sessionMemory`. See the
[configuration reference](/reference/memory-config) for details.
## Troubleshooting
**No results?** Run `openclaw memory status` to check the index. If empty, run
`openclaw memory index --force`.
**Only keyword matches?** Your embedding provider may not be configured. Check
`openclaw memory status --deep`.
**CJK text not found?** Rebuild the FTS index with
`openclaw memory index --force`.
## Further reading
- [Memory](/concepts/memory) -- file layout, backends, tools
- [Memory configuration reference](/reference/memory-config) -- all config knobs

View File

@@ -1,110 +1,102 @@
---
title: "Memory"
summary: "How OpenClaw memory works (workspace files + automatic memory flush)"
title: "Memory Overview"
summary: "How OpenClaw remembers things across sessions"
read_when:
- You want the memory file layout and workflow
- You want to tune the automatic pre-compaction memory flush
- You want to understand how memory works
- You want to know what memory files to write
---
# Memory
# Memory Overview
OpenClaw memory is **plain Markdown in the agent workspace**. The files are the
source of truth; the model only "remembers" what gets written to disk.
OpenClaw remembers things by writing **plain Markdown files** in your agent's
workspace. The model only "remembers" what gets saved to disk -- there is no
hidden state.
Memory search tools are provided by the active memory plugin (default:
`memory-core`). Disable memory plugins with `plugins.slots.memory = "none"`.
## How it works
## Memory files (Markdown)
Your agent has two places to store memories:
The default workspace layout uses two memory layers:
- **`MEMORY.md`** -- long-term memory. Durable facts, preferences, and
decisions. Loaded at the start of every DM session.
- **`memory/YYYY-MM-DD.md`** -- daily notes. Running context and observations.
Today and yesterday's notes are loaded automatically.
- `memory/YYYY-MM-DD.md`
- Daily log (append-only).
- Read today + yesterday at session start.
- `MEMORY.md` (optional)
- Curated long-term memory.
- If both `MEMORY.md` and `memory.md` exist at the workspace root, OpenClaw loads both (deduplicated by realpath so symlinks pointing to the same file are not injected twice).
- **Only load in the main, private session** (never in group contexts).
These files live in the agent workspace (default `~/.openclaw/workspace`).
These files live under the workspace (`agents.defaults.workspace`, default
`~/.openclaw/workspace`). See [Agent workspace](/concepts/agent-workspace) for the full layout.
<Tip>
If you want your agent to remember something, just ask it: "Remember that I
prefer TypeScript." It will write it to the appropriate file.
</Tip>
## Memory tools
OpenClaw exposes two agent-facing tools for these Markdown files:
The agent has two tools for working with memory:
- `memory_search` -- semantic recall over indexed snippets.
- `memory_get` -- targeted read of a specific Markdown file/line range.
- **`memory_search`** -- finds relevant notes using semantic search, even when
the wording differs from the original.
- **`memory_get`** -- reads a specific memory file or line range.
`memory_get` now **degrades gracefully when a file doesn't exist** (for example,
today's daily log before the first write). Both the builtin manager and the QMD
backend return `{ text: "", path }` instead of throwing `ENOENT`, so agents can
handle "nothing recorded yet" and continue their workflow without wrapping the
tool call in try/catch logic.
Both tools are provided by the active memory plugin (default: `memory-core`).
## When to write memory
## Memory search
- Decisions, preferences, and durable facts go to `MEMORY.md`.
- Day-to-day notes and running context go to `memory/YYYY-MM-DD.md`.
- If someone says "remember this," write it down (do not keep it in RAM).
- This area is still evolving. It helps to remind the model to store memories; it will know what to do.
- If you want something to stick, **ask the bot to write it** into memory.
When an embedding provider is configured, `memory_search` uses **hybrid
search** -- combining vector similarity (semantic meaning) with keyword matching
(exact terms like IDs and code symbols). This works out of the box once you have
an API key for any supported provider.
## Automatic memory flush (pre-compaction ping)
<Info>
OpenClaw auto-detects your embedding provider from available API keys. If you
have an OpenAI, Gemini, Voyage, or Mistral key configured, memory search is
enabled automatically.
</Info>
When a session is **close to auto-compaction**, OpenClaw triggers a **silent,
agentic turn** that reminds the model to write durable memory **before** the
context is compacted. The default prompts explicitly say the model _may reply_,
but usually `NO_REPLY` is the correct response so the user never sees this turn.
The active memory plugin owns the prompt/path policy for that flush; the
default `memory-core` plugin writes to the canonical daily file under
`memory/YYYY-MM-DD.md`.
For details on how search works, tuning options, and provider setup, see
[Memory Search](/concepts/memory-search).
This is controlled by `agents.defaults.compaction.memoryFlush`:
## Memory backends
```json5
{
agents: {
defaults: {
compaction: {
reserveTokensFloor: 20000,
memoryFlush: {
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}
<CardGroup cols={3}>
<Card title="Builtin (default)" icon="database" href="/concepts/memory-builtin">
SQLite-based. Works out of the box with keyword search, vector similarity, and
hybrid search. No extra dependencies.
</Card>
<Card title="QMD" icon="search" href="/concepts/memory-qmd">
Local-first sidecar with reranking, query expansion, and the ability to index
directories outside the workspace.
</Card>
<Card title="Honcho" icon="brain" href="/concepts/memory-honcho">
AI-native cross-session memory with user modeling, semantic search, and
multi-agent awareness. Plugin install.
</Card>
</CardGroup>
## Automatic memory flush
Before [compaction](/concepts/compaction) summarizes your conversation, OpenClaw
runs a silent turn that reminds the agent to save important context to memory
files. This is on by default -- you do not need to configure anything.
<Tip>
The memory flush prevents context loss during compaction. If your agent has
important facts in the conversation that are not yet written to a file, they
will be saved automatically before the summary happens.
</Tip>
## CLI
```bash
openclaw memory status # Check index status and provider
openclaw memory search "query" # Search from the command line
openclaw memory index --force # Rebuild the index
```
Details:
## Further reading
- **Soft threshold**: flush triggers when the session token estimate crosses
`contextWindow - reserveTokensFloor - softThresholdTokens`.
- **Silent** by default: prompts include `NO_REPLY` so nothing is delivered.
- **Two prompts**: a user prompt plus a system prompt append the reminder.
- **One flush per compaction cycle** (tracked in `sessions.json`).
- **Workspace must be writable**: if the session runs sandboxed with
`workspaceAccess: "ro"` or `"none"`, the flush is skipped.
For the full compaction lifecycle, see
[Session management + compaction](/reference/session-management-compaction).
## Vector memory search
OpenClaw can build a small vector index over `MEMORY.md` and `memory/*.md` so
semantic queries can find related notes even when wording differs. Hybrid search
(BM25 + vector) is available for combining semantic matching with exact keyword
lookups.
Memory search supports multiple embedding providers (OpenAI, Gemini, Voyage,
Mistral, Ollama, and local GGUF models), an optional QMD sidecar backend for
advanced retrieval, and post-processing features like MMR diversity re-ranking
and temporal decay.
For the full configuration reference -- including embedding provider setup, QMD
backend, hybrid search tuning, multimodal memory, and all config knobs -- see
[Memory configuration reference](/reference/memory-config).
- [Builtin Memory Engine](/concepts/memory-builtin) -- default SQLite backend
- [QMD Memory Engine](/concepts/memory-qmd) -- advanced local-first sidecar
- [Honcho Memory](/concepts/memory-honcho) -- AI-native cross-session memory
- [Memory Search](/concepts/memory-search) -- search pipeline, providers, and
tuning
- [Memory configuration reference](/reference/memory-config) -- all config knobs
- [Compaction](/concepts/compaction) -- how compaction interacts with memory

View File

@@ -147,7 +147,8 @@ OpenClaw ships with the piai catalog. These providers require **no**
- Override per model via `agents.defaults.models["openai/<model>"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`)
- OpenAI Responses WebSocket warm-up defaults to enabled via `params.openaiWsWarmup` (`true`/`false`)
- OpenAI priority processing can be enabled via `agents.defaults.models["openai/<model>"].params.serviceTier`
- OpenAI fast mode can be enabled per model via `agents.defaults.models["<provider>/<model>"].params.fastMode`
- `/fast` and `params.fastMode` map direct `openai/*` Responses requests to `service_tier=priority` on `api.openai.com`
- Use `params.serviceTier` when you want an explicit tier instead of the shared `/fast` toggle
- `openai/gpt-5.3-codex-spark` is intentionally suppressed in OpenClaw because the live OpenAI API rejects it; Spark is treated as Codex-only
```json5
@@ -163,7 +164,7 @@ OpenClaw ships with the piai catalog. These providers require **no**
- Optional rotation: `ANTHROPIC_API_KEYS`, `ANTHROPIC_API_KEY_1`, `ANTHROPIC_API_KEY_2`, plus `OPENCLAW_LIVE_ANTHROPIC_KEY` (single override)
- Example model: `anthropic/claude-opus-4-6`
- CLI: `openclaw onboard --auth-choice token` (paste setup-token) or `openclaw models auth paste-token --provider anthropic`
- Direct API-key models support the shared `/fast` toggle and `params.fastMode`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
- Direct public Anthropic requests support the shared `/fast` toggle and `params.fastMode`, including API-key and OAuth-authenticated traffic sent to `api.anthropic.com`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
- Policy note: setup-token support is technical compatibility; Anthropic has blocked some subscription usage outside Claude Code in the past. Verify current Anthropic terms and decide based on your risk tolerance.
- Recommendation: Anthropic API key auth is the safer, recommended path over subscription setup-token auth.
@@ -181,7 +182,8 @@ OpenClaw ships with the piai catalog. These providers require **no**
- CLI: `openclaw onboard --auth-choice openai-codex` or `openclaw models auth login --provider openai-codex`
- Default transport is `auto` (WebSocket-first, SSE fallback)
- Override per model via `agents.defaults.models["openai-codex/<model>"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`)
- Shares the same `/fast` toggle and `params.fastMode` config as direct `openai/*`
- `params.serviceTier` is also forwarded on native Codex Responses requests (`chatgpt.com/backend-api`)
- Shares the same `/fast` toggle and `params.fastMode` config as direct `openai/*`; OpenClaw maps that to `service_tier=priority`
- `openai-codex/gpt-5.3-codex-spark` remains available when the Codex OAuth catalog exposes it; entitlement-dependent
- Policy note: OpenAI Codex OAuth is explicitly supported for external tools/workflows like OpenClaw.
@@ -247,7 +249,7 @@ OpenClaw ships with the piai 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 +540,8 @@ Example (OpenAIcompatible):
{
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 +552,8 @@ Example (OpenAIcompatible):
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 },

View File

@@ -1,121 +1,80 @@
---
title: "Session Pruning"
summary: "Session pruning: tool-result trimming to reduce context bloat"
summary: "Trimming old tool results to keep context lean and caching efficient"
read_when:
- You want to reduce LLM context growth from tool outputs
- You are tuning agents.defaults.contextPruning
- You want to reduce context growth from tool outputs
- You want to understand Anthropic prompt cache optimization
---
# Session Pruning
Session pruning trims **old tool results** from the in-memory context right before each LLM call. It does **not** rewrite the on-disk session history (`*.jsonl`).
Session pruning trims **old tool results** from the context before each LLM
call. It reduces context bloat from accumulated tool outputs (exec results, file
reads, search results) without touching your conversation messages.
## When it runs
<Info>
Pruning is in-memory only -- it does not modify the on-disk session transcript.
Your full history is always preserved.
</Info>
- When `mode: "cache-ttl"` is enabled and the last Anthropic call for the session is older than `ttl`.
- Only affects the messages sent to the model for that request.
- Only active for Anthropic API calls (and OpenRouter Anthropic models).
- For best results, match `ttl` to your model `cacheRetention` policy (`short` = 5m, `long` = 1h).
- After a prune, the TTL window resets so subsequent requests keep cache until `ttl` expires again.
## Why it matters
## Smart defaults (Anthropic)
Long sessions accumulate tool output that inflates the context window. This
increases cost and can force [compaction](/concepts/compaction) sooner than
necessary.
- **OAuth or setup-token** profiles: enable `cache-ttl` pruning and set heartbeat to `1h`.
- **API key** profiles: enable `cache-ttl` pruning, set heartbeat to `30m`, and default `cacheRetention: "short"` on Anthropic models.
- If you set any of these values explicitly, OpenClaw does **not** override them.
Pruning is especially valuable for **Anthropic prompt caching**. After the cache
TTL expires, the next request re-caches the full prompt. Pruning reduces the
cache-write size, directly lowering cost.
## What this improves (cost + cache behavior)
## How it works
- **Why prune:** Anthropic prompt caching only applies within the TTL. If a session goes idle past the TTL, the next request re-caches the full prompt unless you trim it first.
- **What gets cheaper:** pruning reduces the **cacheWrite** size for that first request after the TTL expires.
- **Why the TTL reset matters:** once pruning runs, the cache window resets, so followup requests can reuse the freshly cached prompt instead of re-caching the full history again.
- **What it does not do:** pruning doesnt add tokens or “double” costs; it only changes what gets cached on that first postTTL request.
1. Wait for the cache TTL to expire (default 5 minutes).
2. Find old tool results (user and assistant messages are never touched).
3. **Soft-trim** oversized results -- keep the head and tail, insert `...`.
4. **Hard-clear** the rest -- replace with a placeholder.
5. Reset the TTL so follow-up requests reuse the fresh cache.
## What can be pruned
## Smart defaults
- Only `toolResult` messages.
- User + assistant messages are **never** modified.
- The last `keepLastAssistants` assistant messages are protected; tool results after that cutoff are not pruned.
- If there arent enough assistant messages to establish the cutoff, pruning is skipped.
- Tool results containing **image blocks** are skipped (never trimmed/cleared).
OpenClaw auto-enables pruning for Anthropic profiles:
## Context window estimation
| Profile type | Pruning enabled | Heartbeat |
| -------------------- | --------------- | --------- |
| OAuth or setup-token | Yes | 1 hour |
| API key | Yes | 30 min |
Pruning uses an estimated context window (chars ≈ tokens × 4). The base window is resolved in this order:
If you set explicit values, OpenClaw does not override them.
1. `models.providers.*.models[].contextWindow` override.
2. Model definition `contextWindow` (from the model registry).
3. Default `200000` tokens.
## Enable or disable
If `agents.defaults.contextTokens` is set, it is treated as a cap (min) on the resolved window.
## Mode
### cache-ttl
- Pruning only runs if the last Anthropic call is older than `ttl` (default `5m`).
- When it runs: same soft-trim + hard-clear behavior as before.
## Soft vs hard pruning
- **Soft-trim**: only for oversized tool results.
- Keeps head + tail, inserts `...`, and appends a note with the original size.
- Skips results with image blocks.
- **Hard-clear**: replaces the entire tool result with `hardClear.placeholder`.
## Tool selection
- `tools.allow` / `tools.deny` support `*` wildcards.
- Deny wins.
- Matching is case-insensitive.
- Empty allow list => all tools allowed.
## Interaction with other limits
- Built-in tools already truncate their own output; session pruning is an extra layer that prevents long-running chats from accumulating too much tool output in the model context.
- Compaction is separate: compaction summarizes and persists, pruning is transient per request. See [/concepts/compaction](/concepts/compaction).
## Defaults (when enabled)
- `ttl`: `"5m"`
- `keepLastAssistants`: `3`
- `softTrimRatio`: `0.3`
- `hardClearRatio`: `0.5`
- `minPrunableToolChars`: `50000`
- `softTrim`: `{ maxChars: 4000, headChars: 1500, tailChars: 1500 }`
- `hardClear`: `{ enabled: true, placeholder: "[Old tool result content cleared]" }`
## Examples
Default (off):
```json5
{
agents: { defaults: { contextPruning: { mode: "off" } } },
}
```
Enable TTL-aware pruning:
```json5
{
agents: { defaults: { contextPruning: { mode: "cache-ttl", ttl: "5m" } } },
}
```
Restrict pruning to specific tools:
Pruning is off by default for non-Anthropic providers. To enable:
```json5
{
agents: {
defaults: {
contextPruning: {
mode: "cache-ttl",
tools: { allow: ["exec", "read"], deny: ["*image*"] },
},
contextPruning: { mode: "cache-ttl", ttl: "5m" },
},
},
}
```
See config reference: [Gateway Configuration](/gateway/configuration)
To disable: set `mode: "off"`.
## Pruning vs compaction
| | Pruning | Compaction |
| ---------- | ------------------ | ----------------------- |
| **What** | Trims tool results | Summarizes conversation |
| **Saved?** | No (per-request) | Yes (in transcript) |
| **Scope** | Tool results only | Entire conversation |
They complement each other -- pruning keeps tool output lean between
compaction cycles.
## Further reading
- [Compaction](/concepts/compaction) -- summarization-based context reduction
- [Gateway Configuration](/gateway/configuration) -- all pruning config knobs
(`contextPruning.*`)

View File

@@ -1,251 +1,84 @@
---
summary: "Agent session tools for listing sessions, fetching history, and sending cross-session messages"
summary: "Agent tools for listing sessions, reading history, and cross-session messaging"
read_when:
- Adding or modifying session tools
- You want to understand what session tools the agent has
- You want to configure cross-session access or sub-agent spawning
title: "Session Tools"
---
# Session Tools
Goal: small, hard-to-misuse tool set so agents can list sessions, fetch history, and send to another session.
OpenClaw gives agents tools to work across sessions -- listing conversations,
reading history, sending messages to other sessions, and spawning sub-agents.
## Tool Names
## Available tools
- `sessions_list`
- `sessions_history`
- `sessions_send`
- `sessions_spawn`
| Tool | What it does |
| ------------------ | ------------------------------------------------------- |
| `sessions_list` | List sessions with optional filters (kind, recency) |
| `sessions_history` | Read the transcript of a specific session |
| `sessions_send` | Send a message to another session and optionally wait |
| `sessions_spawn` | Spawn an isolated sub-agent session for background work |
## Key Model
## Listing and reading sessions
- Main direct chat bucket is always the literal key `"main"` (resolved to the current agents main key).
- Group chats use `agent:<agentId>:<channel>:group:<id>` or `agent:<agentId>:<channel>:channel:<id>` (pass the full key).
- Cron jobs use `cron:<job.id>`.
- Hooks use `hook:<uuid>` unless explicitly set.
- Node sessions use `node-<nodeId>` unless explicitly set.
`sessions_list` returns sessions with their key, kind, channel, model, token
counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
`node`) or recency (`activeMinutes`).
`global` and `unknown` are reserved values and are never listed. If `session.scope = "global"`, we alias it to `main` for all tools so callers never see `global`.
`sessions_history` fetches the conversation transcript for a specific session.
By default, tool results are excluded -- pass `includeTools: true` to see them.
## sessions_list
Both tools accept either a **session key** (like `"main"`) or a **session ID**
from a previous list call.
List sessions as an array of rows.
## Sending cross-session messages
Parameters:
`sessions_send` delivers a message to another session and optionally waits for
the response:
- `kinds?: string[]` filter: any of `"main" | "group" | "cron" | "hook" | "node" | "other"`
- `limit?: number` max rows (default: server default, clamp e.g. 200)
- `activeMinutes?: number` only sessions updated within N minutes
- `messageLimit?: number` 0 = no messages (default 0); >0 = include last N messages
- **Fire-and-forget:** set `timeoutSeconds: 0` to enqueue and return
immediately.
- **Wait for reply:** set a timeout and get the response inline.
Behavior:
After the target responds, OpenClaw can run a **reply-back loop** where the
agents alternate messages (up to 5 turns). The target agent can reply
`REPLY_SKIP` to stop early.
- `messageLimit > 0` fetches `chat.history` per session and includes the last N messages.
- Tool results are filtered out in list output; use `sessions_history` for tool messages.
- When running in a **sandboxed** agent session, session tools default to **spawned-only visibility** (see below).
## Spawning sub-agents
Row shape (JSON):
`sessions_spawn` creates an isolated session for a background task. It is always
non-blocking -- it returns immediately with a `runId` and `childSessionKey`.
- `key`: session key (string)
- `kind`: `main | group | cron | hook | node | other`
- `channel`: `whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`
- `displayName` (group display label if available)
- `updatedAt` (ms)
- `sessionId`
- `model`, `contextTokens`, `totalTokens`
- `thinkingLevel`, `verboseLevel`, `systemSent`, `abortedLastRun`
- `sendPolicy` (session override if set)
- `lastChannel`, `lastTo`
- `deliveryContext` (normalized `{ channel, to, accountId }` when available)
- `transcriptPath` (best-effort path derived from store dir + sessionId)
- `messages?` (only when `messageLimit > 0`)
Key options:
## sessions_history
- `runtime: "subagent"` (default) or `"acp"` for external harness agents.
- `model` and `thinking` overrides for the child session.
- `thread: true` to bind the spawn to a chat thread (Discord, Slack, etc.).
- `sandbox: "require"` to enforce sandboxing on the child.
Fetch transcript for one session.
Sub-agents get the full tool set minus session tools (no recursive spawning).
After completion, an announce step posts the result to the requester's channel.
Parameters:
For ACP-specific behavior, see [ACP Agents](/tools/acp-agents).
- `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)
- `limit?: number` max messages (server clamps)
- `includeTools?: boolean` (default false)
## Visibility
Behavior:
Session tools are scoped to limit what the agent can see:
- `includeTools=false` filters `role: "toolResult"` messages.
- Returns messages array in the raw transcript format.
- When given a `sessionId`, OpenClaw resolves it to the corresponding session key (missing ids error).
| Level | Scope |
| ------- | ---------------------------------------- |
| `self` | Only the current session |
| `tree` | Current session + spawned sub-agents |
| `agent` | All sessions for this agent |
| `all` | All sessions (cross-agent if configured) |
## Gateway session history and live transcript APIs
Default is `tree`. Sandboxed sessions are clamped to `tree` regardless of
config.
Control UI and gateway clients can use the lower level history and live transcript surfaces directly.
## Further reading
HTTP:
- `GET /sessions/{sessionKey}/history`
- Query params: `limit`, `cursor`, `includeTools=1`, `follow=1`
- Unknown sessions return HTTP `404` with `error.type = "not_found"`
- `follow=1` upgrades the response to an SSE stream of transcript updates for that session
WebSocket:
- `sessions.subscribe` subscribes to all session lifecycle and transcript events visible to the client
- `sessions.messages.subscribe { key }` subscribes only to `session.message` events for one session
- `sessions.messages.unsubscribe { key }` removes that targeted transcript subscription
- `session.message` carries appended transcript messages plus live usage metadata when available
- `sessions.changed` emits `phase: "message"` for transcript appends so session lists can refresh counters and previews
## sessions_send
Send a message into another session.
Parameters:
- `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)
- `message` (required)
- `timeoutSeconds?: number` (default >0; 0 = fire-and-forget)
Behavior:
- `timeoutSeconds = 0`: enqueue and return `{ runId, status: "accepted" }`.
- `timeoutSeconds > 0`: wait up to N seconds for completion, then return `{ runId, status: "ok", reply }`.
- If wait times out: `{ runId, status: "timeout", error }`. Run continues; call `sessions_history` later.
- If the run fails: `{ runId, status: "error", error }`.
- Announce delivery runs after the primary run completes and is best-effort; `status: "ok"` does not guarantee the announce was delivered.
- Waits via gateway `agent.wait` (server-side) so reconnects don't drop the wait.
- Agent-to-agent message context is injected for the primary run.
- Inter-session messages are persisted with `message.provenance.kind = "inter_session"` so transcript readers can distinguish routed agent instructions from external user input.
- After the primary run completes, OpenClaw runs a **reply-back loop**:
- Round 2+ alternates between requester and target agents.
- Reply exactly `REPLY_SKIP` to stop the pingpong.
- Max turns is `session.agentToAgent.maxPingPongTurns` (05, default 5).
- Once the loop ends, OpenClaw runs the **agenttoagent announce step** (target agent only):
- Reply exactly `ANNOUNCE_SKIP` to stay silent.
- Any other reply is sent to the target channel.
- Announce step includes the original request + round1 reply + latest pingpong reply.
## Channel Field
- For groups, `channel` is the channel recorded on the session entry.
- For direct chats, `channel` maps from `lastChannel`.
- For cron/hook/node, `channel` is `internal`.
- If missing, `channel` is `unknown`.
## Security / Send Policy
Policy-based blocking by channel/chat type (not per session id).
```json
{
"session": {
"sendPolicy": {
"rules": [
{
"match": { "channel": "discord", "chatType": "group" },
"action": "deny"
}
],
"default": "allow"
}
}
}
```
Runtime override (per session entry):
- `sendPolicy: "allow" | "deny"` (unset = inherit config)
- Settable via `sessions.patch` or owner-only `/send on|off|inherit` (standalone message).
Enforcement points:
- `chat.send` / `agent` (gateway)
- auto-reply delivery logic
## sessions_spawn
Spawn an isolated delegated session.
- Default runtime: OpenClaw sub-agent (`runtime: "subagent"`).
- ACP harness sessions use `runtime: "acp"` and follow ACP-specific targeting/policy rules.
- This section focuses on sub-agent behavior unless noted otherwise. For ACP-specific behavior, see [ACP Agents](/tools/acp-agents).
Parameters:
- `task` (required)
- `runtime?` (`subagent|acp`; defaults to `subagent`)
- `label?` (optional; used for logs/UI)
- `agentId?` (optional)
- `runtime: "subagent"`: target another OpenClaw agent id if allowed by `subagents.allowAgents`
- `runtime: "acp"`: target an ACP harness id if allowed by `acp.allowedAgents`
- `model?` (optional; overrides the sub-agent model; invalid values error)
- `thinking?` (optional; overrides thinking level for the sub-agent run)
- `runTimeoutSeconds?` (defaults to `agents.defaults.subagents.runTimeoutSeconds` when set, otherwise `0`; when set, aborts the sub-agent run after N seconds)
- `thread?` (default false; request thread-bound routing for this spawn when supported by the channel/plugin)
- `mode?` (`run|session`; defaults to `run`, but defaults to `session` when `thread=true`; `mode="session"` requires `thread=true`)
- `cleanup?` (`delete|keep`, default `keep`)
- `sandbox?` (`inherit|require`, default `inherit`; `require` rejects spawn unless the target child runtime is sandboxed)
- `attachments?` (optional array of inline files; subagent runtime only, ACP rejects). Each entry: `{ name, content, encoding?: "utf8" | "base64", mimeType? }`. Files are materialized into the child workspace at `.openclaw/attachments/<uuid>/`. Returns a receipt with sha256 per file.
- `attachAs?` (optional; `{ mountPath? }` hint reserved for future mount implementations)
Allowlist:
- `runtime: "subagent"`: `agents.list[].subagents.allowAgents` controls which OpenClaw agent ids are allowed via `agentId` (`["*"]` to allow any). Default: only the requester agent.
- `runtime: "acp"`: `acp.allowedAgents` controls which ACP harness ids are allowed. This is a separate policy from `subagents.allowAgents`.
- Sandbox inheritance guard: if the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed.
Discovery:
- Use `agents_list` to discover allowed targets for `runtime: "subagent"`.
- For `runtime: "acp"`, use configured ACP harness ids and `acp.allowedAgents`; `agents_list` does not list ACP harness targets.
Behavior:
- Starts a new `agent:<agentId>:subagent:<uuid>` session with `deliver: false`.
- Sub-agents default to the full tool set **minus session tools** (configurable via `tools.subagents.tools`).
- Sub-agents are not allowed to call `sessions_spawn` (no sub-agent → sub-agent spawning).
- Always non-blocking: returns `{ status: "accepted", runId, childSessionKey }` immediately.
- With `thread=true`, channel plugins can bind delivery/routing to a thread target (Discord support is controlled by `session.threadBindings.*` and `channels.discord.threadBindings.*`).
- After completion, OpenClaw runs a sub-agent **announce step** and posts the result to the requester chat channel.
- If the assistant final reply is empty, the latest `toolResult` from sub-agent history is included as `Result`.
- Reply exactly `ANNOUNCE_SKIP` during the announce step to stay silent.
- Announce replies are normalized to `Status`/`Result`/`Notes`; `Status` comes from runtime outcome (not model text).
- Sub-agent sessions are auto-archived after `agents.defaults.subagents.archiveAfterMinutes` (default: 60).
- Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).
## Sandbox Session Visibility
Session tools can be scoped to reduce cross-session access.
Default behavior:
- `tools.sessions.visibility` defaults to `tree` (current session + spawned subagent sessions).
- For sandboxed sessions, `agents.defaults.sandbox.sessionToolsVisibility` can hard-clamp visibility.
Config:
```json5
{
tools: {
sessions: {
// "self" | "tree" | "agent" | "all"
// default: "tree"
visibility: "tree",
},
},
agents: {
defaults: {
sandbox: {
// default: "spawned"
sessionToolsVisibility: "spawned", // or "all"
},
},
},
}
```
Notes:
- `self`: only the current session key.
- `tree`: current session + sessions spawned by the current session.
- `agent`: any session belonging to the current agent id.
- `all`: any session (cross-agent access still requires `tools.agentToAgent`).
- When a session is sandboxed and `sessionToolsVisibility="spawned"`, OpenClaw clamps visibility to `tree` even if you set `tools.sessions.visibility="all"`.
- [Session Management](/concepts/session) -- routing, lifecycle, maintenance
- [ACP Agents](/tools/acp-agents) -- external harness spawning
- [Multi-agent](/concepts/multi-agent) -- multi-agent architecture
- [Gateway Configuration](/gateway/configuration) -- session tool config knobs

View File

@@ -1,310 +1,113 @@
---
summary: "Session management rules, keys, and persistence for chats"
summary: "How OpenClaw manages conversation sessions"
read_when:
- Modifying session handling or storage
- You want to understand session routing and isolation
- You want to configure DM scope for multi-user setups
title: "Session Management"
---
# Session Management
OpenClaw treats **one direct-chat session per agent** as primary. Direct chats collapse to `agent:<agentId>:<mainKey>` (default `main`), while group/channel chats get their own keys. `session.mainKey` is honored.
OpenClaw organizes conversations into **sessions**. Each message is routed to a
session based on where it came from -- DMs, group chats, cron jobs, etc.
Use `session.dmScope` to control how **direct messages** are grouped:
## How messages are routed
- `main` (default): all DMs share the main session for continuity.
- `per-peer`: isolate by sender id across channels.
- `per-channel-peer`: isolate by channel + sender (recommended for multi-user inboxes).
- `per-account-channel-peer`: isolate by account + channel + sender (recommended for multi-account inboxes).
Use `session.identityLinks` to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
| Source | Behavior |
| --------------- | ------------------------- |
| Direct messages | Shared session by default |
| Group chats | Isolated per group |
| Rooms/channels | Isolated per room |
| Cron jobs | Fresh session per run |
| Webhooks | Isolated per hook |
## Secure DM mode (recommended for multi-user setups)
## DM isolation
> **Security Warning:** If your agent can receive DMs from **multiple people**, you should strongly consider enabling secure DM mode. Without it, all users share the same conversation context, which can leak private information between users.
By default, all DMs share one session for continuity. This is fine for
single-user setups.
**Example of the problem with default settings:**
<Warning>
If multiple people can message your agent, enable DM isolation. Without it, all
users share the same conversation context -- Alice's private messages would be
visible to Bob.
</Warning>
- Alice (`<SENDER_A>`) messages your agent about a private topic (for example, a medical appointment)
- Bob (`<SENDER_B>`) messages your agent asking "What were we talking about?"
- Because both DMs share the same session, the model may answer Bob using Alice's prior context.
**The fix:** Set `dmScope` to isolate sessions per user:
**The fix:**
```json5
// ~/.openclaw/openclaw.json
{
session: {
// Secure DM mode: isolate DM context per channel + sender.
dmScope: "per-channel-peer",
dmScope: "per-channel-peer", // isolate by channel + sender
},
}
```
**When to enable this:**
Other options:
- You have pairing approvals for more than one sender
- You use a DM allowlist with multiple entries
- You set `dmPolicy: "open"`
- Multiple phone numbers or accounts can message your agent
- `main` (default) -- all DMs share one session.
- `per-peer` -- isolate by sender (across channels).
- `per-channel-peer` -- isolate by channel + sender (recommended).
- `per-account-channel-peer` -- isolate by account + channel + sender.
Notes:
<Tip>
If the same person contacts you from multiple channels, use
`session.identityLinks` to link their identities so they share one session.
</Tip>
- Default is `dmScope: "main"` for continuity (all DMs share the main session). This is fine for single-user setups.
- Local CLI onboarding writes `session.dmScope: "per-channel-peer"` by default when unset (existing explicit values are preserved).
- For multi-account inboxes on the same channel, prefer `per-account-channel-peer`.
- If the same person contacts you on multiple channels, use `session.identityLinks` to collapse their DM sessions into one canonical identity.
- You can verify your DM settings with `openclaw security audit` (see [security](/cli/security)).
Verify your setup with `openclaw security audit`.
## Gateway is the source of truth
## Session lifecycle
All session state is **owned by the gateway** (the “master” OpenClaw). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
Sessions are reused until they expire:
- In **remote mode**, the session store you care about lives on the remote gateway host, not your Mac.
- Token counts shown in UIs come from the gateways store fields (`inputTokens`, `outputTokens`, `totalTokens`, `contextTokens`). Clients do not parse JSONL transcripts to “fix up” totals.
- **Daily reset** (default) -- new session at 4:00 AM local time on the gateway
host.
- **Idle reset** (optional) -- new session after a period of inactivity. Set
`session.reset.idleMinutes`.
- **Manual reset** -- type `/new` or `/reset` in chat. `/new <model>` also
switches the model.
When both daily and idle resets are configured, whichever expires first wins.
## Where state lives
- On the **gateway host**:
- Store file: `~/.openclaw/agents/<agentId>/sessions/sessions.json` (per agent).
- Transcripts: `~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl` (Telegram topic sessions use `.../<SessionId>-topic-<threadId>.jsonl`).
- The store is a map `sessionKey -> { sessionId, updatedAt, ... }`. Deleting entries is safe; they are recreated on demand.
- Group entries may include `displayName`, `channel`, `subject`, `room`, and `space` to label sessions in UIs.
- Session entries include `origin` metadata (label + routing hints) so UIs can explain where a session came from.
- OpenClaw does **not** read legacy Pi/Tau session folders.
All session state is owned by the **gateway**. UI clients query the gateway for
session data.
## Maintenance
- **Store:** `~/.openclaw/agents/<agentId>/sessions/sessions.json`
- **Transcripts:** `~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl`
OpenClaw applies session-store maintenance to keep `sessions.json` and transcript artifacts bounded over time.
## Session maintenance
### Defaults
- `session.maintenance.mode`: `warn`
- `session.maintenance.pruneAfter`: `30d`
- `session.maintenance.maxEntries`: `500`
- `session.maintenance.rotateBytes`: `10mb`
- `session.maintenance.resetArchiveRetention`: defaults to `pruneAfter` (`30d`)
- `session.maintenance.maxDiskBytes`: unset (disabled)
- `session.maintenance.highWaterBytes`: defaults to `80%` of `maxDiskBytes` when budgeting is enabled
### How it works
Maintenance runs during session-store writes, and you can trigger it on demand with `openclaw sessions cleanup`.
- `mode: "warn"`: reports what would be evicted but does not mutate entries/transcripts.
- `mode: "enforce"`: applies cleanup in this order:
1. prune stale entries older than `pruneAfter`
2. cap entry count to `maxEntries` (oldest first)
3. archive transcript files for removed entries that are no longer referenced
4. purge old `*.deleted.<timestamp>` and `*.reset.<timestamp>` archives by retention policy
5. rotate `sessions.json` when it exceeds `rotateBytes`
6. if `maxDiskBytes` is set, enforce disk budget toward `highWaterBytes` (oldest artifacts first, then oldest sessions)
### Performance caveat for large stores
Large session stores are common in high-volume setups. Maintenance work is write-path work, so very large stores can increase write latency.
What increases cost most:
- very high `session.maintenance.maxEntries` values
- long `pruneAfter` windows that keep stale entries around
- many transcript/archive artifacts in `~/.openclaw/agents/<agentId>/sessions/`
- enabling disk budgets (`maxDiskBytes`) without reasonable pruning/cap limits
What to do:
- use `mode: "enforce"` in production so growth is bounded automatically
- set both time and count limits (`pruneAfter` + `maxEntries`), not just one
- set `maxDiskBytes` + `highWaterBytes` for hard upper bounds in large deployments
- keep `highWaterBytes` meaningfully below `maxDiskBytes` (default is 80%)
- run `openclaw sessions cleanup --dry-run --json` after config changes to verify projected impact before enforcing
- for frequent active sessions, pass `--active-key` when running manual cleanup
### Customize examples
Use a conservative enforce policy:
OpenClaw automatically bounds session storage over time. By default, it runs
in `warn` mode (reports what would be cleaned). Set `session.maintenance.mode`
to `"enforce"` for automatic cleanup:
```json5
{
session: {
maintenance: {
mode: "enforce",
pruneAfter: "45d",
maxEntries: 800,
rotateBytes: "20mb",
resetArchiveRetention: "14d",
pruneAfter: "30d",
maxEntries: 500,
},
},
}
```
Enable a hard disk budget for the sessions directory:
Preview with `openclaw sessions cleanup --dry-run`.
```json5
{
session: {
maintenance: {
mode: "enforce",
maxDiskBytes: "1gb",
highWaterBytes: "800mb",
},
},
}
```
## Inspecting sessions
Tune for larger installs (example):
- `openclaw status` -- session store path and recent activity.
- `openclaw sessions --json` -- all sessions (filter with `--active <minutes>`).
- `/status` in chat -- context usage, model, and toggles.
- `/context list` -- what is in the system prompt.
```json5
{
session: {
maintenance: {
mode: "enforce",
pruneAfter: "14d",
maxEntries: 2000,
rotateBytes: "25mb",
maxDiskBytes: "2gb",
highWaterBytes: "1.6gb",
},
},
}
```
## Further reading
Preview or force maintenance from CLI:
```bash
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce
```
## Session pruning
OpenClaw trims **old tool results** from the in-memory context right before LLM calls by default.
This does **not** rewrite JSONL history. See [/concepts/session-pruning](/concepts/session-pruning).
## Pre-compaction memory flush
When a session nears auto-compaction, OpenClaw can run a **silent memory flush**
turn that reminds the model to write durable notes to disk. This only runs when
the workspace is writable. See [Memory](/concepts/memory) and
[Compaction](/concepts/compaction).
## Mapping transports → session keys
- Direct chats follow `session.dmScope` (default `main`).
- `main`: `agent:<agentId>:<mainKey>` (continuity across devices/channels).
- Multiple phone numbers and channels can map to the same agent main key; they act as transports into one conversation.
- `per-peer`: `agent:<agentId>:direct:<peerId>`.
- `per-channel-peer`: `agent:<agentId>:<channel>:direct:<peerId>`.
- `per-account-channel-peer`: `agent:<agentId>:<channel>:<accountId>:direct:<peerId>` (accountId defaults to `default`).
- If `session.identityLinks` matches a provider-prefixed peer id (for example `telegram:123`), the canonical key replaces `<peerId>` so the same person shares a session across channels.
- Group chats isolate state: `agent:<agentId>:<channel>:group:<id>` (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
- Telegram forum topics append `:topic:<threadId>` to the group id for isolation.
- Legacy `group:<id>` keys are still recognized for migration.
- Inbound contexts may still use `group:<id>`; the channel is inferred from `Provider` and normalized to the canonical `agent:<agentId>:<channel>:group:<id>` form.
- Other sources:
- Cron jobs: `cron:<job.id>` (isolated) or custom `session:<custom-id>` (persistent)
- Webhooks: `hook:<uuid>` (unless explicitly set by the hook)
- Node runs: `node-<nodeId>`
## Lifecycle
- Reset policy: sessions are reused until they expire, and expiry is evaluated on the next inbound message.
- Daily reset: defaults to **4:00 AM local time on the gateway host**. A session is stale once its last update is earlier than the most recent daily reset time.
- Idle reset (optional): `idleMinutes` adds a sliding idle window. When both daily and idle resets are configured, **whichever expires first** forces a new session.
- Legacy idle-only: if you set `session.idleMinutes` without any `session.reset`/`resetByType` config, OpenClaw stays in idle-only mode for backward compatibility.
- Per-type overrides (optional): `resetByType` lets you override the policy for `direct`, `group`, and `thread` sessions (thread = Slack/Discord threads, Telegram topics, Matrix threads when provided by the connector).
- Per-channel overrides (optional): `resetByChannel` overrides the reset policy for a channel (applies to all session types for that channel and takes precedence over `reset`/`resetByType`).
- Reset triggers: exact `/new` or `/reset` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through. `/new <model>` accepts a model alias, `provider/model`, or provider name (fuzzy match) to set the new session model. If `/new` or `/reset` is sent alone, OpenClaw runs a short “hello” greeting turn to confirm the reset.
- Manual reset: delete specific keys from the store or remove the JSONL transcript; the next message recreates them.
- Isolated cron jobs always mint a fresh `sessionId` per run (no idle reuse).
## Send policy (optional)
Block delivery for specific session types without listing individual ids.
```json5
{
session: {
sendPolicy: {
rules: [
{ action: "deny", match: { channel: "discord", chatType: "group" } },
{ action: "deny", match: { keyPrefix: "cron:" } },
// Match the raw session key (including the `agent:<id>:` prefix).
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
],
default: "allow",
},
},
}
```
Runtime override (owner only):
- `/send on` → allow for this session
- `/send off` → deny for this session
- `/send inherit` → clear override and use config rules
Send these as standalone messages so they register.
## Configuration (optional rename example)
```json5
// ~/.openclaw/openclaw.json
{
session: {
scope: "per-sender", // keep group keys separate
dmScope: "main", // DM continuity (set per-channel-peer/per-account-channel-peer for shared inboxes)
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
// Defaults: mode=daily, atHour=4 (gateway host local time).
// If you also set idleMinutes, whichever expires first wins.
mode: "daily",
atHour: 4,
idleMinutes: 120,
},
resetByType: {
thread: { mode: "daily", atHour: 4 },
direct: { mode: "idle", idleMinutes: 240 },
group: { mode: "idle", idleMinutes: 120 },
},
resetByChannel: {
discord: { mode: "idle", idleMinutes: 10080 },
},
resetTriggers: ["/new", "/reset"],
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
mainKey: "main",
},
}
```
## Inspecting
- `openclaw status` — shows store path and recent sessions.
- `openclaw sessions --json` — dumps every entry (filter with `--active <minutes>`).
- `openclaw gateway call sessions.list --params '{}'` — fetch sessions from the running gateway (use `--url`/`--token` for remote gateway access).
- Send `/status` as a standalone message in chat to see whether the agent is reachable, how much of the session context is used, current thinking/fast/verbose toggles, and when your WhatsApp web creds were last refreshed (helps spot relink needs).
- Send `/context list` or `/context detail` to see whats in the system prompt and injected workspace files (and the biggest context contributors).
- Send `/stop` (or standalone abort phrases like `stop`, `stop action`, `stop run`, `stop openclaw`) to abort the current run, clear queued followups for that session, and stop any sub-agent runs spawned from it (the reply includes the stopped count).
- Send `/compact` (optional instructions) as a standalone message to summarize older context and free up window space. See [/concepts/compaction](/concepts/compaction).
- JSONL transcripts can be opened directly to review full turns.
## Tips
- Keep the primary key dedicated to 1:1 traffic; let groups keep their own keys.
- When automating cleanup, delete individual keys instead of the whole store to preserve context elsewhere.
## Session origin metadata
Each session entry records where it came from (best-effort) in `origin`:
- `label`: human label (resolved from conversation label + group subject/channel)
- `provider`: normalized channel id (including extensions)
- `from`/`to`: raw routing ids from the inbound envelope
- `accountId`: provider account id (when multi-account)
- `threadId`: thread/topic id when the channel supports it
The origin fields are populated for direct messages, channels, and groups. If a
connector only updates delivery routing (for example, to keep a DM main session
fresh), it should still provide inbound context so the session keeps its
explainer metadata. Extensions can do this by sending `ConversationLabel`,
`GroupSubject`, `GroupChannel`, `GroupSpace`, and `SenderName` in the inbound
context and calling `recordSessionMetaFromInbound` (or passing the same context
to `updateLastRoute`).
- [Session Pruning](/concepts/session-pruning) -- trimming tool results
- [Compaction](/concepts/compaction) -- summarizing long conversations
- [Session Tools](/concepts/session-tool) -- agent tools for cross-session work
- [Session Management Deep Dive](/reference/session-management-compaction) --
store schema, transcripts, send policy, origin metadata, and advanced config

View File

@@ -1032,7 +1032,16 @@
"concepts/session",
"concepts/session-pruning",
"concepts/session-tool",
"concepts/memory",
{
"group": "Memory",
"pages": [
"concepts/memory",
"concepts/memory-builtin",
"concepts/memory-qmd",
"concepts/memory-honcho",
"concepts/memory-search"
]
},
"concepts/compaction"
]
},
@@ -1146,6 +1155,7 @@
]
},
"tools/btw",
"tools/code-execution",
"tools/diffs",
"tools/elevated",
"tools/exec",
@@ -1431,7 +1441,14 @@
},
{
"group": "Utility",
"pages": ["cli/acp", "cli/clawbot", "cli/completion", "cli/dns", "cli/docs"]
"pages": [
"cli/acp",
"cli/clawbot",
"cli/completion",
"cli/dns",
"cli/docs",
"cli/mcp"
]
}
]
},

View File

@@ -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 },

View File

@@ -523,6 +523,7 @@ BlueBubbles is the recommended iMessage path (plugin-backed, configured under `c
- Core key paths covered here: `channels.bluebubbles`, `channels.bluebubbles.dmPolicy`.
- Optional `channels.bluebubbles.defaultAccount` overrides default account selection when it matches a configured account id.
- Top-level `bindings[]` entries with `type: "acp"` can bind BlueBubbles conversations to persistent ACP sessions. Use a BlueBubbles handle or target string (`chat_id:*`, `chat_guid:*`, `chat_identifier:*`) in `match.peer.id`. Shared field semantics: [ACP Agents](/tools/acp-agents#channel-specific-settings).
- Full BlueBubbles channel configuration is documented in [BlueBubbles](/channels/bluebubbles).
### iMessage
@@ -559,6 +560,7 @@ OpenClaw spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required.
- `attachmentRoots` and `remoteAttachmentRoots` restrict inbound attachment paths (default: `/Users/*/Library/Messages/Attachments`).
- SCP uses strict host-key checking, so ensure the relay host key already exists in `~/.ssh/known_hosts`.
- `channels.imessage.configWrites`: allow or deny iMessage-initiated config writes.
- Top-level `bindings[]` entries with `type: "acp"` can bind iMessage conversations to persistent ACP sessions. Use a normalized handle or explicit chat target (`chat_id:*`, `chat_guid:*`, `chat_identifier:*`) in `match.peer.id`. Shared field semantics: [ACP Agents](/tools/acp-agents#channel-specific-settings).
<Accordion title="iMessage SSH wrapper example">
@@ -904,7 +906,7 @@ Time format in system prompt. Default: `auto` (OS preference).
- Also used as fallback routing when the selected/default model cannot accept image input.
- `imageGenerationModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
- Used by the shared image-generation capability and any future tool/plugin surface that generates images.
- Typical values: `google/gemini-3-pro-image-preview` for the native Nano Banana-style flow, `fal/fal-ai/flux/dev` for fal, or `openai/gpt-image-1` for OpenAI Images.
- Typical values: `google/gemini-3-pro-image-preview` for native Gemini image generation, `fal/fal-ai/flux/dev` for fal, or `openai/gpt-image-1` for OpenAI Images.
- If you select a provider/model directly, configure the matching provider auth/API key too (for example `GEMINI_API_KEY` or `GOOGLE_API_KEY` for `google/*`, `OPENAI_API_KEY` for `openai/*`, `FAL_KEY` for `fal/*`).
- If omitted, `image_generate` can still infer a best-effort provider default from compatible auth-backed image-generation providers.
- `pdfModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
@@ -912,11 +914,13 @@ Time format in system prompt. Default: `auto` (OS preference).
- If omitted, the PDF tool falls back to `imageModel`, then to best-effort provider defaults.
- `pdfMaxBytesMb`: default PDF size limit for the `pdf` tool when `maxBytesMb` is not passed at call time.
- `pdfMaxPages`: default maximum pages considered by extraction fallback mode in the `pdf` tool.
- `verboseDefault`: default verbose level for agents. Values: `"off"`, `"on"`, `"full"`. Default: `"off"`.
- `elevatedDefault`: default elevated-output level for agents. Values: `"off"`, `"on"`, `"ask"`, `"full"`. Default: `"on"`.
- `model.primary`: format `provider/model` (e.g. `anthropic/claude-opus-4-6`). If you omit the provider, OpenClaw assumes `anthropic` (deprecated).
- `models`: the configured model catalog and allowlist for `/model`. Each entry can include `alias` (shortcut) and `params` (provider-specific, for example `temperature`, `maxTokens`, `cacheRetention`, `context1m`).
- `params` merge precedence (config): `agents.defaults.models["provider/model"].params` is the base, then `agents.list[].params` (matching agent id) overrides by key.
- Config writers that mutate these fields (for example `/models set`, `/models set-image`, and fallback add/remove commands) save canonical object form and preserve existing fallback lists when possible.
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 1.
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 4.
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
@@ -997,7 +1001,7 @@ Periodic heartbeat runs.
}
```
- `every`: duration string (ms/s/m/h). Default: `30m`.
- `every`: duration string (ms/s/m/h). Default: `30m` (API-key auth) or `1h` (OAuth auth). Set to `0m` to disable.
- `suppressToolErrorWarnings`: when true, suppresses tool error warning payloads during heartbeat runs.
- `directPolicy`: direct/DM delivery policy. `allow` (default) permits direct-target delivery. `block` suppresses direct-target delivery and emits `reason=dm-blocked`.
- `lightContext`: when true, heartbeat runs use lightweight bootstrap context and keep only `HEARTBEAT.md` from workspace bootstrap files.
@@ -1123,6 +1127,8 @@ See [Streaming](/concepts/streaming) for behavior + chunking details.
See [Typing Indicators](/concepts/typing-indicators).
<a id="agentsdefaultssandbox"></a>
### `agents.defaults.sandbox`
Optional sandboxing for the embedded agent. See [Sandboxing](/gateway/sandboxing) for the full guide.
@@ -1610,6 +1616,9 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
<Accordion title="Session field details">
- **`scope`**: base session grouping strategy for group-chat contexts.
- `per-sender` (default): each sender gets an isolated session within a channel context.
- `global`: all participants in a channel context share a single session (use only when shared context is intended).
- **`dmScope`**: how DMs are grouped.
- `main`: all DMs share the main session.
- `per-peer`: isolate by sender id across channels.
@@ -1622,6 +1631,7 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
- If parent `totalTokens` is above this value, OpenClaw starts a fresh thread session instead of inheriting parent transcript history.
- Set `0` to disable this guard and always allow parent forking.
- **`mainKey`**: legacy field. Runtime now always uses `"main"` for the main direct-chat bucket.
- **`agentToAgent.maxPingPongTurns`**: maximum reply-back turns between agents during agent-to-agent exchanges (integer, range: `0``5`). `0` disables ping-pong chaining.
- **`sendPolicy`**: match by `channel`, `chatType` (`direct|group|channel`, with legacy `dm` alias), `keyPrefix`, or `rawKeyPrefix`. First deny wins.
- **`maintenance`**: session-store cleanup + retention controls.
- `mode`: `warn` emits warnings only; `enforce` applies cleanup.
@@ -2057,7 +2067,7 @@ Notes:
- File permissions are `0700` for directories and `0600` for files.
- Cleanup follows the `cleanup` policy: `delete` always removes attachments; `keep` retains them only when `retainOnSessionKeep: true`.
### `tools.subagents`
### `agents.defaults.subagents`
```json5
{
@@ -2065,7 +2075,7 @@ Notes:
defaults: {
subagents: {
model: "minimax/MiniMax-M2.7",
maxConcurrent: 1,
maxConcurrent: 8,
runTimeoutSeconds: 900,
archiveAfterMinutes: 60,
},
@@ -2082,7 +2092,7 @@ Notes:
## Custom providers and base URLs
OpenClaw uses the pi-coding-agent model catalog. Add custom providers via `models.providers` in config or `~/.openclaw/agents/<agentId>/agent/models.json`.
OpenClaw uses the built-in model catalog. Add custom providers via `models.providers` in config or `~/.openclaw/agents/<agentId>/agent/models.json`.
```json5
{
@@ -2111,7 +2121,7 @@ OpenClaw uses the pi-coding-agent model catalog. Add custom providers via `model
```
- Use `authHeader: true` + `headers` for custom auth needs.
- Override agent config root with `OPENCLAW_AGENT_DIR` (or `PI_CODING_AGENT_DIR`).
- Override agent config root with `OPENCLAW_AGENT_DIR` (or `PI_CODING_AGENT_DIR`, a legacy environment variable alias).
- Merge precedence for matching provider IDs:
- Non-empty agent `models.json` `baseUrl` values win.
- Non-empty agent `apiKey` values win only when that provider is not SecretRef-managed in current config/auth-profile context.
@@ -2354,13 +2364,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>
@@ -2642,6 +2652,50 @@ Convenience flags: `--dev` (uses `~/.openclaw-dev` + port `19001`), `--profile <
See [Multiple Gateways](/gateway/multiple-gateways).
### `gateway.tls`
```json5
{
gateway: {
tls: {
enabled: false,
autoGenerate: false,
certPath: "/etc/openclaw/tls/server.crt",
keyPath: "/etc/openclaw/tls/server.key",
caPath: "/etc/openclaw/tls/ca-bundle.crt",
},
},
}
```
- `enabled`: enables TLS termination at the gateway listener (HTTPS/WSS) (default: `false`).
- `autoGenerate`: auto-generates a local self-signed cert/key pair when explicit files are not configured; for local/dev use only.
- `certPath`: filesystem path to the TLS certificate file.
- `keyPath`: filesystem path to the TLS private key file; keep permission-restricted.
- `caPath`: optional CA bundle path for client verification or custom trust chains.
### `gateway.reload`
```json5
{
gateway: {
reload: {
mode: "hybrid", // off | restart | hot | hybrid
debounceMs: 500,
deferralTimeoutMs: 300000,
},
},
}
```
- `mode`: controls how config edits are applied at runtime.
- `"off"`: ignore live edits; changes require an explicit restart.
- `"restart"`: always restart the gateway process on config change.
- `"hot"`: apply changes in-process without restarting.
- `"hybrid"` (default): try hot reload first; fall back to restart if required.
- `debounceMs`: debounce window in ms before config changes are applied (non-negative integer).
- `deferralTimeoutMs`: maximum time in ms to wait for in-flight operations before forcing a restart (default: `300000` = 5 minutes).
---
## Hooks
@@ -2924,6 +2978,26 @@ Notes:
- See [OAuth](/concepts/oauth).
- Secrets runtime behavior and `audit/configure/apply` tooling: [Secrets Management](/gateway/secrets).
### `auth.cooldowns`
```json5
{
auth: {
cooldowns: {
billingBackoffHours: 5,
billingBackoffHoursByProvider: { anthropic: 3, openai: 8 },
billingMaxHours: 24,
failureWindowHours: 24,
},
},
}
```
- `billingBackoffHours`: base backoff in hours when a profile fails due to billing/insufficient credits (default: `5`).
- `billingBackoffHoursByProvider`: optional per-provider overrides for billing backoff hours.
- `billingMaxHours`: cap in hours for billing backoff exponential growth (default: `24`).
- `failureWindowHours`: rolling window in hours used for backoff counters (default: `24`).
---
## Logging
@@ -2944,6 +3018,128 @@ Notes:
- Default log file: `/tmp/openclaw/openclaw-YYYY-MM-DD.log`.
- Set `logging.file` for a stable path.
- `consoleLevel` bumps to `debug` when `--verbose`.
- `maxFileBytes`: maximum log file size in bytes before writes are suppressed (positive integer; default: `524288000` = 500 MB). Use external log rotation for production deployments.
---
## Diagnostics
```json5
{
diagnostics: {
enabled: true,
flags: ["telegram.*"],
stuckSessionWarnMs: 30000,
otel: {
enabled: false,
endpoint: "https://otel-collector.example.com:4318",
protocol: "http/protobuf", // http/protobuf | grpc
headers: { "x-tenant-id": "my-org" },
serviceName: "openclaw-gateway",
traces: true,
metrics: true,
logs: false,
sampleRate: 1.0,
flushIntervalMs: 5000,
},
cacheTrace: {
enabled: false,
includeMessages: true,
includePrompt: true,
includeSystem: true,
},
},
}
```
- `enabled`: master toggle for instrumentation output (default: `true`).
- `flags`: array of flag strings enabling targeted log output (supports wildcards like `"telegram.*"` or `"*"`).
- `stuckSessionWarnMs`: age threshold in ms for emitting stuck-session warnings while a session remains in processing state.
- `otel.enabled`: enables the OpenTelemetry export pipeline (default: `false`).
- `otel.endpoint`: collector URL for OTel export.
- `otel.protocol`: `"http/protobuf"` (default) or `"grpc"`.
- `otel.headers`: extra HTTP/gRPC metadata headers sent with OTel export requests.
- `otel.serviceName`: service name for resource attributes.
- `otel.traces` / `otel.metrics` / `otel.logs`: enable trace, metrics, or log export.
- `otel.sampleRate`: trace sampling rate `0``1`.
- `otel.flushIntervalMs`: periodic telemetry flush interval in ms.
- `cacheTrace.enabled`: log cache trace snapshots for embedded runs (default: `false`).
- `cacheTrace.includeMessages` / `includePrompt` / `includeSystem`: control what is included in cache trace output (all default: `true`).
---
## Update
```json5
{
update: {
channel: "stable", // stable | beta | dev
checkOnStart: true,
auto: {
enabled: false,
stableDelayHours: 6,
stableJitterHours: 12,
betaCheckIntervalHours: 1,
},
},
}
```
- `channel`: release channel for npm/git installs — `"stable"`, `"beta"`, or `"dev"`.
- `checkOnStart`: check for npm updates when the gateway starts (default: `true`).
- `auto.enabled`: enable background auto-update for package installs (default: `false`).
- `auto.stableDelayHours`: minimum delay in hours before stable-channel auto-apply (default: `6`; max: `168`).
- `auto.stableJitterHours`: extra stable-channel rollout spread window in hours (default: `12`; max: `168`).
- `auto.betaCheckIntervalHours`: how often beta-channel checks run in hours (default: `1`; max: `24`).
---
## ACP
```json5
{
acp: {
enabled: false,
dispatch: { enabled: true },
backend: "acpx",
defaultAgent: "main",
allowedAgents: ["main", "ops"],
maxConcurrentSessions: 10,
stream: {
coalesceIdleMs: 50,
maxChunkChars: 1000,
repeatSuppression: true,
deliveryMode: "live", // live | final_only
hiddenBoundarySeparator: "paragraph", // none | space | newline | paragraph
maxOutputChars: 50000,
maxSessionUpdateChars: 500,
},
runtime: {
ttlMinutes: 30,
},
},
}
```
- `enabled`: global ACP feature gate (default: `false`).
- `dispatch.enabled`: independent gate for ACP session turn dispatch (default: `true`). Set `false` to keep ACP commands available while blocking execution.
- `backend`: default ACP runtime backend id (must match a registered ACP runtime plugin).
- `defaultAgent`: fallback ACP target agent id when spawns do not specify an explicit target.
- `allowedAgents`: allowlist of agent ids permitted for ACP runtime sessions; empty means no additional restriction.
- `maxConcurrentSessions`: maximum concurrently active ACP sessions.
- `stream.coalesceIdleMs`: idle flush window in ms for streamed text.
- `stream.maxChunkChars`: maximum chunk size before splitting streamed block projection.
- `stream.repeatSuppression`: suppress repeated status/tool lines per turn (default: `true`).
- `stream.deliveryMode`: `"live"` streams incrementally; `"final_only"` buffers until turn terminal events.
- `stream.hiddenBoundarySeparator`: separator before visible text after hidden tool events (default: `"paragraph"`).
- `stream.maxOutputChars`: maximum assistant output characters projected per ACP turn.
- `stream.maxSessionUpdateChars`: maximum characters for projected ACP status/update lines.
- `runtime.ttlMinutes`: idle TTL in minutes for ACP session workers before eligible cleanup.
---
@@ -2987,29 +3183,7 @@ Metadata written by CLI guided setup flows (`onboard`, `configure`, `doctor`):
## Identity
```json5
{
agents: {
list: [
{
id: "main",
identity: {
name: "Samantha",
theme: "helpful sloth",
emoji: "🦥",
avatar: "avatars/samantha.png",
},
},
],
},
}
```
Written by the macOS onboarding assistant. Derives defaults:
- `messages.ackReaction` from `identity.emoji` (falls back to 👀)
- `mentionPatterns` from `identity.name`/`identity.emoji`
- `avatar` accepts: workspace-relative path, `http(s)` URL, or `data:` URI
See `agents.list` identity fields under [Agent defaults](#agent-defaults).
---
@@ -3061,6 +3235,48 @@ Current builds no longer include the TCP bridge. Nodes connect over the Gateway
- `webhookToken`: bearer token used for cron webhook POST delivery (`delivery.mode = "webhook"`), if omitted no auth header is sent.
- `webhook`: deprecated legacy fallback webhook URL (http/https) used only for stored jobs that still have `notify: true`.
### `cron.retry`
```json5
{
cron: {
retry: {
maxAttempts: 3,
backoffMs: [30000, 60000, 300000],
retryOn: ["rate_limit", "overloaded", "network", "timeout", "server_error"],
},
},
}
```
- `maxAttempts`: maximum retries for one-shot jobs on transient errors (default: `3`; range: `0``10`).
- `backoffMs`: array of backoff delays in ms for each retry attempt (default: `[30000, 60000, 300000]`; 110 entries).
- `retryOn`: error types that trigger retries — `"rate_limit"`, `"overloaded"`, `"network"`, `"timeout"`, `"server_error"`. Omit to retry all transient types.
Applies only to one-shot cron jobs. Recurring jobs use separate failure handling.
### `cron.failureAlert`
```json5
{
cron: {
failureAlert: {
enabled: false,
after: 3,
cooldownMs: 3600000,
mode: "announce",
accountId: "main",
},
},
}
```
- `enabled`: enable failure alerts for cron jobs (default: `false`).
- `after`: consecutive failures before an alert fires (positive integer, min: `1`).
- `cooldownMs`: minimum milliseconds between repeated alerts for the same job (non-negative integer).
- `mode`: delivery mode — `"announce"` sends via a channel message; `"webhook"` posts to the configured webhook.
- `accountId`: optional account or channel id to scope alert delivery.
See [Cron Jobs](/automation/cron-jobs).
---

View File

@@ -63,6 +63,7 @@ openclaw channels status --probe
<Note>
Gateway config reload watches the active config file path (resolved from profile/state defaults, or `OPENCLAW_CONFIG_PATH` when set).
Default mode is `gateway.reload.mode="hybrid"`.
After the first successful load, the running process serves the active in-memory config snapshot; successful reload swaps that snapshot atomically.
</Note>
## Runtime model

View File

@@ -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 },

View File

@@ -195,6 +195,12 @@ The Gateway treats these as **claims** and enforces server-side allowlists.
- Operator clients resolve by calling `exec.approval.resolve` (requires `operator.approvals` scope).
- For `host=node`, `exec.approval.request` must include `systemRunPlan` (canonical `argv`/`cwd`/`rawCommand`/session metadata). Requests missing `systemRunPlan` are rejected.
## Agent delivery fallback
- `agent` requests can include `deliver=true` to request outbound delivery.
- `bestEffortDeliver=false` keeps strict behavior: unresolved or internal-only delivery targets return `INVALID_REQUEST`.
- `bestEffortDeliver=true` allows fallback to session-only execution when no external deliverable route can be resolved (for example internal/webchat sessions or ambiguous multi-channel configs).
## Versioning
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/schema.ts`.

View File

@@ -402,7 +402,7 @@ Docker installs and the containerized gateway live here:
For Docker gateway deployments, `scripts/docker/setup.sh` can bootstrap sandbox config.
Set `OPENCLAW_SANDBOX=1` (or `true`/`yes`/`on`) to enable that path. You can
override socket location with `OPENCLAW_DOCKER_SOCKET`. Full setup and env
reference: [Docker](/install/docker#enable-agent-sandbox-for-docker-gateway).
reference: [Docker](/install/docker#agent-sandbox).
## setupCommand (one-time container setup)

View File

@@ -21,6 +21,7 @@ Secrets are resolved into an in-memory runtime snapshot.
- Startup fails fast when an effectively active SecretRef cannot be resolved.
- Reload uses atomic swap: full success, or keep the last-known-good snapshot.
- Runtime requests read from the active in-memory snapshot only.
- After the first successful config activation/load, runtime code paths keep reading that active in-memory snapshot until a successful reload swaps it.
- Outbound delivery paths also read from that active snapshot (for example Discord reply/thread delivery and Telegram action sends); they do not re-resolve SecretRefs on each send.
This keeps secret-provider outages off hot request paths.
@@ -288,6 +289,39 @@ Optional per-id errors:
}
```
## MCP server environment variables
MCP server env vars configured via `plugins.entries.acpx.config.mcpServers` support SecretInput. This keeps API keys and tokens out of plaintext config:
```json5
{
plugins: {
entries: {
acpx: {
enabled: true,
config: {
mcpServers: {
github: {
command: "npx",
args: ["-y", "@modelcontextprotocol/server-github"],
env: {
GITHUB_PERSONAL_ACCESS_TOKEN: {
source: "env",
provider: "default",
id: "MCP_GITHUB_PAT",
},
},
},
},
},
},
},
},
}
```
Plaintext string values still work. Env-template refs like `${MCP_SERVER_API_KEY}` and SecretRef objects are resolved during gateway activation before the MCP server process is spawned. As with other SecretRef surfaces, unresolved refs only block activation when the `acpx` plugin is effectively active.
## Sandbox SSH auth material
The core `ssh` sandbox backend also supports SecretRefs for SSH auth material:

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