mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-28 02:11:53 +08:00
Compare commits
2 Commits
fix/reply-
...
codex/code
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8364730c41 | ||
|
|
60c6915b6c |
@@ -9,13 +9,6 @@ Batch workflow for pasted OpenClaw issue/PR refs.
|
||||
Execute, do not summarize.
|
||||
Triage does not commit, push, create PRs, comment, close, label, land, or merge.
|
||||
|
||||
## Peter Review Gate
|
||||
|
||||
Peter always wants to review code before commits.
|
||||
After local fixes and proof, stop with the diff summary, touched files, and test/gate output.
|
||||
Do not commit, push, comment, close, label, land, merge, or otherwise publish until Peter explicitly says he has reviewed and approves that exact diff for that action.
|
||||
If Peter asks for a bundled action like "commit push close", first confirm the code has already been reviewed in chat; if not, stop and ask for review/approval.
|
||||
|
||||
## Companion Skills
|
||||
|
||||
Use `$gitcrawl` first, `$openclaw-pr-maintainer` for live GitHub hygiene, `$github-deep-review` posture for source tracing, and `$openclaw-testing` for proof.
|
||||
|
||||
@@ -29,12 +29,6 @@ OPENCLAW_GATEWAY_TOKEN=
|
||||
# OPENCLAW_CONFIG_PATH=~/.openclaw/openclaw.json
|
||||
# OPENCLAW_HOME=~
|
||||
|
||||
# Allowlist of extra directories that `$include` directives in openclaw.json may
|
||||
# resolve files from. Path-list separated (':' on POSIX, ';' on Windows). Each
|
||||
# entry is tilde-expanded. Without this, `$include` is confined to the directory
|
||||
# containing openclaw.json.
|
||||
# OPENCLAW_INCLUDE_ROOTS=/etc/openclaw/shared:~/.openclaw/shared
|
||||
|
||||
# Optional: import missing keys from your login shell profile.
|
||||
# OPENCLAW_LOAD_SHELL_ENV=1
|
||||
# OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000
|
||||
|
||||
@@ -20,7 +20,8 @@ paths:
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-public-surface-runtime-root.ts
|
||||
- src/plugins/plugin-sdk-dist-alias.ts
|
||||
- src/plugins/bundled-runtime-deps.ts
|
||||
- src/plugins/bundled-runtime-root.ts
|
||||
- src/plugins/captured-registration.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
|
||||
@@ -25,7 +25,8 @@ paths:
|
||||
- src/plugins/bundled-dir.ts
|
||||
- src/plugins/bundled-plugin-metadata.ts
|
||||
- src/plugins/bundled-plugin-scan.ts
|
||||
- src/plugins/plugin-sdk-dist-alias.ts
|
||||
- src/plugins/bundled-runtime-deps*.ts
|
||||
- src/plugins/bundled-runtime-root.ts
|
||||
- src/plugins/cli-registry-loader.ts
|
||||
- src/plugins/config-activation-shared.ts
|
||||
- src/plugins/config-contracts.ts
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -564,6 +564,9 @@ jobs:
|
||||
- name: Smoke test built bundled plugin singleton
|
||||
run: pnpm test:build:singleton
|
||||
|
||||
- name: Smoke test built bundled runtime deps
|
||||
run: pnpm test:build:bundled-runtime-deps
|
||||
|
||||
- name: Check CLI startup memory
|
||||
run: pnpm test:startup:memory
|
||||
|
||||
|
||||
6
.github/workflows/install-smoke.yml
vendored
6
.github/workflows/install-smoke.yml
vendored
@@ -510,3 +510,9 @@ jobs:
|
||||
with:
|
||||
install-bun: "false"
|
||||
install-deps: "true"
|
||||
|
||||
- name: Run fast bundled plugin Docker E2E
|
||||
env:
|
||||
OPENCLAW_BUNDLED_CHANNEL_DEPS_E2E_IMAGE: openclaw-bundled-channel-fast:local
|
||||
OPENCLAW_BUNDLED_CHANNEL_DOCKER_RUN_TIMEOUT: 90s
|
||||
run: timeout 480s pnpm test:docker:bundled-channel-deps:fast
|
||||
|
||||
@@ -646,6 +646,21 @@ jobs:
|
||||
- chunk_id: plugins-runtime-install-h
|
||||
label: plugins/runtime install H
|
||||
timeout_minutes: 120
|
||||
- chunk_id: bundled-channels-core
|
||||
label: bundled channels core
|
||||
timeout_minutes: 90
|
||||
- chunk_id: bundled-channels-update-a
|
||||
label: bundled channels update A
|
||||
timeout_minutes: 45
|
||||
- chunk_id: bundled-channels-update-discord
|
||||
label: bundled channels update Discord
|
||||
timeout_minutes: 30
|
||||
- chunk_id: bundled-channels-update-b
|
||||
label: bundled channels update B
|
||||
timeout_minutes: 45
|
||||
- chunk_id: bundled-channels-contracts
|
||||
label: bundled channels contracts
|
||||
timeout_minutes: 90
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_BASE_URL: ${{ secrets.OPENAI_BASE_URL }}
|
||||
|
||||
@@ -440,7 +440,7 @@ jobs:
|
||||
artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
|
||||
package_sha256: ${{ needs.prepare_release_package.outputs.package_sha256 }}
|
||||
suite_profile: custom
|
||||
docker_lanes: plugins-offline plugin-update
|
||||
docker_lanes: bundled-channel-deps-compat plugins-offline
|
||||
telegram_mode: mock-openai
|
||||
telegram_scenarios: telegram-help-command,telegram-commands-command,telegram-tools-compact-command,telegram-whoami-command,telegram-context-command,telegram-mention-gating
|
||||
secrets:
|
||||
|
||||
4
.github/workflows/package-acceptance.yml
vendored
4
.github/workflows/package-acceptance.yml
vendored
@@ -386,10 +386,10 @@ jobs:
|
||||
docker_lanes="npm-onboard-channel-agent gateway-network config-reload"
|
||||
;;
|
||||
package)
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins-offline plugin-update"
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor bundled-channel-deps-compat plugins-offline plugin-update"
|
||||
;;
|
||||
product)
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor plugins plugin-update mcp-channels cron-mcp-cleanup openai-web-search-minimal openwebui"
|
||||
docker_lanes="npm-onboard-channel-agent doctor-switch update-channel-switch upgrade-survivor published-upgrade-survivor bundled-channel-deps-compat plugins plugin-update mcp-channels cron-mcp-cleanup openai-web-search-minimal openwebui"
|
||||
include_openwebui=true
|
||||
;;
|
||||
full)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"eslint/no-sequences": "error",
|
||||
"eslint/no-self-compare": "error",
|
||||
"eslint/no-shadow": "off",
|
||||
"eslint/no-underscore-dangle": "off",
|
||||
"eslint/no-var": "error",
|
||||
"eslint/no-useless-call": "error",
|
||||
"eslint/no-useless-computed-key": "error",
|
||||
|
||||
@@ -125,7 +125,7 @@ Telegraph style. Root rules only. Read scoped `AGENTS.md` before subtree work.
|
||||
|
||||
## Tests
|
||||
|
||||
- Vitest. Colocated `*.test.ts`; e2e `*.e2e.test.ts`; example models `sonnet-4.6`, `gpt-5.5`; test GPT with 5.5 preferred, 5.4 ok, no GPT-4.x agent-smoke defaults.
|
||||
- Vitest. Colocated `*.test.ts`; e2e `*.e2e.test.ts`; example models `sonnet-4.6`, `gpt-5.4`.
|
||||
- Avoid brittle tests that grep workflow/docs strings for operator policy. Prefer executable behavior, parsed config/schema checks, or live run proof; put release/CI policy reminders in AGENTS/docs instead.
|
||||
- Clean timers/env/globals/mocks/sockets/temp dirs/module state; `--isolate=false` safe.
|
||||
- Hot tests: avoid per-test `vi.resetModules()` + heavy imports. Measure with `pnpm test:perf:imports <file>` / `pnpm test:perf:hotspots --limit N`.
|
||||
|
||||
29
CHANGELOG.md
29
CHANGELOG.md
@@ -2,28 +2,6 @@
|
||||
|
||||
Docs: https://docs.openclaw.ai
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changes
|
||||
|
||||
- Providers/OpenAI: add `extraBody`/`extra_body` passthrough for OpenAI-compatible TTS endpoints, so custom speech servers can receive fields such as `lang` in `/audio/speech` requests. Fixes #39900. Thanks @R3NK0R.
|
||||
- Dependencies: refresh workspace dependency pins, including TypeBox 1.1.37, AWS SDK 3.1041.0, Microsoft Teams 2.0.9, and Marked 18.0.3. Thanks @mariozechner, @aws, and @microsoft.
|
||||
|
||||
### Fixes
|
||||
|
||||
- Doctor/WhatsApp: warn when Linux crontabs still run the legacy `ensure-whatsapp.sh` health check, which can misreport `Gateway inactive` when cron lacks the systemd user-bus environment. Fixes #60204. Thanks @mySebbe.
|
||||
- Slack/setup: print the generated app manifest as plain JSON instead of embedding it inside the framed setup note, so it can be copied into Slack without deleting border characters. Fixes #65751. Thanks @theDanielJLewis.
|
||||
- Channels/WhatsApp: route CLI logout through the live Gateway and stop runtime-backed listeners before channel removal, so removing a WhatsApp account does not leave the old socket replying until restart. Fixes #67746. Thanks @123Mismail.
|
||||
- Voice Call/Twilio: honor TTS directive text and provider voice/model overrides during telephony synthesis, so `[[tts:...]]` tags are not spoken literally and voiceId overrides reach OpenAI/ElevenLabs calls. Fixes #58114. Thanks @legonhilltech-jpg.
|
||||
- Agents/Codex: stop prompting message-tool-only source turns to finish with `NO_REPLY`, so quiet turns are represented by not calling the visible message tool instead of conflicting final-text instructions. Thanks @pashpashpash.
|
||||
- Gateway/config: report failed backup restores as failed in logs and config observe audit records instead of marking them valid. (#70515) Thanks @davidangularme.
|
||||
- Compaction: use the active session model fallback chain for implicit summarization failures without persisting fallback model selection, so Azure content-filter 400s can recover. Fixes #64960. (#74470) Thanks @jalehman and @OpenCodeEngineer.
|
||||
- Gateway/config: allow `gateway config.patch` to update documented subagent thinking defaults. Fixes #75764. (#75802) Thanks @kAIborg24.
|
||||
- Plugins/CLI: keep git plugin install paths credential-free, preserve existing git checkouts until replacement succeeds, honor duplicate npm install mode, and remove managed git repos on uninstall. Thanks @vincentkoc.
|
||||
- Channels/status reactions: remove stale non-terminal lifecycle reactions when a run reaches done or error, so Discord does not leave a permanent thinking emoji after completion. Fixes #75458. Thanks @davelutztx.
|
||||
- Discord/doctor: migrate unsupported per-channel `agentId` entries under guild channel config into top-level `bindings[]` routes, so `openclaw doctor --fix` preserves the intended agent route instead of stripping it as an unknown key. Fixes #62455. Thanks @lobster-biscuit.
|
||||
- Gateway/config: log config health-state write failures instead of silently hiding config observe-recovery write errors. Thanks @sallyom.
|
||||
|
||||
## 2026.4.30
|
||||
|
||||
### Changes
|
||||
@@ -33,6 +11,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/CLI: add first-class `git:` plugin installs with ref checkout, commit metadata, normal scanner/staging, and `plugins update` support for recorded git sources. Thanks @badlogic.
|
||||
- Google Meet: add live caption health for Chrome transcribe mode, including caption observer state, transcript counters, last caption text, and recent transcript lines in status and doctor output. Refs #72478. Thanks @DougButdorf.
|
||||
- Voice Call/Google Meet: add Twilio Meet join phase logs around pre-connect DTMF, realtime stream setup, and initial greeting handoff for easier live-call debugging. Thanks @donkeykong91 and @PfanP.
|
||||
- Agents/Codex: default Codex-harness direct source replies to the OpenClaw `message` tool when visible reply delivery is not explicitly configured, keeping channel-visible output as a deliberate tool call. Thanks @pashpashpash.
|
||||
- macOS app: move recent session context rows into a Context submenu while keeping usage and cost details root-level, so the menu bar companion stays compact with many active sessions. Thanks @guti.
|
||||
- Gateway/SDK: add SDK-facing tools.invoke RPC with shared HTTP policy, typed approval/refusal results, and SDK helper support. Refs #74705. Thanks @BunsDev and @ai-hpc.
|
||||
- Discord: keep active buttons, selects, and forms working across Gateway restarts until they expire, so multi-step Discord interactions are less likely to break during upgrades or restarts. Thanks @amknight.
|
||||
@@ -43,9 +22,6 @@ Docs: https://docs.openclaw.ai
|
||||
- BlueBubbles: add opt-in `channels.bluebubbles.replyContextApiFallback` that fetches the original message from the BlueBubbles HTTP API when the in-memory reply-context cache misses (multi-instance deployments sharing one BB account, post-restart, after long-lived TTL/LRU eviction). Off by default; channel-level setting propagates to accounts that omit the flag through `mergeAccountConfig`; routed through the typed `BlueBubblesClient` so every fetch is SSRF-guarded by the same three-mode policy as every other BB client request; reply-id shape is validated and part-index prefixes (`p:0/<guid>`) are stripped before the request; concurrent webhooks for the same `replyToId` coalesce into one fetch and successful responses populate the reply cache for subsequent hits. Also promotes BlueBubbles attachment download failures from verbose to runtime error so silently-dropped inbound images are visible at default log level, and extends `sanitizeForLog` to redact `?password=…`/`?token=…` query params and `Authorization:` headers before they reach the log sink (CWE-532). (#71820) Thanks @coletebou and @zqchris.
|
||||
- CLI/proxy: add `openclaw proxy validate` so operators can verify effective proxy configuration, proxy reachability, and expected allow/deny destination behavior before deploying proxy-routed OpenClaw commands. (#73438) Thanks @jesse-merhi.
|
||||
- Agents/Codex: default Codex app-server dynamic tools to native-first, keeping OpenClaw integration tools while leaving file, patch, exec, and process ownership to the Codex harness. (#75308) Thanks @pashpashpash.
|
||||
- Agents/Codex: default Codex-harness direct source replies to the OpenClaw `message` tool when visible reply delivery is not explicitly configured, keeping channel-visible output as a deliberate tool call. (#75765) Thanks @pashpashpash.
|
||||
- Heartbeats/agents: add a structured `heartbeat_respond` tool for tool-capable heartbeat runs so agents can record quiet outcomes or explicit notification text without relying only on `HEARTBEAT_OK` parsing. (#75765) Thanks @pashpashpash.
|
||||
- Gateway/config: allow `$include` directives to read files from operator-approved `OPENCLAW_INCLUDE_ROOTS` directories while preserving default config-directory confinement. Thanks @ificator.
|
||||
|
||||
### Fixes
|
||||
|
||||
@@ -61,7 +37,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/TTS: keep bundled speech-provider discovery available on cold package Gateway paths and add bundled plugin matrix runtime probes for health, readiness, RPC, TTS discovery, and post-ready runtime-deps watchdog coverage. Refs #75283. Thanks @vincentkoc.
|
||||
- Google Meet/Twilio: show delegated voice call ID, DTMF, and intro-greeting state in `googlemeet doctor`, and avoid claiming DTMF was sent when no Meet PIN sequence was configured. Refs #72478. Thanks @DougButdorf.
|
||||
- Plugins/tools: prefer built bundled plugin code during tool discovery and skip channel runtime hydration while preserving companion provider registrations, reducing per-run plugin-tool prep cost without dropping executable plugin tools. Fixes #75290. Thanks @thanos-openclaw.
|
||||
- Plugins/loader: scope plugin-tool registry reuse to the enabled plugin plan and stored Gateway method keys, so embedded runner tool lookup can reuse compatible startup registries without hiding enabled non-startup plugin tools. Fixes #75520. Thanks @whtoo.
|
||||
- Voice Call/Twilio: send notify-mode initial TwiML directly in the outbound create-call request while keeping conversation and pre-connect DTMF calls webhook-driven, so one-shot notify calls do not depend on a first-answer webhook fetch. Supersedes #72758. Thanks @tyshepps.
|
||||
- Discord/Slack: defer status-reaction cleanup until run finalization so queued, thinking, tool, and terminal reactions no longer flicker during normal progress updates. (#75582)
|
||||
- Discord/voice: leave Discord voice off for text-only configs unless `channels.discord.voice` is explicitly configured, avoiding default `GuildVoiceStates` traffic and idle gateway CPU pressure for bots that do not use `/vc`. Fixes #73753; refs #74044. Thanks @sanchezm86 and @SecureCloudProjO.
|
||||
@@ -108,7 +83,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/sessions: yield during bulk transcript title/preview hydration and copy compaction checkpoints asynchronously, keeping the Gateway event loop responsive for large session stores and large transcripts. Refs #75330 and #75414. Thanks @amknight.
|
||||
- Gateway/sessions: stream bounded transcript reads for session detail, history, artifacts, compaction, and send/subscribe sequence paths so small Gateway requests no longer materialize large transcripts or OOM on oversized session logs. Thanks @vincentkoc.
|
||||
- Gateway/chat: bound chat-history transcript reads to the requested display window so large session logs no longer OOM the Gateway when clients ask for a small history page. Thanks @vincentkoc.
|
||||
- BlueBubbles: detect audio attachments by Apple UTIs (`public.audio`, `public.mpeg-4-audio`, `com.apple.m4a-audio`, `com.apple.coreaudio-format`) in addition to `audio/*` MIME, so iMessage voice notes whose webhook payload only carries the UTI are now classified as audio in the inbound `<media:audio>` placeholder instead of falling through to the generic `<media:attachment>` tag. Thanks @omarshahine.
|
||||
- Voice Call/Twilio: honor stored pre-connect TwiML before realtime webhook shortcuts and reject DTMF sequences outside conversation mode, so Meet PIN entry cannot be skipped or silently dropped. Thanks @donkeykong91 and @PfanP.
|
||||
- Docs/sandboxing: clarify that sandbox setup scripts (`sandbox-setup.sh`, `sandbox-common-setup.sh`, `sandbox-browser-setup.sh`) are only available from a source checkout, and add inline `docker build` commands for npm-installed users so sandbox image setup works without cloning the repo. Fixes #75485. Thanks @amknight.
|
||||
- Google Meet/Voice Call: play Twilio Meet DTMF before opening the realtime media stream and carry the intro as the initial Voice Call message, so the greeting is generated after Meet admits the phone participant instead of racing a live-call TwiML update. Thanks @donkeykong91 and @PfanP.
|
||||
@@ -180,7 +154,6 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/failover: carry `sessionId`, `lane`, `provider`, `model`, and `profileId` attribution through `FailoverError` and `describeFailoverError`/`coerceToFailoverError` so structured error logs (e.g. `gateway.err.log` ingestion) can attribute exhausted-fallback wrapper errors to the originating session and last-attempted provider instead of dropping the metadata after the per-profile errors. Fixes #42713. (#73506) Thanks @wenxu007.
|
||||
- Context Engine: treat assembled prompt as the default authority for preemptive overflow prechecks so engines that return a windowed, self-contained context no longer trigger false hard-fail compactions on huge raw history. Engines whose assembled view can hide overflow risk can opt back into the legacy behavior with `AssembleResult.promptAuthority: "preassembly_may_overflow"`. (#74255) Thanks @100yenadmin.
|
||||
- Mattermost: refresh current native slash command registrations before accepting callbacks so stale tokens from deleted or regenerated commands stop being accepted without a gateway restart while failed validations stay briefly cached and lookup starts are rate-limited per command, gate each callback against the resolved command's own startup token so a token leaked for one slash command cannot poison another command's failure cache, redact slash validation lookup errors, and add a body read timeout to the multi-account routing path so slow callback senders cannot tie up the dispatcher. Thanks @feynman-hou and @eleqtrizit.
|
||||
- Security/dotenv: block `COMSPEC` in workspace `.env` so a malicious repo cannot redirect Windows `cmd.exe` resolution, and lock in case-insensitive workspace-`.env` regression coverage for the full Windows shell trust-root family (`COMSPEC`, `PROGRAMFILES`, `PROGRAMW6432`, `SYSTEMROOT`, `WINDIR`). (#74460) Thanks @mmaps.
|
||||
|
||||
## 2026.4.29
|
||||
|
||||
|
||||
@@ -63,6 +63,7 @@ COPY openclaw.mjs ./
|
||||
COPY ui/package.json ./ui/package.json
|
||||
COPY patches ./patches
|
||||
COPY scripts/postinstall-bundled-plugins.mjs scripts/preinstall-package-manager-warning.mjs scripts/npm-runner.mjs scripts/windows-cmd-helpers.mjs ./scripts/
|
||||
COPY scripts/lib/bundled-runtime-deps-install.mjs ./scripts/lib/bundled-runtime-deps-install.mjs
|
||||
COPY scripts/lib/package-dist-imports.mjs ./scripts/lib/package-dist-imports.mjs
|
||||
|
||||
COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/
|
||||
@@ -267,10 +268,12 @@ RUN --mount=type=cache,id=openclaw-bookworm-apt-cache,target=/var/cache/apt,shar
|
||||
RUN ln -sf /app/openclaw.mjs /usr/local/bin/openclaw \
|
||||
&& chmod 755 /app/openclaw.mjs
|
||||
|
||||
# Pre-create the default state dir so first-run Docker named volumes mounted
|
||||
# here inherit node ownership instead of root-owned state.
|
||||
# Pre-create the default state and runtime-deps dirs so first-run Docker named
|
||||
# volumes mounted here inherit node ownership instead of root-owned state.
|
||||
RUN install -d -m 0700 -o node -g node /home/node/.openclaw && \
|
||||
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700'
|
||||
install -d -m 0700 -o node -g node /var/lib/openclaw/plugin-runtime-deps && \
|
||||
stat -c '%U:%G %a' /home/node/.openclaw | grep -qx 'node:node 700' && \
|
||||
stat -c '%U:%G %a' /var/lib/openclaw/plugin-runtime-deps | grep -qx 'node:node 700'
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
|
||||
@@ -2343,7 +2343,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
public let type: AnyCodable
|
||||
public let title: String?
|
||||
public let message: String?
|
||||
public let format: AnyCodable?
|
||||
public let options: [[String: AnyCodable]]?
|
||||
public let initialvalue: AnyCodable?
|
||||
public let placeholder: String?
|
||||
@@ -2355,7 +2354,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
type: AnyCodable,
|
||||
title: String?,
|
||||
message: String?,
|
||||
format: AnyCodable?,
|
||||
options: [[String: AnyCodable]]?,
|
||||
initialvalue: AnyCodable?,
|
||||
placeholder: String?,
|
||||
@@ -2366,7 +2364,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.format = format
|
||||
self.options = options
|
||||
self.initialvalue = initialvalue
|
||||
self.placeholder = placeholder
|
||||
@@ -2379,7 +2376,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
case type
|
||||
case title
|
||||
case message
|
||||
case format
|
||||
case options
|
||||
case initialvalue = "initialValue"
|
||||
case placeholder
|
||||
@@ -2806,24 +2802,6 @@ public struct ChannelsStartParams: Codable, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChannelsStopParams: Codable, Sendable {
|
||||
public let channel: String
|
||||
public let accountid: String?
|
||||
|
||||
public init(
|
||||
channel: String,
|
||||
accountid: String?)
|
||||
{
|
||||
self.channel = channel
|
||||
self.accountid = accountid
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case channel
|
||||
case accountid = "accountId"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChannelsLogoutParams: Codable, Sendable {
|
||||
public let channel: String
|
||||
public let accountid: String?
|
||||
|
||||
@@ -2343,7 +2343,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
public let type: AnyCodable
|
||||
public let title: String?
|
||||
public let message: String?
|
||||
public let format: AnyCodable?
|
||||
public let options: [[String: AnyCodable]]?
|
||||
public let initialvalue: AnyCodable?
|
||||
public let placeholder: String?
|
||||
@@ -2355,7 +2354,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
type: AnyCodable,
|
||||
title: String?,
|
||||
message: String?,
|
||||
format: AnyCodable?,
|
||||
options: [[String: AnyCodable]]?,
|
||||
initialvalue: AnyCodable?,
|
||||
placeholder: String?,
|
||||
@@ -2366,7 +2364,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
self.type = type
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.format = format
|
||||
self.options = options
|
||||
self.initialvalue = initialvalue
|
||||
self.placeholder = placeholder
|
||||
@@ -2379,7 +2376,6 @@ public struct WizardStep: Codable, Sendable {
|
||||
case type
|
||||
case title
|
||||
case message
|
||||
case format
|
||||
case options
|
||||
case initialvalue = "initialValue"
|
||||
case placeholder
|
||||
@@ -2806,24 +2802,6 @@ public struct ChannelsStartParams: Codable, Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChannelsStopParams: Codable, Sendable {
|
||||
public let channel: String
|
||||
public let accountid: String?
|
||||
|
||||
public init(
|
||||
channel: String,
|
||||
accountid: String?)
|
||||
{
|
||||
self.channel = channel
|
||||
self.accountid = accountid
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case channel
|
||||
case accountid = "accountId"
|
||||
}
|
||||
}
|
||||
|
||||
public struct ChannelsLogoutParams: Codable, Sendable {
|
||||
public let channel: String
|
||||
public let accountid: String?
|
||||
|
||||
@@ -23,10 +23,12 @@ services:
|
||||
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
|
||||
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
|
||||
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
|
||||
OPENCLAW_PLUGIN_STAGE_DIR: /var/lib/openclaw/plugin-runtime-deps
|
||||
TZ: ${OPENCLAW_TZ:-UTC}
|
||||
volumes:
|
||||
- ${OPENCLAW_CONFIG_DIR:-${HOME:-/tmp}/.openclaw}:/home/node/.openclaw
|
||||
- ${OPENCLAW_WORKSPACE_DIR:-${HOME:-/tmp}/.openclaw/workspace}:/home/node/.openclaw/workspace
|
||||
- openclaw-plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
|
||||
## Uncomment the lines below to enable sandbox isolation
|
||||
## (agents.defaults.sandbox). Requires Docker CLI in the image
|
||||
## (build with --build-arg OPENCLAW_INSTALL_DOCKER_CLI=1) or use
|
||||
@@ -85,13 +87,18 @@ services:
|
||||
CLAUDE_AI_SESSION_KEY: ${CLAUDE_AI_SESSION_KEY:-}
|
||||
CLAUDE_WEB_SESSION_KEY: ${CLAUDE_WEB_SESSION_KEY:-}
|
||||
CLAUDE_WEB_COOKIE: ${CLAUDE_WEB_COOKIE:-}
|
||||
OPENCLAW_PLUGIN_STAGE_DIR: /var/lib/openclaw/plugin-runtime-deps
|
||||
TZ: ${OPENCLAW_TZ:-UTC}
|
||||
volumes:
|
||||
- ${OPENCLAW_CONFIG_DIR:-${HOME:-/tmp}/.openclaw}:/home/node/.openclaw
|
||||
- ${OPENCLAW_WORKSPACE_DIR:-${HOME:-/tmp}/.openclaw/workspace}:/home/node/.openclaw/workspace
|
||||
- openclaw-plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
|
||||
stdin_open: true
|
||||
tty: true
|
||||
init: true
|
||||
entrypoint: ["node", "dist/index.js"]
|
||||
depends_on:
|
||||
- openclaw-gateway
|
||||
|
||||
volumes:
|
||||
openclaw-plugin-runtime-deps:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
9a22f3ea41864de9cc754d7237a5e1403582f21eeec421463e3faa80a243e7de config-baseline.json
|
||||
7fa5e58fa863a13f2b9e06ea0fc536304f21e4e8929068f96f93968de91d1aae config-baseline.core.json
|
||||
c401cd3450f1737bc92418cfea301d20b54b7fbef9e6049834acc01af338e538 config-baseline.channel.json
|
||||
1deb67d0a40456e77cb67685f6ae2f14a8ddc2c4be488d4b1a1f1127598982dd config-baseline.json
|
||||
ac7537ed5b5a2d9e7fa50977aa99f5e0babfbe1a93c7c14b93a184b36bb4f539 config-baseline.core.json
|
||||
f3326cd9490169afefe93625f63699266b75db93855ed439c9692e3c286a990c config-baseline.channel.json
|
||||
7731a0b93cb335b56fac4c807447ba659fea51ea7a6cd844dc0ef5616669ee75 config-baseline.plugin.json
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
1046659bf30e5d543e82f587e847811cfe4925a1df42da801e4ae7c887dddde4 plugin-sdk-api-baseline.json
|
||||
8d5a24ad601f92bf627deaf1fe03a05abb02bfe4c4d786f703a73752093fdab9 plugin-sdk-api-baseline.jsonl
|
||||
37787172adf7a55a32097599b4bf5729fc7138c8743c6f4c9d58fc8d01df72a1 plugin-sdk-api-baseline.json
|
||||
0ec4957528477832085c638a5f7f691c878ba199f3e81f330f162c27cfd9ebf4 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -16,8 +16,8 @@ Text is supported everywhere; media and reactions vary by channel.
|
||||
- Slack multi-person DMs route as group chats, so group policy, mention
|
||||
behavior, and group-session rules apply to MPIM conversations.
|
||||
- WhatsApp setup is install-on-demand: onboarding can show the setup flow before
|
||||
the plugin package is installed, and the Gateway loads the WhatsApp runtime
|
||||
only when the channel is actually active.
|
||||
Baileys runtime dependencies are staged, and the Gateway loads the WhatsApp
|
||||
runtime only when the channel is actually active.
|
||||
|
||||
## Supported channels
|
||||
|
||||
|
||||
@@ -11,16 +11,13 @@ QQ Bot connects to OpenClaw via the official QQ Bot API (WebSocket gateway). The
|
||||
plugin supports C2C private chat, group @messages, and guild channel messages with
|
||||
rich media (images, voice, video, files).
|
||||
|
||||
Status: downloadable plugin. Direct messages, group chats, guild channels, and
|
||||
Status: bundled plugin. Direct messages, group chats, guild channels, and
|
||||
media are supported. Reactions and threads are not supported.
|
||||
|
||||
## Install
|
||||
## Bundled plugin
|
||||
|
||||
Install QQ Bot before setup:
|
||||
|
||||
```bash
|
||||
openclaw plugins install @openclaw/qqbot
|
||||
```
|
||||
Current OpenClaw releases bundle QQ Bot, so normal packaged builds do not need
|
||||
a separate `openclaw plugins install` step.
|
||||
|
||||
## Setup
|
||||
|
||||
|
||||
@@ -492,8 +492,6 @@ Behavior notes:
|
||||
<Accordion title="Logout behavior">
|
||||
`openclaw channels logout --channel whatsapp [--account <id>]` clears WhatsApp auth state for that account.
|
||||
|
||||
When a Gateway is reachable, logout first stops the live WhatsApp listener for the selected account so the linked session does not keep receiving messages until the next restart. `openclaw channels remove --channel whatsapp` also stops the live listener before disabling or deleting account config.
|
||||
|
||||
In legacy auth directories, `oauth.json` is preserved while Baileys auth files are removed.
|
||||
|
||||
</Accordion>
|
||||
@@ -553,14 +551,6 @@ Behavior notes:
|
||||
openclaw logs --follow
|
||||
```
|
||||
|
||||
If `~/.openclaw/logs/whatsapp-health.log` says `Gateway inactive` but
|
||||
`openclaw gateway status` and `openclaw channels status --probe` show the
|
||||
gateway and WhatsApp are healthy, run `openclaw doctor`. On Linux, doctor
|
||||
warns about legacy crontab entries that still invoke
|
||||
`~/.openclaw/bin/ensure-whatsapp.sh`; remove those stale entries with
|
||||
`crontab -e` because cron can lack the systemd user-bus environment and
|
||||
make that old script misreport gateway health.
|
||||
|
||||
If needed, re-link with `channels login`.
|
||||
|
||||
</Accordion>
|
||||
@@ -584,8 +574,6 @@ Behavior notes:
|
||||
|
||||
Ack reactions are independent pre-reply receipts. A successful reaction does not prove that the later text or media reply was accepted by WhatsApp.
|
||||
|
||||
Reply-capable channels now wait for reply-runtime readiness before normal startup so the first live WhatsApp reply does not pay plugin runtime installation, model discovery, auth preparation, or tool-contract cold setup. If the first reply path is still cold, fix that readiness failure instead of raising WhatsApp socket or query timeouts.
|
||||
|
||||
Check gateway logs for `auto-reply delivery failed` or `auto-reply was not accepted by WhatsApp provider`.
|
||||
|
||||
</Accordion>
|
||||
|
||||
22
docs/ci.md
22
docs/ci.md
@@ -181,14 +181,14 @@ Keep `workflow_ref` and `package_ref` separate. `workflow_ref` is the trusted wo
|
||||
### Suite profiles
|
||||
|
||||
- `smoke` — `npm-onboard-channel-agent`, `gateway-network`, `config-reload`
|
||||
- `package` — `npm-onboard-channel-agent`, `doctor-switch`, `update-channel-switch`, `upgrade-survivor`, `published-upgrade-survivor`, `plugins-offline`, `plugin-update`
|
||||
- `package` — `npm-onboard-channel-agent`, `doctor-switch`, `update-channel-switch`, `upgrade-survivor`, `published-upgrade-survivor`, `bundled-channel-deps-compat`, `plugins-offline`, `plugin-update`
|
||||
- `product` — `package` plus `mcp-channels`, `cron-mcp-cleanup`, `openai-web-search-minimal`, `openwebui`
|
||||
- `full` — full Docker release-path chunks with OpenWebUI
|
||||
- `custom` — exact `docker_lanes`; required when `suite_profile=custom`
|
||||
|
||||
The `package` profile uses offline plugin coverage so published-package validation is not gated on live ClawHub availability. The optional Telegram lane reuses the `package-under-test` artifact in `NPM Telegram Beta E2E`, with the published npm spec path kept for standalone dispatches.
|
||||
|
||||
Release checks call Package Acceptance with `source=ref`, `package_ref=<release-ref>`, `workflow_ref=<release workflow ref>`, `suite_profile=custom`, `docker_lanes='plugins-offline plugin-update'`, and `telegram_mode=mock-openai`. Release-path Docker chunks cover the overlapping package/update/plugin lanes; Package Acceptance keeps offline plugin, update, and Telegram proof against the same resolved package tarball. Cross-OS release checks still cover OS-specific onboarding, installer, and platform behavior; package/update product validation should start with Package Acceptance. The `published-upgrade-survivor` Docker lane validates one published package baseline per run. In Package Acceptance, the resolved `package-under-test` tarball is always the candidate and `published_upgrade_survivor_baseline` selects the fallback published baseline, defaulting to `openclaw@latest`; failed-lane rerun commands preserve that baseline. Set `published_upgrade_survivor_baselines=release-history` to expand the lane across a deduped history matrix: the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. Set `published_upgrade_survivor_scenarios=reported-issues` to expand the same baselines across issue-shaped fixtures for Feishu config, preserved bootstrap/persona files, tilde log paths, and stale legacy plugin dependency roots. Local aggregate runs can pass exact package specs with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, keep a single lane with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC` such as `openclaw@2026.4.15`, or set `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` for the scenario matrix. The published lane configures the baseline with a baked `openclaw config set` command recipe, records recipe steps in `summary.json`, and probes `/healthz`, `/readyz`, plus RPC status after Gateway start. The Windows packaged and installer fresh lanes also verify that an installed package can import a browser-control override from a raw absolute Windows path. The OpenAI cross-OS agent-turn smoke defaults to `OPENCLAW_CROSS_OS_OPENAI_MODEL` when set, otherwise `openai/gpt-5.5`, so the install and gateway proof stays on the preferred GPT-5 test model.
|
||||
Release checks call Package Acceptance with `source=ref`, `package_ref=<release-ref>`, `workflow_ref=<release workflow ref>`, `suite_profile=custom`, `docker_lanes='bundled-channel-deps-compat plugins-offline'`, and `telegram_mode=mock-openai`. Release-path Docker chunks cover the overlapping package/update/plugin lanes; Package Acceptance keeps the artifact-native bundled-channel compat, offline plugin, and Telegram proof against the same resolved package tarball. Cross-OS release checks still cover OS-specific onboarding, installer, and platform behavior; package/update product validation should start with Package Acceptance. The `published-upgrade-survivor` Docker lane validates one published package baseline per run. In Package Acceptance, the resolved `package-under-test` tarball is always the candidate and `published_upgrade_survivor_baseline` selects the fallback published baseline, defaulting to `openclaw@latest`; failed-lane rerun commands preserve that baseline. Set `published_upgrade_survivor_baselines=release-history` to expand the lane across a deduped history matrix: the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. Set `published_upgrade_survivor_scenarios=reported-issues` to expand the same baselines across issue-shaped fixtures for Feishu config/runtime-deps, preserved bootstrap/persona files, tilde log paths, and stale versioned runtime-deps roots. Local aggregate runs can pass exact package specs with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, keep a single lane with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC` such as `openclaw@2026.4.15`, or set `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` for the scenario matrix. The published lane configures the baseline with a baked `openclaw config set` command recipe, records recipe steps in `summary.json`, and probes `/healthz`, `/readyz`, plus RPC status after Gateway start. The Windows packaged and installer fresh lanes also verify that an installed package can import a browser-control override from a raw absolute Windows path. The OpenAI cross-OS agent-turn smoke defaults to `OPENCLAW_CROSS_OS_OPENAI_MODEL` when set, otherwise `openai/gpt-5.4-mini`, so the install and gateway proof stays fast and deterministic.
|
||||
|
||||
### Legacy compatibility windows
|
||||
|
||||
@@ -290,9 +290,9 @@ The reusable live/E2E workflow asks `scripts/test-docker-all.mjs --plan-json` wh
|
||||
Release Docker coverage runs smaller chunked jobs with `OPENCLAW_SKIP_DOCKER_BUILD=1` so each chunk pulls only the image kind it needs and executes multiple lanes through the same weighted scheduler:
|
||||
|
||||
- `OPENCLAW_DOCKER_ALL_PROFILE=release-path`
|
||||
- `OPENCLAW_DOCKER_ALL_CHUNK=core | package-update-openai | package-update-anthropic | package-update-core | plugins-runtime-plugins | plugins-runtime-services | plugins-runtime-install-a..h`
|
||||
- `OPENCLAW_DOCKER_ALL_CHUNK=core | package-update-openai | package-update-anthropic | package-update-core | plugins-runtime-plugins | plugins-runtime-services | plugins-runtime-install-a..h | bundled-channels`
|
||||
|
||||
Current release Docker chunks are `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, and `plugins-runtime-install-a` through `plugins-runtime-install-h`. `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` remain aggregate plugin/runtime aliases. The `install-e2e` lane alias remains the aggregate manual rerun alias for both provider installer lanes.
|
||||
Current release Docker chunks are `core`, `package-update-openai`, `package-update-anthropic`, `package-update-core`, `plugins-runtime-plugins`, `plugins-runtime-services`, `plugins-runtime-install-a` through `plugins-runtime-install-h`, `bundled-channels-core`, `bundled-channels-update-a`, `bundled-channels-update-discord`, `bundled-channels-update-b`, and `bundled-channels-contracts`. The aggregate `bundled-channels` chunk remains available for manual one-shot reruns, and `plugins-runtime-core`, `plugins-runtime`, and `plugins-integrations` remain aggregate plugin/runtime aliases. The `install-e2e` lane alias remains the aggregate manual rerun alias for both provider installer lanes. The `bundled-channels` chunk runs split `bundled-channel-*` and `bundled-channel-update-*` lanes rather than the serial all-in-one `bundled-channel-deps` lane.
|
||||
|
||||
OpenWebUI is folded into `plugins-runtime-services` when full release-path coverage requests it, and keeps a standalone `openwebui` chunk only for OpenWebUI-only dispatches. Bundled-channel update lanes retry once for transient npm network failures.
|
||||
|
||||
@@ -332,13 +332,13 @@ The pull request guard stays light: it only starts for changes under `.github/ac
|
||||
|
||||
### Security categories
|
||||
|
||||
| Category | Surface |
|
||||
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `/codeql-security-high/core-auth-secrets` | Auth, secrets, sandbox, cron, and gateway baseline |
|
||||
| `/codeql-security-high/channel-runtime-boundary` | Core channel implementation contracts plus the channel plugin runtime, gateway, Plugin SDK, secrets, audit touchpoints |
|
||||
| `/codeql-security-high/network-ssrf-boundary` | Core SSRF, IP parsing, network guard, web-fetch, and Plugin SDK SSRF policy surfaces |
|
||||
| `/codeql-security-high/mcp-process-tool-boundary` | MCP servers, process execution helpers, outbound delivery, and agent tool-execution gates |
|
||||
| `/codeql-security-high/plugin-trust-boundary` | Plugin install, loader, manifest, registry, package-manager install, source-loading, and Plugin SDK package contract trust surfaces |
|
||||
| Category | Surface |
|
||||
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `/codeql-security-high/core-auth-secrets` | Auth, secrets, sandbox, cron, and gateway baseline |
|
||||
| `/codeql-security-high/channel-runtime-boundary` | Core channel implementation contracts plus the channel plugin runtime, gateway, Plugin SDK, secrets, audit touchpoints |
|
||||
| `/codeql-security-high/network-ssrf-boundary` | Core SSRF, IP parsing, network guard, web-fetch, and Plugin SDK SSRF policy surfaces |
|
||||
| `/codeql-security-high/mcp-process-tool-boundary` | MCP servers, process execution helpers, outbound delivery, and agent tool-execution gates |
|
||||
| `/codeql-security-high/plugin-trust-boundary` | Plugin install, loader, manifest, registry, runtime-dependency staging, source-loading, and Plugin SDK package contract trust surfaces |
|
||||
|
||||
### Platform-specific security shards
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ openclaw channels remove --channel telegram --delete
|
||||
</Tip>
|
||||
|
||||
`channels remove` only operates on installed/configured channel plugins. Use `channels add` first for installable catalog channels.
|
||||
For runtime-backed channel plugins, `channels remove` also asks the running Gateway to stop the selected account before it updates config, so disabling or deleting an account does not leave the old listener active until restart.
|
||||
|
||||
Common non-interactive add surfaces include:
|
||||
|
||||
@@ -95,7 +94,6 @@ openclaw channels logout --channel whatsapp
|
||||
|
||||
- `channels login` supports `--verbose`.
|
||||
- `channels login` and `logout` can infer the channel when only one supported login target is configured.
|
||||
- `channels logout` prefers the live Gateway path when reachable, so logout stops any active listener before clearing channel auth state. If a local Gateway is not reachable, it falls back to local auth cleanup.
|
||||
- Run `channels login` from a terminal on the gateway host. Agent `exec` blocks this interactive login flow; channel-native agent login tools, such as `whatsapp_login`, should be used from chat when available.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -52,7 +52,7 @@ Available sections:
|
||||
Notes:
|
||||
|
||||
- Choosing where the Gateway runs always updates `gateway.mode`. You can select "Continue" without other sections if that is all you need.
|
||||
- After local config writes, configure installs selected downloadable plugins when the chosen setup path requires them. Remote gateway config does not install local plugin packages.
|
||||
- After local config writes, configure materializes newly required bundled plugin runtime dependencies. This is a narrow package-manager repair step, not a full `openclaw doctor` run. Remote gateway config does not install local plugin dependencies.
|
||||
- Channel-oriented services (Slack/Discord/Matrix/Microsoft Teams) prompt for channel/room allowlists during setup. You can enter names or IDs; the wizard resolves names to IDs when possible.
|
||||
- If you run the daemon install step, token auth requires a token, and `gateway.auth.token` is SecretRef-managed, configure validates the SecretRef but does not persist resolved plaintext token values into supervisor service environment metadata.
|
||||
- If token auth requires a token and the configured token SecretRef is unresolved, configure blocks daemon install with actionable remediation guidance.
|
||||
|
||||
@@ -44,8 +44,7 @@ Notes:
|
||||
- `doctor --fix --non-interactive` reports missing or stale gateway service definitions but does not install or rewrite them outside update repair mode. Run `openclaw gateway install` for a missing service, or `openclaw gateway install --force` when you intentionally want to replace the launcher.
|
||||
- State integrity checks now detect orphan transcript files in the sessions directory. Archiving them as `.deleted.<timestamp>` requires an interactive confirmation; `--fix`, `--yes`, and headless runs leave them in place.
|
||||
- Doctor also scans `~/.openclaw/cron/jobs.json` (or `cron.store`) for legacy cron job shapes and can rewrite them in place before the scheduler has to auto-normalize them at runtime.
|
||||
- On Linux, doctor warns when the user's crontab still runs legacy `~/.openclaw/bin/ensure-whatsapp.sh`; that script is no longer maintained and can log false WhatsApp gateway outages when cron lacks the systemd user-bus environment.
|
||||
- Doctor cleans legacy plugin dependency staging state created by older OpenClaw versions. It also repairs missing configured downloadable plugins when the registry can resolve them.
|
||||
- Doctor repairs missing bundled plugin runtime dependencies without writing into packaged global installs. For root-owned npm installs or hardened systemd units, set `OPENCLAW_PLUGIN_STAGE_DIR` to a writable directory such as `/var/lib/openclaw/plugin-runtime-deps`; it can also be a path-list such as `/opt/openclaw/plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps`, where earlier roots are read-only lookup layers and the final root is the repair target.
|
||||
- Doctor repairs stale plugin config by removing missing plugin ids from `plugins.allow`/`plugins.entries`, plus matching dangling channel config, heartbeat targets, and channel model overrides when plugin discovery is healthy.
|
||||
- Doctor quarantines invalid plugin config by disabling the affected `plugins.entries.<id>` entry and removing its invalid `config` payload. Gateway startup already skips only that bad plugin so other plugins and channels can keep running.
|
||||
- Set `OPENCLAW_SERVICE_REPAIR_POLICY=external` when another supervisor owns the gateway lifecycle. Doctor still reports gateway/service health and applies non-service repairs, but skips service install/start/restart/bootstrap and legacy service cleanup.
|
||||
|
||||
@@ -146,7 +146,7 @@ When you set `--url`, the CLI does not fall back to config or environment creden
|
||||
openclaw gateway health --url ws://127.0.0.1:18789
|
||||
```
|
||||
|
||||
The HTTP `/healthz` endpoint is a liveness probe: it returns once the server can answer HTTP. The HTTP `/readyz` endpoint is stricter and stays red while startup plugin sidecars, channels, or configured hooks are still settling. Local or authenticated detailed readiness responses include an `eventLoop` diagnostic block with event-loop delay, event-loop utilization, CPU core ratio, and a `degraded` flag.
|
||||
The HTTP `/healthz` endpoint is a liveness probe: it returns once the server can answer HTTP. The HTTP `/readyz` endpoint is stricter and stays red while startup plugin runtime dependencies, sidecars, channels, or configured hooks are still settling. Local or authenticated detailed readiness responses include an `eventLoop` diagnostic block with event-loop delay, event-loop utilization, CPU core ratio, and a `degraded` flag.
|
||||
|
||||
### `gateway usage-cost`
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ Benefits:
|
||||
|
||||
For end-to-end provider checks, prefer `openclaw infer ...` once lower-level
|
||||
provider tests are green. It exercises the shipped CLI, config loading,
|
||||
default-agent resolution, bundled plugin activation, and the shared capability
|
||||
runtime before the provider request is made.
|
||||
default-agent resolution, bundled plugin activation, runtime-dependency repair,
|
||||
and the shared capability runtime before the provider request is made.
|
||||
|
||||
## Command tree
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ Gateway token options in non-interactive mode:
|
||||
- With `--install-daemon`, if token mode requires a token and the configured token SecretRef is unresolved, onboarding fails closed with remediation guidance.
|
||||
- With `--install-daemon`, if both `gateway.auth.token` and `gateway.auth.password` are configured and `gateway.auth.mode` is unset, onboarding blocks install until mode is set explicitly.
|
||||
- Local onboarding writes `gateway.mode="local"` into the config. If a later config file is missing `gateway.mode`, treat that as config damage or an incomplete manual edit, not as a valid local-mode shortcut.
|
||||
- Local onboarding installs selected downloadable plugins when the chosen setup path requires them.
|
||||
- Remote onboarding only writes connection info for the remote Gateway and does not install local plugin packages.
|
||||
- Local onboarding materializes newly required bundled plugin runtime dependencies after writing config, before workspace/bootstrap, daemon install, or health checks continue. This is a narrow package-manager repair step, not a full `openclaw doctor` run.
|
||||
- Remote onboarding only writes connection info for the remote Gateway and does not install local bundled plugin dependencies.
|
||||
- `--allow-unconfigured` is a separate gateway runtime escape hatch. It does not mean onboarding may omit `gateway.mode`.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
summary: "CLI reference for `openclaw plugins` (list, install, marketplace, uninstall, enable/disable, doctor)"
|
||||
summary: "CLI reference for `openclaw plugins` (list, install, marketplace, uninstall, enable/disable, deps, doctor)"
|
||||
read_when:
|
||||
- You want to install or manage Gateway plugins or compatible bundles
|
||||
- You want to debug plugin load failures
|
||||
@@ -42,6 +42,10 @@ openclaw plugins disable <id>
|
||||
openclaw plugins registry
|
||||
openclaw plugins registry --refresh
|
||||
openclaw plugins uninstall <id>
|
||||
openclaw plugins deps
|
||||
openclaw plugins deps --repair
|
||||
openclaw plugins deps --prune
|
||||
openclaw plugins deps --json
|
||||
openclaw plugins doctor
|
||||
openclaw plugins update <id-or-npm-spec>
|
||||
openclaw plugins update --all
|
||||
@@ -125,13 +129,13 @@ current OpenClaw or a local checkout until a newer npm package is published.
|
||||
|
||||
Bare specs and `@latest` stay on the stable track. If npm resolves either of those to a prerelease, OpenClaw stops and asks you to opt in explicitly with a prerelease tag such as `@beta`/`@rc` or an exact prerelease version such as `@1.2.3-beta.4`.
|
||||
|
||||
If a bare install spec matches an official plugin id (for example `diffs`), OpenClaw installs the catalog entry directly. To install an npm package with the same name, use an explicit scoped spec (for example `@scope/diffs`).
|
||||
If a bare install spec matches a bundled plugin id (for example `diffs`), OpenClaw installs the bundled plugin directly. To install an npm package with the same name, use an explicit scoped spec (for example `@scope/diffs`).
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Git repositories">
|
||||
Use `git:<repo>` to install directly from a git repository. Supported forms include `git:github.com/owner/repo`, `git:owner/repo`, full `https://`, `ssh://`, `git://`, `file://`, and `git@host:owner/repo.git` clone URLs. Add `@<ref>` or `#<ref>` to check out a branch, tag, or commit before install.
|
||||
|
||||
Git installs clone into a temporary directory, check out the requested ref when present, then use the normal plugin directory installer. That means manifest validation, dangerous-code scanning, package-manager install work, and install records behave like npm installs. Recorded git installs include the source URL/ref plus the resolved commit so `openclaw plugins update` can re-resolve the source later.
|
||||
Git installs clone into a temporary directory, check out the requested ref when present, then use the normal plugin directory installer. That means manifest validation, dangerous-code scanning, runtime dependency staging, and install records behave like local-path installs. Recorded git installs include the source URL/ref plus the resolved commit so `openclaw plugins update` can re-resolve the source later.
|
||||
|
||||
After installing from git, use `openclaw plugins inspect <id> --runtime --json` to verify runtime registrations such as gateway methods and CLI commands. If the plugin registered a CLI root with `api.registerCli`, execute that command directly through the OpenClaw root CLI, for example `openclaw demo-plugin ping`.
|
||||
|
||||
@@ -241,7 +245,7 @@ directory remains inert so normal packaged installs still use compiled dist.
|
||||
|
||||
For runtime hook debugging:
|
||||
|
||||
- `openclaw plugins inspect <id> --runtime --json` shows registered hooks and diagnostics from a module-loaded inspection pass. Runtime inspection never installs dependencies; use `openclaw doctor --fix` to clean legacy dependency state or install missing configured downloadable plugins.
|
||||
- `openclaw plugins inspect <id> --runtime --json` shows registered hooks and diagnostics from a module-loaded inspection pass. Runtime inspection never downloads missing bundled runtime dependencies; use `openclaw plugins deps --repair` when repair is needed.
|
||||
- `openclaw gateway status --deep --require-rpc` confirms the reachable Gateway, service/process hints, config path, and RPC health.
|
||||
- Non-bundled conversation hooks (`llm_input`, `llm_output`, `before_agent_finalize`, `agent_end`) require `plugins.entries.<id>.hooks.allowConversationAccess=true`.
|
||||
|
||||
@@ -263,6 +267,21 @@ Plugin install metadata is machine-managed state, not user config. Installs and
|
||||
|
||||
When OpenClaw sees shipped legacy `plugins.installs` records in config, it moves them into the plugin index and removes the config key; if either write fails, the config records are kept so the install metadata is not lost.
|
||||
|
||||
### Runtime deps
|
||||
|
||||
```bash
|
||||
openclaw plugins deps
|
||||
openclaw plugins deps --repair
|
||||
openclaw plugins deps --prune
|
||||
openclaw plugins deps --json
|
||||
```
|
||||
|
||||
`plugins deps` inspects the packaged runtime dependency stage for OpenClaw-owned bundled plugins selected by plugin config, enabled/configured channels, configured model providers, or bundled manifest defaults. It is not the install/update path for third-party npm or ClawHub plugins.
|
||||
|
||||
Use `--repair` when a packaged install reports missing bundled runtime dependencies during Gateway startup or `plugins doctor`. Repair installs only missing enabled bundled-plugin deps with lifecycle scripts disabled. Use `--prune` to remove stale unknown external runtime-dependency roots left behind by older packaged layouts.
|
||||
|
||||
For the full plan, staging, and repair lifecycle, see [Plugin dependency resolution](/plugins/dependency-resolution).
|
||||
|
||||
### Uninstall
|
||||
|
||||
```bash
|
||||
@@ -317,7 +336,7 @@ openclaw plugins inspect <id> --runtime
|
||||
openclaw plugins inspect <id> --json
|
||||
```
|
||||
|
||||
Inspect shows identity, load status, source, manifest capabilities, policy flags, diagnostics, install metadata, bundle capabilities, and any detected MCP or LSP server support without importing plugin runtime by default. Add `--runtime` to load the plugin module and include registered hooks, tools, commands, services, gateway methods, and HTTP routes. Runtime inspection reports missing plugin dependencies directly; installs and repairs stay in `openclaw plugins install`, `openclaw plugins update`, and `openclaw doctor --fix`.
|
||||
Inspect shows identity, load status, source, manifest capabilities, policy flags, diagnostics, install metadata, bundle capabilities, and any detected MCP or LSP server support without importing plugin runtime by default. Add `--runtime` to load the plugin module and include registered hooks, tools, commands, services, gateway methods, and HTTP routes. Runtime inspection fails with a repair hint when bundled runtime dependencies are missing; use `openclaw plugins deps --repair` to repair them explicitly.
|
||||
|
||||
Plugin-owned CLI commands are installed as root `openclaw` command groups. After `inspect --runtime` shows a command under `cliCommands`, run it as `openclaw <command> ...`; for example a plugin that registers `demo-git` can be verified with `openclaw demo-git ping`.
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ If an exact pinned npm plugin update resolves to an artifact whose integrity dif
|
||||
<Note>
|
||||
Post-update plugin sync failures fail the update result and stop restart follow-up work. Fix the plugin install or update error, then rerun `openclaw update`.
|
||||
|
||||
When the updated Gateway starts, plugin loading is verify-only: startup does not run package managers or mutate dependency trees. Package-manager `update.run` restarts bypass the normal idle deferral and restart cooldown after the package tree has been swapped, so the old process cannot keep lazy-loading removed chunks.
|
||||
When the updated Gateway starts, enabled bundled plugin runtime dependencies are staged before plugin activation. Package-manager `update.run` restarts bypass the normal idle deferral and restart cooldown after the package tree has been swapped, so the old process cannot keep lazy-loading removed chunks. Service-manager restarts still drain runtime-dependency staging before closing the Gateway.
|
||||
|
||||
If pnpm bootstrap still fails, the updater stops early with a package-manager-specific error instead of trying `npm run build` inside the checkout.
|
||||
</Note>
|
||||
|
||||
@@ -89,7 +89,7 @@ This works with local models too, for example a second Ollama model dedicated to
|
||||
}
|
||||
```
|
||||
|
||||
When unset, compaction starts with the active session model. If summarization fails with a model-fallback-eligible provider error, OpenClaw retries that compaction attempt through the session's existing model fallback chain. The fallback choice is temporary and is not written back to session state. An explicit `agents.defaults.compaction.model` override remains exact and does not inherit the session fallback chain.
|
||||
When unset, compaction uses the agent's primary model.
|
||||
|
||||
### Identifier preservation
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ For multi-endpoint setups, `provider` can also be a custom
|
||||
`models.providers.<id>` entry, such as `ollama-5080`, when that provider sets
|
||||
`api: "ollama"` or another embedding adapter owner.
|
||||
|
||||
For local embeddings with no API key, set `provider: "local"`. Source checkouts
|
||||
may still require native build approval: `pnpm approve-builds` then
|
||||
`pnpm rebuild node-llama-cpp`.
|
||||
For local embeddings with no API key, set `provider: "local"`. Packaged
|
||||
installs retain the native `node-llama-cpp` runtime in OpenClaw's managed plugin
|
||||
runtime-deps tree; run `openclaw doctor --fix` if that tree needs repair.
|
||||
|
||||
Some OpenAI-compatible embedding endpoints require asymmetric labels such as
|
||||
`input_type: "query"` for searches and `input_type: "document"` or `"passage"`
|
||||
|
||||
@@ -774,7 +774,7 @@ See the full channel index: [Channels](/channels).
|
||||
|
||||
Group messages default to **require mention** (metadata mention or safe regex patterns). Applies to WhatsApp, Telegram, Discord, Google Chat, and iMessage group chats.
|
||||
|
||||
Visible replies are controlled separately. Group/channel rooms default to `messages.groupChat.visibleReplies: "message_tool"`: OpenClaw still processes the turn, but normal final replies stay private and visible room output requires `message(action=send)`. Set `"automatic"` only when you want the legacy behavior where normal replies are posted back to the room. To apply the same tool-only visible-reply behavior to direct chats too, set `messages.visibleReplies: "message_tool"`; the Codex harness also uses that tool-only behavior as its unset direct-chat default.
|
||||
Visible replies are controlled separately. Group/channel rooms default to `messages.groupChat.visibleReplies: "message_tool"`: OpenClaw still processes the turn, but normal final replies stay private and visible room output requires `message(action=send)`. Set `"automatic"` only when you want the legacy behavior where normal replies are posted back to the room. To apply the same tool-only visible-reply behavior to direct chats too, set `messages.visibleReplies: "message_tool"`.
|
||||
|
||||
If the message tool is unavailable under the active tool policy, OpenClaw falls back to automatic visible replies instead of silently suppressing the response. `openclaw doctor` warns about this mismatch.
|
||||
|
||||
@@ -789,7 +789,7 @@ The gateway hot-reloads `messages` config after the file is saved. Restart only
|
||||
```json5
|
||||
{
|
||||
messages: {
|
||||
visibleReplies: "automatic", // global default for direct/source chats; Codex harness defaults unset direct chats to message_tool
|
||||
visibleReplies: "automatic", // global default for direct/source chats
|
||||
groupChat: {
|
||||
historyLimit: 50,
|
||||
visibleReplies: "message_tool", // default; use "automatic" for legacy final replies
|
||||
@@ -803,7 +803,7 @@ The gateway hot-reloads `messages` config after the file is saved. Restart only
|
||||
|
||||
`messages.groupChat.historyLimit` sets the global default. Channels can override with `channels.<channel>.historyLimit` (or per-account). Set `0` to disable.
|
||||
|
||||
`messages.visibleReplies` is the global source-turn default; `messages.groupChat.visibleReplies` overrides it for group/channel source turns. When `messages.visibleReplies` is unset, a harness can provide its own direct/source default; the Codex harness defaults to `message_tool`. Channel allowlists and mention gating still decide whether a turn is processed.
|
||||
`messages.visibleReplies` is the global source-turn default; `messages.groupChat.visibleReplies` overrides it for group/channel source turns. Channel allowlists and mention gating still decide whether a turn is processed.
|
||||
|
||||
#### DM history limits
|
||||
|
||||
|
||||
@@ -286,7 +286,6 @@ cannot roll back unrelated user settings.
|
||||
- Default is `15000` milliseconds.
|
||||
- `OPENCLAW_HANDSHAKE_TIMEOUT_MS` still takes precedence for one-off service or shell overrides.
|
||||
- Prefer fixing startup/event-loop stalls first; this knob is for hosts that are healthy but slow during warmup.
|
||||
- Reply-capable channels wait for reply-runtime readiness before normal startup; do not use handshake or socket timeout tuning to hide first-reply plugin/model/auth/tool cold setup.
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -523,12 +522,6 @@ cannot roll back unrelated user settings.
|
||||
- **Unsupported write-through**: root includes, include arrays, and includes
|
||||
with sibling overrides fail closed for OpenClaw-owned writes instead of
|
||||
flattening the config
|
||||
- **Confinement**: `$include` paths must resolve under the directory holding
|
||||
`openclaw.json`. To share a tree across machines or users, set
|
||||
`OPENCLAW_INCLUDE_ROOTS` to a path-list (`:` on POSIX, `;` on Windows) of
|
||||
additional directories that includes may reference. Symlinks are resolved
|
||||
and re-checked, so a path that lexically lives in a config dir but whose
|
||||
real target escapes every allowed root is still rejected.
|
||||
- **Error handling**: clear errors for missing files, parse errors, and circular includes
|
||||
|
||||
</Accordion>
|
||||
|
||||
@@ -298,8 +298,6 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
|
||||
Doctor only auto-migrates `notify: true` jobs when it can do so without changing behavior. If a job combines legacy notify fallback with an existing non-webhook delivery mode, doctor warns and leaves that job for manual review.
|
||||
|
||||
On Linux, doctor also warns when the user's crontab still invokes legacy `~/.openclaw/bin/ensure-whatsapp.sh`. That host-local script is not maintained by current OpenClaw and can write false `Gateway inactive` messages to `~/.openclaw/logs/whatsapp-health.log` when cron cannot reach the systemd user bus. Remove the stale crontab entry with `crontab -e`; use `openclaw channels status --probe`, `openclaw doctor`, and `openclaw gateway status` for current health checks.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="3c. Session lock cleanup">
|
||||
Doctor scans every agent session directory for stale write-lock files — files left behind when a session exited abnormally. For each lock file found it reports: the path, PID, whether the PID is still alive, lock age, and whether it is considered stale (dead PID or older than 30 minutes). In `--fix` / `--repair` mode it removes stale lock files automatically; otherwise it prints a note and instructs you to rerun with `--fix`.
|
||||
@@ -341,10 +339,10 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
<Accordion title="7. Sandbox image repair">
|
||||
When sandboxing is enabled, doctor checks Docker images and offers to build or switch to legacy names if the current image is missing.
|
||||
</Accordion>
|
||||
<Accordion title="7b. Plugin install cleanup">
|
||||
Doctor removes legacy OpenClaw-generated plugin dependency staging state in `openclaw doctor --fix` / `openclaw doctor --repair` mode. This covers stale generated dependency roots, old install-stage directories, and package-local debris from earlier bundled-plugin dependency repair code.
|
||||
<Accordion title="7b. Bundled plugin runtime deps">
|
||||
Doctor verifies runtime dependencies only for bundled plugins that are active in the current config or enabled by their bundled manifest default, for example `plugins.entries.discord.enabled: true`, legacy `channels.discord.enabled: true`, configured `models.providers.*` / agent model refs, or a default-enabled bundled plugin without provider ownership. If any are missing, doctor reports the packages and installs them in `openclaw doctor --fix` / `openclaw doctor --repair` mode. External plugins still use `openclaw plugins install` / `openclaw plugins update`; doctor does not install dependencies for arbitrary plugin paths.
|
||||
|
||||
Doctor can also reinstall configured downloadable plugins when the config references them but the local plugin registry cannot find them. Gateway startup and config reload do not run package managers; plugin installs remain explicit doctor/install/update work.
|
||||
During doctor repair, bundled runtime-dependency npm installs report spinner progress in TTY sessions and periodic line progress in piped/headless output. Gateway startup and config reload enter plugin-plan mode before importing bundled plugin runtime modules; normal runtime imports are verify-only and do not spawn package-manager repair. These installs are scoped to the plugin runtime install root, run with scripts disabled, do not write a package lock, and are guarded by an install-root lock so concurrent CLI or Gateway starts do not mutate the same `node_modules` tree at the same time. Stale legacy locks from killed Docker/container starts are reclaimed when their owner metadata cannot prove a current process incarnation and the lock files are old.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="8. Gateway service migrations and cleanup hints">
|
||||
|
||||
@@ -82,7 +82,6 @@ If you want a heartbeat to do something very specific (e.g. "check Gmail PubSub
|
||||
## Response contract
|
||||
|
||||
- If nothing needs attention, reply with **`HEARTBEAT_OK`**.
|
||||
- Tool-capable heartbeat runs may instead call `heartbeat_respond` with `notify: false` for no visible update, or `notify: true` plus `notificationText` for an alert. When present, the structured tool response takes precedence over the text fallback.
|
||||
- During heartbeat runs, OpenClaw treats `HEARTBEAT_OK` as an ack when it appears at the **start or end** of the reply. The token is stripped and the reply is dropped if the remaining content is **≤ `ackMaxChars`** (default: 300).
|
||||
- If `HEARTBEAT_OK` appears in the **middle** of a reply, it is not treated specially.
|
||||
- For alerts, **do not** include `HEARTBEAT_OK`; return only the alert text.
|
||||
|
||||
@@ -41,9 +41,6 @@ openclaw logs --follow
|
||||
raise the file log level.
|
||||
- To capture verbose-only details in file logs, set `logging.level` to `debug` or
|
||||
`trace`.
|
||||
- Trace logging also includes diagnostic timing summaries for selected hot paths,
|
||||
such as plugin tool factory preparation. See
|
||||
[/tools/plugin#slow-plugin-tool-setup](/tools/plugin#slow-plugin-tool-setup).
|
||||
|
||||
## Console capture
|
||||
|
||||
|
||||
@@ -511,7 +511,7 @@ Plugins run **in-process** with the Gateway. Treat them as trusted code:
|
||||
- If you install or update plugins (`openclaw plugins install <package>`, `openclaw plugins update <id>`), treat it like running untrusted code:
|
||||
- The install path is the per-plugin directory under the active plugin install root.
|
||||
- OpenClaw runs a built-in dangerous-code scan before install/update. `critical` findings block by default.
|
||||
- npm and git plugin installs run package-manager dependency convergence only during the explicit install/update flow. Local paths and archives are treated as self-contained plugin packages; OpenClaw copies/references them without running `npm install`.
|
||||
- OpenClaw uses `npm pack`, then runs a project-local `npm install --omit=dev --ignore-scripts` in that directory. Inherited global npm install settings are ignored so dependencies stay under the plugin install path.
|
||||
- Prefer pinned, exact versions (`@scope/pkg@1.2.3`), and inspect the unpacked code on disk before enabling.
|
||||
- `--dangerously-force-unsafe-install` is break-glass only for built-in scan false positives on plugin install/update flows. It does not bypass plugin `before_install` hook policy blocks and does not bypass scan failures.
|
||||
- Gateway-backed skill dependency installs follow the same dangerous/suspicious split: built-in `critical` findings block unless the caller explicitly sets `dangerouslyForceUnsafeInstall`, while suspicious findings still warn only. `openclaw skills install` remains the separate ClawHub skill download/install flow.
|
||||
|
||||
@@ -517,7 +517,7 @@ Look for:
|
||||
- `browser.executablePath not found` → configured path is invalid.
|
||||
- `browser.cdpUrl must be http(s) or ws(s)` → the configured CDP URL uses an unsupported scheme such as `file:` or `ftp:`.
|
||||
- `browser.cdpUrl has invalid port` → the configured CDP URL has a bad or out-of-range port.
|
||||
- `Playwright is not available in this gateway build; '<feature>' is unsupported.` → the current gateway install lacks the core browser runtime dependency; reinstall or update OpenClaw, then restart the gateway. ARIA snapshots and basic page screenshots can still work, but navigation, AI snapshots, CSS-selector element screenshots, and PDF export stay unavailable.
|
||||
- `Playwright is not available in this gateway build; '<feature>' is unsupported.` → the current gateway install lacks the bundled browser plugin's `playwright-core` runtime dependency; run `openclaw doctor --fix`, then restart the gateway. ARIA snapshots and basic page screenshots can still work, but navigation, AI snapshots, CSS-selector element screenshots, and PDF export stay unavailable.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Chrome MCP / existing-session signatures">
|
||||
|
||||
@@ -103,12 +103,11 @@ Both resolve from process env at activation time. SecretRef details are document
|
||||
|
||||
## Path-related env vars
|
||||
|
||||
| Variable | Purpose |
|
||||
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `OPENCLAW_HOME` | Override the home directory used for all internal path resolution (`~/.openclaw/`, agent dirs, sessions, credentials). Useful when running OpenClaw as a dedicated service user. |
|
||||
| `OPENCLAW_STATE_DIR` | Override the state directory (default `~/.openclaw`). |
|
||||
| `OPENCLAW_CONFIG_PATH` | Override the config file path (default `~/.openclaw/openclaw.json`). |
|
||||
| `OPENCLAW_INCLUDE_ROOTS` | Path-list of directories where `$include` directives may resolve files outside the config directory (default: none — `$include` is confined to the config dir). Tilde-expanded. |
|
||||
| Variable | Purpose |
|
||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `OPENCLAW_HOME` | Override the home directory used for all internal path resolution (`~/.openclaw/`, agent dirs, sessions, credentials). Useful when running OpenClaw as a dedicated service user. |
|
||||
| `OPENCLAW_STATE_DIR` | Override the state directory (default `~/.openclaw`). |
|
||||
| `OPENCLAW_CONFIG_PATH` | Override the config file path (default `~/.openclaw/openclaw.json`). |
|
||||
|
||||
## Logging
|
||||
|
||||
|
||||
@@ -498,8 +498,8 @@ openclaw infer image generate \
|
||||
```
|
||||
|
||||
This covers CLI argument parsing, config/default-agent resolution, bundled
|
||||
plugin activation, the shared image-generation runtime, and the live provider
|
||||
request. Plugin dependencies are expected to be present before runtime load.
|
||||
plugin activation, on-demand bundled runtime-dependency repair, the shared
|
||||
image-generation runtime, and the live provider request.
|
||||
|
||||
## Music generation live
|
||||
|
||||
|
||||
@@ -160,9 +160,9 @@ inside every shard.
|
||||
- `pnpm test:docker:npm-onboard-channel-agent`
|
||||
- Builds an npm tarball from the current checkout, installs it globally in
|
||||
Docker, runs non-interactive OpenAI API-key onboarding, configures Telegram
|
||||
by default, verifies the packaged plugin runtime loads without startup
|
||||
dependency repair, runs doctor, and runs one local agent turn against a
|
||||
mocked OpenAI endpoint.
|
||||
by default, verifies enabling the plugin installs runtime dependencies on
|
||||
demand, runs doctor, and runs one local agent turn against a mocked OpenAI
|
||||
endpoint.
|
||||
- Use `OPENCLAW_NPM_ONBOARD_CHANNEL=discord` to run the same packaged-install
|
||||
lane with Discord.
|
||||
- `pnpm test:docker:session-runtime-context`
|
||||
@@ -227,17 +227,17 @@ gh workflow run package-acceptance.yml --ref main \
|
||||
-f suite_profile=smoke
|
||||
```
|
||||
|
||||
- `pnpm test:docker:plugins`
|
||||
- `pnpm test:docker:bundled-channel-deps`
|
||||
- Packs and installs the current OpenClaw build in Docker, starts the Gateway
|
||||
with OpenAI configured, then enables bundled channel/plugins via config
|
||||
edits.
|
||||
- Verifies setup discovery leaves unconfigured downloadable plugins absent,
|
||||
the first configured doctor repair installs each missing downloadable
|
||||
plugin explicitly, and a second restart does not run hidden dependency
|
||||
repair.
|
||||
- Verifies setup discovery leaves unconfigured plugin runtime dependencies
|
||||
absent, the first configured Gateway or doctor run installs each bundled
|
||||
plugin's runtime dependencies on demand, and a second restart does not
|
||||
reinstall dependencies that were already activated.
|
||||
- Also installs a known older npm baseline, enables Telegram before running
|
||||
`openclaw update --tag <candidate>`, and verifies the candidate's
|
||||
post-update doctor cleans legacy plugin dependency debris without a
|
||||
post-update doctor repairs bundled channel runtime dependencies without a
|
||||
harness-side postinstall repair.
|
||||
- `pnpm test:parallels:npm-update`
|
||||
- Runs the native packaged-install update smoke across Parallels guests. Each
|
||||
@@ -263,9 +263,9 @@ gh workflow run package-acceptance.yml --ref main \
|
||||
- The script writes nested lane logs under `/tmp/openclaw-parallels-npm-update.*`.
|
||||
Inspect `windows-update.log`, `macos-update.log`, or `linux-update.log`
|
||||
before assuming the outer wrapper is hung.
|
||||
- Windows update can spend 10 to 15 minutes in post-update doctor and package
|
||||
update work on a cold guest; that is still healthy when the nested npm
|
||||
debug log is advancing.
|
||||
- Windows update can spend 10 to 15 minutes in post-update doctor/runtime
|
||||
dependency repair on a cold guest; that is still healthy when the nested
|
||||
npm debug log is advancing.
|
||||
- Do not run this aggregate wrapper in parallel with individual Parallels
|
||||
macOS, Windows, or Linux smoke lanes. They share VM state and can collide on
|
||||
snapshot restore, package serving, or guest gateway state.
|
||||
@@ -600,7 +600,7 @@ These Docker runners split into two buckets:
|
||||
`OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`. Override those env vars when you
|
||||
explicitly want the larger exhaustive scan.
|
||||
- `test:docker:all` builds the live Docker image once via `test:docker:live-build`, packs OpenClaw once as an npm tarball through `scripts/package-openclaw-for-docker.mjs`, then builds/reuses two `scripts/e2e/Dockerfile` images. The bare image is only the Node/Git runner for install/update/plugin-dependency lanes; those lanes mount the prebuilt tarball. The functional image installs the same tarball into `/app` for built-app functionality lanes. Docker lane definitions live in `scripts/lib/docker-e2e-scenarios.mjs`; planner logic lives in `scripts/lib/docker-e2e-plan.mjs`; `scripts/test-docker-all.mjs` executes the selected plan. The aggregate uses a weighted local scheduler: `OPENCLAW_DOCKER_ALL_PARALLELISM` controls process slots, while resource caps keep heavy live, npm-install, and multi-service lanes from all starting at once. If a single lane is heavier than the active caps, the scheduler can still start it when the pool is empty and then keeps it running alone until capacity is available again. Defaults are 10 slots, `OPENCLAW_DOCKER_ALL_LIVE_LIMIT=9`, `OPENCLAW_DOCKER_ALL_NPM_LIMIT=10`, and `OPENCLAW_DOCKER_ALL_SERVICE_LIMIT=7`; tune `OPENCLAW_DOCKER_ALL_WEIGHT_LIMIT` or `OPENCLAW_DOCKER_ALL_DOCKER_LIMIT` only when the Docker host has more headroom. The runner performs a Docker preflight by default, removes stale OpenClaw E2E containers, prints status every 30 seconds, stores successful lane timings in `.artifacts/docker-tests/lane-timings.json`, and uses those timings to start longer lanes first on later runs. Use `OPENCLAW_DOCKER_ALL_DRY_RUN=1` to print the weighted lane manifest without building or running Docker, or `node scripts/test-docker-all.mjs --plan-json` to print the CI plan for selected lanes, package/image needs, and credentials.
|
||||
- `Package Acceptance` is the GitHub-native package gate for "does this installable tarball work as a product?" It resolves one candidate package from `source=npm`, `source=ref`, `source=url`, or `source=artifact`, uploads it as `package-under-test`, then runs the reusable Docker E2E lanes against that exact tarball instead of repacking the selected ref. `workflow_ref` selects the trusted workflow/harness scripts, while `package_ref` selects the source commit/branch/tag to pack when `source=ref`; this lets current acceptance logic validate older trusted commits. Profiles are ordered by breadth: `smoke` is quick install/channel/agent plus gateway/config, `package` is the package/update/plugin contract plus the keyless upgrade-survivor fixture, the published-baseline upgrade survivor lane, and the default native replacement for most Parallels package/update coverage, `product` adds MCP channels, cron/subagent cleanup, OpenAI web search, and OpenWebUI, and `full` runs the release-path Docker chunks with OpenWebUI. For `published-upgrade-survivor`, Package Acceptance always uses `package-under-test` as the candidate and `published_upgrade_survivor_baseline` as the fallback published baseline, defaulting to `openclaw@latest`; set `published_upgrade_survivor_baselines=release-history` to shard the lane across a deduped matrix of the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. The published lane configures its baseline with a baked `openclaw config set` command recipe, then records recipe steps in the lane summary. Release validation runs a custom package delta (`plugins-offline plugin-update`) plus Telegram package QA because the release-path Docker chunks already cover the overlapping package/update/plugin lanes. Targeted GitHub Docker rerun commands generated from artifacts include prior package artifact, prepared image inputs, and the published upgrade-survivor baseline list when available, so failed lanes can avoid rebuilding the package and images.
|
||||
- `Package Acceptance` is the GitHub-native package gate for "does this installable tarball work as a product?" It resolves one candidate package from `source=npm`, `source=ref`, `source=url`, or `source=artifact`, uploads it as `package-under-test`, then runs the reusable Docker E2E lanes against that exact tarball instead of repacking the selected ref. `workflow_ref` selects the trusted workflow/harness scripts, while `package_ref` selects the source commit/branch/tag to pack when `source=ref`; this lets current acceptance logic validate older trusted commits. Profiles are ordered by breadth: `smoke` is quick install/channel/agent plus gateway/config, `package` is the package/update/plugin contract plus the keyless upgrade-survivor fixture, the published-baseline upgrade survivor lane, and the default native replacement for most Parallels package/update coverage, `product` adds MCP channels, cron/subagent cleanup, OpenAI web search, and OpenWebUI, and `full` runs the release-path Docker chunks with OpenWebUI. For `published-upgrade-survivor`, Package Acceptance always uses `package-under-test` as the candidate and `published_upgrade_survivor_baseline` as the fallback published baseline, defaulting to `openclaw@latest`; set `published_upgrade_survivor_baselines=release-history` to shard the lane across a deduped matrix of the latest six stable releases, `2026.4.23`, and the latest stable release before `2026-03-15`. The published lane configures its baseline with a baked `openclaw config set` command recipe, then records recipe steps in the lane summary. Release validation runs a custom package delta (`bundled-channel-deps-compat plugins-offline`) plus Telegram package QA because the release-path Docker chunks already cover the overlapping package/update/plugin lanes. Targeted GitHub Docker rerun commands generated from artifacts include prior package artifact, prepared image inputs, and the published upgrade-survivor baseline list when available, so failed lanes can avoid rebuilding the package and images.
|
||||
- Build and release checks run `scripts/check-cli-bootstrap-imports.mjs` after tsdown. The guard walks the static built graph from `dist/entry.js` and `dist/cli/run-main.js` and fails if pre-dispatch startup imports package dependencies such as Commander, prompt UI, undici, or logging before command dispatch; it also keeps the bundled gateway run chunk under budget and rejects static imports of known cold gateway paths. Packaged CLI smoke also covers root help, onboard help, doctor help, status, config schema, and a model-list command.
|
||||
- Package Acceptance legacy compatibility is capped at `2026.4.25` (`2026.4.25-beta.*` included). Through that cutoff, the harness tolerates only shipped-package metadata gaps: omitted private QA inventory entries, missing `gateway install --wrapper`, missing patch files in the tarball-derived git fixture, missing persisted `update.channel`, legacy plugin install-record locations, missing marketplace install-record persistence, and config metadata migration during `plugins update`. For packages after `2026.4.25`, those paths are strict failures.
|
||||
- Container smoke runners: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:npm-onboard-channel-agent`, `test:docker:update-channel-switch`, `test:docker:upgrade-survivor`, `test:docker:published-upgrade-survivor`, `test:docker:session-runtime-context`, `test:docker:agents-delete-shared-workspace`, `test:docker:gateway-network`, `test:docker:browser-cdp-snapshot`, `test:docker:mcp-channels`, `test:docker:pi-bundle-mcp-tools`, `test:docker:cron-mcp-cleanup`, `test:docker:plugins`, `test:docker:plugin-update`, and `test:docker:config-reload` boot one or more real containers and verify higher-level integration paths.
|
||||
@@ -615,9 +615,9 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
- Observability smoke: `pnpm qa:otel:smoke` is a private QA source-checkout lane. It is intentionally not part of package Docker release lanes because the npm tarball omits QA Lab.
|
||||
- Open WebUI live smoke: `pnpm test:docker:openwebui` (script: `scripts/e2e/openwebui-docker.sh`)
|
||||
- Onboarding wizard (TTY, full scaffolding): `pnpm test:docker:onboard` (script: `scripts/e2e/onboard-docker.sh`)
|
||||
- Npm tarball onboarding/channel/agent smoke: `pnpm test:docker:npm-onboard-channel-agent` installs the packed OpenClaw tarball globally in Docker, configures OpenAI via env-ref onboarding plus Telegram by default, runs doctor, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_CURRENT_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host rebuild with `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, or switch channel with `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`.
|
||||
- Npm tarball onboarding/channel/agent smoke: `pnpm test:docker:npm-onboard-channel-agent` installs the packed OpenClaw tarball globally in Docker, configures OpenAI via env-ref onboarding plus Telegram by default, verifies doctor repairs activated plugin runtime deps, and runs one mocked OpenAI agent turn. Reuse a prebuilt tarball with `OPENCLAW_CURRENT_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host rebuild with `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, or switch channel with `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`.
|
||||
- Update channel switch smoke: `pnpm test:docker:update-channel-switch` installs the packed OpenClaw tarball globally in Docker, switches from package `stable` to git `dev`, verifies the persisted channel and plugin post-update work, then switches back to package `stable` and checks update status.
|
||||
- Upgrade survivor smoke: `pnpm test:docker:upgrade-survivor` installs the packed OpenClaw tarball over a dirty old-user fixture with agents, channel config, plugin allowlists, stale plugin dependency state, and existing workspace/session files. It runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks config/state preservation plus startup/status budgets.
|
||||
- Upgrade survivor smoke: `pnpm test:docker:upgrade-survivor` installs the packed OpenClaw tarball over a dirty old-user fixture with agents, channel config, plugin allowlists, stale plugin runtime-deps state, and existing workspace/session files. It runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks config/state preservation plus startup/status budgets.
|
||||
- Published upgrade survivor smoke: `pnpm test:docker:published-upgrade-survivor` installs `openclaw@latest` by default, seeds realistic existing-user files, configures that baseline with a baked command recipe, validates the resulting config, updates that published install to the candidate tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks configured intents, state preservation, startup, `/healthz`, `/readyz`, and RPC status budgets. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, ask the aggregate scheduler to expand exact baselines with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, and expand issue-shaped fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS` such as `reported-issues`; Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
|
||||
- Session runtime context smoke: `pnpm test:docker:session-runtime-context` verifies hidden runtime context transcript persistence plus doctor repair of affected duplicated prompt-rewrite branches.
|
||||
- Bun global install smoke: `bash scripts/e2e/bun-global-install-smoke.sh` packs the current tree, installs it with `bun install -g` in an isolated home, and verifies `openclaw infer image providers --json` returns bundled image providers instead of hanging. Reuse a prebuilt tarball with `OPENCLAW_BUN_GLOBAL_SMOKE_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, skip the host build with `OPENCLAW_BUN_GLOBAL_SMOKE_HOST_BUILD=0`, or copy `dist/` from a built Docker image with `OPENCLAW_BUN_GLOBAL_SMOKE_DIST_IMAGE=openclaw-dockerfile-smoke:local`.
|
||||
@@ -634,7 +634,9 @@ The live-model Docker runners also bind-mount only the needed CLI auth homes (or
|
||||
Set `OPENCLAW_PLUGINS_E2E_CLAWHUB=0` to skip the ClawHub block, or override the default kitchen-sink package/runtime pair with `OPENCLAW_PLUGINS_E2E_CLAWHUB_SPEC` and `OPENCLAW_PLUGINS_E2E_CLAWHUB_ID`. Without `OPENCLAW_CLAWHUB_URL`/`CLAWHUB_URL`, the test uses a hermetic local ClawHub fixture server.
|
||||
- Plugin update unchanged smoke: `pnpm test:docker:plugin-update` (script: `scripts/e2e/plugin-update-unchanged-docker.sh`)
|
||||
- Config reload metadata smoke: `pnpm test:docker:config-reload` (script: `scripts/e2e/config-reload-source-docker.sh`)
|
||||
- Plugins: `pnpm test:docker:plugins` covers install smoke, local ClawHub fixture installs, marketplace updates, npm package dependency installs, and Claude-bundle enable/inspect. `pnpm test:docker:plugin-update` covers unchanged update behavior for installed plugins.
|
||||
- Bundled plugin runtime deps: `pnpm test:docker:bundled-channel-deps` builds a small Docker runner image by default, builds and packs OpenClaw once on the host, then mounts that tarball into each Linux install scenario. Reuse the image with `OPENCLAW_SKIP_DOCKER_BUILD=1`, skip the host rebuild after a fresh local build with `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, or point at an existing tarball with `OPENCLAW_CURRENT_PACKAGE_TGZ=/path/to/openclaw-*.tgz`. The full Docker aggregate and release-path bundled-channel chunks pre-pack this tarball once, then shard bundled channel checks into independent lanes, including separate update lanes for Telegram, Discord, Slack, Feishu, memory-lancedb, and ACPX. Release chunks split channel smokes, update targets, and setup/runtime contracts into `bundled-channels-core`, `bundled-channels-update-a`, `bundled-channels-update-b`, and `bundled-channels-contracts`; the aggregate `bundled-channels` chunk remains available for manual reruns. The release workflow also splits provider installer chunks and bundled plugin install/uninstall chunks; legacy `package-update`, `plugins-runtime`, and `plugins-integrations` chunks remain aggregate aliases for manual reruns. Use `OPENCLAW_BUNDLED_CHANNELS=telegram,slack` to narrow the channel matrix when running the bundled lane directly, or `OPENCLAW_BUNDLED_CHANNEL_UPDATE_TARGETS=telegram,acpx` to narrow the update scenario. Per-scenario Docker runs default to `OPENCLAW_BUNDLED_CHANNEL_DOCKER_RUN_TIMEOUT=900s`; the multi-target update scenario defaults to `OPENCLAW_BUNDLED_CHANNEL_UPDATE_DOCKER_RUN_TIMEOUT=2400s`. The lane also verifies that `channels.<id>.enabled=false` and `plugins.entries.<id>.enabled=false` suppress doctor/runtime-dependency repair.
|
||||
- Narrow bundled plugin runtime deps while iterating by disabling unrelated scenarios, for example:
|
||||
`OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps`.
|
||||
|
||||
To prebuild and reuse the shared functional image manually:
|
||||
|
||||
|
||||
@@ -122,19 +122,19 @@ Expected output:
|
||||
OpenClaw runs in Docker, but Docker is not the source of truth.
|
||||
All long-lived state must survive restarts, rebuilds, and reboots.
|
||||
|
||||
| Component | Location | Persistence mechanism | Notes |
|
||||
| ------------------- | ------------------------------------------------------ | ---------------------- | ------------------------------------------------------------- |
|
||||
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, `.env` |
|
||||
| Model auth profiles | `/home/node/.openclaw/agents/` | Host volume mount | `agents/<agentId>/agent/auth-profiles.json` (OAuth, API keys) |
|
||||
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
|
||||
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
|
||||
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
|
||||
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
|
||||
| Plugin packages | `/home/node/.openclaw/npm`, `/home/node/.openclaw/git` | Host volume mount | Downloadable plugin package roots |
|
||||
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
|
||||
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
|
||||
| OS packages | Container filesystem | Docker image | Do not install at runtime |
|
||||
| Docker container | Ephemeral | Restartable | Safe to destroy |
|
||||
| Component | Location | Persistence mechanism | Notes |
|
||||
| ------------------- | ---------------------------------------- | ---------------------- | ------------------------------------------------------------- |
|
||||
| Gateway config | `/home/node/.openclaw/` | Host volume mount | Includes `openclaw.json`, `.env` |
|
||||
| Model auth profiles | `/home/node/.openclaw/agents/` | Host volume mount | `agents/<agentId>/agent/auth-profiles.json` (OAuth, API keys) |
|
||||
| Skill configs | `/home/node/.openclaw/skills/` | Host volume mount | Skill-level state |
|
||||
| Agent workspace | `/home/node/.openclaw/workspace/` | Host volume mount | Code and agent artifacts |
|
||||
| WhatsApp session | `/home/node/.openclaw/` | Host volume mount | Preserves QR login |
|
||||
| Gmail keyring | `/home/node/.openclaw/` | Host volume + password | Requires `GOG_KEYRING_PASSWORD` |
|
||||
| Plugin runtime deps | `/var/lib/openclaw/plugin-runtime-deps/` | Docker named volume | Generated bundled plugin deps and runtime mirrors |
|
||||
| External binaries | `/usr/local/bin/` | Docker image | Must be baked at build time |
|
||||
| Node runtime | Container filesystem | Docker image | Rebuilt every image build |
|
||||
| OS packages | Container filesystem | Docker image | Do not install at runtime |
|
||||
| Docker container | Ephemeral | Restartable | Safe to destroy |
|
||||
|
||||
## Updates
|
||||
|
||||
|
||||
@@ -126,9 +126,10 @@ The setup script accepts these optional environment variables:
|
||||
| ------------------------------------------ | --------------------------------------------------------------- |
|
||||
| `OPENCLAW_IMAGE` | Use a remote image instead of building locally |
|
||||
| `OPENCLAW_DOCKER_APT_PACKAGES` | Install extra apt packages during build (space-separated) |
|
||||
| `OPENCLAW_EXTENSIONS` | Include selected bundled plugin helpers at build time |
|
||||
| `OPENCLAW_EXTENSIONS` | Pre-install plugin deps at build time (space-separated names) |
|
||||
| `OPENCLAW_EXTRA_MOUNTS` | Extra host bind mounts (comma-separated `source:target[:opts]`) |
|
||||
| `OPENCLAW_HOME_VOLUME` | Persist `/home/node` in a named Docker volume |
|
||||
| `OPENCLAW_PLUGIN_STAGE_DIR` | Container path for generated bundled plugin deps and mirrors |
|
||||
| `OPENCLAW_SANDBOX` | Opt in to sandbox bootstrap (`1`, `true`, `yes`, `on`) |
|
||||
| `OPENCLAW_SKIP_ONBOARDING` | Skip the interactive onboarding step (`1`, `true`, `yes`, `on`) |
|
||||
| `OPENCLAW_DOCKER_SOCKET` | Override Docker socket path |
|
||||
@@ -162,8 +163,11 @@ export OTEL_SERVICE_NAME="openclaw-gateway"
|
||||
```
|
||||
|
||||
The official OpenClaw Docker release image includes the bundled
|
||||
`diagnostics-otel` plugin source. To enable export, allow and enable the
|
||||
`diagnostics-otel` plugin in config, then set
|
||||
`diagnostics-otel` plugin source. Depending on the image and cache state, the
|
||||
Gateway may still stage plugin-local OpenTelemetry runtime dependencies the
|
||||
first time the plugin is enabled, so allow that first boot to reach the package
|
||||
registry or prewarm the image in your release lane. To enable export, allow and
|
||||
enable the `diagnostics-otel` plugin in config, then set
|
||||
`diagnostics.otel.enabled=true` or use the config example in
|
||||
[OpenTelemetry export](/gateway/opentelemetry). Collector auth headers are
|
||||
configured through `diagnostics.otel.headers`, not through Docker environment
|
||||
@@ -269,16 +273,24 @@ That mounted config directory is where OpenClaw keeps:
|
||||
- `agents/<agentId>/agent/auth-profiles.json` for stored provider OAuth/API-key auth
|
||||
- `.env` for env-backed runtime secrets such as `OPENCLAW_GATEWAY_TOKEN`
|
||||
|
||||
Installed downloadable plugins store their package state under the mounted
|
||||
OpenClaw home, so plugin install records and package roots survive container
|
||||
replacement. Gateway startup does not generate bundled-plugin dependency trees.
|
||||
Bundled plugin runtime dependencies and mirrored runtime files are generated
|
||||
state, not user config. Compose stores them in the named Docker volume
|
||||
`openclaw-plugin-runtime-deps` mounted at
|
||||
`/var/lib/openclaw/plugin-runtime-deps`. Keeping that high-churn tree out of the
|
||||
host config bind mount avoids slow Docker Desktop/WSL file operations and stale
|
||||
Windows handles during cold Gateway startup.
|
||||
|
||||
The default Compose file sets `OPENCLAW_PLUGIN_STAGE_DIR` to that path for both
|
||||
`openclaw-gateway` and `openclaw-cli`, so `openclaw doctor --fix`, channel
|
||||
login/setup commands, and Gateway startup all use the same generated runtime
|
||||
volume.
|
||||
|
||||
For full persistence details on VM deployments, see
|
||||
[Docker VM Runtime - What persists where](/install/docker-vm-runtime#what-persists-where).
|
||||
|
||||
**Disk growth hotspots:** watch `media/`, session JSONL files,
|
||||
`cron/runs/*.jsonl`, installed plugin package roots, and rolling file logs
|
||||
under `/tmp/openclaw/`.
|
||||
**Disk growth hotspots:** watch `media/`, session JSONL files, `cron/runs/*.jsonl`,
|
||||
the `openclaw-plugin-runtime-deps` Docker volume, and rolling file logs under
|
||||
`/tmp/openclaw/`.
|
||||
|
||||
### Shell helpers (optional)
|
||||
|
||||
|
||||
@@ -107,21 +107,37 @@ bun add -g openclaw@latest
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Read-only package tree">
|
||||
OpenClaw treats packaged global installs as read-only at runtime, even when the global package directory is writable by the current user. Plugin package installs live in OpenClaw-owned npm/git roots under the user config directory, and Gateway startup does not mutate the OpenClaw package tree.
|
||||
OpenClaw treats packaged global installs as read-only at runtime, even when the global package directory is writable by the current user. Bundled plugin runtime dependencies are staged into a writable runtime directory instead of mutating the package tree. This keeps `openclaw update` from racing with a running gateway or local agent that is repairing plugin dependencies during the same install.
|
||||
|
||||
Some Linux npm setups install global packages under root-owned directories such as `/usr/lib/node_modules/openclaw`. OpenClaw supports that layout because plugin install/update commands write outside that global package directory.
|
||||
Some Linux npm setups install global packages under root-owned directories such as `/usr/lib/node_modules/openclaw`. OpenClaw supports that layout through the same external staging path.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Hardened systemd units">
|
||||
Give OpenClaw write access to its config/state roots so explicit plugin installs, plugin updates, and doctor cleanup can persist their changes:
|
||||
Set a writable stage directory that is included in `ReadWritePaths`:
|
||||
|
||||
```ini
|
||||
Environment=OPENCLAW_PLUGIN_STAGE_DIR=/var/lib/openclaw/plugin-runtime-deps
|
||||
ReadWritePaths=/var/lib/openclaw /home/openclaw/.openclaw /tmp
|
||||
```
|
||||
|
||||
`OPENCLAW_PLUGIN_STAGE_DIR` also accepts a path list. OpenClaw resolves bundled plugin runtime dependencies left-to-right across the listed roots, treats earlier roots as read-only preinstalled layers, and installs or repairs only into the final writable root:
|
||||
|
||||
```ini
|
||||
Environment=OPENCLAW_PLUGIN_STAGE_DIR=/opt/openclaw/plugin-runtime-deps:/var/lib/openclaw/plugin-runtime-deps
|
||||
ReadWritePaths=/var/lib/openclaw /home/openclaw/.openclaw /tmp
|
||||
```
|
||||
|
||||
If `OPENCLAW_PLUGIN_STAGE_DIR` is not set, OpenClaw uses `$STATE_DIRECTORY` when systemd provides it, then falls back to `~/.openclaw/plugin-runtime-deps`. The repair step treats that stage as an OpenClaw-owned local package root and ignores user npm prefix and global settings, so global-install npm config does not redirect bundled plugin dependencies into `~/node_modules` or the global package tree.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="Disk-space preflight">
|
||||
Before package updates and explicit plugin installs, OpenClaw tries a best-effort disk-space check for the target volume. Low space produces a warning with the checked path, but does not block the update because filesystem quotas, snapshots, and network volumes can change after the check. The actual package-manager install and post-install verification remain authoritative.
|
||||
Before package updates and bundled runtime-dependency repairs, OpenClaw tries a best-effort disk-space check for the target volume. Low space produces a warning with the checked path, but does not block the update because filesystem quotas, snapshots, and network volumes can change after the check. The actual npm install, copy, and post-install verification remain authoritative.
|
||||
</Accordion>
|
||||
<Accordion title="Bundled plugin runtime dependencies">
|
||||
Packaged installs keep bundled plugin runtime dependencies out of the read-only package tree. On startup and during `openclaw doctor --fix`, OpenClaw repairs runtime dependencies only for bundled plugins that are active in config, active through legacy channel config, or enabled by their bundled manifest default. Persisted channel auth state alone does not trigger Gateway startup runtime-dependency repair.
|
||||
|
||||
Explicit disablement wins. A disabled plugin or channel does not get its runtime dependencies repaired just because it exists in the package. External plugins and custom load paths still use `openclaw plugins install` or `openclaw plugins update`.
|
||||
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
|
||||
@@ -61,8 +61,10 @@ to narrow plugin loading before broader registry materialization:
|
||||
- explicit provider setup/runtime resolution narrows to plugins that own the
|
||||
requested provider id
|
||||
- Gateway startup planning uses `activation.onStartup` for explicit startup
|
||||
imports and startup opt-outs; plugins without startup metadata load only
|
||||
through narrower activation triggers
|
||||
imports and startup opt-outs; every plugin should declare it as OpenClaw
|
||||
moves away from implicit startup imports, while plugins without static
|
||||
capability metadata and without `activation.onStartup` still use the
|
||||
deprecated implicit startup sidecar fallback for compatibility
|
||||
|
||||
The activation planner exposes both an ids-only API for existing callers and a
|
||||
plan API for new diagnostics. Plan entries report why a plugin was selected,
|
||||
@@ -116,7 +118,8 @@ loader state when code or installed artifacts are actually loaded, such as:
|
||||
- `PluginLoaderCacheState` and compatible active runtime registries
|
||||
- jiti/module caches and public-surface loader caches used to avoid importing
|
||||
the same runtime surface repeatedly
|
||||
- filesystem caches for installed plugin artifacts
|
||||
- runtime dependency mirrors and filesystem caches for installed plugin
|
||||
artifacts
|
||||
- short-lived per-call maps for path normalization or duplicate resolution
|
||||
|
||||
Those caches are data-plane implementation details. They must not answer
|
||||
|
||||
@@ -123,7 +123,7 @@ OpenClaw's plugin system has four layers:
|
||||
Core decides whether a discovered plugin is enabled, disabled, blocked, or selected for an exclusive slot such as memory.
|
||||
</Step>
|
||||
<Step title="Runtime loading">
|
||||
Native OpenClaw plugins are loaded in-process and register capabilities into a central registry. Packaged JavaScript loads through native `require`; source TypeScript falls back to Jiti. Compatible bundles are normalized into registry records without importing runtime code.
|
||||
Native OpenClaw plugins are loaded in-process via jiti and register capabilities into a central registry. Compatible bundles are normalized into registry records without importing runtime code.
|
||||
</Step>
|
||||
<Step title="Surface consumption">
|
||||
The rest of OpenClaw reads the registry to expose tools, channels, provider setup, hooks, HTTP routes, CLI commands, and services.
|
||||
|
||||
@@ -258,12 +258,13 @@ dual-format packages from being partially installed as bundles.
|
||||
- Third-party compatible bundles do not get startup `npm install` repair. They
|
||||
should be installed through `openclaw plugins install` and ship everything
|
||||
they need in the installed plugin directory.
|
||||
- OpenClaw-owned bundled plugins are either shipped lightweight in core or
|
||||
downloadable through the plugin installer. Gateway startup never runs a
|
||||
package manager for them.
|
||||
- `openclaw doctor --fix` removes legacy staged dependency directories and can
|
||||
install configured downloadable plugins that are missing from the local
|
||||
plugin index.
|
||||
- OpenClaw-owned packaged bundled plugins have a narrow exception: when one is
|
||||
enabled, Gateway startup can repair missing declared runtime dependencies
|
||||
before import. Operators can inspect or repair that stage with
|
||||
`openclaw plugins deps`.
|
||||
- The release pipeline is still responsible for shipping a complete bundled
|
||||
dependency payload when possible (see the postpublish verification rule in
|
||||
[Releasing](/reference/RELEASING)).
|
||||
|
||||
## Security
|
||||
|
||||
|
||||
@@ -22,10 +22,6 @@ it only posts to the channel when it calls `message(action="send")`. Set
|
||||
`messages.visibleReplies: "automatic"` to keep direct-chat final replies on the
|
||||
legacy automatic delivery path.
|
||||
|
||||
Codex heartbeat turns also get the `heartbeat_respond` tool by default, so the
|
||||
agent can record whether the wake should stay quiet or notify without encoding
|
||||
that control flow in final text.
|
||||
|
||||
If you are trying to orient yourself, start with
|
||||
[Agent runtimes](/concepts/agent-runtimes). The short version is:
|
||||
`openai/gpt-5.5` is the model ref, `codex` is the runtime, and Telegram,
|
||||
@@ -458,10 +454,11 @@ By default, the plugin starts OpenClaw's managed Codex binary locally with:
|
||||
codex app-server --listen stdio://
|
||||
```
|
||||
|
||||
The managed binary is shipped with the `codex` plugin package. This keeps the
|
||||
app-server version tied to the bundled plugin instead of whichever separate
|
||||
Codex CLI happens to be installed locally. Set `appServer.command` only when
|
||||
you intentionally want to run a different executable.
|
||||
The managed binary is declared as a bundled plugin runtime dependency and staged
|
||||
with the rest of the `codex` plugin dependencies. This keeps the app-server
|
||||
version tied to the bundled plugin instead of whichever separate Codex CLI
|
||||
happens to be installed locally. Set `appServer.command` only when you
|
||||
intentionally want to run a different executable.
|
||||
|
||||
By default, OpenClaw starts local Codex harness sessions in YOLO mode:
|
||||
`approvalPolicy: "never"`, `approvalsReviewer: "user"`, and
|
||||
@@ -593,8 +590,7 @@ Codex dynamic tools default to the `native-first` profile. In that mode,
|
||||
OpenClaw does not expose dynamic tools that duplicate Codex-native workspace
|
||||
operations: `read`, `write`, `edit`, `apply_patch`, `exec`, `process`, and
|
||||
`update_plan`. OpenClaw integration tools such as messaging, sessions, media,
|
||||
cron, browser, nodes, gateway, `heartbeat_respond`, and `web_search` remain
|
||||
available.
|
||||
cron, browser, nodes, gateway, and `web_search` remain available.
|
||||
|
||||
Supported top-level Codex plugin fields:
|
||||
|
||||
|
||||
@@ -135,6 +135,9 @@ Current compatibility records include:
|
||||
- legacy channel route key and comparable-target helper aliases while plugins
|
||||
move to `openclaw/plugin-sdk/channel-route`
|
||||
- activation hints that are being replaced by manifest contribution ownership
|
||||
- deprecated implicit startup sidecar loading for plugins that have not declared
|
||||
`activation.onStartup`; maintainers can test the future stricter behavior with
|
||||
`OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS=1`
|
||||
- `setup-api` runtime fallback while setup descriptors move to cold
|
||||
`setup.requiresRuntime: false` metadata
|
||||
- provider `discovery` hooks while provider catalog hooks move to
|
||||
|
||||
@@ -1,103 +1,214 @@
|
||||
---
|
||||
summary: "How OpenClaw installs plugin packages and resolves plugin dependencies"
|
||||
summary: "How OpenClaw plans, stages, and repairs bundled plugin runtime dependencies"
|
||||
read_when:
|
||||
- You are debugging plugin package installs
|
||||
- You are debugging bundled plugin runtime dependency repair
|
||||
- You are changing plugin startup, doctor, or package-manager install behavior
|
||||
- You are maintaining packaged OpenClaw installs or bundled plugin manifests
|
||||
title: "Plugin dependency resolution"
|
||||
sidebarTitle: "Dependencies"
|
||||
---
|
||||
|
||||
# Plugin dependency resolution
|
||||
OpenClaw does not install every bundled plugin dependency tree at package install
|
||||
time. It first derives an effective plugin plan from config and plugin metadata,
|
||||
then stages runtime dependencies only for bundled OpenClaw-owned plugins that
|
||||
the plan can actually load.
|
||||
|
||||
OpenClaw keeps plugin dependency work at install/update time. Runtime loading
|
||||
does not run package managers, repair dependency trees, or mutate the OpenClaw
|
||||
package directory.
|
||||
This page covers packaged runtime dependencies for bundled OpenClaw plugins.
|
||||
Third-party plugins and custom plugin paths still use explicit plugin
|
||||
installation commands such as `openclaw plugins install` and
|
||||
`openclaw plugins update`.
|
||||
|
||||
## Responsibility split
|
||||
|
||||
Plugin packages own their dependency graph:
|
||||
OpenClaw owns the plan and policy:
|
||||
|
||||
- runtime dependencies live in the plugin package `dependencies` or
|
||||
`optionalDependencies`
|
||||
- SDK/core imports are peer or supplied OpenClaw imports
|
||||
- local development plugins bring their own already-installed dependencies
|
||||
- npm and git plugins are installed into OpenClaw-owned package roots
|
||||
- which plugins are active for this config
|
||||
- which dependency roots are writable or read-only
|
||||
- when repair is allowed
|
||||
- which plugin ids are staged for startup
|
||||
- final checks before importing plugin runtime modules
|
||||
|
||||
OpenClaw owns only the plugin lifecycle:
|
||||
The package manager owns dependency convergence:
|
||||
|
||||
- discover the plugin source
|
||||
- install or update the package when explicitly requested
|
||||
- record the install metadata
|
||||
- load the plugin entrypoint
|
||||
- fail with an actionable error when dependencies are missing
|
||||
- package graph resolution
|
||||
- production, optional, and peer dependency handling
|
||||
- `node_modules` layout
|
||||
- package integrity
|
||||
- lock and install metadata
|
||||
|
||||
In practice, OpenClaw should decide what needs to exist. `pnpm` or `npm` should
|
||||
make the filesystem match that decision.
|
||||
|
||||
OpenClaw also owns the per-install-root coordination lock. Package managers
|
||||
protect their own install transaction, but they do not serialize OpenClaw's
|
||||
manifest writes, isolated-stage copy/rename, final validation, or plugin import
|
||||
against another Gateway, doctor, or CLI process touching the same runtime
|
||||
dependency root.
|
||||
|
||||
## Effective plugin plan
|
||||
|
||||
The effective plugin plan is derived from config plus discovered plugin
|
||||
metadata. These inputs can activate bundled plugin runtime dependencies:
|
||||
|
||||
- `plugins.entries.<id>.enabled`
|
||||
- `plugins.allow`, `plugins.deny`, and `plugins.enabled`
|
||||
- legacy channel config such as `channels.telegram.enabled`
|
||||
- configured providers, models, or CLI backend references that require a plugin
|
||||
- bundled manifest defaults such as `enabledByDefault`
|
||||
- the installed plugin index and bundled manifest metadata
|
||||
|
||||
Explicit disablement wins. A disabled plugin, denied plugin id, disabled plugin
|
||||
system, or disabled channel does not trigger runtime dependency repair. Persisted
|
||||
auth state alone also does not activate a bundled channel or provider.
|
||||
|
||||
The plugin plan is the stable input. The generated dependency materialization is
|
||||
an output of that plan.
|
||||
|
||||
## Startup flow
|
||||
|
||||
Gateway startup parses config and builds the startup plugin lookup table before
|
||||
plugin runtime modules are loaded. Startup then stages runtime dependencies only
|
||||
for the `startupPluginIds` selected by that plan.
|
||||
|
||||
For packaged installs, dependency staging is allowed before plugin import. After
|
||||
staging, the runtime loader imports startup plugins with install repair disabled;
|
||||
at that point missing dependency materialization is treated as a load failure,
|
||||
not another repair loop.
|
||||
|
||||
When startup dependency staging is deferred behind the HTTP bind, Gateway
|
||||
readiness stays blocked on the `plugin-runtime-deps` reason until the selected
|
||||
startup plugin dependencies are materialized and the startup plugin runtime has
|
||||
loaded.
|
||||
|
||||
## When repair runs
|
||||
|
||||
Runtime dependency repair should run when one of these is true:
|
||||
|
||||
- the effective plugin plan changed and adds bundled plugins that need runtime
|
||||
dependencies
|
||||
- the generated dependency manifest no longer matches the effective plan
|
||||
- expected installed package sentinels are missing or incomplete
|
||||
- `openclaw doctor --fix` or `openclaw plugins deps --repair` was requested
|
||||
|
||||
Runtime dependency repair should not run just because OpenClaw started. A normal
|
||||
startup with an unchanged plan and complete dependency materialization should
|
||||
skip package-manager work.
|
||||
|
||||
Commands that edit config, enable plugins, or repair doctor findings can enter
|
||||
plugin plan mode once, materialize the newly required bundled dependencies, then
|
||||
return to the normal command flow. Local `openclaw onboard` and
|
||||
`openclaw configure` do this automatically after they successfully write config,
|
||||
so the next Gateway run does not discover missing bundled plugin packages after
|
||||
startup has already begun. Remote onboarding/configure stays read-only for local
|
||||
runtime deps.
|
||||
|
||||
## Hot reload rule
|
||||
|
||||
Hot reload paths that can change active plugins must go back through plugin plan
|
||||
mode before loading plugin runtime. The reload should compare the new effective
|
||||
plugin plan with the previous one, stage missing dependencies for newly active
|
||||
bundled plugins, then load or restart the affected runtime.
|
||||
|
||||
If a config reload does not change the effective plugin plan, it should not
|
||||
repair bundled runtime dependencies.
|
||||
|
||||
## Package manager execution
|
||||
|
||||
OpenClaw writes a generated install manifest for the selected bundled runtime
|
||||
dependencies and runs the package manager in the runtime dependency install
|
||||
root. It prefers `pnpm` when available and falls back to the Node-bundled `npm`
|
||||
runner.
|
||||
|
||||
The `pnpm` path uses production dependencies, disables lifecycle scripts, ignores
|
||||
the workspace, and keeps the store inside the install root:
|
||||
|
||||
```bash
|
||||
pnpm install \
|
||||
--prod \
|
||||
--ignore-scripts \
|
||||
--ignore-workspace \
|
||||
--config.frozen-lockfile=false \
|
||||
--config.minimum-release-age=0 \
|
||||
--config.store-dir=<install-root>/.openclaw-pnpm-store \
|
||||
--config.node-linker=hoisted \
|
||||
--config.virtual-store-dir=.pnpm
|
||||
```
|
||||
|
||||
The `npm` fallback uses the safe npm install wrapper with production
|
||||
dependencies, lifecycle scripts disabled, workspace mode disabled, audit
|
||||
disabled, fund output disabled, legacy peer dependency behavior, and package-lock
|
||||
output enabled for the generated install root.
|
||||
|
||||
After install, OpenClaw validates the staged dependency tree before making it
|
||||
visible to the runtime dependency root. Isolated staging is copied into the
|
||||
runtime dependency root and validated again.
|
||||
|
||||
The whole repair/materialization section is guarded by an install-root lock.
|
||||
Current lock owners record PID, process start-time when available, and creation
|
||||
time. Legacy locks without process start-time or creation-time evidence are only
|
||||
reclaimed by filesystem age, so recycled Docker PID 1 locks recover without
|
||||
expiring normal long-running current installs by age alone.
|
||||
|
||||
## Install roots
|
||||
|
||||
OpenClaw uses stable per-source roots:
|
||||
Packaged installs must not mutate read-only package directories. OpenClaw can
|
||||
read dependency roots from packaged layers, but writes generated runtime
|
||||
dependencies to a writable stage such as:
|
||||
|
||||
- npm packages install under `~/.openclaw/npm`
|
||||
- git packages clone under `~/.openclaw/git`
|
||||
- local/path/archive installs are copied or referenced without dependency repair
|
||||
- `OPENCLAW_PLUGIN_STAGE_DIR`
|
||||
- `$STATE_DIRECTORY`
|
||||
- `~/.openclaw/plugin-runtime-deps`
|
||||
- `/var/lib/openclaw/plugin-runtime-deps` in container-style installs
|
||||
|
||||
npm installs run in the npm root with:
|
||||
The writable root is the final materialization target. Older read-only roots are
|
||||
kept as compatibility layers only when needed.
|
||||
|
||||
When a packaged OpenClaw update changes the versioned writable root but the
|
||||
selected bundled-plugin dependency plan is still satisfied by a previous staged
|
||||
root, repair reuses that previous `node_modules` tree instead of running the
|
||||
package manager again. The new versioned root still gets its own current package
|
||||
runtime mirror, so plugin code comes from the current OpenClaw package while
|
||||
unchanged dependency trees are shared across updates. Reuse skips previous roots
|
||||
with an active OpenClaw runtime-dependency lock, so a new root does not link to a
|
||||
dependency tree that another Gateway, doctor, or CLI process is currently
|
||||
repairing.
|
||||
|
||||
## Doctor and CLI commands
|
||||
|
||||
Use `plugins deps` to inspect or repair bundled plugin runtime dependency
|
||||
materialization:
|
||||
|
||||
```bash
|
||||
npm install --prefix ~/.openclaw/npm <spec> --omit=dev --ignore-scripts --no-audit --no-fund
|
||||
openclaw plugins deps
|
||||
openclaw plugins deps --json
|
||||
openclaw plugins deps --repair
|
||||
openclaw plugins deps --prune
|
||||
```
|
||||
|
||||
git installs clone or refresh the repository, then run:
|
||||
Use doctor when the dependency state is part of broader install health:
|
||||
|
||||
```bash
|
||||
npm install --omit=dev --ignore-scripts --no-audit --no-fund
|
||||
```
|
||||
|
||||
The installed plugin then loads from that package directory, so package-local
|
||||
`node_modules` resolution works the same way it does for a normal Node package.
|
||||
|
||||
## Local plugins
|
||||
|
||||
Local plugins are treated as developer-controlled directories. OpenClaw does not
|
||||
run `npm install`, `pnpm install`, or dependency repair for them. If a local
|
||||
plugin has dependencies, install them in that plugin before loading it.
|
||||
|
||||
TypeScript local plugins can use the emergency Jiti path. Packaged JavaScript
|
||||
plugins load through native import/require instead of Jiti.
|
||||
|
||||
## Startup and reload
|
||||
|
||||
Gateway startup and config reload never install plugin dependencies. They read
|
||||
the plugin install records, compute the entrypoint, and load it.
|
||||
|
||||
If a dependency is missing at runtime, the plugin fails to load and the error
|
||||
should point the operator to an explicit fix:
|
||||
|
||||
```bash
|
||||
openclaw plugins update <id>
|
||||
openclaw plugins install <source>
|
||||
openclaw doctor
|
||||
openclaw doctor --fix
|
||||
```
|
||||
|
||||
`doctor --fix` can clean legacy OpenClaw-generated dependency state and install
|
||||
configured downloadable plugins that are missing from the local install records.
|
||||
It does not repair dependencies for an already-installed local plugin.
|
||||
`plugins deps` and doctor operate on OpenClaw-owned bundled plugin runtime
|
||||
dependencies selected by the effective plugin plan. They are not third-party
|
||||
plugin install or update commands.
|
||||
|
||||
## Bundled plugins
|
||||
## Troubleshooting
|
||||
|
||||
Lightweight and core-critical bundled plugins are shipped as part of OpenClaw.
|
||||
They should either have no heavy runtime dependency tree or be moved out to a
|
||||
downloadable package on ClawHub/npm.
|
||||
If a packaged install reports missing bundled runtime dependencies:
|
||||
|
||||
Bundled plugin manifests must not request dependency staging. Large or optional
|
||||
plugin functionality should be packaged as a normal plugin and installed through
|
||||
the same npm/git/ClawHub path as third-party plugins.
|
||||
1. Run `openclaw plugins deps --json` to inspect the selected plan and missing
|
||||
packages.
|
||||
2. Run `openclaw plugins deps --repair` or `openclaw doctor --fix` to repair the
|
||||
writable dependency stage.
|
||||
3. If the install root is read-only, set `OPENCLAW_PLUGIN_STAGE_DIR` to a
|
||||
writable path and rerun repair.
|
||||
4. Restart Gateway after repair if the missing dependency blocked startup plugin
|
||||
loading.
|
||||
|
||||
## Legacy cleanup
|
||||
|
||||
Older OpenClaw versions generated bundled-plugin dependency roots at startup or
|
||||
during doctor repair. Current doctor cleanup removes those stale directories and
|
||||
symlinks when `--fix` is used, including old `plugin-runtime-deps` roots,
|
||||
`.openclaw-runtime-deps*` manifests, generated plugin `node_modules`, install
|
||||
stage directories, and package-local pnpm stores.
|
||||
|
||||
These paths are legacy debris only. New installs should not create them.
|
||||
In source checkouts, the workspace install usually provides bundled plugin
|
||||
dependencies. Run `pnpm install` for source dependency repair instead of using
|
||||
packaged runtime dependency repair as the first step.
|
||||
|
||||
@@ -255,15 +255,24 @@ embedded agent harness ids that do not already have an ownership field.
|
||||
This block is metadata only. It does not register runtime behavior, and it does
|
||||
not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints.
|
||||
Current consumers use it as a narrowing hint before broader plugin loading, so
|
||||
missing non-startup activation metadata usually only costs performance; it
|
||||
should not change correctness while manifest ownership fallbacks still exist.
|
||||
missing activation metadata usually only costs performance; it should not
|
||||
change correctness while legacy manifest ownership fallbacks still exist.
|
||||
|
||||
Every plugin should set `activation.onStartup` intentionally. Set it to `true`
|
||||
only when the plugin must run during Gateway startup. Set it to `false` when
|
||||
the plugin is inert at startup and should load only from narrower triggers.
|
||||
Omitting `onStartup` no longer startup-loads the plugin implicitly; use explicit
|
||||
activation metadata for startup, channel, config, agent-harness, memory, or
|
||||
other narrower activation triggers.
|
||||
Every plugin should set `activation.onStartup` intentionally as OpenClaw moves
|
||||
away from implicit startup imports. Set it to `true` only when the plugin must
|
||||
run during Gateway startup. Set it to `false` when the plugin is inert at
|
||||
startup and should load only from narrower triggers. Omitting `onStartup` keeps
|
||||
the deprecated legacy implicit startup sidecar fallback for plugins with no
|
||||
static capability metadata; future versions may stop startup-loading those
|
||||
plugins unless they declare `activation.onStartup: true`. Plugin status and
|
||||
compatibility reports warn with `legacy-implicit-startup-sidecar` when a plugin
|
||||
still relies on that fallback.
|
||||
|
||||
For migration testing, set
|
||||
`OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS=1` to disable only that
|
||||
deprecated fallback. This opt-in mode does not block explicit
|
||||
`activation.onStartup: true` plugins or plugins loaded by channel, config,
|
||||
agent-harness, memory, or other narrower activation triggers.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -279,21 +288,21 @@ other narrower activation triggers.
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Type | What it means |
|
||||
| ------------------ | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `onStartup` | No | `boolean` | Explicit Gateway startup activation. Every plugin should set this. `true` imports the plugin during startup; `false` keeps it startup-lazy unless another matched trigger requires loading. |
|
||||
| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. |
|
||||
| `onAgentHarnesses` | No | `string[]` | Embedded agent harness runtime ids that should include this plugin in activation/load plans. Use top-level `cliBackends` for CLI backend aliases. |
|
||||
| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. |
|
||||
| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. |
|
||||
| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. |
|
||||
| `onConfigPaths` | No | `string[]` | Root-relative config paths that should include this plugin in startup/load plans when the path is present and not explicitly disabled. |
|
||||
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. |
|
||||
| Field | Required | Type | What it means |
|
||||
| ------------------ | -------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `onStartup` | No | `boolean` | Explicit Gateway startup activation. Every plugin should set this. `true` imports the plugin during startup; `false` opts out of the deprecated implicit sidecar startup fallback unless another matched trigger requires loading. |
|
||||
| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. |
|
||||
| `onAgentHarnesses` | No | `string[]` | Embedded agent harness runtime ids that should include this plugin in activation/load plans. Use top-level `cliBackends` for CLI backend aliases. |
|
||||
| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. |
|
||||
| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. |
|
||||
| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. |
|
||||
| `onConfigPaths` | No | `string[]` | Root-relative config paths that should include this plugin in startup/load plans when the path is present and not explicitly disabled. |
|
||||
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. |
|
||||
|
||||
Current live consumers:
|
||||
|
||||
- Gateway startup planning uses `activation.onStartup` for explicit startup
|
||||
import
|
||||
import and opt-out of deprecated implicit sidecar startup fallback
|
||||
- command-triggered CLI planning falls back to legacy
|
||||
`commandAliases[].cliCommand` or `commandAliases[].name`
|
||||
- agent-runtime startup planning uses `activation.onAgentHarnesses` for
|
||||
@@ -1015,7 +1024,7 @@ Important examples:
|
||||
| `openclaw.extensions` | Declares native plugin entrypoints. Must stay inside the plugin package directory. |
|
||||
| `openclaw.runtimeExtensions` | Declares built JavaScript runtime entrypoints for installed packages. Must stay inside the plugin package directory. |
|
||||
| `openclaw.setupEntry` | Lightweight setup-only entrypoint used during onboarding, deferred channel startup, and read-only channel status/SecretRef discovery. Must stay inside the plugin package directory. |
|
||||
| `openclaw.runtimeSetupEntry` | Declares the built JavaScript setup entrypoint for installed packages. Requires `setupEntry`, must exist, and must stay inside the plugin package directory. |
|
||||
| `openclaw.runtimeSetupEntry` | Declares the built JavaScript setup entrypoint for installed packages. Must stay inside the plugin package directory. |
|
||||
| `openclaw.channel` | Cheap channel catalog metadata like labels, docs paths, aliases, and selection copy. |
|
||||
| `openclaw.channel.commands` | Static native command and native skill auto-default metadata used by config, audit, and command-list surfaces before channel runtime loads. |
|
||||
| `openclaw.channel.configuredState` | Lightweight configured-state checker metadata that can answer "does env-only setup already exist?" without loading the full channel runtime. |
|
||||
|
||||
@@ -294,9 +294,9 @@ supports `${ENV_VAR}` expansion:
|
||||
## Runtime dependencies
|
||||
|
||||
`memory-lancedb` depends on the native `@lancedb/lancedb` package. Packaged
|
||||
OpenClaw treats that package as part of the plugin package. Gateway startup
|
||||
does not repair plugin dependencies; if the dependency is missing, reinstall or
|
||||
update the plugin package and restart the Gateway.
|
||||
OpenClaw installs first try the bundled runtime dependency and can repair the
|
||||
plugin runtime dependency under OpenClaw state when the bundled import is not
|
||||
available.
|
||||
|
||||
If an older install logs a missing `dist/package.json` or missing
|
||||
`@lancedb/lancedb` error during plugin load, upgrade OpenClaw and restart the
|
||||
|
||||
@@ -63,26 +63,6 @@ Harnesses may use the plan for decisions that need to match PI behavior, but
|
||||
should still treat it as host-owned attempt state. Do not mutate it or use it to
|
||||
switch providers/models inside a turn.
|
||||
|
||||
## Optional runtime warmup
|
||||
|
||||
Harnesses may implement `prepareReplyRuntime(params)` when they need to warm a
|
||||
native runtime before channels start accepting reply traffic. OpenClaw calls
|
||||
this hook during gateway reply-runtime readiness checks, after provider/model
|
||||
selection and auth-profile resolution but before the first channel-triggered
|
||||
reply.
|
||||
|
||||
Use it for additive startup work such as initializing a shared daemon client,
|
||||
probing a native app-server handshake, or validating harness-owned auth state.
|
||||
Do not use it to mutate transcripts, switch models, or start a user-visible
|
||||
turn. The hook receives:
|
||||
|
||||
- `config`: the resolved OpenClaw config
|
||||
- `agentDir`: the default agent directory for the reply runtime
|
||||
- `workspaceDir`: the resolved workspace for runtime/plugin loading
|
||||
- `provider` and `modelId`: the selected reply model ref components
|
||||
- `authProfileId`: the resolved harness auth profile when the selected harness
|
||||
owns auth forwarding
|
||||
|
||||
## Register a harness
|
||||
|
||||
**Import:** `openclaw/plugin-sdk/agent-harness`
|
||||
|
||||
@@ -28,12 +28,9 @@ JavaScript when available:
|
||||
`extensions` and `setupEntry` remain valid source entries for workspace and git
|
||||
checkout development. `runtimeExtensions` and `runtimeSetupEntry` are preferred
|
||||
when OpenClaw loads an installed package and let npm packages avoid runtime
|
||||
TypeScript compilation. Explicit runtime entries are required: `runtimeSetupEntry`
|
||||
requires `setupEntry`, and missing `runtimeExtensions` or `runtimeSetupEntry`
|
||||
artifacts fail install/discovery instead of silently falling back to source. If
|
||||
an installed package only declares a TypeScript source entry, OpenClaw will use a
|
||||
matching built `dist/*.js` peer when one exists, then fall back to the TypeScript
|
||||
source.
|
||||
TypeScript compilation. If an installed package only declares a TypeScript
|
||||
source entry, OpenClaw will use a matching built `dist/*.js` peer when one
|
||||
exists, then fall back to the TypeScript source.
|
||||
|
||||
All entry paths must stay inside the plugin package directory. Runtime entries
|
||||
and inferred built JavaScript peers do not make an escaping `extensions` or
|
||||
|
||||
@@ -355,8 +355,8 @@ Facade-loaded bundled plugin public surfaces (`api.ts`, `runtime-api.ts`,
|
||||
active runtime config snapshot when OpenClaw is already running. If no runtime
|
||||
snapshot exists yet, they fall back to the resolved config file on disk.
|
||||
Packaged bundled plugin facades should be loaded through OpenClaw's plugin
|
||||
facade loaders; direct imports from `dist/extensions/...` bypass the manifest
|
||||
and runtime sidecar checks that packaged installs use for plugin-owned code.
|
||||
facade loaders; direct imports from `dist/extensions/...` bypass staged runtime
|
||||
dependency mirrors that packaged installs use for plugin-owned dependencies.
|
||||
|
||||
Provider plugins can expose a narrow plugin-local contract barrel when a
|
||||
helper is intentionally provider-specific and does not belong in a generic SDK
|
||||
|
||||
@@ -513,14 +513,14 @@ openclaw plugins install <package-name>
|
||||
```
|
||||
|
||||
<Info>
|
||||
For npm-sourced installs, `openclaw plugins install` installs the package under `~/.openclaw/npm` with lifecycle scripts disabled. Keep plugin dependency trees pure JS/TS and avoid packages that require `postinstall` builds.
|
||||
For npm-sourced installs, `openclaw plugins install` runs project-local `npm install --ignore-scripts` (no lifecycle scripts), ignoring inherited global npm install settings. Keep plugin dependency trees pure JS/TS and avoid packages that require `postinstall` builds.
|
||||
</Info>
|
||||
|
||||
<Note>
|
||||
Gateway startup does not install plugin dependencies. npm/git/ClawHub install flows own dependency convergence; local plugins must already have their dependencies installed.
|
||||
Bundled OpenClaw-owned plugins are the only startup repair exception: when a packaged install sees one enabled by plugin config, legacy channel config, or its bundled default-enabled manifest, startup installs that plugin's missing runtime dependencies before import. Operators can inspect or repair that stage with `openclaw plugins deps`. Third-party plugins should not rely on startup installs; keep using the explicit plugin installer.
|
||||
</Note>
|
||||
|
||||
Bundled package metadata is explicit, not inferred from built JavaScript at gateway startup. Runtime dependencies belong in the plugin package that owns them; packaged OpenClaw startup never repairs or mirrors plugin dependencies.
|
||||
Bundled package-level runtime deps are explicit metadata, not inferred from built JavaScript at gateway startup. If a shared OpenClaw root dependency must be available inside the external bundled-plugin runtime mirror, declare it in `openclaw.bundle.mirroredRootRuntimeDependencies` in the root package manifest.
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -479,12 +479,9 @@ Legacy `plugins.entries.openai.config.personality` is still read as a compatibil
|
||||
| Format | `messages.tts.providers.openai.responseFormat` | `opus` for voice notes, `mp3` for files |
|
||||
| API key | `messages.tts.providers.openai.apiKey` | Falls back to `OPENAI_API_KEY` |
|
||||
| Base URL | `messages.tts.providers.openai.baseUrl` | `https://api.openai.com/v1` |
|
||||
| Extra body | `messages.tts.providers.openai.extraBody` / `extra_body` | (unset) |
|
||||
|
||||
Available models: `gpt-4o-mini-tts`, `tts-1`, `tts-1-hd`. Available voices: `alloy`, `ash`, `ballad`, `cedar`, `coral`, `echo`, `fable`, `juniper`, `marin`, `onyx`, `nova`, `sage`, `shimmer`, `verse`.
|
||||
|
||||
`extraBody` is merged into `/audio/speech` request JSON after OpenClaw's generated fields, so use it for OpenAI-compatible endpoints that require additional keys such as `lang`. Prototype keys are ignored.
|
||||
|
||||
```json5
|
||||
{
|
||||
messages: {
|
||||
|
||||
@@ -211,10 +211,11 @@ Validation` or from the `main`/release workflow ref so workflow logic and
|
||||
- npm release preflight fails closed unless the tarball includes both
|
||||
`dist/control-ui/index.html` and a non-empty `dist/control-ui/assets/` payload
|
||||
so we do not ship an empty browser dashboard again
|
||||
- Post-publish verification also checks that published plugin entrypoints and
|
||||
package metadata are present in the installed registry layout. A release that
|
||||
ships missing plugin runtime payloads fails the postpublish verifier and
|
||||
cannot be promoted to `latest`.
|
||||
- Post-publish verification also checks that the published registry install
|
||||
contains non-empty bundled plugin runtime deps under the root `dist/*`
|
||||
layout. A release that ships with missing or empty bundled plugin
|
||||
dependency payloads fails the postpublish verifier and cannot be promoted
|
||||
to `latest`.
|
||||
- `pnpm test:install:smoke` also enforces the npm pack `unpackedSize` budget on
|
||||
the candidate update tarball, so installer e2e catches accidental pack bloat
|
||||
before the release publish path
|
||||
@@ -276,7 +277,7 @@ ref once as `release-package-under-test` and reuses that artifact in both
|
||||
release-path Docker checks and Package Acceptance. This keeps all
|
||||
package-facing boxes on the same bytes and avoids repeated package builds.
|
||||
The cross-OS OpenAI install smoke uses `OPENCLAW_CROSS_OS_OPENAI_MODEL` when the
|
||||
repo/org variable is set, otherwise `openai/gpt-5.5`, because this lane is
|
||||
repo/org variable is set, otherwise `openai/gpt-5.4-mini`, because this lane is
|
||||
proving package install, onboarding, gateway startup, and one live agent turn
|
||||
rather than benchmarking the slowest default model. The broader live provider
|
||||
matrix remains the place for model-specific coverage.
|
||||
@@ -369,8 +370,13 @@ Release Docker coverage includes:
|
||||
`plugins-runtime-install-a`, `plugins-runtime-install-b`,
|
||||
`plugins-runtime-install-c`, `plugins-runtime-install-d`,
|
||||
`plugins-runtime-install-e`, `plugins-runtime-install-f`,
|
||||
`plugins-runtime-install-g`, and `plugins-runtime-install-h`
|
||||
`plugins-runtime-install-g`, `plugins-runtime-install-h`,
|
||||
`bundled-channels-core`, `bundled-channels-update-a`,
|
||||
`bundled-channels-update-discord`, `bundled-channels-update-b`, and
|
||||
`bundled-channels-contracts`
|
||||
- OpenWebUI coverage inside the `plugins-runtime-services` chunk when requested
|
||||
- split bundled-channel dependency lanes across channel-smoke, update-target,
|
||||
and setup/runtime contract chunks instead of one large bundled-channel job
|
||||
- split bundled plugin install/uninstall lanes
|
||||
`bundled-plugin-install-uninstall-0` through
|
||||
`bundled-plugin-install-uninstall-23`
|
||||
@@ -424,11 +430,11 @@ Supported candidate sources:
|
||||
|
||||
`OpenClaw Release Checks` runs Package Acceptance with `source=ref`,
|
||||
`package_ref=<release-ref>`, `suite_profile=custom`,
|
||||
`docker_lanes=plugins-offline plugin-update`, and `telegram_mode=mock-openai`.
|
||||
The release-path Docker chunks cover the overlapping install, update, and
|
||||
plugin-update lanes; Package Acceptance keeps offline plugin fixtures, plugin
|
||||
update, and Telegram package QA against the same resolved tarball. It is the
|
||||
GitHub-native
|
||||
`docker_lanes=bundled-channel-deps-compat plugins-offline`, and
|
||||
`telegram_mode=mock-openai`. The release-path Docker chunks cover the
|
||||
overlapping install, update, and plugin-update lanes; Package Acceptance keeps
|
||||
artifact-native bundled-channel compat, offline plugin fixtures, and Telegram
|
||||
package QA against the same resolved tarball. It is the GitHub-native
|
||||
replacement for most of the package/update coverage that previously required
|
||||
Parallels. Cross-OS release checks still matter for OS-specific onboarding,
|
||||
installer, and platform behavior, but package/update product validation should
|
||||
|
||||
@@ -53,11 +53,11 @@ or Docker-facing stages need it.
|
||||
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Release target | **Job:** `Resolve target ref`<br />**Backing workflow:** none<br />**Tests:** selected ref, optional expected SHA, profile, rerun group, and focused live suite filter.<br />**Rerun:** `rerun_group=release-checks`. |
|
||||
| Package artifact | **Job:** `Prepare release package artifact`<br />**Backing workflow:** none<br />**Tests:** packs or resolves one candidate tarball and uploads `release-package-under-test` for downstream package-facing checks.<br />**Rerun:** the affected package, cross-OS, or live/E2E group. |
|
||||
| Install smoke | **Job:** `Run install smoke`<br />**Backing workflow:** `Install Smoke`<br />**Tests:** full install path with root Dockerfile smoke image reuse, QR package install, root and gateway Docker smokes, installer Docker tests, Bun global install image-provider smoke, and fast bundled-plugin install/uninstall E2E.<br />**Rerun:** `rerun_group=install-smoke`. |
|
||||
| Install smoke | **Job:** `Run install smoke`<br />**Backing workflow:** `Install Smoke`<br />**Tests:** full install path with root Dockerfile smoke image reuse, QR package install, root and gateway Docker smokes, installer Docker tests, Bun global install image-provider smoke, and fast bundled-plugin Docker E2E.<br />**Rerun:** `rerun_group=install-smoke`. |
|
||||
| Cross-OS | **Job:** `cross_os_release_checks`<br />**Backing workflow:** `OpenClaw Cross-OS Release Checks (Reusable)`<br />**Tests:** fresh and upgrade lanes on Linux, Windows, and macOS for the selected provider and mode, using the candidate tarball plus a baseline package.<br />**Rerun:** `rerun_group=cross-os`. |
|
||||
| Repo and live E2E | **Job:** `Run repo/live E2E validation`<br />**Backing workflow:** `OpenClaw Live And E2E Checks (Reusable)`<br />**Tests:** repository E2E, live cache, OpenAI websocket streaming, native live provider and plugin shards, and Docker-backed live model/backend/gateway harnesses selected by `release_profile`.<br />**Rerun:** `rerun_group=live-e2e`, optionally with `live_suite_filter`. |
|
||||
| Docker release path | **Job:** `Run Docker release-path validation`<br />**Backing workflow:** `OpenClaw Live And E2E Checks (Reusable)`<br />**Tests:** release-path Docker chunks against the shared package artifact.<br />**Rerun:** `rerun_group=live-e2e`. |
|
||||
| Package Acceptance | **Job:** `Run package acceptance`<br />**Backing workflow:** `Package Acceptance`<br />**Tests:** offline plugin package fixtures, plugin update, and mock-OpenAI Telegram package acceptance against the same tarball.<br />**Rerun:** `rerun_group=package`. |
|
||||
| Package Acceptance | **Job:** `Run package acceptance`<br />**Backing workflow:** `Package Acceptance`<br />**Tests:** artifact-native bundled-channel dependency compatibility, offline plugin package fixtures, and mock-OpenAI Telegram package acceptance against the same tarball.<br />**Rerun:** `rerun_group=package`. |
|
||||
| QA parity | **Job:** `Run QA Lab parity lane` and `Run QA Lab parity report`<br />**Backing workflow:** direct jobs<br />**Tests:** candidate and baseline agentic parity packs, then the parity report.<br />**Rerun:** `rerun_group=qa-parity` or `rerun_group=qa`. |
|
||||
| QA live Matrix | **Job:** `Run QA Lab live Matrix lane`<br />**Backing workflow:** direct job<br />**Tests:** fast live Matrix QA profile in the `qa-live-shared` environment.<br />**Rerun:** `rerun_group=qa-live` or `rerun_group=qa`. |
|
||||
| QA live Telegram | **Job:** `Run QA Lab live Telegram lane`<br />**Backing workflow:** direct job<br />**Tests:** live Telegram QA with Convex CI credential leases.<br />**Rerun:** `rerun_group=qa-live` or `rerun_group=qa`. |
|
||||
@@ -68,15 +68,18 @@ or Docker-facing stages need it.
|
||||
The Docker release-path stage runs these chunks when `live_suite_filter` is
|
||||
empty:
|
||||
|
||||
| Chunk | Coverage |
|
||||
| --------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `core` | Core Docker release-path smoke lanes. |
|
||||
| `package-update-openai` | OpenAI package install and update behavior. |
|
||||
| `package-update-anthropic` | Anthropic package install and update behavior. |
|
||||
| `package-update-core` | Provider-neutral package and update behavior. |
|
||||
| `plugins-runtime-plugins` | Plugin runtime lanes that exercise plugin behavior. |
|
||||
| `plugins-runtime-services` | Service-backed plugin runtime lanes; includes OpenWebUI when requested. |
|
||||
| `plugins-runtime-install-a` through `plugins-runtime-install-h` | Plugin install/runtime batches split for parallel release validation. |
|
||||
| Chunk | Coverage |
|
||||
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| `core` | Core Docker release-path smoke lanes. |
|
||||
| `package-update-openai` | OpenAI package install and update behavior. |
|
||||
| `package-update-anthropic` | Anthropic package install and update behavior. |
|
||||
| `package-update-core` | Provider-neutral package and update behavior. |
|
||||
| `plugins-runtime-plugins` | Plugin runtime lanes that exercise plugin behavior. |
|
||||
| `plugins-runtime-services` | Service-backed plugin runtime lanes; includes OpenWebUI when requested. |
|
||||
| `plugins-runtime-install-a` through `plugins-runtime-install-h` | Plugin install/runtime batches split for parallel release validation. |
|
||||
| `bundled-channels-core` | Bundled channel Docker behavior. |
|
||||
| `bundled-channels-update-a`, `bundled-channels-update-discord`, `bundled-channels-update-b` | Bundled channel update behavior. |
|
||||
| `bundled-channels-contracts` | Bundled channel contract checks in the Docker release path. |
|
||||
|
||||
Use targeted `docker_lanes=<lane[,lane]>` on the reusable live/E2E workflow when
|
||||
only one Docker lane failed. The release artifacts include per-lane rerun
|
||||
|
||||
@@ -284,7 +284,7 @@ For custom OpenAI-compatible endpoints or overriding provider defaults:
|
||||
| `local.modelCacheDir` | `string` | node-llama-cpp default | Cache dir for downloaded models |
|
||||
| `local.contextSize` | `number \| "auto"` | `4096` | Context window size for the embedding context. 4096 covers typical chunks (128–512 tokens) while bounding non-weight VRAM. Lower to 1024–2048 on constrained hosts. `"auto"` uses the model's trained maximum — not recommended for 8B+ models (Qwen3-Embedding-8B: 40 960 tokens → ~32 GB VRAM vs ~8.8 GB at 4096). |
|
||||
|
||||
Default model: `embeddinggemma-300m-qat-Q8_0.gguf` (~0.6 GB, auto-downloaded). Source checkouts still require native build approval: `pnpm approve-builds` then `pnpm rebuild node-llama-cpp`.
|
||||
Default model: `embeddinggemma-300m-qat-Q8_0.gguf` (~0.6 GB, auto-downloaded). Packaged installs repair the native `node-llama-cpp` runtime through managed plugin runtime deps when `provider: "local"` is configured. Source checkouts still require native build approval: `pnpm approve-builds` then `pnpm rebuild node-llama-cpp`.
|
||||
|
||||
Use the standalone CLI to verify the same provider path the Gateway uses:
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ title: "Tests"
|
||||
- CLI backend live Docker probes can be run as focused lanes, for example `pnpm test:docker:live-cli-backend:codex`, `pnpm test:docker:live-cli-backend:codex:resume`, or `pnpm test:docker:live-cli-backend:codex:mcp`. Claude and Gemini have matching `:resume` and `:mcp` aliases.
|
||||
- `pnpm test:docker:openwebui`: Starts Dockerized OpenClaw + Open WebUI, signs in through Open WebUI, checks `/api/models`, then runs a real proxied chat through `/api/chat/completions`. Requires a usable live model key (for example OpenAI in `~/.profile`), pulls an external Open WebUI image, and is not expected to be CI-stable like the normal unit/e2e suites.
|
||||
- `pnpm test:docker:mcp-channels`: Starts a seeded Gateway container and a second client container that spawns `openclaw mcp serve`, then verifies routed conversation discovery, transcript reads, attachment metadata, live event queue behavior, outbound send routing, and Claude-style channel + permission notifications over the real stdio bridge. The Claude notification assertion reads the raw stdio MCP frames directly so the smoke reflects what the bridge actually emits.
|
||||
- `pnpm test:docker:upgrade-survivor`: Installs the packed OpenClaw tarball over a dirty old-user fixture, runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks that agents, channel config, plugin allowlists, workspace/session files, stale legacy plugin dependency state, startup, and RPC status survive.
|
||||
- `pnpm test:docker:published-upgrade-survivor`: Installs `openclaw@latest` by default, seeds realistic existing-user files without live provider or channel keys, configures that baseline with a baked `openclaw config set` command recipe, updates that published install to the packed OpenClaw tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks that configured intents, workspace/session files, stale plugin config and legacy dependency state, startup, `/healthz`, `/readyz`, and RPC status survive or repair cleanly. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, expand an exact matrix with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, or add scenario fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues`; Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
|
||||
- `pnpm test:docker:upgrade-survivor`: Installs the packed OpenClaw tarball over a dirty old-user fixture, runs package update plus non-interactive doctor without live provider or channel keys, then starts a loopback Gateway and checks that agents, channel config, plugin allowlists, workspace/session files, stale plugin runtime-deps state, startup, and RPC status survive.
|
||||
- `pnpm test:docker:published-upgrade-survivor`: Installs `openclaw@latest` by default, seeds realistic existing-user files without live provider or channel keys, configures that baseline with a baked `openclaw config set` command recipe, updates that published install to the packed OpenClaw tarball, runs non-interactive doctor, writes `.artifacts/upgrade-survivor/summary.json`, then starts a loopback Gateway and checks that configured intents, workspace/session files, stale plugin config/runtime-deps state, startup, `/healthz`, `/readyz`, and RPC status survive or repair cleanly. Override one baseline with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPEC`, expand an exact matrix with `OPENCLAW_UPGRADE_SURVIVOR_BASELINE_SPECS`, or add scenario fixtures with `OPENCLAW_UPGRADE_SURVIVOR_SCENARIOS=reported-issues`; Package Acceptance exposes those as `published_upgrade_survivor_baseline`, `published_upgrade_survivor_baselines`, and `published_upgrade_survivor_scenarios`.
|
||||
|
||||
## Local PR gate
|
||||
|
||||
|
||||
@@ -809,8 +809,8 @@ permission modes, see
|
||||
| `ACP is disabled by policy (acp.enabled=false)` | ACP globally disabled. | Set `acp.enabled=true`. |
|
||||
| `ACP dispatch is disabled by policy (acp.dispatch.enabled=false)` | Automatic dispatch from normal thread messages disabled. | Set `acp.dispatch.enabled=true` to resume automatic thread routing; explicit `sessions_spawn({ runtime: "acp" })` calls still work. |
|
||||
| `ACP agent "<id>" is not allowed by policy` | Agent not in allowlist. | Use allowed `agentId` or update `acp.allowedAgents`. |
|
||||
| `/acp doctor` reports backend not ready right after startup | Backend plugin is missing, disabled, blocked by allow/deny policy, or its configured executable is unavailable. | Install/enable the backend plugin, rerun `/acp doctor`, and inspect the backend install or policy error if it stays unhealthy. |
|
||||
| Harness command not found | Adapter CLI is not installed, the external plugin is missing, or first-run `npx` fetch failed for a non-Codex adapter. | Run `/acp doctor`, install/prewarm the adapter on the Gateway host, or configure the acpx agent command explicitly. |
|
||||
| `/acp doctor` reports backend not ready right after startup | Plugin dependency probe or self-repair is still running. | Wait briefly and rerun `/acp doctor`; if it stays unhealthy, inspect the backend install error and plugin allow/deny policy. |
|
||||
| Harness command not found | Adapter CLI is not installed, staged plugin deps are missing, or first-run `npx` fetch failed for a non-Codex adapter. | Run `/acp doctor`, repair plugin dependencies, install/prewarm the adapter on the Gateway host, or configure the acpx agent command explicitly. |
|
||||
| Model-not-found from the harness | Model id is valid for another provider/harness but not this ACP target. | Use a model listed by that harness, configure the model in the harness, or omit the override. |
|
||||
| Vendor auth error from the harness | OpenClaw is healthy, but the target CLI/provider is not logged in. | Log in or provide the required provider key on the Gateway host environment. |
|
||||
| `Unable to resolve session target: ...` | Bad key/id/label token. | Run `/acp sessions`, copy exact key/label, retry. |
|
||||
|
||||
@@ -96,10 +96,10 @@ What still needs Playwright:
|
||||
Element screenshots also reject `--full-page`; the route returns `fullPage is
|
||||
not supported for element screenshots`.
|
||||
|
||||
If you see `Playwright is not available in this gateway build`, the packaged
|
||||
Gateway is missing the core browser runtime dependency. Reinstall or update
|
||||
OpenClaw, then restart the gateway. For Docker, also install the Chromium
|
||||
browser binaries as shown below.
|
||||
If you see `Playwright is not available in this gateway build`, repair the
|
||||
bundled browser plugin runtime dependencies so `playwright-core` is installed,
|
||||
then restart the gateway. For packaged installs, run `openclaw doctor --fix`.
|
||||
For Docker, also install the Chromium browser binaries as shown below.
|
||||
|
||||
#### Docker Playwright install
|
||||
|
||||
|
||||
@@ -26,11 +26,6 @@ When enabled, the plugin prepends concise usage guidance into system-prompt spac
|
||||
## Quick start
|
||||
|
||||
<Steps>
|
||||
<Step title="Install the plugin">
|
||||
```bash
|
||||
openclaw plugins install diffs
|
||||
```
|
||||
</Step>
|
||||
<Step title="Enable the plugin">
|
||||
```json5
|
||||
{
|
||||
|
||||
@@ -96,15 +96,22 @@ Gateway startup skips plugin discovery/load work and `openclaw doctor` preserves
|
||||
the disabled plugin config instead of auto-removing it. Re-enable plugins before
|
||||
running doctor cleanup if you want stale plugin ids removed.
|
||||
|
||||
Plugin dependency installation happens only during explicit install/update or
|
||||
doctor repair flows. Gateway startup, config reload, and runtime inspection do
|
||||
not run package managers or repair dependency trees. Local plugins must already
|
||||
have their dependencies installed, while npm, git, and ClawHub plugins are
|
||||
installed under OpenClaw's managed plugin roots with package-local
|
||||
dependencies. External plugins and custom load paths must still be installed
|
||||
through `openclaw plugins install`.
|
||||
See [Plugin dependency resolution](/plugins/dependency-resolution) for the
|
||||
install-time lifecycle.
|
||||
Packaged OpenClaw installs do not eagerly install every bundled plugin's
|
||||
runtime dependency tree. When a bundled OpenClaw-owned plugin is active from
|
||||
plugin config, legacy channel config, or a default-enabled manifest, startup
|
||||
repairs only that plugin's declared runtime dependencies before importing it.
|
||||
Persisted channel auth state alone does not activate a bundled channel for
|
||||
Gateway startup runtime-dependency repair.
|
||||
Explicit disablement still wins: `plugins.entries.<id>.enabled: false`,
|
||||
`plugins.deny`, `plugins.enabled: false`, and `channels.<id>.enabled: false`
|
||||
prevent automatic bundled runtime-dependency repair for that plugin/channel.
|
||||
A non-empty `plugins.allow` also bounds default-enabled bundled runtime-dependency
|
||||
repair; explicit bundled channel enablement (`channels.<id>.enabled: true`) can
|
||||
still repair that channel's plugin dependencies.
|
||||
External plugins and custom load paths must still be installed through
|
||||
`openclaw plugins install`.
|
||||
See [Plugin dependency resolution](/plugins/dependency-resolution) for the full
|
||||
planning and staging lifecycle.
|
||||
|
||||
## Plugin types
|
||||
|
||||
@@ -130,9 +137,7 @@ peer such as `src/index.ts` to `dist/index.js`.
|
||||
Use `openclaw.runtimeExtensions` when published runtime files do not live at the
|
||||
same paths as the source entries. When present, `runtimeExtensions` must contain
|
||||
exactly one entry for every `extensions` entry. Mismatched lists fail install and
|
||||
plugin discovery rather than silently falling back to source paths. If you also
|
||||
publish `openclaw.setupEntry`, use `openclaw.runtimeSetupEntry` for its built
|
||||
JavaScript peer; that file is required when declared.
|
||||
plugin discovery rather than silently falling back to source paths.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -187,7 +192,7 @@ current OpenClaw or a local checkout until a newer npm package is published.
|
||||
|
||||
<Accordion title="Memory plugins">
|
||||
- `memory-core` — bundled memory search (default via `plugins.slots.memory`)
|
||||
- `memory-lancedb` — LanceDB-backed long-term memory with auto-recall/capture (set `plugins.slots.memory = "memory-lancedb"`)
|
||||
- `memory-lancedb` — install-on-demand long-term memory with auto-recall/capture (set `plugins.slots.memory = "memory-lancedb"`)
|
||||
|
||||
See [Memory LanceDB](/plugins/memory-lancedb) for OpenAI-compatible
|
||||
embedding setup, Ollama examples, recall limits, and troubleshooting.
|
||||
@@ -334,37 +339,6 @@ do not run in live chat traffic, check these first:
|
||||
Gateway session/status surfaces and, when debugging provider payloads, start
|
||||
the Gateway with `--raw-stream --raw-stream-path <path>`.
|
||||
|
||||
### Slow plugin tool setup
|
||||
|
||||
If agent turns appear to stall while preparing tools, enable trace logging and
|
||||
check for plugin tool factory timing lines:
|
||||
|
||||
```bash
|
||||
openclaw config set logging.level trace
|
||||
openclaw logs --follow
|
||||
```
|
||||
|
||||
Look for:
|
||||
|
||||
```text
|
||||
[trace:plugin-tools] factory timings ...
|
||||
```
|
||||
|
||||
The summary lists total factory time and the slowest plugin tool factories,
|
||||
including plugin id, declared tool names, result shape, and whether the tool is
|
||||
optional. Slow lines are promoted to warnings when a single factory takes at
|
||||
least 1s or total plugin tool factory prep takes at least 5s.
|
||||
|
||||
If one plugin dominates the timing, inspect its runtime registrations:
|
||||
|
||||
```bash
|
||||
openclaw plugins inspect <plugin-id> --runtime --json
|
||||
```
|
||||
|
||||
Then update, reinstall, or disable that plugin. Plugin authors should move
|
||||
expensive dependency loading behind the tool execution path instead of doing it
|
||||
inside the tool factory.
|
||||
|
||||
### Duplicate channel or tool ownership
|
||||
|
||||
Symptoms:
|
||||
|
||||
@@ -892,7 +892,6 @@ OpenAI and ElevenLabs output formats are fixed per channel as listed above.
|
||||
<ParamField path="model" type="string">OpenAI TTS model id (e.g. `gpt-4o-mini-tts`).</ParamField>
|
||||
<ParamField path="voice" type="string">Voice name (e.g. `alloy`, `cedar`).</ParamField>
|
||||
<ParamField path="instructions" type="string">Explicit OpenAI `instructions` field. When set, persona prompt fields are **not** auto-mapped.</ParamField>
|
||||
<ParamField path="extraBody / extra_body" type="Record<string, unknown>">Extra JSON fields merged into `/audio/speech` request bodies after generated OpenAI TTS fields. Use this for OpenAI-compatible endpoints such as Kokoro that require provider-specific keys like `lang`; unsafe prototype keys are ignored.</ParamField>
|
||||
<ParamField path="baseUrl" type="string">
|
||||
Override the OpenAI TTS endpoint. Resolution order: config → `OPENAI_TTS_BASE_URL` → `https://api.openai.com/v1`. Non-default values are treated as OpenAI-compatible TTS endpoints, so custom model and voice names are accepted.
|
||||
</ParamField>
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
],
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ describe("prepareAcpxCodexAuthConfig", () => {
|
||||
});
|
||||
|
||||
const wrapper = await fs.readFile(generated.wrapperPath, "utf8");
|
||||
expect(wrapper).toContain('"@agentclientprotocol/claude-agent-acp@0.31.4"');
|
||||
expect(wrapper).toContain('"@agentclientprotocol/claude-agent-acp@0.31.1"');
|
||||
expect(wrapper).toContain('"--", "claude-agent-acp"');
|
||||
expect(wrapper).not.toContain("@agentclientprotocol/claude-agent-acp@^0.31.0");
|
||||
expect(wrapper).not.toContain("@agentclientprotocol/claude-agent-acp@0.31.0");
|
||||
@@ -379,7 +379,7 @@ describe("prepareAcpxCodexAuthConfig", () => {
|
||||
rawConfig: {
|
||||
agents: {
|
||||
claude: {
|
||||
command: "npx -y @agentclientprotocol/claude-agent-acp@0.31.4 --permission-mode bypass",
|
||||
command: "npx -y @agentclientprotocol/claude-agent-acp@0.31.1 --permission-mode bypass",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -425,7 +425,7 @@ describe("prepareAcpxCodexAuthConfig", () => {
|
||||
const root = await makeTempDir();
|
||||
const stateDir = path.join(root, "state");
|
||||
const command =
|
||||
"node ./custom-claude-wrapper.mjs @agentclientprotocol/claude-agent-acp@0.31.4 --flag";
|
||||
"node ./custom-claude-wrapper.mjs @agentclientprotocol/claude-agent-acp@0.31.1 --flag";
|
||||
const pluginConfig = resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
agents: {
|
||||
|
||||
@@ -7,7 +7,7 @@ const CODEX_ACP_PACKAGE = "@zed-industries/codex-acp";
|
||||
const CODEX_ACP_PACKAGE_RANGE = "^0.12.0";
|
||||
const CODEX_ACP_BIN = "codex-acp";
|
||||
const CLAUDE_ACP_PACKAGE = "@agentclientprotocol/claude-agent-acp";
|
||||
const CLAUDE_ACP_PACKAGE_VERSION = "0.31.4";
|
||||
const CLAUDE_ACP_PACKAGE_VERSION = "0.31.1";
|
||||
const CLAUDE_ACP_BIN = "claude-agent-acp";
|
||||
const RUN_CONFIGURED_COMMAND_SENTINEL = "--openclaw-run-configured";
|
||||
const requireFromHere = createRequire(import.meta.url);
|
||||
@@ -113,7 +113,7 @@ async function resolveInstalledAcpPackageBinPath(
|
||||
|
||||
async function resolveInstalledCodexAcpBinPath(): Promise<string | undefined> {
|
||||
// Keep OpenClaw's isolated CODEX_HOME wrapper, but launch the plugin-local
|
||||
// Codex ACP adapter when the package dependency is available.
|
||||
// Codex ACP adapter when runtime-deps staging made it available.
|
||||
return await resolveInstalledAcpPackageBinPath(CODEX_ACP_PACKAGE, CODEX_ACP_BIN);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { z } from "openclaw/plugin-sdk/zod";
|
||||
|
||||
const ACPX_PERMISSION_MODES = ["approve-all", "approve-reads", "deny-all"] as const;
|
||||
export const ACPX_PERMISSION_MODES = ["approve-all", "approve-reads", "deny-all"] as const;
|
||||
export type AcpxPermissionMode = (typeof ACPX_PERMISSION_MODES)[number];
|
||||
|
||||
const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"] as const;
|
||||
export const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"] as const;
|
||||
export type AcpxNonInteractivePermissionPolicy = (typeof ACPX_NON_INTERACTIVE_POLICIES)[number];
|
||||
|
||||
export const DEFAULT_ACPX_TIMEOUT_SECONDS = 120;
|
||||
|
||||
@@ -13,10 +13,17 @@ import type {
|
||||
AcpxMcpServer,
|
||||
ResolvedAcpxPluginConfig,
|
||||
} from "./config-schema.js";
|
||||
export { type ResolvedAcpxPluginConfig } from "./config-schema.js";
|
||||
export {
|
||||
type AcpxMcpServer,
|
||||
type AcpxNonInteractivePermissionPolicy,
|
||||
type AcpxPermissionMode,
|
||||
type AcpxPluginConfig,
|
||||
type McpServerConfig,
|
||||
type ResolvedAcpxPluginConfig,
|
||||
} from "./config-schema.js";
|
||||
|
||||
const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
|
||||
const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
|
||||
export const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
|
||||
export const ACPX_OPENCLAW_TOOLS_MCP_SERVER_NAME = "openclaw-tools";
|
||||
const requireFromHere = createRequire(import.meta.url);
|
||||
|
||||
function isAcpxPluginRoot(dir: string): boolean {
|
||||
@@ -139,7 +146,9 @@ function resolveTsxImportSpecifier(): string {
|
||||
}
|
||||
}
|
||||
|
||||
function resolvePluginToolsMcpServerConfig(moduleUrl: string = import.meta.url): McpServerConfig {
|
||||
export function resolvePluginToolsMcpServerConfig(
|
||||
moduleUrl: string = import.meta.url,
|
||||
): McpServerConfig {
|
||||
const pluginRoot = resolveAcpxPluginRoot(moduleUrl);
|
||||
const openClawRoot = resolveOpenClawRoot(pluginRoot);
|
||||
const distEntry = path.join(openClawRoot, "dist", "mcp", "plugin-tools-serve.js");
|
||||
@@ -156,7 +165,9 @@ function resolvePluginToolsMcpServerConfig(moduleUrl: string = import.meta.url):
|
||||
};
|
||||
}
|
||||
|
||||
function resolveOpenClawToolsMcpServerConfig(moduleUrl: string = import.meta.url): McpServerConfig {
|
||||
export function resolveOpenClawToolsMcpServerConfig(
|
||||
moduleUrl: string = import.meta.url,
|
||||
): McpServerConfig {
|
||||
const pluginRoot = resolveAcpxPluginRoot(moduleUrl);
|
||||
const openClawRoot = resolveOpenClawRoot(pluginRoot);
|
||||
const distEntry = path.join(openClawRoot, "dist", "mcp", "openclaw-tools-serve.js");
|
||||
|
||||
@@ -4,17 +4,23 @@ import { describe, expect, it } from "vitest";
|
||||
type AcpxPackageManifest = {
|
||||
dependencies?: Record<string, string>;
|
||||
devDependencies?: Record<string, string>;
|
||||
openclaw?: {
|
||||
bundle?: {
|
||||
stageRuntimeDependencies?: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
describe("acpx package manifest", () => {
|
||||
it("keeps runtime dependencies in the package manifest", () => {
|
||||
it("opts into staging bundled runtime dependencies", () => {
|
||||
const packageJson = JSON.parse(
|
||||
fs.readFileSync(new URL("../package.json", import.meta.url), "utf8"),
|
||||
) as AcpxPackageManifest;
|
||||
|
||||
expect(packageJson.dependencies?.acpx).toBeDefined();
|
||||
expect(packageJson.dependencies?.["@zed-industries/codex-acp"]).toBe("0.12.0");
|
||||
expect(packageJson.dependencies?.["@agentclientprotocol/claude-agent-acp"]).toBe("0.31.4");
|
||||
expect(packageJson.dependencies?.["@agentclientprotocol/claude-agent-acp"]).toBe("0.31.1");
|
||||
expect(packageJson.devDependencies?.["@agentclientprotocol/claude-agent-acp"]).toBeUndefined();
|
||||
expect(packageJson.openclaw?.bundle?.stageRuntimeDependencies).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,7 +100,7 @@ export function createMantleAnthropicStreamFn(deps?: {
|
||||
),
|
||||
});
|
||||
const base = buildMantleAnthropicBaseOptions(model, options, apiKey);
|
||||
// Plugin package deps can give this plugin a distinct physical SDK copy.
|
||||
// Staged plugin runtime deps can give this plugin a distinct physical SDK copy.
|
||||
// The client API is the same, but the SDK class private field makes types nominal.
|
||||
const streamClient = client as unknown as AnthropicStreamClient;
|
||||
if (!options?.reasoning || requiresDefaultSampling(model.id)) {
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
},
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
|
||||
@@ -5,14 +5,17 @@
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock": "3.1041.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1041.0",
|
||||
"@aws-sdk/credential-provider-node": "3.972.39"
|
||||
"@aws-sdk/client-bedrock": "3.1040.0",
|
||||
"@aws-sdk/client-bedrock-runtime": "3.1040.0",
|
||||
"@aws-sdk/credential-provider-node": "3.972.38"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
},
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
},
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
"@openclaw/plugin-sdk": "workspace:*"
|
||||
},
|
||||
"openclaw": {
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
},
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildMessagePlaceholder,
|
||||
isBlueBubblesAudioAttachment,
|
||||
normalizeWebhookMessage,
|
||||
normalizeWebhookReaction,
|
||||
} from "./monitor-normalize.js";
|
||||
import { normalizeWebhookMessage, normalizeWebhookReaction } from "./monitor-normalize.js";
|
||||
|
||||
function createFallbackDmPayload(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
@@ -145,62 +140,3 @@ describe("normalizeWebhookReaction", () => {
|
||||
expect(result?.action).toBe("added");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isBlueBubblesAudioAttachment", () => {
|
||||
it("detects audio by `audio/*` MIME type", () => {
|
||||
expect(isBlueBubblesAudioAttachment({ mimeType: "audio/x-m4a" })).toBe(true);
|
||||
expect(isBlueBubblesAudioAttachment({ mimeType: "audio/mp4" })).toBe(true);
|
||||
});
|
||||
|
||||
it("detects audio by Apple UTI even when MIME is missing", () => {
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "public.audio" })).toBe(true);
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "public.mpeg-4-audio" })).toBe(true);
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "com.apple.m4a-audio" })).toBe(true);
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "com.apple.coreaudio-format" })).toBe(true);
|
||||
});
|
||||
|
||||
it("treats UTI matching as case-insensitive", () => {
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "Public.Audio" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for image / video / unknown attachments", () => {
|
||||
expect(isBlueBubblesAudioAttachment({ mimeType: "image/jpeg" })).toBe(false);
|
||||
expect(isBlueBubblesAudioAttachment({ mimeType: "video/quicktime" })).toBe(false);
|
||||
expect(isBlueBubblesAudioAttachment({ uti: "public.jpeg" })).toBe(false);
|
||||
expect(isBlueBubblesAudioAttachment({})).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildMessagePlaceholder audio detection", () => {
|
||||
function makeMsg(attachments: Array<{ mimeType?: string; uti?: string }>) {
|
||||
return {
|
||||
text: "",
|
||||
senderId: "+15551234567",
|
||||
senderIdExplicit: false,
|
||||
isGroup: false,
|
||||
attachments,
|
||||
} as Parameters<typeof buildMessagePlaceholder>[0];
|
||||
}
|
||||
|
||||
it("emits <media:audio> for `audio/*` MIME (existing behavior)", () => {
|
||||
expect(buildMessagePlaceholder(makeMsg([{ mimeType: "audio/x-m4a" }]))).toContain(
|
||||
"<media:audio>",
|
||||
);
|
||||
});
|
||||
|
||||
it("emits <media:audio> for Apple `public.audio` UTI when MIME is missing", () => {
|
||||
expect(buildMessagePlaceholder(makeMsg([{ uti: "public.audio" }]))).toContain("<media:audio>");
|
||||
});
|
||||
|
||||
it("emits <media:audio> for Apple `com.apple.m4a-audio` UTI", () => {
|
||||
expect(buildMessagePlaceholder(makeMsg([{ uti: "com.apple.m4a-audio" }]))).toContain(
|
||||
"<media:audio>",
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to <media:attachment> for non-audio mixes", () => {
|
||||
expect(
|
||||
buildMessagePlaceholder(makeMsg([{ uti: "public.audio" }, { mimeType: "image/jpeg" }])),
|
||||
).toContain("<media:attachment>");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,32 +59,6 @@ export function extractAttachments(message: Record<string, unknown>): BlueBubble
|
||||
return out;
|
||||
}
|
||||
|
||||
// Apple UTIs used by BlueBubbles for voice notes / audio attachments. Webhook
|
||||
// payloads sometimes carry only a UTI without a normalized `audio/*` MIME
|
||||
// (notably iMessage voice notes recorded on macOS 26 Tahoe), so audio
|
||||
// detection must consult both. Intentionally narrow: covers what BB emits for
|
||||
// iMessage voice notes today (m4a/MPEG-4 audio). Broader UTIs like
|
||||
// `public.aiff-audio`, `public.wav`, `public.mp3` are not iMessage voice-note
|
||||
// formats and pull in `audio/*` MIME paths anyway.
|
||||
const APPLE_AUDIO_UTIS = new Set<string>([
|
||||
"public.audio",
|
||||
"public.mpeg-4-audio",
|
||||
"com.apple.m4a-audio",
|
||||
"com.apple.coreaudio-format",
|
||||
]);
|
||||
|
||||
export function isBlueBubblesAudioAttachment(attachment: BlueBubblesAttachment): boolean {
|
||||
const mime = attachment.mimeType?.trim().toLowerCase();
|
||||
if (mime && mime.startsWith("audio/")) {
|
||||
return true;
|
||||
}
|
||||
const uti = attachment.uti?.trim().toLowerCase();
|
||||
if (uti && APPLE_AUDIO_UTIS.has(uti)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildAttachmentPlaceholder(attachments: BlueBubblesAttachment[]): string {
|
||||
if (attachments.length === 0) {
|
||||
return "";
|
||||
@@ -92,7 +66,7 @@ function buildAttachmentPlaceholder(attachments: BlueBubblesAttachment[]): strin
|
||||
const mimeTypes = attachments.map((entry) => entry.mimeType ?? "");
|
||||
const allImages = mimeTypes.every((entry) => entry.startsWith("image/"));
|
||||
const allVideos = mimeTypes.every((entry) => entry.startsWith("video/"));
|
||||
const allAudio = attachments.every(isBlueBubblesAudioAttachment);
|
||||
const allAudio = mimeTypes.every((entry) => entry.startsWith("audio/"));
|
||||
const tag = allImages
|
||||
? "<media:image>"
|
||||
: allVideos
|
||||
|
||||
@@ -3,33 +3,33 @@ import {
|
||||
getChannelSurface,
|
||||
type ResolverContext,
|
||||
type SecretDefaults,
|
||||
type SecretTargetRegistryEntry,
|
||||
} from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
||||
|
||||
export const secretTargetRegistryEntries: import("openclaw/plugin-sdk/channel-secret-basic-runtime").SecretTargetRegistryEntry[] =
|
||||
[
|
||||
{
|
||||
id: "channels.bluebubbles.accounts.*.password",
|
||||
targetType: "channels.bluebubbles.accounts.*.password",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.bluebubbles.accounts.*.password",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "channels.bluebubbles.password",
|
||||
targetType: "channels.bluebubbles.password",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.bluebubbles.password",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
];
|
||||
export const secretTargetRegistryEntries = [
|
||||
{
|
||||
id: "channels.bluebubbles.accounts.*.password",
|
||||
targetType: "channels.bluebubbles.accounts.*.password",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.bluebubbles.accounts.*.password",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "channels.bluebubbles.password",
|
||||
targetType: "channels.bluebubbles.password",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "channels.bluebubbles.password",
|
||||
secretShape: "secret_input",
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
] satisfies SecretTargetRegistryEntry[];
|
||||
|
||||
export function collectRuntimeConfigAssignments(params: {
|
||||
config: { channels?: Record<string, unknown> };
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
"openclaw": {
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
],
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"commander": "^14.0.3",
|
||||
"express": "5.2.1",
|
||||
"playwright-core": "1.59.1",
|
||||
"typebox": "1.1.37",
|
||||
"typebox": "1.1.36",
|
||||
"ws": "^8.20.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
type ChromeMcpModule = typeof import("./chrome-mcp.js");
|
||||
export type ChromeMcpModule = typeof import("./chrome-mcp.js");
|
||||
|
||||
export async function getChromeMcpModule(): Promise<ChromeMcpModule> {
|
||||
return await import("./chrome-mcp.js");
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
DEFAULT_BROWSER_SCREENSHOT_TIMEOUT_MS,
|
||||
} from "./constants.js";
|
||||
|
||||
export type { BrowserFormField } from "./client-actions.types.js";
|
||||
export type { BrowserActRequest, BrowserFormField } from "./client-actions.types.js";
|
||||
|
||||
type BrowserActResponse = {
|
||||
export type BrowserActResponse = {
|
||||
ok: true;
|
||||
targetId: string;
|
||||
url?: string;
|
||||
|
||||
@@ -8,7 +8,12 @@ import type {
|
||||
} from "./client.types.js";
|
||||
import type { BrowserDoctorReport } from "./doctor.js";
|
||||
|
||||
export type { BrowserStatus, BrowserTab, BrowserTransport } from "./client.types.js";
|
||||
export type {
|
||||
BrowserStatus,
|
||||
BrowserTab,
|
||||
BrowserTransport,
|
||||
SnapshotAriaNode,
|
||||
} from "./client.types.js";
|
||||
export type { BrowserDoctorCheck, BrowserDoctorReport } from "./doctor.js";
|
||||
|
||||
export type ProfileStatus = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type BrowserTransport = "cdp" | "chrome-mcp";
|
||||
type BrowserHeadlessSource =
|
||||
export type BrowserHeadlessSource =
|
||||
| "request"
|
||||
| "env"
|
||||
| "profile"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BrowserStatus, BrowserTransport } from "./client.types.js";
|
||||
|
||||
type BrowserDoctorCheckStatus = "pass" | "warn" | "fail" | "info";
|
||||
export type BrowserDoctorCheckStatus = "pass" | "warn" | "fail" | "info";
|
||||
|
||||
export type BrowserDoctorCheck = {
|
||||
id: string;
|
||||
|
||||
@@ -5,11 +5,11 @@ export const DEFAULT_FILL_FIELD_TYPE = "text";
|
||||
|
||||
type BrowserFormFieldValue = NonNullable<BrowserFormField["value"]>;
|
||||
|
||||
function normalizeBrowserFormFieldRef(value: unknown): string {
|
||||
export function normalizeBrowserFormFieldRef(value: unknown): string {
|
||||
return normalizeOptionalString(value) ?? "";
|
||||
}
|
||||
|
||||
function normalizeBrowserFormFieldType(value: unknown): string {
|
||||
export function normalizeBrowserFormFieldType(value: unknown): string {
|
||||
const type = normalizeOptionalString(value) ?? "";
|
||||
return type || DEFAULT_FILL_FIELD_TYPE;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ function canUseNodeFs(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_BROWSER_TMP_DIR = canUseNodeFs()
|
||||
export const DEFAULT_BROWSER_TMP_DIR = canUseNodeFs()
|
||||
? resolvePreferredOpenClawTmpDir()
|
||||
: DEFAULT_FALLBACK_BROWSER_TMP_DIR;
|
||||
export const DEFAULT_TRACE_DIR = DEFAULT_BROWSER_TMP_DIR;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
export const CDP_PORT_RANGE_START = 18800;
|
||||
export const CDP_PORT_RANGE_END = 18899;
|
||||
|
||||
const PROFILE_NAME_REGEX = /^[a-z0-9][a-z0-9-]*$/;
|
||||
export const PROFILE_NAME_REGEX = /^[a-z0-9][a-z0-9-]*$/;
|
||||
|
||||
export function isValidProfileName(name: string): boolean {
|
||||
if (!name || name.length > 64) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { saveMediaBuffer } from "../media/store.js";
|
||||
|
||||
type BrowserProxyFile = {
|
||||
export type BrowserProxyFile = {
|
||||
path: string;
|
||||
base64: string;
|
||||
mimeType?: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { CONTENT_ROLES, INTERACTIVE_ROLES, STRUCTURAL_ROLES } from "./snapshot-roles.js";
|
||||
|
||||
type RoleRef = {
|
||||
export type RoleRef = {
|
||||
role: string;
|
||||
name?: string;
|
||||
/** Index used only when role+name duplicates exist. */
|
||||
@@ -10,7 +10,7 @@ type RoleRef = {
|
||||
|
||||
export type RoleRefMap = Record<string, RoleRef>;
|
||||
|
||||
type RoleSnapshotStats = {
|
||||
export type RoleSnapshotStats = {
|
||||
lines: number;
|
||||
chars: number;
|
||||
refs: number;
|
||||
|
||||
@@ -9,7 +9,7 @@ export const ACT_ERROR_CODES = {
|
||||
targetIdMismatch: "ACT_TARGET_ID_MISMATCH",
|
||||
} as const;
|
||||
|
||||
type ActErrorCode = (typeof ACT_ERROR_CODES)[keyof typeof ACT_ERROR_CODES];
|
||||
export type ActErrorCode = (typeof ACT_ERROR_CODES)[keyof typeof ACT_ERROR_CODES];
|
||||
|
||||
export function jsonActError(
|
||||
res: BrowserResponse,
|
||||
|
||||
@@ -24,8 +24,8 @@ export function isActKind(value: unknown): value is ActKind {
|
||||
return (ACT_KINDS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
type ClickButton = "left" | "right" | "middle";
|
||||
type ClickModifier = "Alt" | "Control" | "ControlOrMeta" | "Meta" | "Shift";
|
||||
export type ClickButton = "left" | "right" | "middle";
|
||||
export type ClickModifier = "Alt" | "Control" | "ControlOrMeta" | "Meta" | "Shift";
|
||||
|
||||
const ALLOWED_CLICK_MODIFIERS = new Set<ClickModifier>([
|
||||
"Alt",
|
||||
|
||||
@@ -84,7 +84,7 @@ export async function requirePwAi(
|
||||
501,
|
||||
[
|
||||
`Playwright is not available in this gateway build; '${feature}' is unsupported.`,
|
||||
"Reinstall or update OpenClaw so the core browser runtime dependency is present, then restart the gateway. In Docker, also install Chromium with the bundled playwright-core CLI.",
|
||||
"Repair the bundled browser plugin runtime dependencies so playwright-core is installed, then restart the gateway. In Docker, also install Chromium with the bundled playwright-core CLI.",
|
||||
"Docs: /tools/browser#playwright-requirement",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
@@ -19,7 +19,7 @@ function normalizeOptionalString(value: unknown): string | undefined {
|
||||
return readStringValue(value)?.trim() || undefined;
|
||||
}
|
||||
|
||||
type BrowserSnapshotPlan = {
|
||||
export type BrowserSnapshotPlan = {
|
||||
format: "ai" | "aria";
|
||||
mode?: "efficient";
|
||||
labels?: boolean;
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { MockFn } from "../test-utils/vitest-mock-fn.js";
|
||||
import { installChromeUserDataDirHooks } from "./chrome-user-data-dir.test-harness.js";
|
||||
import { getFreePort } from "./test-port.js";
|
||||
|
||||
export { getFreePort } from "./test-port.js";
|
||||
|
||||
type HarnessState = {
|
||||
testPort: number;
|
||||
cdpBaseUrl: string;
|
||||
@@ -47,7 +49,7 @@ export function getBrowserControlServerBaseUrl(): string {
|
||||
return `http://127.0.0.1:${state.testPort}`;
|
||||
}
|
||||
|
||||
function restoreGatewayPortEnv(prevGatewayPort: string | undefined): void {
|
||||
export function restoreGatewayPortEnv(prevGatewayPort: string | undefined): void {
|
||||
if (prevGatewayPort === undefined) {
|
||||
delete process.env.OPENCLAW_GATEWAY_PORT;
|
||||
return;
|
||||
@@ -484,7 +486,7 @@ export async function startBrowserControlServerFromConfig() {
|
||||
return await (await loadBrowserServerModule()).startBrowserControlServerFromConfig();
|
||||
}
|
||||
|
||||
async function stopBrowserControlServer(): Promise<void> {
|
||||
export async function stopBrowserControlServer(): Promise<void> {
|
||||
await (await loadBrowserServerModule()).stopBrowserControlServer();
|
||||
}
|
||||
|
||||
@@ -533,7 +535,7 @@ export async function resetBrowserControlServerTestContext(): Promise<void> {
|
||||
delete process.env.OPENCLAW_GATEWAY_PASSWORD;
|
||||
}
|
||||
|
||||
function restoreGatewayAuthEnv(
|
||||
export function restoreGatewayAuthEnv(
|
||||
prevGatewayToken: string | undefined,
|
||||
prevGatewayPassword: string | undefined,
|
||||
): void {
|
||||
|
||||
@@ -21,7 +21,7 @@ export function isPrimaryTrackedBrowserSessionKey(sessionKey: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function resolveBrowserTabCleanupRuntimeConfig(): ResolvedBrowserTabCleanupConfig {
|
||||
export function resolveBrowserTabCleanupRuntimeConfig(): ResolvedBrowserTabCleanupConfig {
|
||||
const cfg = getRuntimeConfig();
|
||||
return resolveBrowserConfig(cfg.browser, cfg).tabCleanup;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/text-runtime";
|
||||
import { browserCloseTab } from "./client.js";
|
||||
|
||||
type TrackedSessionBrowserTab = {
|
||||
export type TrackedSessionBrowserTab = {
|
||||
sessionKey: string;
|
||||
targetId: string;
|
||||
baseUrl?: string;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user