mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-18 03:52:42 +08:00
Compare commits
6 Commits
fix/api-er
...
codex/matr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50b43cf057 | ||
|
|
105167b25b | ||
|
|
fc19bb525b | ||
|
|
ae2e1eadec | ||
|
|
24634064a4 | ||
|
|
9fc744768b |
@@ -17,7 +17,7 @@ Use this skill for release and publish-time workflow. Keep ordinary development
|
||||
|
||||
## Keep release channel naming aligned
|
||||
|
||||
- `stable`: tagged releases only, published to npm `latest` and then mirrored onto npm `beta` unless `beta` already points at a newer prerelease
|
||||
- `stable`: tagged releases only, with npm dist-tag `latest`
|
||||
- `beta`: prerelease tags like `vYYYY.M.D-beta.N`, with npm dist-tag `beta`
|
||||
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
|
||||
- `dev`: moving head on `main`
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -85,7 +85,6 @@ apps/ios/*.mobileprovision
|
||||
# Local untracked files
|
||||
.local/
|
||||
docs/.local/
|
||||
docs/internal/
|
||||
tmp/
|
||||
IDENTITY.md
|
||||
USER.md
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
{
|
||||
"globs": ["docs/**/*.md", "docs/**/*.mdx", "README.md"],
|
||||
"ignores": [
|
||||
"docs/zh-CN/**",
|
||||
"docs/.i18n/**",
|
||||
"docs/reference/templates/**",
|
||||
"**/.local/**"
|
||||
],
|
||||
"ignores": ["docs/zh-CN/**", "docs/.i18n/**", "docs/reference/templates/**", "**/.local/**"],
|
||||
"config": {
|
||||
"default": true,
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
**/node_modules/
|
||||
**/.runtime-deps-*/
|
||||
docs/.generated/
|
||||
|
||||
541
CHANGELOG.md
541
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,6 @@ WORKDIR /app
|
||||
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./
|
||||
COPY ui/package.json ./ui/package.json
|
||||
COPY patches ./patches
|
||||
COPY scripts/postinstall-bundled-plugins.mjs ./scripts/postinstall-bundled-plugins.mjs
|
||||
|
||||
COPY --from=ext-deps /out/ ./${OPENCLAW_BUNDLED_PLUGIN_DIR}/
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ OpenClaw does **not** model one gateway as a multi-tenant, adversarial user boun
|
||||
- If multiple users need OpenClaw, use one VPS (or host/OS user boundary) per user.
|
||||
- For advanced setups, multiple gateways on one machine are possible, but only with strict isolation and are not the recommended default.
|
||||
- Exec behavior is host-first by default: `agents.defaults.sandbox.mode` defaults to `off`.
|
||||
- `tools.exec.host` defaults to `auto`: sandbox when sandbox runtime is active for the session, otherwise gateway.
|
||||
- `tools.exec.host` defaults to `sandbox` as a routing preference, but if sandbox runtime is not active for the session, exec runs on the gateway host.
|
||||
- Implicit exec calls (no explicit host in the tool call) follow the same behavior.
|
||||
- This is expected in OpenClaw's one-user trusted-operator model. If you need isolation, enable sandbox mode (`non-main`/`all`) and keep strict tool policy.
|
||||
|
||||
|
||||
@@ -65,8 +65,8 @@ android {
|
||||
applicationId = "ai.openclaw.app"
|
||||
minSdk = 31
|
||||
targetSdk = 36
|
||||
versionCode = 2026033000
|
||||
versionName = "2026.3.30"
|
||||
versionCode = 2026032900
|
||||
versionName = "2026.3.29"
|
||||
ndk {
|
||||
// Support all major ABIs — native libs are tiny (~47 KB per ABI)
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// Shared iOS version defaults.
|
||||
// Generated overrides live in build/Version.xcconfig (git-ignored).
|
||||
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.30
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.30
|
||||
OPENCLAW_BUILD_VERSION = 2026033000
|
||||
OPENCLAW_GATEWAY_VERSION = 2026.3.29
|
||||
OPENCLAW_MARKETING_VERSION = 2026.3.29
|
||||
OPENCLAW_BUILD_VERSION = 2026032900
|
||||
|
||||
#include? "../build/Version.xcconfig"
|
||||
|
||||
@@ -65,9 +65,9 @@ Release behavior:
|
||||
- Beta release also switches the app to `OpenClawPushTransport=relay`, `OpenClawPushDistribution=official`, and `OpenClawPushAPNsEnvironment=production`.
|
||||
- The beta flow does not modify `apps/ios/.local-signing.xcconfig` or `apps/ios/LocalSigning.xcconfig`.
|
||||
- Root `package.json.version` is the only version source for iOS.
|
||||
- A root version like `2026.3.30-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.30`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.30`
|
||||
- A root version like `2026.3.29-beta.1` becomes:
|
||||
- `CFBundleShortVersionString = 2026.3.29`
|
||||
- `CFBundleVersion = next TestFlight build number for 2026.3.29`
|
||||
|
||||
Required env for beta builds:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@preconcurrency import ActivityKit
|
||||
import ActivityKit
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2026.3.30</string>
|
||||
<string>2026.3.29</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2026033000</string>
|
||||
<string>2026032900</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>OpenClaw</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
"exportName": "CliBackendPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1628,
|
||||
"line": 1616,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -397,7 +397,7 @@
|
||||
"exportName": "MediaUnderstandingProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1279,
|
||||
"line": 1267,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -415,7 +415,7 @@
|
||||
"exportName": "OpenClawPluginApi",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1672,
|
||||
"line": 1660,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -424,7 +424,7 @@
|
||||
"exportName": "OpenClawPluginConfigSchema",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 101,
|
||||
"line": 100,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -433,7 +433,7 @@
|
||||
"exportName": "PluginLogger",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 72,
|
||||
"line": 71,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -451,7 +451,7 @@
|
||||
"exportName": "ProviderAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 176,
|
||||
"line": 175,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -460,7 +460,7 @@
|
||||
"exportName": "ProviderAuthResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 161,
|
||||
"line": 160,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -469,7 +469,7 @@
|
||||
"exportName": "ProviderRuntimeModel",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 317,
|
||||
"line": 316,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -523,7 +523,7 @@
|
||||
"exportName": "SpeechProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1254,
|
||||
"line": 1242,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -1513,6 +1513,24 @@
|
||||
"path": "src/infra/heartbeat-events.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function isWhatsAppGroupJid(value: string): boolean;",
|
||||
"exportName": "isWhatsAppGroupJid",
|
||||
"kind": "function",
|
||||
"source": {
|
||||
"line": 17,
|
||||
"path": "extensions/whatsapp/src/normalize-target.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function isWhatsAppUserTarget(value: string): boolean;",
|
||||
"exportName": "isWhatsAppUserTarget",
|
||||
"kind": "function",
|
||||
"source": {
|
||||
"line": 30,
|
||||
"path": "extensions/whatsapp/src/normalize-target.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function keepHttpServerTaskAlive(params: { server: CloseAwareServer; abortSignal?: AbortSignal | undefined; onAbort?: (() => void | Promise<void>) | undefined; }): Promise<void>;",
|
||||
"exportName": "keepHttpServerTaskAlive",
|
||||
@@ -1545,7 +1563,7 @@
|
||||
"exportName": "normalizeChannelId",
|
||||
"kind": "function",
|
||||
"source": {
|
||||
"line": 89,
|
||||
"line": 80,
|
||||
"path": "src/channels/plugins/registry.ts"
|
||||
}
|
||||
},
|
||||
@@ -1603,6 +1621,15 @@
|
||||
"path": "src/channels/plugins/normalize/whatsapp.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function normalizeWhatsAppTarget(value: string): string | null;",
|
||||
"exportName": "normalizeWhatsAppTarget",
|
||||
"kind": "function",
|
||||
"source": {
|
||||
"line": 47,
|
||||
"path": "extensions/whatsapp/src/normalize-target.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export function onHeartbeatEvent(listener: (evt: HeartbeatEventPayload) => void): () => void;",
|
||||
"exportName": "onHeartbeatEvent",
|
||||
@@ -1720,33 +1747,6 @@
|
||||
"path": "src/channels/plugins/message-capabilities.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export const isWhatsAppGroupJid: (value: string) => boolean;",
|
||||
"exportName": "isWhatsAppGroupJid",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 13,
|
||||
"path": "src/plugin-sdk/whatsapp-targets.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export const isWhatsAppUserTarget: (value: string) => boolean;",
|
||||
"exportName": "isWhatsAppUserTarget",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 15,
|
||||
"path": "src/plugin-sdk/whatsapp-targets.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export const normalizeWhatsAppTarget: (value: string) => string | null;",
|
||||
"exportName": "normalizeWhatsAppTarget",
|
||||
"kind": "const",
|
||||
"source": {
|
||||
"line": 17,
|
||||
"path": "src/plugin-sdk/whatsapp-targets.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"declaration": "export type BaseProbeResult = BaseProbeResult<TError>;",
|
||||
"exportName": "BaseProbeResult",
|
||||
@@ -3747,7 +3747,7 @@
|
||||
"exportName": "MediaUnderstandingProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1279,
|
||||
"line": 1267,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3765,7 +3765,7 @@
|
||||
"exportName": "OpenClawPluginApi",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1672,
|
||||
"line": 1660,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3774,7 +3774,7 @@
|
||||
"exportName": "OpenClawPluginCommandDefinition",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1398,
|
||||
"line": 1386,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3783,7 +3783,7 @@
|
||||
"exportName": "OpenClawPluginConfigSchema",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 101,
|
||||
"line": 100,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3792,7 +3792,7 @@
|
||||
"exportName": "OpenClawPluginDefinition",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1654,
|
||||
"line": 1642,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3801,7 +3801,7 @@
|
||||
"exportName": "OpenClawPluginService",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1621,
|
||||
"line": 1609,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3810,7 +3810,7 @@
|
||||
"exportName": "OpenClawPluginServiceContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1613,
|
||||
"line": 1601,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3819,7 +3819,7 @@
|
||||
"exportName": "OpenClawPluginToolContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 116,
|
||||
"line": 115,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3828,7 +3828,7 @@
|
||||
"exportName": "OpenClawPluginToolFactory",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 141,
|
||||
"line": 140,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3837,7 +3837,7 @@
|
||||
"exportName": "PluginCommandContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1294,
|
||||
"line": 1282,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3846,7 +3846,7 @@
|
||||
"exportName": "PluginInteractiveTelegramHandlerContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1427,
|
||||
"line": 1415,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3855,7 +3855,7 @@
|
||||
"exportName": "PluginLogger",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 72,
|
||||
"line": 71,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3873,7 +3873,7 @@
|
||||
"exportName": "ProviderAugmentModelCatalogContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 710,
|
||||
"line": 709,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3882,7 +3882,7 @@
|
||||
"exportName": "ProviderAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 176,
|
||||
"line": 175,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3891,7 +3891,7 @@
|
||||
"exportName": "ProviderAuthDoctorHintContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 514,
|
||||
"line": 513,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3900,7 +3900,7 @@
|
||||
"exportName": "ProviderAuthMethod",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 255,
|
||||
"line": 254,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3909,7 +3909,7 @@
|
||||
"exportName": "ProviderAuthMethodNonInteractiveContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 239,
|
||||
"line": 238,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3918,7 +3918,7 @@
|
||||
"exportName": "ProviderAuthResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 161,
|
||||
"line": 160,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3927,7 +3927,7 @@
|
||||
"exportName": "ProviderBuildMissingAuthMessageContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 622,
|
||||
"line": 621,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3936,7 +3936,7 @@
|
||||
"exportName": "ProviderBuildUnknownModelHintContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 638,
|
||||
"line": 637,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3945,7 +3945,7 @@
|
||||
"exportName": "ProviderBuiltInModelSuppressionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 654,
|
||||
"line": 653,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3954,7 +3954,7 @@
|
||||
"exportName": "ProviderBuiltInModelSuppressionResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 663,
|
||||
"line": 662,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3963,7 +3963,7 @@
|
||||
"exportName": "ProviderCacheTtlEligibilityContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 610,
|
||||
"line": 609,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3972,7 +3972,7 @@
|
||||
"exportName": "ProviderCatalogContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 276,
|
||||
"line": 275,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3981,7 +3981,7 @@
|
||||
"exportName": "ProviderCatalogResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 299,
|
||||
"line": 298,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3990,7 +3990,7 @@
|
||||
"exportName": "ProviderDefaultThinkingPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 687,
|
||||
"line": 686,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -3999,7 +3999,7 @@
|
||||
"exportName": "ProviderDiscoveryContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 726,
|
||||
"line": 725,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4008,7 +4008,7 @@
|
||||
"exportName": "ProviderFetchUsageSnapshotContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 495,
|
||||
"line": 494,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4017,7 +4017,7 @@
|
||||
"exportName": "ProviderModernModelPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 697,
|
||||
"line": 696,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4026,7 +4026,7 @@
|
||||
"exportName": "ProviderNormalizeResolvedModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 360,
|
||||
"line": 359,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4035,7 +4035,7 @@
|
||||
"exportName": "ProviderPreparedRuntimeAuth",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 442,
|
||||
"line": 441,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4044,7 +4044,7 @@
|
||||
"exportName": "ProviderPrepareDynamicModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 351,
|
||||
"line": 350,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4053,7 +4053,7 @@
|
||||
"exportName": "ProviderPrepareExtraParamsContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 528,
|
||||
"line": 527,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4062,7 +4062,7 @@
|
||||
"exportName": "ProviderPrepareRuntimeAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 421,
|
||||
"line": 420,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4071,7 +4071,7 @@
|
||||
"exportName": "ProviderResolvedUsageAuth",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 482,
|
||||
"line": 481,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4080,7 +4080,7 @@
|
||||
"exportName": "ProviderResolveDynamicModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 334,
|
||||
"line": 333,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4089,7 +4089,7 @@
|
||||
"exportName": "ProviderResolveUsageAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 463,
|
||||
"line": 462,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4098,7 +4098,7 @@
|
||||
"exportName": "ProviderRuntimeModel",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 317,
|
||||
"line": 316,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4107,7 +4107,7 @@
|
||||
"exportName": "ProviderThinkingPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 675,
|
||||
"line": 674,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4125,7 +4125,7 @@
|
||||
"exportName": "ProviderWrapStreamFnContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 561,
|
||||
"line": 560,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4170,7 +4170,7 @@
|
||||
"exportName": "SpeechProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1254,
|
||||
"line": 1242,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4262,7 +4262,7 @@
|
||||
"exportName": "MediaUnderstandingProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1279,
|
||||
"line": 1267,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4280,7 +4280,7 @@
|
||||
"exportName": "OpenClawPluginApi",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1672,
|
||||
"line": 1660,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4289,7 +4289,7 @@
|
||||
"exportName": "OpenClawPluginCommandDefinition",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1398,
|
||||
"line": 1386,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4298,7 +4298,7 @@
|
||||
"exportName": "OpenClawPluginConfigSchema",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 101,
|
||||
"line": 100,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4307,7 +4307,7 @@
|
||||
"exportName": "OpenClawPluginDefinition",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1654,
|
||||
"line": 1642,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4316,7 +4316,7 @@
|
||||
"exportName": "OpenClawPluginService",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1621,
|
||||
"line": 1609,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4325,7 +4325,7 @@
|
||||
"exportName": "OpenClawPluginServiceContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1613,
|
||||
"line": 1601,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4334,7 +4334,7 @@
|
||||
"exportName": "OpenClawPluginToolContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 116,
|
||||
"line": 115,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4343,7 +4343,7 @@
|
||||
"exportName": "OpenClawPluginToolFactory",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 141,
|
||||
"line": 140,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4352,7 +4352,7 @@
|
||||
"exportName": "PluginCommandContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1294,
|
||||
"line": 1282,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4361,7 +4361,7 @@
|
||||
"exportName": "PluginInteractiveTelegramHandlerContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1427,
|
||||
"line": 1415,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4370,7 +4370,7 @@
|
||||
"exportName": "PluginLogger",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 72,
|
||||
"line": 71,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4379,7 +4379,7 @@
|
||||
"exportName": "ProviderAugmentModelCatalogContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 710,
|
||||
"line": 709,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4388,7 +4388,7 @@
|
||||
"exportName": "ProviderAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 176,
|
||||
"line": 175,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4397,7 +4397,7 @@
|
||||
"exportName": "ProviderAuthDoctorHintContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 514,
|
||||
"line": 513,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4406,7 +4406,7 @@
|
||||
"exportName": "ProviderAuthMethod",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 255,
|
||||
"line": 254,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4415,7 +4415,7 @@
|
||||
"exportName": "ProviderAuthMethodNonInteractiveContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 239,
|
||||
"line": 238,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4424,7 +4424,7 @@
|
||||
"exportName": "ProviderAuthResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 161,
|
||||
"line": 160,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4433,7 +4433,7 @@
|
||||
"exportName": "ProviderBuildMissingAuthMessageContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 622,
|
||||
"line": 621,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4442,7 +4442,7 @@
|
||||
"exportName": "ProviderBuildUnknownModelHintContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 638,
|
||||
"line": 637,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4451,7 +4451,7 @@
|
||||
"exportName": "ProviderBuiltInModelSuppressionContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 654,
|
||||
"line": 653,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4460,7 +4460,7 @@
|
||||
"exportName": "ProviderBuiltInModelSuppressionResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 663,
|
||||
"line": 662,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4469,7 +4469,7 @@
|
||||
"exportName": "ProviderCacheTtlEligibilityContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 610,
|
||||
"line": 609,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4478,7 +4478,7 @@
|
||||
"exportName": "ProviderCatalogContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 276,
|
||||
"line": 275,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4487,7 +4487,7 @@
|
||||
"exportName": "ProviderCatalogResult",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 299,
|
||||
"line": 298,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4496,7 +4496,7 @@
|
||||
"exportName": "ProviderDefaultThinkingPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 687,
|
||||
"line": 686,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4505,7 +4505,7 @@
|
||||
"exportName": "ProviderDiscoveryContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 726,
|
||||
"line": 725,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4514,7 +4514,7 @@
|
||||
"exportName": "ProviderFetchUsageSnapshotContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 495,
|
||||
"line": 494,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4523,7 +4523,7 @@
|
||||
"exportName": "ProviderModernModelPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 697,
|
||||
"line": 696,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4532,7 +4532,7 @@
|
||||
"exportName": "ProviderNormalizeConfigContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 386,
|
||||
"line": 385,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4541,7 +4541,7 @@
|
||||
"exportName": "ProviderNormalizeModelIdContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 375,
|
||||
"line": 374,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4550,7 +4550,7 @@
|
||||
"exportName": "ProviderNormalizeResolvedModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 360,
|
||||
"line": 359,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4559,7 +4559,7 @@
|
||||
"exportName": "ProviderNormalizeTransportContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 398,
|
||||
"line": 397,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4568,7 +4568,7 @@
|
||||
"exportName": "ProviderPreparedRuntimeAuth",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 442,
|
||||
"line": 441,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4577,7 +4577,7 @@
|
||||
"exportName": "ProviderPrepareDynamicModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 351,
|
||||
"line": 350,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4586,7 +4586,7 @@
|
||||
"exportName": "ProviderPrepareExtraParamsContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 528,
|
||||
"line": 527,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4595,7 +4595,7 @@
|
||||
"exportName": "ProviderPrepareRuntimeAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 421,
|
||||
"line": 420,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4604,7 +4604,7 @@
|
||||
"exportName": "ProviderResolveConfigApiKeyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 410,
|
||||
"line": 409,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4613,7 +4613,7 @@
|
||||
"exportName": "ProviderResolvedUsageAuth",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 482,
|
||||
"line": 481,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4622,7 +4622,7 @@
|
||||
"exportName": "ProviderResolveDynamicModelContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 334,
|
||||
"line": 333,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4631,7 +4631,7 @@
|
||||
"exportName": "ProviderResolveUsageAuthContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 463,
|
||||
"line": 462,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4640,7 +4640,7 @@
|
||||
"exportName": "ProviderRuntimeModel",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 317,
|
||||
"line": 316,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4649,7 +4649,7 @@
|
||||
"exportName": "ProviderThinkingPolicyContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 675,
|
||||
"line": 674,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4658,7 +4658,7 @@
|
||||
"exportName": "ProviderWrapStreamFnContext",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 561,
|
||||
"line": 560,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
},
|
||||
@@ -4667,7 +4667,7 @@
|
||||
"exportName": "SpeechProviderPlugin",
|
||||
"kind": "type",
|
||||
"source": {
|
||||
"line": 1254,
|
||||
"line": 1242,
|
||||
"path": "src/plugins/types.ts"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
{"declaration":"export type ChannelStatusIssue = ChannelStatusIssue;","entrypoint":"index","exportName":"ChannelStatusIssue","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"ClawdbotConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
|
||||
{"declaration":"export type CliBackendConfig = CliBackendConfig;","entrypoint":"index","exportName":"CliBackendConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":47,"sourcePath":"src/config/types.agent-defaults.ts"}
|
||||
{"declaration":"export type CliBackendPlugin = CliBackendPlugin;","entrypoint":"index","exportName":"CliBackendPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1628,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type CliBackendPlugin = CliBackendPlugin;","entrypoint":"index","exportName":"CliBackendPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1616,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type CompiledConfiguredBinding = CompiledConfiguredBinding;","entrypoint":"index","exportName":"CompiledConfiguredBinding","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":38,"sourcePath":"src/channels/plugins/binding-types.ts"}
|
||||
{"declaration":"export type ConfiguredBindingConversation = ConversationRef;","entrypoint":"index","exportName":"ConfiguredBindingConversation","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":13,"sourcePath":"src/channels/plugins/binding-types.ts"}
|
||||
{"declaration":"export type ConfiguredBindingResolution = ConfiguredBindingResolution;","entrypoint":"index","exportName":"ConfiguredBindingResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":49,"sourcePath":"src/channels/plugins/binding-types.ts"}
|
||||
@@ -42,21 +42,21 @@
|
||||
{"declaration":"export type ImageGenerationResolution = ImageGenerationResolution;","entrypoint":"index","exportName":"ImageGenerationResolution","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/image-generation/types.ts"}
|
||||
{"declaration":"export type ImageGenerationResult = ImageGenerationResult;","entrypoint":"index","exportName":"ImageGenerationResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":36,"sourcePath":"src/image-generation/types.ts"}
|
||||
{"declaration":"export type ImageGenerationSourceImage = ImageGenerationSourceImage;","entrypoint":"index","exportName":"ImageGenerationSourceImage","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":14,"sourcePath":"src/image-generation/types.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1279,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"index","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1267,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"index","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1672,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":101,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":72,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"index","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1660,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"index","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"index","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":71,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"index","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":176,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":161,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":317,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"index","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":175,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"index","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":160,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"index","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":316,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ReplyPayload = ReplyPayload;","entrypoint":"index","exportName":"ReplyPayload","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/auto-reply/types.ts"}
|
||||
{"declaration":"export type RuntimeEnv = RuntimeEnv;","entrypoint":"index","exportName":"RuntimeEnv","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/runtime.ts"}
|
||||
{"declaration":"export type RuntimeLogger = RuntimeLogger;","entrypoint":"index","exportName":"RuntimeLogger","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/plugins/runtime/types-core.ts"}
|
||||
{"declaration":"export type SecretInput = SecretInput;","entrypoint":"index","exportName":"SecretInput","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":16,"sourcePath":"src/config/types.secrets.ts"}
|
||||
{"declaration":"export type SecretRef = SecretRef;","entrypoint":"index","exportName":"SecretRef","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":10,"sourcePath":"src/config/types.secrets.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1254,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"index","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":1242,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type StatefulBindingTargetDescriptor = StatefulBindingTargetDescriptor;","entrypoint":"index","exportName":"StatefulBindingTargetDescriptor","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":17,"sourcePath":"src/channels/plugins/binding-types.ts"}
|
||||
{"declaration":"export type StatefulBindingTargetDriver = StatefulBindingTargetDriver;","entrypoint":"index","exportName":"StatefulBindingTargetDriver","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":15,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
|
||||
{"declaration":"export type StatefulBindingTargetReadyResult = StatefulBindingTargetReadyResult;","entrypoint":"index","exportName":"StatefulBindingTargetReadyResult","importSpecifier":"openclaw/plugin-sdk","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/channels/plugins/stateful-target-drivers.ts"}
|
||||
@@ -165,16 +165,19 @@
|
||||
{"declaration":"export function emitHeartbeatEvent(evt: Omit<HeartbeatEventPayload, \"ts\">): void;","entrypoint":"channel-runtime","exportName":"emitHeartbeatEvent","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":51,"sourcePath":"src/infra/heartbeat-events.ts"}
|
||||
{"declaration":"export function enqueueSystemEvent(text: string, options: SystemEventOptions): boolean;","entrypoint":"channel-runtime","exportName":"enqueueSystemEvent","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":91,"sourcePath":"src/infra/system-events.ts"}
|
||||
{"declaration":"export function getLastHeartbeatEvent(): HeartbeatEventPayload | null;","entrypoint":"channel-runtime","exportName":"getLastHeartbeatEvent","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":61,"sourcePath":"src/infra/heartbeat-events.ts"}
|
||||
{"declaration":"export function isWhatsAppGroupJid(value: string): boolean;","entrypoint":"channel-runtime","exportName":"isWhatsAppGroupJid","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":17,"sourcePath":"extensions/whatsapp/src/normalize-target.ts"}
|
||||
{"declaration":"export function isWhatsAppUserTarget(value: string): boolean;","entrypoint":"channel-runtime","exportName":"isWhatsAppUserTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":30,"sourcePath":"extensions/whatsapp/src/normalize-target.ts"}
|
||||
{"declaration":"export function keepHttpServerTaskAlive(params: { server: CloseAwareServer; abortSignal?: AbortSignal | undefined; onAbort?: (() => void | Promise<void>) | undefined; }): Promise<void>;","entrypoint":"channel-runtime","exportName":"keepHttpServerTaskAlive","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":79,"sourcePath":"src/plugin-sdk/channel-lifecycle.ts"}
|
||||
{"declaration":"export function looksLikeSignalTargetId(raw: string, normalized?: string | undefined): boolean;","entrypoint":"channel-runtime","exportName":"looksLikeSignalTargetId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/channels/plugins/normalize/signal.ts"}
|
||||
{"declaration":"export function looksLikeWhatsAppTargetId(raw: string): boolean;","entrypoint":"channel-runtime","exportName":"looksLikeWhatsAppTargetId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":20,"sourcePath":"src/channels/plugins/normalize/whatsapp.ts"}
|
||||
{"declaration":"export function normalizeChannelId(raw?: string | null | undefined): ChannelId | null;","entrypoint":"channel-runtime","exportName":"normalizeChannelId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":89,"sourcePath":"src/channels/plugins/registry.ts"}
|
||||
{"declaration":"export function normalizeChannelId(raw?: string | null | undefined): ChannelId | null;","entrypoint":"channel-runtime","exportName":"normalizeChannelId","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":80,"sourcePath":"src/channels/plugins/registry.ts"}
|
||||
{"declaration":"export function normalizeChatType(raw?: string | undefined): ChatType | undefined;","entrypoint":"channel-runtime","exportName":"normalizeChatType","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":3,"sourcePath":"src/channels/chat-type.ts"}
|
||||
{"declaration":"export function normalizePollDurationHours(value: number | undefined, options: { defaultHours: number; maxHours: number; }): number;","entrypoint":"channel-runtime","exportName":"normalizePollDurationHours","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":93,"sourcePath":"src/polls.ts"}
|
||||
{"declaration":"export function normalizePollInput(input: PollInput, options?: NormalizePollOptions): NormalizedPollInput;","entrypoint":"channel-runtime","exportName":"normalizePollInput","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":36,"sourcePath":"src/polls.ts"}
|
||||
{"declaration":"export function normalizeSignalMessagingTarget(raw: string): string | undefined;","entrypoint":"channel-runtime","exportName":"normalizeSignalMessagingTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/normalize/signal.ts"}
|
||||
{"declaration":"export function normalizeWhatsAppAllowFromEntries(allowFrom: (string | number)[]): string[];","entrypoint":"channel-runtime","exportName":"normalizeWhatsAppAllowFromEntries","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":12,"sourcePath":"src/channels/plugins/normalize/whatsapp.ts"}
|
||||
{"declaration":"export function normalizeWhatsAppMessagingTarget(raw: string): string | undefined;","entrypoint":"channel-runtime","exportName":"normalizeWhatsAppMessagingTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":4,"sourcePath":"src/channels/plugins/normalize/whatsapp.ts"}
|
||||
{"declaration":"export function normalizeWhatsAppTarget(value: string): string | null;","entrypoint":"channel-runtime","exportName":"normalizeWhatsAppTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":47,"sourcePath":"extensions/whatsapp/src/normalize-target.ts"}
|
||||
{"declaration":"export function onHeartbeatEvent(listener: (evt: HeartbeatEventPayload) => void): () => void;","entrypoint":"channel-runtime","exportName":"onHeartbeatEvent","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":57,"sourcePath":"src/infra/heartbeat-events.ts"}
|
||||
{"declaration":"export function recordChannelActivity(params: { channel: ChannelId; accountId?: string | null | undefined; direction: ChannelDirection; at?: number | undefined; }): void;","entrypoint":"channel-runtime","exportName":"recordChannelActivity","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":26,"sourcePath":"src/infra/channel-activity.ts"}
|
||||
{"declaration":"export function reduceInteractiveReply<TState>(interactive: InteractiveReply | undefined, initialState: TState, reduce: (state: TState, block: InteractiveReplyBlock, index: number) => TState): TState;","entrypoint":"channel-runtime","exportName":"reduceInteractiveReply","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":3,"sourcePath":"src/channels/plugins/outbound/interactive.ts"}
|
||||
@@ -188,9 +191,6 @@
|
||||
{"declaration":"export function waitUntilAbort(signal?: AbortSignal | undefined, onAbort?: (() => void | Promise<void>) | undefined): Promise<void>;","entrypoint":"channel-runtime","exportName":"waitUntilAbort","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"function","recordType":"export","sourceLine":38,"sourcePath":"src/plugin-sdk/channel-lifecycle.ts"}
|
||||
{"declaration":"export const CHANNEL_MESSAGE_ACTION_NAMES: readonly [\"send\", \"broadcast\", \"poll\", \"poll-vote\", \"react\", \"reactions\", \"read\", \"edit\", \"unsend\", \"reply\", \"sendWithEffect\", \"renameGroup\", \"setGroupIcon\", \"addParticipant\", \"removeParticipant\", \"leaveGroup\", \"sendAttachment\", \"delete\", \"pin\", \"unpin\", \"list-pins\", \"permissions\", \"thread-create\", \"thread-list\", \"thread-reply\", \"search\", \"sticker\", \"sticker-search\", \"member-info\", \"role-info\", \"emoji-list\", \"emoji-upload\", \"sticker-upload\", \"role-add\", \"role-remove\", \"channel-info\", \"channel-list\", \"channel-create\", \"channel-edit\", \"channel-delete\", \"channel-move\", \"category-create\", \"category-edit\", \"category-delete\", \"topic-create\", \"topic-edit\", \"voice-status\", \"event-list\", \"event-create\", \"timeout\", \"kick\", \"ban\", \"set-profile\", \"set-presence\", \"set-profile\", \"download-file\", \"upload-file\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_ACTION_NAMES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-action-names.ts"}
|
||||
{"declaration":"export const CHANNEL_MESSAGE_CAPABILITIES: readonly [\"interactive\", \"buttons\", \"cards\", \"components\", \"blocks\"];","entrypoint":"channel-runtime","exportName":"CHANNEL_MESSAGE_CAPABILITIES","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":1,"sourcePath":"src/channels/plugins/message-capabilities.ts"}
|
||||
{"declaration":"export const isWhatsAppGroupJid: (value: string) => boolean;","entrypoint":"channel-runtime","exportName":"isWhatsAppGroupJid","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":13,"sourcePath":"src/plugin-sdk/whatsapp-targets.ts"}
|
||||
{"declaration":"export const isWhatsAppUserTarget: (value: string) => boolean;","entrypoint":"channel-runtime","exportName":"isWhatsAppUserTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":15,"sourcePath":"src/plugin-sdk/whatsapp-targets.ts"}
|
||||
{"declaration":"export const normalizeWhatsAppTarget: (value: string) => string | null;","entrypoint":"channel-runtime","exportName":"normalizeWhatsAppTarget","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"const","recordType":"export","sourceLine":17,"sourcePath":"src/plugin-sdk/whatsapp-targets.ts"}
|
||||
{"declaration":"export type BaseProbeResult = BaseProbeResult<TError>;","entrypoint":"channel-runtime","exportName":"BaseProbeResult","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type BaseTokenResolution = BaseTokenResolution;","entrypoint":"channel-runtime","exportName":"BaseTokenResolution","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":566,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
{"declaration":"export type ChannelAccountSnapshot = ChannelAccountSnapshot;","entrypoint":"channel-runtime","exportName":"ChannelAccountSnapshot","importSpecifier":"openclaw/plugin-sdk/channel-runtime","kind":"type","recordType":"export","sourceLine":145,"sourcePath":"src/channels/plugins/types.core.ts"}
|
||||
@@ -412,54 +412,54 @@
|
||||
{"declaration":"export type ChannelPlugin = ChannelPlugin<ResolvedAccount, Probe, Audit>;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":77,"sourcePath":"src/channels/plugins/types.plugin.ts"}
|
||||
{"declaration":"export type GatewayBindUrlResult = GatewayBindUrlResult;","entrypoint":"core","exportName":"GatewayBindUrlResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/gateway-bind-url.ts"}
|
||||
{"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":115,"sourcePath":"src/gateway/server-methods/types.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1279,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"core","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1267,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"core","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1672,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1398,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":101,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1654,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1621,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1613,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":116,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":141,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1294,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1427,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":72,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"core","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1660,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"core","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1386,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"core","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"core","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1642,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"core","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1609,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"core","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1601,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"core","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":115,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"core","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":140,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"core","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1282,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"core","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1415,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"core","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":71,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginRuntime = PluginRuntime;","entrypoint":"core","exportName":"PluginRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":54,"sourcePath":"src/plugins/runtime/types.ts"}
|
||||
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":710,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":176,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":514,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":255,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":161,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":622,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"core","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":638,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":654,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":663,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":610,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":276,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":299,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":687,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":726,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":495,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":697,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":360,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":442,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":351,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":528,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":421,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":334,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":317,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":675,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"core","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":709,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"core","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":175,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"core","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":513,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"core","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"core","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"core","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":160,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"core","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":621,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"core","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":637,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":653,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"core","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":662,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"core","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":609,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":275,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"core","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":298,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":686,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"core","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":725,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"core","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":494,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"core","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":696,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"core","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":359,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"core","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":441,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":350,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"core","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":527,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"core","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":420,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"core","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":481,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"core","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":333,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"core","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":462,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"core","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":316,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"core","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":674,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderUsageSnapshot = ProviderUsageSnapshot;","entrypoint":"core","exportName":"ProviderUsageSnapshot","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/provider-usage.types.ts"}
|
||||
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":561,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"core","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type RoutePeer = RoutePeer;","entrypoint":"core","exportName":"RoutePeer","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":21,"sourcePath":"src/routing/resolve-route.ts"}
|
||||
{"declaration":"export type RoutePeerKind = ChatType;","entrypoint":"core","exportName":"RoutePeerKind","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":19,"sourcePath":"src/routing/resolve-route.ts"}
|
||||
{"declaration":"export type SecretFileReadOptions = SecretFileReadOptions;","entrypoint":"core","exportName":"SecretFileReadOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":7,"sourcePath":"src/infra/secret-file.ts"}
|
||||
{"declaration":"export type SecretFileReadResult = SecretFileReadResult;","entrypoint":"core","exportName":"SecretFileReadResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/infra/secret-file.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1254,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"core","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1242,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type TailscaleStatusCommandResult = TailscaleStatusCommandResult;","entrypoint":"core","exportName":"TailscaleStatusCommandResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/shared/tailscale-status.ts"}
|
||||
{"declaration":"export type TailscaleStatusCommandRunner = TailscaleStatusCommandRunner;","entrypoint":"core","exportName":"TailscaleStatusCommandRunner","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/shared/tailscale-status.ts"}
|
||||
{"declaration":"export type UsageProviderId = UsageProviderId;","entrypoint":"core","exportName":"UsageProviderId","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":20,"sourcePath":"src/infra/provider-usage.types.ts"}
|
||||
@@ -469,52 +469,52 @@
|
||||
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"plugin-entry","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":137,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
|
||||
{"declaration":"export function emptyPluginConfigSchema(): OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"emptyPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"function","recordType":"export","sourceLine":108,"sourcePath":"src/plugins/config-schema.ts"}
|
||||
{"declaration":"export type AnyAgentTool = AnyAgentTool;","entrypoint":"plugin-entry","exportName":"AnyAgentTool","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":9,"sourcePath":"src/agents/tools/common.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1279,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type MediaUnderstandingProviderPlugin = MediaUnderstandingProvider;","entrypoint":"plugin-entry","exportName":"MediaUnderstandingProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1267,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawConfig = OpenClawConfig;","entrypoint":"plugin-entry","exportName":"OpenClawConfig","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":32,"sourcePath":"src/config/types.openclaw.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1672,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1398,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":101,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1654,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1621,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1613,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":116,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":141,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1294,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1427,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":72,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":710,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":176,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":514,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":255,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":239,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":161,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":622,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":638,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":654,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":663,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":610,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":276,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":299,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":687,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":726,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":495,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":697,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeConfigContext = ProviderNormalizeConfigContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeConfigContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":386,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeModelIdContext = ProviderNormalizeModelIdContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeModelIdContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":375,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":360,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeTransportContext = ProviderNormalizeTransportContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeTransportContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":398,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":442,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":351,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":528,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":421,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveConfigApiKeyContext = ProviderResolveConfigApiKeyContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveConfigApiKeyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":410,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":334,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":463,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":317,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":675,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":561,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1254,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginApi = OpenClawPluginApi;","entrypoint":"plugin-entry","exportName":"OpenClawPluginApi","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1660,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginCommandDefinition = OpenClawPluginCommandDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginCommandDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1386,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginConfigSchema = OpenClawPluginConfigSchema;","entrypoint":"plugin-entry","exportName":"OpenClawPluginConfigSchema","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":100,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginDefinition = OpenClawPluginDefinition;","entrypoint":"plugin-entry","exportName":"OpenClawPluginDefinition","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1642,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginService = OpenClawPluginService;","entrypoint":"plugin-entry","exportName":"OpenClawPluginService","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1609,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginServiceContext = OpenClawPluginServiceContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginServiceContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1601,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolContext = OpenClawPluginToolContext;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":115,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type OpenClawPluginToolFactory = OpenClawPluginToolFactory;","entrypoint":"plugin-entry","exportName":"OpenClawPluginToolFactory","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":140,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginCommandContext = PluginCommandContext;","entrypoint":"plugin-entry","exportName":"PluginCommandContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1282,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginInteractiveTelegramHandlerContext = PluginInteractiveTelegramHandlerContext;","entrypoint":"plugin-entry","exportName":"PluginInteractiveTelegramHandlerContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1415,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type PluginLogger = PluginLogger;","entrypoint":"plugin-entry","exportName":"PluginLogger","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":71,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAugmentModelCatalogContext = ProviderAugmentModelCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderAugmentModelCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":709,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthContext = ProviderAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":175,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthDoctorHintContext = ProviderAuthDoctorHintContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthDoctorHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":513,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethod = ProviderAuthMethod;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethod","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":254,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthMethodNonInteractiveContext = ProviderAuthMethodNonInteractiveContext;","entrypoint":"plugin-entry","exportName":"ProviderAuthMethodNonInteractiveContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":238,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderAuthResult = ProviderAuthResult;","entrypoint":"plugin-entry","exportName":"ProviderAuthResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":160,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildMissingAuthMessageContext = ProviderBuildMissingAuthMessageContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildMissingAuthMessageContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":621,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuildUnknownModelHintContext = ProviderBuildUnknownModelHintContext;","entrypoint":"plugin-entry","exportName":"ProviderBuildUnknownModelHintContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":637,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionContext = ProviderBuiltInModelSuppressionContext;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":653,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderBuiltInModelSuppressionResult = ProviderBuiltInModelSuppressionResult;","entrypoint":"plugin-entry","exportName":"ProviderBuiltInModelSuppressionResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":662,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCacheTtlEligibilityContext = ProviderCacheTtlEligibilityContext;","entrypoint":"plugin-entry","exportName":"ProviderCacheTtlEligibilityContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":609,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderCatalogContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":275,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderCatalogResult = ProviderCatalogResult;","entrypoint":"plugin-entry","exportName":"ProviderCatalogResult","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":298,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDefaultThinkingPolicyContext = ProviderDefaultThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderDefaultThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":686,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderDiscoveryContext = ProviderCatalogContext;","entrypoint":"plugin-entry","exportName":"ProviderDiscoveryContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":725,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderFetchUsageSnapshotContext = ProviderFetchUsageSnapshotContext;","entrypoint":"plugin-entry","exportName":"ProviderFetchUsageSnapshotContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":494,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderModernModelPolicyContext = ProviderModernModelPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderModernModelPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":696,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeConfigContext = ProviderNormalizeConfigContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeConfigContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":385,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeModelIdContext = ProviderNormalizeModelIdContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeModelIdContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":374,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeResolvedModelContext = ProviderNormalizeResolvedModelContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeResolvedModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":359,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderNormalizeTransportContext = ProviderNormalizeTransportContext;","entrypoint":"plugin-entry","exportName":"ProviderNormalizeTransportContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":397,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPreparedRuntimeAuth = ProviderPreparedRuntimeAuth;","entrypoint":"plugin-entry","exportName":"ProviderPreparedRuntimeAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":441,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":350,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareExtraParamsContext = ProviderPrepareExtraParamsContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareExtraParamsContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":527,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderPrepareRuntimeAuthContext = ProviderPrepareRuntimeAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderPrepareRuntimeAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":420,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveConfigApiKeyContext = ProviderResolveConfigApiKeyContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveConfigApiKeyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":409,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolvedUsageAuth = ProviderResolvedUsageAuth;","entrypoint":"plugin-entry","exportName":"ProviderResolvedUsageAuth","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":481,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveDynamicModelContext = ProviderResolveDynamicModelContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveDynamicModelContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":333,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderResolveUsageAuthContext = ProviderResolveUsageAuthContext;","entrypoint":"plugin-entry","exportName":"ProviderResolveUsageAuthContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":462,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderRuntimeModel = ProviderRuntimeModel;","entrypoint":"plugin-entry","exportName":"ProviderRuntimeModel","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":316,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderThinkingPolicyContext = ProviderThinkingPolicyContext;","entrypoint":"plugin-entry","exportName":"ProviderThinkingPolicyContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":674,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type ProviderWrapStreamFnContext = ProviderWrapStreamFnContext;","entrypoint":"plugin-entry","exportName":"ProviderWrapStreamFnContext","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":560,"sourcePath":"src/plugins/types.ts"}
|
||||
{"declaration":"export type SpeechProviderPlugin = SpeechProviderPlugin;","entrypoint":"plugin-entry","exportName":"SpeechProviderPlugin","importSpecifier":"openclaw/plugin-sdk/plugin-entry","kind":"type","recordType":"export","sourceLine":1242,"sourcePath":"src/plugins/types.ts"}
|
||||
{"category":"provider","entrypoint":"provider-onboard","importSpecifier":"openclaw/plugin-sdk/provider-onboard","recordType":"module","sourceLine":1,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
|
||||
{"declaration":"export function applyAgentDefaultModelPrimary(cfg: OpenClawConfig, primary: string): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyAgentDefaultModelPrimary","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":267,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
|
||||
{"declaration":"export function applyOnboardAuthAgentModelsAndProviders(cfg: OpenClawConfig, params: { agentModels: Record<string, AgentModelEntryConfig>; providers: Record<string, ModelProviderConfig>; }): OpenClawConfig;","entrypoint":"provider-onboard","exportName":"applyOnboardAuthAgentModelsAndProviders","importSpecifier":"openclaw/plugin-sdk/provider-onboard","kind":"function","recordType":"export","sourceLine":244,"sourcePath":"src/plugin-sdk/provider-onboard.ts"}
|
||||
|
||||
@@ -47,10 +47,6 @@
|
||||
"source": "Quick Start",
|
||||
"target": "快速开始"
|
||||
},
|
||||
{
|
||||
"source": "Diffs",
|
||||
"target": "Diffs"
|
||||
},
|
||||
{
|
||||
"source": "Capability Cookbook",
|
||||
"target": "能力扩展手册"
|
||||
|
||||
@@ -129,7 +129,7 @@ Example `package.json`:
|
||||
}
|
||||
```
|
||||
|
||||
Each entry points to a hook directory containing `HOOK.md` and a handler file. The loader tries `handler.ts`, `handler.js`, `index.ts`, `index.js` in order.
|
||||
Each entry points to a hook directory containing `HOOK.md` and `handler.ts` (or `index.ts`).
|
||||
Hook packs can ship dependencies; they will be installed under `~/.openclaw/hooks/<id>`.
|
||||
Each `openclaw.hooks` entry must stay inside the package directory after symlink
|
||||
resolution; entries that escape are rejected.
|
||||
@@ -236,9 +236,6 @@ Each event includes:
|
||||
sessionId?: string,
|
||||
// Agent bootstrap events (agent:bootstrap):
|
||||
bootstrapFiles?: WorkspaceBootstrapFile[],
|
||||
sessionKey?: string, // routing session key
|
||||
sessionId?: string, // internal session UUID
|
||||
agentId?: string, // resolved agent ID
|
||||
// Message events (see Message Events section for full details):
|
||||
from?: string, // message:received
|
||||
to?: string, // message:sent
|
||||
@@ -268,25 +265,6 @@ Triggered when agent commands are issued:
|
||||
Internal hook payloads emit these as `type: "session"` with `action: "compact:before"` / `action: "compact:after"`; listeners subscribe with the combined keys above.
|
||||
Specific handler registration uses the literal key format `${type}:${action}`. For these events, register `session:compact:before` and `session:compact:after`.
|
||||
|
||||
`session:compact:before` context fields:
|
||||
|
||||
- `sessionId`: internal session UUID
|
||||
- `missingSessionKey`: true when no session key was available
|
||||
- `messageCount`: number of messages before compaction
|
||||
- `tokenCount`: token count before compaction (may be absent)
|
||||
- `messageCountOriginal`: message count from the full untruncated session history
|
||||
- `tokenCountOriginal`: token count of the full original history (may be absent)
|
||||
|
||||
`session:compact:after` context fields (in addition to `sessionId` and `missingSessionKey`):
|
||||
|
||||
- `messageCount`: message count after compaction
|
||||
- `tokenCount`: token count after compaction (may be absent)
|
||||
- `compactedCount`: number of messages that were compacted/removed
|
||||
- `summaryLength`: character length of the generated compaction summary
|
||||
- `tokensBefore`: token count from before compaction (for delta calculation)
|
||||
- `tokensAfter`: token count after compaction
|
||||
- `firstKeptEntryId`: ID of the first message entry retained after compaction
|
||||
|
||||
### Agent Events
|
||||
|
||||
- **`agent:bootstrap`**: Before workspace bootstrap files are injected (hooks may mutate `context.bootstrapFiles`)
|
||||
@@ -315,16 +293,12 @@ Session events include rich context about the session and changes:
|
||||
label?: string | null, // Human-readable session label
|
||||
|
||||
// AI model configuration
|
||||
model?: string | null, // Model override (e.g., "claude-sonnet-4-6")
|
||||
model?: string | null, // Model override (e.g., "claude-opus-4-5")
|
||||
thinkingLevel?: string | null, // Thinking level ("off"|"low"|"med"|"high")
|
||||
verboseLevel?: string | null, // Verbose output level
|
||||
reasoningLevel?: string | null, // Reasoning mode override
|
||||
elevatedLevel?: string | null, // Elevated mode override
|
||||
responseUsage?: "off" | "tokens" | "full" | "on" | null, // Usage display mode ("on" is backwards-compat alias for "full")
|
||||
fastMode?: boolean | null, // Fast/turbo mode toggle
|
||||
spawnedWorkspaceDir?: string | null, // Workspace dir override for spawned subagents
|
||||
subagentRole?: "orchestrator" | "leaf" | null, // Subagent role assignment
|
||||
subagentControlScope?: "children" | "none" | null, // Scope of subagent control
|
||||
responseUsage?: "off" | "tokens" | "full" | null, // Usage display mode
|
||||
|
||||
// Tool execution settings
|
||||
execHost?: string | null, // Exec host (sandbox|gateway|node)
|
||||
@@ -344,7 +318,7 @@ Session events include rich context about the session and changes:
|
||||
}
|
||||
```
|
||||
|
||||
**Security note:** Only privileged clients (including the Control UI) can trigger `session:patch` events. Standard WebChat clients are blocked from patching sessions, so the hook will not fire from those connections.
|
||||
**Security note:** Only privileged clients (including the Control UI) can trigger `session:patch` events. Standard WebChat clients are blocked from patching sessions (see PR #20800), so the hook will not fire from those connections.
|
||||
|
||||
See `SessionsPatchParamsSchema` in `src/gateway/protocol/schema/sessions.ts` for the complete type definition.
|
||||
|
||||
@@ -521,78 +495,6 @@ The `pluginId` field is stamped automatically by the hook runner from the plugin
|
||||
|
||||
If the gateway is unavailable or does not support plugin approvals, the tool call falls back to a soft block using the `description` as the block reason.
|
||||
|
||||
#### before_install
|
||||
|
||||
Runs after the built-in install security scan and before installation continues. OpenClaw fires this hook for interactive skill installs as well as plugin bundle, package, and single-file installs.
|
||||
|
||||
Return fields:
|
||||
|
||||
- **`findings`**: Additional scan findings to surface as warnings
|
||||
- **`block`**: Set to `true` to block the install
|
||||
- **`blockReason`**: Human-readable reason shown when blocked
|
||||
|
||||
Event fields:
|
||||
|
||||
- **`targetType`**: Install target category (`skill` or `plugin`)
|
||||
- **`targetName`**: Human-readable skill name or plugin id for the install target
|
||||
- **`sourcePath`**: Absolute path to the install target content being scanned
|
||||
- **`sourcePathKind`**: Whether the scanned content is a `file` or `directory`
|
||||
- **`origin`**: Normalized install origin when available (for example `openclaw-bundled`, `openclaw-workspace`, `plugin-bundle`, `plugin-package`, or `plugin-file`)
|
||||
- **`request`**: Provenance for the install request, including `kind`, `mode`, and optional `requestedSpecifier`
|
||||
- **`builtinScan`**: Structured result of the built-in scanner, including `status`, summary counts, findings, and optional `error`
|
||||
- **`skill`**: Skill install metadata when `targetType` is `skill`, including `installId` and the selected `installSpec`
|
||||
- **`plugin`**: Plugin install metadata when `targetType` is `plugin`, including the canonical `pluginId`, normalized `contentType`, optional `packageName` / `manifestId` / `version`, and `extensions`
|
||||
|
||||
Example event (plugin package install):
|
||||
|
||||
```json
|
||||
{
|
||||
"targetType": "plugin",
|
||||
"targetName": "acme-audit",
|
||||
"sourcePath": "/var/folders/.../openclaw-plugin-acme-audit/package",
|
||||
"sourcePathKind": "directory",
|
||||
"origin": "plugin-package",
|
||||
"request": {
|
||||
"kind": "plugin-npm",
|
||||
"mode": "install",
|
||||
"requestedSpecifier": "@acme/openclaw-plugin-audit@1.4.2"
|
||||
},
|
||||
"builtinScan": {
|
||||
"status": "ok",
|
||||
"scannedFiles": 12,
|
||||
"critical": 0,
|
||||
"warn": 1,
|
||||
"info": 0,
|
||||
"findings": [
|
||||
{
|
||||
"severity": "warn",
|
||||
"ruleId": "network_fetch",
|
||||
"file": "dist/index.js",
|
||||
"line": 88,
|
||||
"message": "Dynamic network fetch detected during install review."
|
||||
}
|
||||
]
|
||||
},
|
||||
"plugin": {
|
||||
"pluginId": "acme-audit",
|
||||
"contentType": "package",
|
||||
"packageName": "@acme/openclaw-plugin-audit",
|
||||
"manifestId": "acme-audit",
|
||||
"version": "1.4.2",
|
||||
"extensions": ["./dist/index.js"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Skill installs use the same event shape with `targetType: "skill"` and a `skill` object instead of `plugin`.
|
||||
|
||||
Decision semantics:
|
||||
|
||||
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_install`: `{ block: false }` is treated as no decision.
|
||||
|
||||
Use this hook for external security scanners, policy engines, or enterprise approval gates that need to audit install sources before they are installed.
|
||||
|
||||
#### Compaction lifecycle
|
||||
|
||||
Compaction lifecycle hooks exposed through the plugin hook runner:
|
||||
@@ -600,91 +502,12 @@ Compaction lifecycle hooks exposed through the plugin hook runner:
|
||||
- **`before_compaction`**: Runs before compaction with count/token metadata
|
||||
- **`after_compaction`**: Runs after compaction with compaction summary metadata
|
||||
|
||||
### Complete Plugin Hook Reference
|
||||
|
||||
All 27 hooks registered via the Plugin SDK. Hooks marked **sequential** run in priority order and can modify results; **parallel** hooks are fire-and-forget.
|
||||
|
||||
#### Model and prompt hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| ---------------------- | -------------------------------------------- | ---------- | ---------------------------------------------------------- |
|
||||
| `before_model_resolve` | Before model/provider lookup | Sequential | `{ modelOverride?, providerOverride? }` |
|
||||
| `before_prompt_build` | After model resolved, session messages ready | Sequential | `{ systemPrompt?, prependContext?, appendSystemContext? }` |
|
||||
| `before_agent_start` | Legacy combined hook (prefer the two above) | Sequential | Union of both result shapes |
|
||||
| `llm_input` | Immediately before the LLM API call | Parallel | `void` |
|
||||
| `llm_output` | Immediately after LLM response received | Parallel | `void` |
|
||||
|
||||
#### Agent lifecycle hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| ------------------- | ---------------------------------------------- | --------- | ------- |
|
||||
| `agent_end` | After agent run completes (success or failure) | Parallel | `void` |
|
||||
| `before_reset` | When `/new` or `/reset` clears a session | Parallel | `void` |
|
||||
| `before_compaction` | Before compaction summarizes history | Parallel | `void` |
|
||||
| `after_compaction` | After compaction completes | Parallel | `void` |
|
||||
|
||||
#### Session lifecycle hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| --------------- | ------------------------- | --------- | ------- |
|
||||
| `session_start` | When a new session begins | Parallel | `void` |
|
||||
| `session_end` | When a session ends | Parallel | `void` |
|
||||
|
||||
#### Message flow hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| ---------------------- | ------------------------------------------------- | -------------------- | ----------------------------- |
|
||||
| `inbound_claim` | Before command/agent dispatch; first-claim wins | Sequential | `{ handled: boolean }` |
|
||||
| `message_received` | After an inbound message is received | Parallel | `void` |
|
||||
| `before_dispatch` | After commands parsed, before model dispatch | Sequential | `{ handled: boolean, text? }` |
|
||||
| `message_sending` | Before an outbound message is delivered | Sequential | `{ content?, cancel? }` |
|
||||
| `message_sent` | After an outbound message is delivered | Parallel | `void` |
|
||||
| `before_message_write` | Before a message is written to session transcript | **Sync**, sequential | `{ block?, message? }` |
|
||||
|
||||
#### Tool execution hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| --------------------- | --------------------------------------------- | -------------------- | ----------------------------------------------------- |
|
||||
| `before_tool_call` | Before each tool call | Sequential | `{ params?, block?, blockReason?, requireApproval? }` |
|
||||
| `after_tool_call` | After a tool call completes | Parallel | `void` |
|
||||
| `tool_result_persist` | Before a tool result is written to transcript | **Sync**, sequential | `{ message? }` |
|
||||
|
||||
#### Subagent hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| -------------------------- | ------------------------------------------ | ---------- | --------------------------------- |
|
||||
| `subagent_spawning` | Before a subagent session is created | Sequential | `{ status, threadBindingReady? }` |
|
||||
| `subagent_delivery_target` | After spawning, to resolve delivery target | Sequential | `{ origin? }` |
|
||||
| `subagent_spawned` | After a subagent is fully spawned | Parallel | `void` |
|
||||
| `subagent_ended` | When a subagent session terminates | Parallel | `void` |
|
||||
|
||||
#### Gateway hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| --------------- | ------------------------------------------ | --------- | ------- |
|
||||
| `gateway_start` | After the gateway process is fully started | Parallel | `void` |
|
||||
| `gateway_stop` | When the gateway is shutting down | Parallel | `void` |
|
||||
|
||||
#### Install hooks
|
||||
|
||||
| Hook | When | Execution | Returns |
|
||||
| ---------------- | ----------------------------------------------------- | ---------- | ------------------------------------- |
|
||||
| `before_install` | After built-in security scan, before install proceeds | Sequential | `{ findings?, block?, blockReason? }` |
|
||||
|
||||
<Note>
|
||||
Two hooks (`tool_result_persist` and `before_message_write`) are **synchronous only** — they must not return a Promise. Returning a Promise from these hooks is caught at runtime and the result is discarded with a warning.
|
||||
</Note>
|
||||
|
||||
For full handler signatures and context types, see [Plugin Architecture](/plugins/architecture).
|
||||
|
||||
### Future Events
|
||||
|
||||
The following event types are planned for the internal hook event stream.
|
||||
Note that `session_start` and `session_end` already exist as [Plugin Hook API](/plugins/architecture#provider-runtime-hooks) hooks
|
||||
but are not yet available as internal hook event keys in `HOOK.md` metadata:
|
||||
Planned event types:
|
||||
|
||||
- **`session:start`**: When a new session begins (planned for internal hook stream; available as plugin hook `session_start`)
|
||||
- **`session:end`**: When a session ends (planned for internal hook stream; available as plugin hook `session_end`)
|
||||
- **`session:start`**: When a new session begins
|
||||
- **`session:end`**: When a session ends
|
||||
- **`agent:error`**: When an agent encounters an error
|
||||
|
||||
## Creating Custom Hooks
|
||||
@@ -1100,8 +923,8 @@ metadata: { "openclaw": { "events": ["command"] } } # General - more overhead
|
||||
|
||||
The gateway logs hook loading at startup:
|
||||
|
||||
```text
|
||||
Registered hook: session-memory -> command:new, command:reset
|
||||
```
|
||||
Registered hook: session-memory -> command:new
|
||||
Registered hook: bootstrap-extra-files -> agent:bootstrap
|
||||
Registered hook: command-logger -> command
|
||||
Registered hook: boot-md -> gateway:startup
|
||||
|
||||
@@ -103,7 +103,7 @@ openclaw config set channels.discord.enabled true --strict-json
|
||||
openclaw gateway
|
||||
```
|
||||
|
||||
If OpenClaw is already running as a background service, restart it via the OpenClaw Mac app or by stopping and restarting the `openclaw gateway run` process.
|
||||
If OpenClaw is already running as a background service, use `openclaw gateway restart` instead.
|
||||
|
||||
</Step>
|
||||
|
||||
@@ -948,15 +948,11 @@ Default slash command settings:
|
||||
Config path:
|
||||
|
||||
- `channels.discord.execApprovals.enabled`
|
||||
- `channels.discord.execApprovals.approvers` (optional; falls back to owner IDs inferred from `allowFrom` and explicit DM `defaultTo` when possible)
|
||||
- `channels.discord.execApprovals.approvers`
|
||||
- `channels.discord.execApprovals.target` (`dm` | `channel` | `both`, default: `dm`)
|
||||
- `agentFilter`, `sessionFilter`, `cleanupAfterResolve`
|
||||
|
||||
Discord becomes an approval client when `enabled: true` and at least one approver can be resolved, either from `execApprovals.approvers` or from the account's existing owner config (`allowFrom`, legacy `dm.allowFrom`, or explicit DM `defaultTo`).
|
||||
|
||||
When `target` is `channel` or `both`, the approval prompt is visible in the channel. Only resolved approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.
|
||||
|
||||
Discord also renders the shared approval buttons used by other chat channels. The native Discord adapter mainly adds approver DM routing and channel fanout.
|
||||
When `target` is `channel` or `both`, the approval prompt is visible in the channel. Only configured approvers can use the buttons; other users receive an ephemeral denial. Approval prompts include the command text, so only enable channel delivery in trusted channels. If the channel ID cannot be derived from the session key, OpenClaw falls back to DM delivery.
|
||||
|
||||
Gateway auth for this handler uses the same shared credential resolution contract as other Gateway clients:
|
||||
|
||||
@@ -965,7 +961,7 @@ Default slash command settings:
|
||||
- remote-mode support via `gateway.remote.*` when applicable
|
||||
- URL overrides are override-safe: CLI overrides do not reuse implicit credentials, and env overrides use env credentials only
|
||||
|
||||
Exec approvals expire after 30 minutes by default. If approvals fail with unknown approval IDs, verify approver resolution and feature enablement.
|
||||
If approvals fail with unknown approval IDs, verify approver list and feature enablement.
|
||||
|
||||
Related docs: [Exec approvals](/tools/exec-approvals)
|
||||
|
||||
@@ -996,7 +992,7 @@ Default gate behavior:
|
||||
|
||||
## Components v2 UI
|
||||
|
||||
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept `components` for custom UI (advanced; requires constructing a component payload via the discord tool), while legacy `embeds` remain available but are not recommended.
|
||||
OpenClaw uses Discord components v2 for exec approvals and cross-context markers. Discord message actions can also accept `components` for custom UI (advanced; requires Carbon component instances), while legacy `embeds` remain available but are not recommended.
|
||||
|
||||
- `channels.discord.ui.components.accentColor` sets the accent color used by Discord component containers (hex).
|
||||
- Set per account with `channels.discord.accounts.<id>.ui.components.accentColor`.
|
||||
|
||||
@@ -81,7 +81,7 @@ Lark (global) tenants should use [https://open.larksuite.com/app](https://open.l
|
||||
2. Fill in the app name + description
|
||||
3. Choose an app icon
|
||||
|
||||

|
||||

|
||||
|
||||
### 3. Copy credentials
|
||||
|
||||
@@ -92,7 +92,7 @@ From **Credentials & Basic Info**, copy:
|
||||
|
||||
❗ **Important:** keep the App Secret private.
|
||||
|
||||

|
||||

|
||||
|
||||
### 4. Configure permissions
|
||||
|
||||
@@ -126,7 +126,7 @@ On **Permissions**, click **Batch import** and paste:
|
||||
}
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
### 5. Enable bot capability
|
||||
|
||||
@@ -135,7 +135,7 @@ In **App Capability** > **Bot**:
|
||||
1. Enable bot capability
|
||||
2. Set the bot name
|
||||
|
||||

|
||||

|
||||
|
||||
### 6. Configure event subscription
|
||||
|
||||
@@ -151,7 +151,7 @@ In **Event Subscription**:
|
||||
|
||||
⚠️ If the gateway is not running, the long-connection setup may fail to save.
|
||||
|
||||

|
||||

|
||||
|
||||
### 7. Publish the app
|
||||
|
||||
@@ -206,7 +206,7 @@ When using webhook mode, set both `channels.feishu.verificationToken` and `chann
|
||||
|
||||
The screenshot below shows where to find the **Verification Token**. The **Encrypt Key** is listed in the same **Encryption** section.
|
||||
|
||||

|
||||

|
||||
|
||||
### Configure via environment variables
|
||||
|
||||
@@ -395,8 +395,6 @@ In addition to allowing the group itself, **all messages** in that group are gat
|
||||
|
||||
---
|
||||
|
||||
<a id="get-groupuser-ids"></a>
|
||||
|
||||
## Get group/user IDs
|
||||
|
||||
### Group IDs (chat_id)
|
||||
|
||||
@@ -54,8 +54,6 @@ If you want...
|
||||
- Direct chats use the main session (or per-sender if configured).
|
||||
- Heartbeats are skipped for group sessions.
|
||||
|
||||
<a id="pattern-personal-dms-public-groups-single-agent"></a>
|
||||
|
||||
## Pattern: personal DMs + public groups (single agent)
|
||||
|
||||
Yes — this works well if your “personal” traffic is **DMs** and your “public” traffic is **groups**.
|
||||
|
||||
@@ -806,23 +806,21 @@ openclaw message poll --channel telegram --target -1001234567890:topic:42 \
|
||||
Config path:
|
||||
|
||||
- `channels.telegram.execApprovals.enabled`
|
||||
- `channels.telegram.execApprovals.approvers` (optional; falls back to numeric owner IDs inferred from `allowFrom` and direct `defaultTo` when possible)
|
||||
- `channels.telegram.execApprovals.approvers`
|
||||
- `channels.telegram.execApprovals.target` (`dm` | `channel` | `both`, default: `dm`)
|
||||
- `agentFilter`, `sessionFilter`
|
||||
|
||||
Approvers must be numeric Telegram user IDs. Telegram becomes an exec approval client when `enabled` is true and at least one approver can be resolved, either from `execApprovals.approvers` or from the account's numeric owner config (`allowFrom` and direct-message `defaultTo`). Approval requests otherwise fall back to other configured approval routes or the exec approval fallback policy.
|
||||
|
||||
Telegram also renders the shared approval buttons used by other chat channels. The native Telegram adapter mainly adds approver DM routing, channel/topic fanout, and typing hints before delivery.
|
||||
Approvers must be numeric Telegram user IDs. When `enabled` is false or `approvers` is empty, Telegram does not act as an exec approval client. Approval requests fall back to other configured approval routes or the exec approval fallback policy.
|
||||
|
||||
Delivery rules:
|
||||
|
||||
- `target: "dm"` sends approval prompts only to resolved approver DMs
|
||||
- `target: "dm"` sends approval prompts only to configured approver DMs
|
||||
- `target: "channel"` sends the prompt back to the originating Telegram chat/topic
|
||||
- `target: "both"` sends to approver DMs and the originating chat/topic
|
||||
|
||||
Only resolved approvers can approve or deny. Non-approvers cannot use `/approve` and cannot use Telegram approval buttons.
|
||||
Only configured approvers can approve or deny. Non-approvers cannot use `/approve` and cannot use Telegram approval buttons.
|
||||
|
||||
Channel delivery shows the command text in the chat, so only enable `channel` or `both` in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for both the approval prompt and the post-approval follow-up. Exec approvals expire after 30 minutes by default.
|
||||
Channel delivery shows the command text in the chat, so only enable `channel` or `both` in trusted groups/topics. When the prompt lands in a forum topic, OpenClaw preserves the topic for both the approval prompt and the post-approval follow-up.
|
||||
|
||||
Inline approval buttons also depend on `channels.telegram.capabilities.inlineButtons` allowing the target surface (`dm`, `group`, or `all`).
|
||||
|
||||
@@ -934,7 +932,7 @@ Primary reference:
|
||||
- top-level `bindings[]` with `type: "acp"` and canonical topic id `chatId:topic:topicId` in `match.peer.id`: persistent ACP topic binding fields (see [ACP Agents](/tools/acp-agents#channel-specific-settings)).
|
||||
- `channels.telegram.direct.<id>.topics.<threadId>.agentId`: route DM topics to a specific agent (same behavior as forum topics).
|
||||
- `channels.telegram.execApprovals.enabled`: enable Telegram as a chat-based exec approval client for this account.
|
||||
- `channels.telegram.execApprovals.approvers`: Telegram user IDs allowed to approve or deny exec requests. Optional when `channels.telegram.allowFrom` or a direct `channels.telegram.defaultTo` already identifies the owner.
|
||||
- `channels.telegram.execApprovals.approvers`: Telegram user IDs allowed to approve or deny exec requests. Required when exec approvals are enabled.
|
||||
- `channels.telegram.execApprovals.target`: `dm | channel | both` (default: `dm`). `channel` and `both` preserve the originating Telegram topic when present.
|
||||
- `channels.telegram.execApprovals.agentFilter`: optional agent ID filter for forwarded approval prompts.
|
||||
- `channels.telegram.execApprovals.sessionFilter`: optional session key filter (substring or regex) for forwarded approval prompts.
|
||||
|
||||
@@ -147,10 +147,6 @@ Per-session `mcpServers` are not supported in bridge mode. If an ACP client
|
||||
sends them during `newSession` or `loadSession`, the bridge returns a clear
|
||||
error instead of silently ignoring them.
|
||||
|
||||
If you want ACPX-backed sessions to see OpenClaw plugin tools, enable the
|
||||
gateway-side ACPX plugin bridge instead of trying to pass per-session
|
||||
`mcpServers`. See [ACP Agents](/tools/acp-agents#plugin-tools-mcp-bridge).
|
||||
|
||||
## Use from `acpx` (Codex, Claude, other ACP clients)
|
||||
|
||||
If you want a coding agent such as Codex or Claude Code to talk to your
|
||||
|
||||
@@ -64,7 +64,6 @@ This page describes the current CLI behavior. If commands change, update this do
|
||||
|
||||
- `--dev`: isolate state under `~/.openclaw-dev` and shift default ports.
|
||||
- `--profile <name>`: isolate state under `~/.openclaw-<name>`.
|
||||
- `--container <name>`: target a named container for execution.
|
||||
- `--no-color`: disable ANSI colors.
|
||||
- `--update`: shorthand for `openclaw update` (source installs only).
|
||||
- `-V`, `--version`, `-v`: print version and exit.
|
||||
@@ -156,21 +155,11 @@ openclaw [--dev] [--profile <name>] <command>
|
||||
list
|
||||
add
|
||||
delete
|
||||
bindings
|
||||
bind
|
||||
unbind
|
||||
set-identity
|
||||
acp
|
||||
mcp
|
||||
status
|
||||
health
|
||||
sessions
|
||||
cleanup
|
||||
tasks
|
||||
list
|
||||
show
|
||||
notify
|
||||
cancel
|
||||
gateway
|
||||
call
|
||||
health
|
||||
@@ -204,7 +193,7 @@ openclaw [--dev] [--profile <name>] <command>
|
||||
fallbacks list|add|remove|clear
|
||||
image-fallbacks list|add|remove|clear
|
||||
scan
|
||||
auth add|login|login-github-copilot|setup-token|paste-token
|
||||
auth add|setup-token|paste-token
|
||||
auth order get|set|clear
|
||||
sandbox
|
||||
list
|
||||
@@ -360,18 +349,7 @@ Options:
|
||||
- `--non-interactive`
|
||||
- `--mode <local|remote>`
|
||||
- `--flow <quickstart|advanced|manual>` (manual is an alias for advanced)
|
||||
- `--auth-choice <choice>` where `<choice>` is one of:
|
||||
`setup-token`, `token`, `chutes`, `deepseek-api-key`, `openai-codex`, `openai-api-key`,
|
||||
`openrouter-api-key`, `kilocode-api-key`, `litellm-api-key`, `ai-gateway-api-key`,
|
||||
`cloudflare-ai-gateway-api-key`, `moonshot-api-key`, `moonshot-api-key-cn`,
|
||||
`kimi-code-api-key`, `synthetic-api-key`, `venice-api-key`, `together-api-key`,
|
||||
`huggingface-api-key`, `apiKey`, `gemini-api-key`, `google-gemini-cli`, `zai-api-key`,
|
||||
`zai-coding-global`, `zai-coding-cn`, `zai-global`, `zai-cn`, `xiaomi-api-key`,
|
||||
`minimax-global-oauth`, `minimax-global-api`, `minimax-cn-oauth`, `minimax-cn-api`,
|
||||
`opencode-zen`, `opencode-go`, `github-copilot`, `copilot-proxy`, `xai-api-key`,
|
||||
`mistral-api-key`, `volcengine-api-key`, `byteplus-api-key`, `qianfan-api-key`,
|
||||
`modelstudio-standard-api-key-cn`, `modelstudio-standard-api-key`,
|
||||
`modelstudio-api-key-cn`, `modelstudio-api-key`, `custom-api-key`, `skip`
|
||||
- `--auth-choice <setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ollama|ai-gateway-api-key|moonshot-api-key|moonshot-api-key-cn|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|mistral-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|opencode-go|custom-api-key|skip>`
|
||||
- `--token-provider <id>` (non-interactive; used with `--auth-choice token`)
|
||||
- `--token <token>` (non-interactive; used with `--auth-choice token`)
|
||||
- `--token-profile-id <id>` (non-interactive; default: `<provider>:manual`)
|
||||
@@ -389,8 +367,8 @@ Options:
|
||||
- `--minimax-api-key <key>`
|
||||
- `--opencode-zen-api-key <key>`
|
||||
- `--opencode-go-api-key <key>`
|
||||
- `--custom-base-url <url>` (non-interactive; used with `--auth-choice custom-api-key`)
|
||||
- `--custom-model-id <id>` (non-interactive; used with `--auth-choice custom-api-key`)
|
||||
- `--custom-base-url <url>` (non-interactive; used with `--auth-choice custom-api-key` or `--auth-choice ollama`)
|
||||
- `--custom-model-id <id>` (non-interactive; used with `--auth-choice custom-api-key` or `--auth-choice ollama`)
|
||||
- `--custom-api-key <key>` (non-interactive; optional; used with `--auth-choice custom-api-key`; falls back to `CUSTOM_API_KEY` when omitted)
|
||||
- `--custom-provider-id <id>` (non-interactive; optional custom provider id)
|
||||
- `--custom-compatibility <openai|anthropic>` (non-interactive; optional; default `openai`)
|
||||
@@ -409,11 +387,8 @@ Options:
|
||||
- `--daemon-runtime <node|bun>`
|
||||
- `--skip-channels`
|
||||
- `--skip-skills`
|
||||
- `--skip-search`
|
||||
- `--skip-health`
|
||||
- `--skip-ui`
|
||||
- `--cloudflare-ai-gateway-account-id <id>`
|
||||
- `--cloudflare-ai-gateway-gateway-id <id>`
|
||||
- `--node-manager <npm|pnpm|bun>` (pnpm recommended; bun not recommended for Gateway runtime)
|
||||
- `--json`
|
||||
|
||||
@@ -454,9 +429,6 @@ Options:
|
||||
- `--yes`: accept defaults without prompting (headless).
|
||||
- `--non-interactive`: skip prompts; apply safe migrations only.
|
||||
- `--deep`: scan system services for extra gateway installs.
|
||||
- `--repair` (alias: `--fix`): attempt automatic repairs for detected issues.
|
||||
- `--force`: force repairs even when not strictly needed.
|
||||
- `--generate-gateway-token`: generate a new gateway auth token.
|
||||
|
||||
## Channel helpers
|
||||
|
||||
@@ -610,19 +582,15 @@ Run one agent turn via the Gateway (or `--local` embedded).
|
||||
|
||||
Required:
|
||||
|
||||
- `-m, --message <text>`
|
||||
- `--message <text>`
|
||||
|
||||
Options:
|
||||
|
||||
- `-t, --to <dest>` (for session key and optional delivery)
|
||||
- `--to <dest>` (for session key and optional delivery)
|
||||
- `--session-id <id>`
|
||||
- `--agent <id>` (agent id; overrides routing bindings)
|
||||
- `--thinking <off|minimal|low|medium|high|xhigh>` (provider support varies; not model-gated at CLI level)
|
||||
- `--verbose <on|off>`
|
||||
- `--channel <channel>` (delivery channel; omit to use the main session channel)
|
||||
- `--reply-to <target>` (delivery target override, separate from session routing)
|
||||
- `--reply-channel <channel>` (delivery channel override)
|
||||
- `--reply-account <id>` (delivery account id override)
|
||||
- `--thinking <off|minimal|low|medium|high|xhigh>` (GPT-5.2 + Codex models only)
|
||||
- `--verbose <on|full|off>`
|
||||
- `--channel <whatsapp|telegram|discord|slack|mattermost|signal|imessage|msteams>`
|
||||
- `--local`
|
||||
- `--deliver`
|
||||
- `--json`
|
||||
@@ -756,12 +724,6 @@ Options:
|
||||
- `--verbose`
|
||||
- `--store <path>`
|
||||
- `--active <minutes>`
|
||||
- `--agent <id>` (filter sessions by agent)
|
||||
- `--all-agents` (show sessions across all agents)
|
||||
|
||||
Subcommands:
|
||||
|
||||
- `sessions cleanup` — remove expired or orphaned sessions
|
||||
|
||||
## Reset / Uninstall
|
||||
|
||||
@@ -799,15 +761,6 @@ Notes:
|
||||
|
||||
- `--non-interactive` requires `--yes` and explicit scopes (or `--all`).
|
||||
|
||||
### `tasks`
|
||||
|
||||
List and manage task runs across agents.
|
||||
|
||||
- `tasks list` — show active and recent task runs
|
||||
- `tasks show <id>` — show details for a specific task run
|
||||
- `tasks notify <id>` — send a notification for a task run
|
||||
- `tasks cancel <id>` — cancel a running task
|
||||
|
||||
## Gateway
|
||||
|
||||
### `gateway`
|
||||
@@ -865,16 +818,10 @@ Notes:
|
||||
|
||||
Tail Gateway file logs via RPC.
|
||||
|
||||
Options:
|
||||
Notes:
|
||||
|
||||
- `--limit <n>`: maximum number of log lines to return
|
||||
- `--max-bytes <n>`: maximum bytes to read from the log file
|
||||
- `--follow`: follow the log file (tail -f style)
|
||||
- `--interval <ms>`: polling interval in ms when following
|
||||
- `--local-time`: display timestamps in local time
|
||||
- `--json`: emit line-delimited JSON
|
||||
- `--plain`: disable structured formatting
|
||||
- `--no-color`: disable ANSI colors
|
||||
- TTY sessions render a colorized, structured view; non-TTY falls back to plain text.
|
||||
- `--json` emits line-delimited JSON (one log event per line).
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -931,10 +878,9 @@ Anthropic Claude CLI migration:
|
||||
|
||||
```bash
|
||||
openclaw models auth login --provider anthropic --method cli --set-default
|
||||
openclaw onboard --auth-choice anthropic-cli
|
||||
```
|
||||
|
||||
Note: `--auth-choice anthropic-cli` is a deprecated legacy alias. Use `models auth login` instead.
|
||||
|
||||
### `models` (root)
|
||||
|
||||
`openclaw models` is an alias for `models status`.
|
||||
@@ -1022,13 +968,11 @@ Options:
|
||||
- `--set-image`
|
||||
- `--json`
|
||||
|
||||
### `models auth add|login|login-github-copilot|setup-token|paste-token`
|
||||
### `models auth add|setup-token|paste-token`
|
||||
|
||||
Options:
|
||||
|
||||
- `add`: interactive auth helper
|
||||
- `login`: `--provider <name>`, `--method <method>`, `--set-default`
|
||||
- `login-github-copilot`: GitHub Copilot OAuth login flow
|
||||
- `setup-token`: `--provider <name>` (default `anthropic`), `--yes`
|
||||
- `paste-token`: `--provider <name>`, `--profile-id <id>`, `--expires-in <duration>`
|
||||
|
||||
@@ -1129,6 +1073,7 @@ Subcommands:
|
||||
- `nodes reject <requestId>`
|
||||
- `nodes rename --node <id|name|ip> --name <displayName>`
|
||||
- `nodes invoke --node <id|name|ip> --command <command> [--params <json>] [--invoke-timeout <ms>] [--idempotency-key <key>]`
|
||||
- `nodes run --node <id|name|ip> [--cwd <path>] [--env KEY=VAL] [--command-timeout <ms>] [--needs-screen-recording] [--invoke-timeout <ms>] <command...>` (mac node or headless node host)
|
||||
- `nodes notify --node <id|name|ip> [--title <text>] [--body <text>] [--sound <name>] [--priority <passive|active|timeSensitive>] [--delivery <system|overlay|auto>] [--invoke-timeout <ms>]` (mac only)
|
||||
|
||||
Camera:
|
||||
|
||||
@@ -410,45 +410,13 @@ Example config shape:
|
||||
}
|
||||
```
|
||||
|
||||
### Stdio transport
|
||||
Typical fields:
|
||||
|
||||
Launches a local child process and communicates over stdin/stdout.
|
||||
|
||||
| Field | Description |
|
||||
| -------------------------- | --------------------------------- |
|
||||
| `command` | Executable to spawn (required) |
|
||||
| `args` | Array of command-line arguments |
|
||||
| `env` | Extra environment variables |
|
||||
| `cwd` / `workingDirectory` | Working directory for the process |
|
||||
|
||||
### SSE / HTTP transport
|
||||
|
||||
Connects to a remote MCP server over HTTP Server-Sent Events.
|
||||
|
||||
| Field | Description |
|
||||
| --------- | ---------------------------------------------------------------- |
|
||||
| `url` | HTTP or HTTPS URL of the remote server (required) |
|
||||
| `headers` | Optional key-value map of HTTP headers (for example auth tokens) |
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"remote-tools": {
|
||||
"url": "https://mcp.example.com",
|
||||
"headers": {
|
||||
"Authorization": "Bearer <token>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sensitive values in `url` (userinfo) and `headers` are redacted in logs and
|
||||
status output.
|
||||
- `command`
|
||||
- `args`
|
||||
- `env`
|
||||
- `cwd` or `workingDirectory`
|
||||
- `url`
|
||||
|
||||
These commands manage saved config only. They do not start the channel bridge,
|
||||
open a live MCP client session, or prove the target server is reachable.
|
||||
@@ -462,6 +430,6 @@ Current limits:
|
||||
- conversation discovery depends on existing Gateway session route metadata
|
||||
- no generic push protocol beyond the Claude-specific adapter
|
||||
- no message edit or react tools yet
|
||||
- HTTP/SSE transport connects to a single remote server; no multiplexed upstream yet
|
||||
- no dedicated HTTP MCP transport yet
|
||||
- `permissions_list_open` only includes approvals observed while the bridge is
|
||||
connected
|
||||
|
||||
@@ -37,10 +37,13 @@ openclaw nodes status --last-connected 24h
|
||||
Use `--connected` to only show currently-connected nodes. Use `--last-connected <duration>` to
|
||||
filter to nodes that connected within a duration (e.g. `24h`, `7d`).
|
||||
|
||||
## Invoke
|
||||
## Invoke / run
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node <id|name|ip> --command <command> --params <json>
|
||||
openclaw nodes run --node <id|name|ip> <command...>
|
||||
openclaw nodes run --raw "git status"
|
||||
openclaw nodes run --agent main --node <id|name|ip> --raw "git status"
|
||||
```
|
||||
|
||||
Invoke flags:
|
||||
@@ -48,8 +51,25 @@ Invoke flags:
|
||||
- `--params <json>`: JSON object string (default `{}`).
|
||||
- `--invoke-timeout <ms>`: node invoke timeout (default `15000`).
|
||||
- `--idempotency-key <key>`: optional idempotency key.
|
||||
- `system.run` and `system.run.prepare` are blocked here; use the `exec` tool with `host=node` for shell execution.
|
||||
|
||||
For shell execution on a node, use the `exec` tool with `host=node` instead of `openclaw nodes run`.
|
||||
The `nodes` CLI is now capability-focused: direct RPC via `nodes invoke`, plus pairing, camera,
|
||||
screen, location, canvas, and notifications.
|
||||
### Exec-style defaults
|
||||
|
||||
`nodes run` mirrors the model’s exec behavior (defaults + approvals):
|
||||
|
||||
- Reads `tools.exec.*` (plus `agents.list[].tools.exec.*` overrides).
|
||||
- Uses exec approvals (`exec.approval.request`) before invoking `system.run`.
|
||||
- `--node` can be omitted when `tools.exec.node` is set.
|
||||
- Requires a node that advertises `system.run` (macOS companion app or headless node host).
|
||||
|
||||
Flags:
|
||||
|
||||
- `--cwd <path>`: working directory.
|
||||
- `--env <key=val>`: env override (repeatable). Note: node hosts ignore `PATH` overrides (and `tools.exec.pathPrepend` is not applied to node hosts).
|
||||
- `--command-timeout <ms>`: command timeout.
|
||||
- `--invoke-timeout <ms>`: node invoke timeout (default `30000`).
|
||||
- `--needs-screen-recording`: require screen recording permission.
|
||||
- `--raw <command>`: run a shell string (`/bin/sh -lc` or `cmd.exe /c`).
|
||||
In allowlist mode on Windows node hosts, `cmd.exe /c` shell-wrapper runs require approval
|
||||
(allowlist entry alone does not auto-allow the wrapper form).
|
||||
- `--agent <id>`: agent-scoped approvals/allowlists (defaults to configured agent).
|
||||
- `--ask <off|on-miss|always>`, `--security <deny|allowlist|full>`: overrides.
|
||||
|
||||
@@ -87,7 +87,6 @@ These run inside the agent loop or gateway pipeline:
|
||||
- **`agent_end`**: inspect the final message list and run metadata after completion.
|
||||
- **`before_compaction` / `after_compaction`**: observe or annotate compaction cycles.
|
||||
- **`before_tool_call` / `after_tool_call`**: intercept tool params/results.
|
||||
- **`before_install`**: inspect built-in scan findings and optionally block skill or plugin installs.
|
||||
- **`tool_result_persist`**: synchronously transform tool results before they are written to the session transcript.
|
||||
- **`message_received` / `message_sending` / `message_sent`**: inbound + outbound message hooks.
|
||||
- **`session_start` / `session_end`**: session lifecycle boundaries.
|
||||
@@ -97,8 +96,6 @@ Hook decision rules for outbound/tool guards:
|
||||
|
||||
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_tool_call`: `{ block: false }` is a no-op and does not clear a prior block.
|
||||
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_install`: `{ block: false }` is a no-op and does not clear a prior block.
|
||||
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
|
||||
- `message_sending`: `{ cancel: false }` is a no-op and does not clear a prior cancel.
|
||||
|
||||
|
||||
@@ -1,86 +1,123 @@
|
||||
---
|
||||
summary: "How OpenClaw summarizes long conversations to stay within model limits"
|
||||
summary: "Context window + compaction: how OpenClaw keeps sessions under model limits"
|
||||
read_when:
|
||||
- You want to understand auto-compaction and /compact
|
||||
- You are debugging long sessions hitting context limits
|
||||
title: "Compaction"
|
||||
---
|
||||
|
||||
# Compaction
|
||||
# Context Window & Compaction
|
||||
|
||||
Every model has a context window -- the maximum number of tokens it can process.
|
||||
When a conversation approaches that limit, OpenClaw **compacts** older messages
|
||||
into a summary so the chat can continue.
|
||||
Every model has a **context window** (max tokens it can see). Long-running chats accumulate messages and tool results; once the window is tight, OpenClaw **compacts** older history to stay within limits.
|
||||
|
||||
## How it works
|
||||
## What compaction is
|
||||
|
||||
1. Older conversation turns are summarized into a compact entry.
|
||||
2. The summary is saved in the session transcript.
|
||||
3. Recent messages are kept intact.
|
||||
Compaction **summarizes older conversation** into a compact summary entry and keeps recent messages intact. The summary is stored in the session history, so future requests use:
|
||||
|
||||
The full conversation history stays on disk. Compaction only changes what the
|
||||
model sees on the next turn.
|
||||
- The compaction summary
|
||||
- Recent messages after the compaction point
|
||||
|
||||
## Auto-compaction
|
||||
Compaction **persists** in the session’s JSONL history.
|
||||
|
||||
Auto-compaction is on by default. It runs when the session nears the context
|
||||
limit, or when the model returns a context-overflow error (in which case
|
||||
OpenClaw compacts and retries).
|
||||
## Configuration
|
||||
|
||||
<Info>
|
||||
Before compacting, OpenClaw automatically reminds the agent to save important
|
||||
notes to [memory](/concepts/memory) files. This prevents context loss.
|
||||
</Info>
|
||||
Use the `agents.defaults.compaction` setting in your `openclaw.json` to configure compaction behavior (mode, target tokens, etc.).
|
||||
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). You can override this with `identifierPolicy: "off"` or provide custom text with `identifierPolicy: "custom"` and `identifierInstructions`.
|
||||
|
||||
## Manual compaction
|
||||
You can optionally specify a different model for compaction summarization via `agents.defaults.compaction.model`. This is useful when your primary model is a local or small model and you want compaction summaries produced by a more capable model. The override accepts any `provider/model-id` string:
|
||||
|
||||
Type `/compact` in any chat to force a compaction. Add instructions to guide
|
||||
the summary:
|
||||
|
||||
```
|
||||
/compact Focus on the API design decisions
|
||||
```
|
||||
|
||||
## Using a different model
|
||||
|
||||
By default, compaction uses your agent's primary model. You can use a more
|
||||
capable model for better summaries:
|
||||
|
||||
```json5
|
||||
```json
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
compaction: {
|
||||
model: "openrouter/anthropic/claude-sonnet-4-6",
|
||||
},
|
||||
},
|
||||
},
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"compaction": {
|
||||
"model": "openrouter/anthropic/claude-sonnet-4-6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This also works with local models, for example a second Ollama model dedicated to summarization or a fine-tuned compaction specialist:
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"compaction": {
|
||||
"model": "ollama/llama3.1:8b"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When unset, compaction uses the agent's primary model.
|
||||
|
||||
## Auto-compaction (default on)
|
||||
|
||||
When a session nears or exceeds the model’s context window, OpenClaw triggers auto-compaction and may retry the original request using the compacted context.
|
||||
|
||||
You’ll see:
|
||||
|
||||
- `🧹 Auto-compaction complete` in verbose mode
|
||||
- `/status` showing `🧹 Compactions: <count>`
|
||||
|
||||
Before compaction, OpenClaw can run a **silent memory flush** turn to store
|
||||
durable notes to disk. See [Memory](/concepts/memory) for details and config.
|
||||
|
||||
## Manual compaction
|
||||
|
||||
Use `/compact` (optionally with instructions) to force a compaction pass:
|
||||
|
||||
```
|
||||
/compact Focus on decisions and open questions
|
||||
```
|
||||
|
||||
## Context window source
|
||||
|
||||
Context window is model-specific. OpenClaw uses the model definition from the configured provider catalog to determine limits.
|
||||
|
||||
## Compaction vs pruning
|
||||
|
||||
| | Compaction | Pruning |
|
||||
| ---------------- | ----------------------------- | -------------------------------- |
|
||||
| **What it does** | Summarizes older conversation | Trims old tool results |
|
||||
| **Saved?** | Yes (in session transcript) | No (in-memory only, per request) |
|
||||
| **Scope** | Entire conversation | Tool results only |
|
||||
- **Compaction**: summarises and **persists** in JSONL.
|
||||
- **Session pruning**: trims old **tool results** only, **in-memory**, per request.
|
||||
|
||||
[Session pruning](/concepts/session-pruning) is a lighter-weight complement that
|
||||
trims tool output without summarizing.
|
||||
See [/concepts/session-pruning](/concepts/session-pruning) for pruning details.
|
||||
|
||||
## Troubleshooting
|
||||
## OpenAI server-side compaction
|
||||
|
||||
**Compacting too often?** The model's context window may be small, or tool
|
||||
outputs may be large. Try enabling
|
||||
[session pruning](/concepts/session-pruning).
|
||||
OpenClaw also supports OpenAI Responses server-side compaction hints for
|
||||
compatible direct OpenAI models. This is separate from local OpenClaw
|
||||
compaction and can run alongside it.
|
||||
|
||||
**Context feels stale after compaction?** Use `/compact Focus on <topic>` to
|
||||
guide the summary, or enable the [memory flush](/concepts/memory) so notes
|
||||
survive.
|
||||
- Local compaction: OpenClaw summarizes and persists into session JSONL.
|
||||
- Server-side compaction: OpenAI compacts context on the provider side when
|
||||
`store` + `context_management` are enabled.
|
||||
|
||||
**Need a clean slate?** `/new` starts a fresh session without compacting.
|
||||
See [OpenAI provider](/providers/openai) for model params and overrides.
|
||||
|
||||
For advanced configuration (reserve tokens, identifier preservation, custom
|
||||
context engines, OpenAI server-side compaction), see the
|
||||
[Session Management Deep Dive](/reference/session-management-compaction).
|
||||
## Custom context engines
|
||||
|
||||
Compaction behavior is owned by the active
|
||||
[context engine](/concepts/context-engine). The legacy engine uses the built-in
|
||||
summarization described above. Plugin engines (selected via
|
||||
`plugins.slots.contextEngine`) can implement any compaction strategy — DAG
|
||||
summaries, vector retrieval, incremental condensation, etc.
|
||||
|
||||
When a plugin engine sets `ownsCompaction: true`, OpenClaw delegates all
|
||||
compaction decisions to the engine and does not run built-in auto-compaction.
|
||||
|
||||
When `ownsCompaction` is `false` or unset, OpenClaw may still use Pi's
|
||||
built-in in-attempt auto-compaction, but the active engine's `compact()` method
|
||||
still handles `/compact` and overflow recovery. There is no automatic fallback
|
||||
to the legacy engine's compaction path.
|
||||
|
||||
If you are building a non-owning context engine, implement `compact()` by
|
||||
calling `delegateCompactionToRuntime(...)` from `openclaw/plugin-sdk/core`.
|
||||
|
||||
## Tips
|
||||
|
||||
- Use `/compact` when sessions feel stale or context is bloated.
|
||||
- Large tool outputs are already truncated; pruning can further reduce tool-result buildup.
|
||||
- If you need a fresh slate, `/new` or `/reset` starts a new session id.
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
title: "Builtin Memory Engine"
|
||||
summary: "The default SQLite-based memory backend with keyword, vector, and hybrid search"
|
||||
read_when:
|
||||
- You want to understand the default memory backend
|
||||
- You want to configure embedding providers or hybrid search
|
||||
---
|
||||
|
||||
# Builtin Memory Engine
|
||||
|
||||
The builtin engine is the default memory backend. It stores your memory index in
|
||||
a per-agent SQLite database and needs no extra dependencies to get started.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Keyword search** via FTS5 full-text indexing (BM25 scoring).
|
||||
- **Vector search** via embeddings from any supported provider.
|
||||
- **Hybrid search** that combines both for best results.
|
||||
- **CJK support** via trigram tokenization for Chinese, Japanese, and Korean.
|
||||
- **sqlite-vec acceleration** for in-database vector queries (optional).
|
||||
|
||||
## Getting started
|
||||
|
||||
If you have an API key for OpenAI, Gemini, Voyage, or Mistral, the builtin
|
||||
engine auto-detects it and enables vector search. No config needed.
|
||||
|
||||
To set a provider explicitly:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Without an embedding provider, only keyword search is available.
|
||||
|
||||
## Supported embedding providers
|
||||
|
||||
| Provider | ID | Auto-detected | Notes |
|
||||
| -------- | --------- | ------------- | ----------------------------------- |
|
||||
| OpenAI | `openai` | Yes | Default: `text-embedding-3-small` |
|
||||
| Gemini | `gemini` | Yes | Supports multimodal (image + audio) |
|
||||
| Voyage | `voyage` | Yes | |
|
||||
| Mistral | `mistral` | Yes | |
|
||||
| Ollama | `ollama` | No | Local, set explicitly |
|
||||
| Local | `local` | Yes (first) | GGUF model, ~0.6 GB download |
|
||||
|
||||
Auto-detection picks the first provider whose API key can be resolved, in the
|
||||
order shown. Set `memorySearch.provider` to override.
|
||||
|
||||
## How indexing works
|
||||
|
||||
OpenClaw indexes `MEMORY.md` and `memory/*.md` into chunks (~400 tokens with
|
||||
80-token overlap) and stores them in a per-agent SQLite database.
|
||||
|
||||
- **Index location:** `~/.openclaw/memory/<agentId>.sqlite`
|
||||
- **File watching:** changes to memory files trigger a debounced reindex (1.5s).
|
||||
- **Auto-reindex:** when the embedding provider, model, or chunking config
|
||||
changes, the entire index is rebuilt automatically.
|
||||
- **Reindex on demand:** `openclaw memory index --force`
|
||||
|
||||
<Info>
|
||||
You can also index Markdown files outside the workspace with
|
||||
`memorySearch.extraPaths`. See the
|
||||
[configuration reference](/reference/memory-config#additional-memory-paths).
|
||||
</Info>
|
||||
|
||||
## When to use
|
||||
|
||||
The builtin engine is the right choice for most users:
|
||||
|
||||
- Works out of the box with no extra dependencies.
|
||||
- Handles keyword and vector search well.
|
||||
- Supports all embedding providers.
|
||||
- Hybrid search combines the best of both retrieval approaches.
|
||||
|
||||
Consider switching to [QMD](/concepts/memory-qmd) if you need reranking, query
|
||||
expansion, or want to index directories outside the workspace.
|
||||
|
||||
Consider [Honcho](/concepts/memory-honcho) if you want cross-session memory with
|
||||
automatic user modeling.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Memory search disabled?** Check `openclaw memory status`. If no provider is
|
||||
detected, set one explicitly or add an API key.
|
||||
|
||||
**Stale results?** Run `openclaw memory index --force` to rebuild. The watcher
|
||||
may miss changes in rare edge cases.
|
||||
|
||||
**sqlite-vec not loading?** OpenClaw falls back to in-process cosine similarity
|
||||
automatically. Check logs for the specific load error.
|
||||
|
||||
## Configuration
|
||||
|
||||
For embedding provider setup, hybrid search tuning (weights, MMR, temporal
|
||||
decay), batch indexing, multimodal memory, sqlite-vec, extra paths, and all
|
||||
other config knobs, see the
|
||||
[Memory configuration reference](/reference/memory-config).
|
||||
@@ -1,140 +0,0 @@
|
||||
---
|
||||
title: "Honcho Memory"
|
||||
summary: "AI-native cross-session memory via the Honcho plugin"
|
||||
read_when:
|
||||
- You want persistent memory that works across sessions and channels
|
||||
- You want AI-powered recall and user modeling
|
||||
---
|
||||
|
||||
# Honcho Memory
|
||||
|
||||
[Honcho](https://honcho.dev) adds AI-native memory to OpenClaw. It persists
|
||||
conversations to a dedicated service and builds user and agent models over time,
|
||||
giving your agent cross-session context that goes beyond workspace Markdown
|
||||
files.
|
||||
|
||||
## What it provides
|
||||
|
||||
- **Cross-session memory** -- conversations are persisted after every turn, so
|
||||
context carries across session resets, compaction, and channel switches.
|
||||
- **User modeling** -- Honcho maintains a profile for each user (preferences,
|
||||
facts, communication style) and for the agent (personality, learned
|
||||
behaviors).
|
||||
- **Semantic search** -- search over observations from past conversations, not
|
||||
just the current session.
|
||||
- **Multi-agent awareness** -- parent agents automatically track spawned
|
||||
sub-agents, with parents added as observers in child sessions.
|
||||
|
||||
## Available tools
|
||||
|
||||
Honcho registers tools that the agent can use during conversation:
|
||||
|
||||
**Data retrieval (fast, no LLM call):**
|
||||
|
||||
| Tool | What it does |
|
||||
| --------------------------- | ------------------------------------------------------ |
|
||||
| `honcho_context` | Full user representation across sessions |
|
||||
| `honcho_search_conclusions` | Semantic search over stored conclusions |
|
||||
| `honcho_search_messages` | Find messages across sessions (filter by sender, date) |
|
||||
| `honcho_session` | Current session history and summary |
|
||||
|
||||
**Q&A (LLM-powered):**
|
||||
|
||||
| Tool | What it does |
|
||||
| ------------ | ------------------------------------------------------------------------- |
|
||||
| `honcho_ask` | Ask about the user. `depth='quick'` for facts, `'thorough'` for synthesis |
|
||||
|
||||
## Getting started
|
||||
|
||||
Install the plugin and run setup:
|
||||
|
||||
```bash
|
||||
openclaw plugins install @honcho-ai/openclaw-honcho
|
||||
openclaw honcho setup
|
||||
openclaw gateway --force
|
||||
```
|
||||
|
||||
The setup command prompts for your API credentials, writes the config, and
|
||||
optionally migrates existing workspace memory files.
|
||||
|
||||
<Info>
|
||||
Honcho can run entirely locally (self-hosted) or via the managed API at
|
||||
`api.honcho.dev`. No external dependencies are required for the self-hosted
|
||||
option.
|
||||
</Info>
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings live under `plugins.entries["openclaw-honcho"].config`:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
"openclaw-honcho": {
|
||||
config: {
|
||||
apiKey: "your-api-key", // omit for self-hosted
|
||||
workspaceId: "openclaw", // memory isolation
|
||||
baseUrl: "https://api.honcho.dev",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For self-hosted instances, point `baseUrl` to your local server (for example
|
||||
`http://localhost:8000`) and omit the API key.
|
||||
|
||||
## Migrating existing memory
|
||||
|
||||
If you have existing workspace memory files (`USER.md`, `MEMORY.md`,
|
||||
`IDENTITY.md`, `memory/`, `canvas/`), `openclaw honcho setup` detects and
|
||||
offers to migrate them.
|
||||
|
||||
<Info>
|
||||
Migration is non-destructive -- files are uploaded to Honcho. Originals are
|
||||
never deleted or moved.
|
||||
</Info>
|
||||
|
||||
## How it works
|
||||
|
||||
After every AI turn, the conversation is persisted to Honcho. Both user and
|
||||
agent messages are observed, allowing Honcho to build and refine its models over
|
||||
time.
|
||||
|
||||
During conversation, Honcho tools query the service in the `before_prompt_build`
|
||||
phase, injecting relevant context before the model sees the prompt. This ensures
|
||||
accurate turn boundaries and relevant recall.
|
||||
|
||||
## Honcho vs builtin memory
|
||||
|
||||
| | Builtin / QMD | Honcho |
|
||||
| ----------------- | ---------------------------- | ----------------------------------- |
|
||||
| **Storage** | Workspace Markdown files | Dedicated service (local or hosted) |
|
||||
| **Cross-session** | Via memory files | Automatic, built-in |
|
||||
| **User modeling** | Manual (write to MEMORY.md) | Automatic profiles |
|
||||
| **Search** | Vector + keyword (hybrid) | Semantic over observations |
|
||||
| **Multi-agent** | Not tracked | Parent/child awareness |
|
||||
| **Dependencies** | None (builtin) or QMD binary | Plugin install |
|
||||
|
||||
Honcho and the builtin memory system can work together. When QMD is configured,
|
||||
additional tools become available for searching local Markdown files alongside
|
||||
Honcho's cross-session memory.
|
||||
|
||||
## CLI commands
|
||||
|
||||
```bash
|
||||
openclaw honcho setup # Configure API key and migrate files
|
||||
openclaw honcho status # Check connection status
|
||||
openclaw honcho ask <question> # Query Honcho about the user
|
||||
openclaw honcho search <query> [-k N] [-d D] # Semantic search over memory
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Plugin source code](https://github.com/plastic-labs/openclaw-honcho)
|
||||
- [Honcho documentation](https://docs.honcho.dev)
|
||||
- [Honcho OpenClaw integration guide](https://docs.honcho.dev/v3/guides/integrations/openclaw)
|
||||
- [Memory](/concepts/memory) -- OpenClaw memory overview
|
||||
- [Context Engines](/concepts/context-engine) -- how plugin context engines work
|
||||
@@ -1,157 +0,0 @@
|
||||
---
|
||||
title: "QMD Memory Engine"
|
||||
summary: "Local-first search sidecar with BM25, vectors, reranking, and query expansion"
|
||||
read_when:
|
||||
- You want to set up QMD as your memory backend
|
||||
- You want advanced memory features like reranking or extra indexed paths
|
||||
---
|
||||
|
||||
# QMD Memory Engine
|
||||
|
||||
[QMD](https://github.com/tobi/qmd) is a local-first search sidecar that runs
|
||||
alongside OpenClaw. It combines BM25, vector search, and reranking in a single
|
||||
binary, and can index content beyond your workspace memory files.
|
||||
|
||||
## What it adds over builtin
|
||||
|
||||
- **Reranking and query expansion** for better recall.
|
||||
- **Index extra directories** -- project docs, team notes, anything on disk.
|
||||
- **Index session transcripts** -- recall earlier conversations.
|
||||
- **Fully local** -- runs via Bun + node-llama-cpp, auto-downloads GGUF models.
|
||||
- **Automatic fallback** -- if QMD is unavailable, OpenClaw falls back to the
|
||||
builtin engine seamlessly.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Install QMD: `bun install -g https://github.com/tobi/qmd`
|
||||
- SQLite build that allows extensions (`brew install sqlite` on macOS).
|
||||
- QMD must be on the gateway's `PATH`.
|
||||
- macOS and Linux work out of the box. Windows is best supported via WSL2.
|
||||
|
||||
### Enable
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
OpenClaw creates a self-contained QMD home under
|
||||
`~/.openclaw/agents/<agentId>/qmd/` and manages the sidecar lifecycle
|
||||
automatically -- collections, updates, and embedding runs are handled for you.
|
||||
|
||||
## How the sidecar works
|
||||
|
||||
- OpenClaw creates collections from your workspace memory files and any
|
||||
configured `memory.qmd.paths`, then runs `qmd update` + `qmd embed` on boot
|
||||
and periodically (default every 5 minutes).
|
||||
- Boot refresh runs in the background so chat startup is not blocked.
|
||||
- Searches use the configured `searchMode` (default: `search`; also supports
|
||||
`vsearch` and `query`). If a mode fails, OpenClaw retries with `qmd query`.
|
||||
- If QMD fails entirely, OpenClaw falls back to the builtin SQLite engine.
|
||||
|
||||
<Info>
|
||||
The first search may be slow -- QMD auto-downloads GGUF models (~2 GB) for
|
||||
reranking and query expansion on the first `qmd query` run.
|
||||
</Info>
|
||||
|
||||
## Indexing extra paths
|
||||
|
||||
Point QMD at additional directories to make them searchable:
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
paths: [{ name: "docs", path: "~/notes", pattern: "**/*.md" }],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Snippets from extra paths appear as `qmd/<collection>/<relative-path>` in
|
||||
search results. `memory_get` understands this prefix and reads from the correct
|
||||
collection root.
|
||||
|
||||
## Indexing session transcripts
|
||||
|
||||
Enable session indexing to recall earlier conversations:
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
qmd: {
|
||||
sessions: { enabled: true },
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Transcripts are exported as sanitized User/Assistant turns into a dedicated QMD
|
||||
collection under `~/.openclaw/agents/<id>/qmd/sessions/`.
|
||||
|
||||
## Search scope
|
||||
|
||||
By default, QMD search results are only surfaced in DM sessions (not groups or
|
||||
channels). Configure `memory.qmd.scope` to change this:
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
qmd: {
|
||||
scope: {
|
||||
default: "deny",
|
||||
rules: [{ action: "allow", match: { chatType: "direct" } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
When scope denies a search, OpenClaw logs a warning with the derived channel and
|
||||
chat type so empty results are easier to debug.
|
||||
|
||||
## Citations
|
||||
|
||||
When `memory.citations` is `auto` or `on`, search snippets include a
|
||||
`Source: <path#line>` footer. Set `memory.citations = "off"` to omit the footer
|
||||
while still passing the path to the agent internally.
|
||||
|
||||
## When to use
|
||||
|
||||
Choose QMD when you need:
|
||||
|
||||
- Reranking for higher-quality results.
|
||||
- To search project docs or notes outside the workspace.
|
||||
- To recall past session conversations.
|
||||
- Fully local search with no API keys.
|
||||
|
||||
For simpler setups, the [builtin engine](/concepts/memory-builtin) works well
|
||||
with no extra dependencies.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**QMD not found?** Ensure the binary is on the gateway's `PATH`. If OpenClaw
|
||||
runs as a service, create a symlink:
|
||||
`sudo ln -s ~/.bun/bin/qmd /usr/local/bin/qmd`.
|
||||
|
||||
**First search very slow?** QMD downloads GGUF models on first use. Pre-warm
|
||||
with `qmd query "test"` using the same XDG dirs OpenClaw uses.
|
||||
|
||||
**Search times out?** Increase `memory.qmd.limits.timeoutMs` (default: 4000ms).
|
||||
Set to `120000` for slower hardware.
|
||||
|
||||
**Empty results in group chats?** Check `memory.qmd.scope` -- the default only
|
||||
allows DM sessions.
|
||||
|
||||
## Configuration
|
||||
|
||||
For the full config surface (`memory.qmd.*`), search modes, update intervals,
|
||||
scope rules, and all other knobs, see the
|
||||
[Memory configuration reference](/reference/memory-config).
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
title: "Memory Search"
|
||||
summary: "How memory search finds relevant notes using embeddings and hybrid retrieval"
|
||||
read_when:
|
||||
- You want to understand how memory_search works
|
||||
- You want to choose an embedding provider
|
||||
- You want to tune search quality
|
||||
---
|
||||
|
||||
# Memory Search
|
||||
|
||||
`memory_search` finds relevant notes from your memory files, even when the
|
||||
wording differs from the original text. It works by indexing memory into small
|
||||
chunks and searching them using embeddings, keywords, or both.
|
||||
|
||||
## Quick start
|
||||
|
||||
If you have an OpenAI, Gemini, Voyage, or Mistral API key configured, memory
|
||||
search works automatically. To set a provider explicitly:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai", // or "gemini", "local", "ollama", etc.
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
For local embeddings with no API key, use `provider: "local"` (requires
|
||||
node-llama-cpp).
|
||||
|
||||
## Supported providers
|
||||
|
||||
| Provider | ID | Needs API key | Notes |
|
||||
| -------- | --------- | ------------- | ----------------------------- |
|
||||
| OpenAI | `openai` | Yes | Auto-detected, fast |
|
||||
| Gemini | `gemini` | Yes | Supports image/audio indexing |
|
||||
| Voyage | `voyage` | Yes | Auto-detected |
|
||||
| Mistral | `mistral` | Yes | Auto-detected |
|
||||
| Ollama | `ollama` | No | Local, must set explicitly |
|
||||
| Local | `local` | No | GGUF model, ~0.6 GB download |
|
||||
|
||||
## How search works
|
||||
|
||||
OpenClaw runs two retrieval paths in parallel and merges the results:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Q["Query"] --> E["Embedding"]
|
||||
Q --> T["Tokenize"]
|
||||
E --> VS["Vector Search"]
|
||||
T --> BM["BM25 Search"]
|
||||
VS --> M["Weighted Merge"]
|
||||
BM --> M
|
||||
M --> R["Top Results"]
|
||||
```
|
||||
|
||||
- **Vector search** finds notes with similar meaning ("gateway host" matches
|
||||
"the machine running OpenClaw").
|
||||
- **BM25 keyword search** finds exact matches (IDs, error strings, config
|
||||
keys).
|
||||
|
||||
If only one path is available (no embeddings or no FTS), the other runs alone.
|
||||
|
||||
## Improving search quality
|
||||
|
||||
Two optional features help when you have a large note history:
|
||||
|
||||
### Temporal decay
|
||||
|
||||
Old notes gradually lose ranking weight so recent information surfaces first.
|
||||
With the default half-life of 30 days, a note from last month scores at 50% of
|
||||
its original weight. Evergreen files like `MEMORY.md` are never decayed.
|
||||
|
||||
<Tip>
|
||||
Enable temporal decay if your agent has months of daily notes and stale
|
||||
information keeps outranking recent context.
|
||||
</Tip>
|
||||
|
||||
### MMR (diversity)
|
||||
|
||||
Reduces redundant results. If five notes all mention the same router config, MMR
|
||||
ensures the top results cover different topics instead of repeating.
|
||||
|
||||
<Tip>
|
||||
Enable MMR if `memory_search` keeps returning near-duplicate snippets from
|
||||
different daily notes.
|
||||
</Tip>
|
||||
|
||||
### Enable both
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
query: {
|
||||
hybrid: {
|
||||
mmr: { enabled: true },
|
||||
temporalDecay: { enabled: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Multimodal memory
|
||||
|
||||
With Gemini Embedding 2, you can index images and audio files alongside
|
||||
Markdown. Search queries remain text, but they match against visual and audio
|
||||
content. See the [Memory configuration reference](/reference/memory-config) for
|
||||
setup.
|
||||
|
||||
## Session memory search
|
||||
|
||||
You can optionally index session transcripts so `memory_search` can recall
|
||||
earlier conversations. This is opt-in via
|
||||
`memorySearch.experimental.sessionMemory`. See the
|
||||
[configuration reference](/reference/memory-config) for details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**No results?** Run `openclaw memory status` to check the index. If empty, run
|
||||
`openclaw memory index --force`.
|
||||
|
||||
**Only keyword matches?** Your embedding provider may not be configured. Check
|
||||
`openclaw memory status --deep`.
|
||||
|
||||
**CJK text not found?** Rebuild the FTS index with
|
||||
`openclaw memory index --force`.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Memory](/concepts/memory) -- file layout, backends, tools
|
||||
- [Memory configuration reference](/reference/memory-config) -- all config knobs
|
||||
@@ -1,102 +1,111 @@
|
||||
---
|
||||
title: "Memory Overview"
|
||||
summary: "How OpenClaw remembers things across sessions"
|
||||
title: "Memory"
|
||||
summary: "How OpenClaw memory works (workspace files + automatic memory flush)"
|
||||
read_when:
|
||||
- You want to understand how memory works
|
||||
- You want to know what memory files to write
|
||||
- You want the memory file layout and workflow
|
||||
- You want to tune the automatic pre-compaction memory flush
|
||||
---
|
||||
|
||||
# Memory Overview
|
||||
# Memory
|
||||
|
||||
OpenClaw remembers things by writing **plain Markdown files** in your agent's
|
||||
workspace. The model only "remembers" what gets saved to disk -- there is no
|
||||
hidden state.
|
||||
OpenClaw memory is **plain Markdown in the agent workspace**. The files are the
|
||||
source of truth; the model only "remembers" what gets written to disk.
|
||||
|
||||
## How it works
|
||||
Memory search tools are provided by the active memory plugin (default:
|
||||
`memory-core`). Disable memory plugins with `plugins.slots.memory = "none"`.
|
||||
|
||||
Your agent has two places to store memories:
|
||||
## Memory files (Markdown)
|
||||
|
||||
- **`MEMORY.md`** -- long-term memory. Durable facts, preferences, and
|
||||
decisions. Loaded at the start of every DM session.
|
||||
- **`memory/YYYY-MM-DD.md`** -- daily notes. Running context and observations.
|
||||
Today and yesterday's notes are loaded automatically.
|
||||
The default workspace layout uses two memory layers:
|
||||
|
||||
These files live in the agent workspace (default `~/.openclaw/workspace`).
|
||||
- `memory/YYYY-MM-DD.md`
|
||||
- Daily log (append-only).
|
||||
- Read today + yesterday at session start.
|
||||
- `MEMORY.md` (optional)
|
||||
- Curated long-term memory.
|
||||
- If both `MEMORY.md` and `memory.md` exist at the workspace root, OpenClaw loads both (deduplicated by realpath so symlinks pointing to the same file are not injected twice).
|
||||
- **Only load in the main, private session** (never in group contexts).
|
||||
|
||||
<Tip>
|
||||
If you want your agent to remember something, just ask it: "Remember that I
|
||||
prefer TypeScript." It will write it to the appropriate file.
|
||||
</Tip>
|
||||
These files live under the workspace (`agents.defaults.workspace`, default
|
||||
`~/.openclaw/workspace`). See [Agent workspace](/concepts/agent-workspace) for the full layout.
|
||||
|
||||
## Memory tools
|
||||
|
||||
The agent has two tools for working with memory:
|
||||
OpenClaw exposes two agent-facing tools for these Markdown files:
|
||||
|
||||
- **`memory_search`** -- finds relevant notes using semantic search, even when
|
||||
the wording differs from the original.
|
||||
- **`memory_get`** -- reads a specific memory file or line range.
|
||||
- `memory_search` -- semantic recall over indexed snippets.
|
||||
- `memory_get` -- targeted read of a specific Markdown file/line range.
|
||||
|
||||
Both tools are provided by the active memory plugin (default: `memory-core`).
|
||||
`memory_get` now **degrades gracefully when a file doesn't exist** (for example,
|
||||
today's daily log before the first write). Both the builtin manager and the QMD
|
||||
backend return `{ text: "", path }` instead of throwing `ENOENT`, so agents can
|
||||
handle "nothing recorded yet" and continue their workflow without wrapping the
|
||||
tool call in try/catch logic.
|
||||
|
||||
## Memory search
|
||||
## When to write memory
|
||||
|
||||
When an embedding provider is configured, `memory_search` uses **hybrid
|
||||
search** -- combining vector similarity (semantic meaning) with keyword matching
|
||||
(exact terms like IDs and code symbols). This works out of the box once you have
|
||||
an API key for any supported provider.
|
||||
- Decisions, preferences, and durable facts go to `MEMORY.md`.
|
||||
- Day-to-day notes and running context go to `memory/YYYY-MM-DD.md`.
|
||||
- If someone says "remember this," write it down (do not keep it in RAM).
|
||||
- This area is still evolving. It helps to remind the model to store memories; it will know what to do.
|
||||
- If you want something to stick, **ask the bot to write it** into memory.
|
||||
|
||||
<Info>
|
||||
OpenClaw auto-detects your embedding provider from available API keys. If you
|
||||
have an OpenAI, Gemini, Voyage, or Mistral key configured, memory search is
|
||||
enabled automatically.
|
||||
</Info>
|
||||
## Automatic memory flush (pre-compaction ping)
|
||||
|
||||
For details on how search works, tuning options, and provider setup, see
|
||||
[Memory Search](/concepts/memory-search).
|
||||
When a session is **close to auto-compaction**, OpenClaw triggers a **silent,
|
||||
agentic turn** that reminds the model to write durable memory **before** the
|
||||
context is compacted. The default prompts explicitly say the model _may reply_,
|
||||
but usually `NO_REPLY` is the correct response so the user never sees this turn.
|
||||
The active memory plugin owns the prompt/path policy for that flush; the
|
||||
default `memory-core` plugin writes to the canonical daily file under
|
||||
`memory/YYYY-MM-DD.md`.
|
||||
|
||||
## Memory backends
|
||||
This is controlled by `agents.defaults.compaction.memoryFlush`:
|
||||
|
||||
<CardGroup cols={3}>
|
||||
<Card title="Builtin (default)" icon="database" href="/concepts/memory-builtin">
|
||||
SQLite-based. Works out of the box with keyword search, vector similarity, and
|
||||
hybrid search. No extra dependencies.
|
||||
</Card>
|
||||
<Card title="QMD" icon="search" href="/concepts/memory-qmd">
|
||||
Local-first sidecar with reranking, query expansion, and the ability to index
|
||||
directories outside the workspace.
|
||||
</Card>
|
||||
<Card title="Honcho" icon="brain" href="/concepts/memory-honcho">
|
||||
AI-native cross-session memory with user modeling, semantic search, and
|
||||
multi-agent awareness. Plugin install.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Automatic memory flush
|
||||
|
||||
Before [compaction](/concepts/compaction) summarizes your conversation, OpenClaw
|
||||
runs a silent turn that reminds the agent to save important context to memory
|
||||
files. This is on by default -- you do not need to configure anything.
|
||||
|
||||
<Tip>
|
||||
The memory flush prevents context loss during compaction. If your agent has
|
||||
important facts in the conversation that are not yet written to a file, they
|
||||
will be saved automatically before the summary happens.
|
||||
</Tip>
|
||||
|
||||
## CLI
|
||||
|
||||
```bash
|
||||
openclaw memory status # Check index status and provider
|
||||
openclaw memory search "query" # Search from the command line
|
||||
openclaw memory index --force # Rebuild the index
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
compaction: {
|
||||
reserveTokensFloor: 20000,
|
||||
memoryFlush: {
|
||||
enabled: true,
|
||||
softThresholdTokens: 4000,
|
||||
systemPrompt: "Session nearing compaction. Store durable memories now.",
|
||||
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading
|
||||
Details:
|
||||
|
||||
- [Builtin Memory Engine](/concepts/memory-builtin) -- default SQLite backend
|
||||
- [QMD Memory Engine](/concepts/memory-qmd) -- advanced local-first sidecar
|
||||
- [Honcho Memory](/concepts/memory-honcho) -- AI-native cross-session memory
|
||||
- [Memory Search](/concepts/memory-search) -- search pipeline, providers, and
|
||||
tuning
|
||||
- [Memory configuration reference](/reference/memory-config) -- all config knobs
|
||||
- [Compaction](/concepts/compaction) -- how compaction interacts with memory
|
||||
- **Soft threshold**: flush triggers when the session token estimate crosses
|
||||
`contextWindow - reserveTokensFloor - softThresholdTokens`.
|
||||
- **Silent** by default: prompts include `NO_REPLY` so nothing is delivered.
|
||||
- **Two prompts**: a user prompt plus a system prompt append the reminder.
|
||||
- **One flush per compaction cycle** (tracked in `sessions.json`).
|
||||
- **Workspace must be writable**: if the session runs sandboxed with
|
||||
`workspaceAccess: "ro"` or `"none"`, the flush is skipped.
|
||||
|
||||
For the full compaction lifecycle, see
|
||||
[Session management + compaction](/reference/session-management-compaction).
|
||||
|
||||
## Vector memory search
|
||||
|
||||
OpenClaw can build a small vector index over `MEMORY.md` and `memory/*.md` so
|
||||
semantic queries can find related notes even when wording differs. Hybrid search
|
||||
(BM25 + vector) is available for combining semantic matching with exact keyword
|
||||
lookups.
|
||||
|
||||
Memory search adapter ids come from the active memory plugin. The default
|
||||
`memory-core` plugin ships built-ins for OpenAI, Gemini, Voyage, Mistral,
|
||||
Ollama, and local GGUF models, plus an optional QMD sidecar backend for
|
||||
advanced retrieval and post-processing features like MMR diversity re-ranking
|
||||
and temporal decay.
|
||||
|
||||
For the full configuration reference -- including embedding provider setup, QMD
|
||||
backend, hybrid search tuning, multimodal memory, and all config knobs -- see
|
||||
[Memory configuration reference](/reference/memory-config).
|
||||
|
||||
@@ -147,8 +147,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- Override per model via `agents.defaults.models["openai/<model>"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`)
|
||||
- OpenAI Responses WebSocket warm-up defaults to enabled via `params.openaiWsWarmup` (`true`/`false`)
|
||||
- OpenAI priority processing can be enabled via `agents.defaults.models["openai/<model>"].params.serviceTier`
|
||||
- `/fast` and `params.fastMode` map direct `openai/*` Responses requests to `service_tier=priority` on `api.openai.com`
|
||||
- Use `params.serviceTier` when you want an explicit tier instead of the shared `/fast` toggle
|
||||
- OpenAI fast mode can be enabled per model via `agents.defaults.models["<provider>/<model>"].params.fastMode`
|
||||
- `openai/gpt-5.3-codex-spark` is intentionally suppressed in OpenClaw because the live OpenAI API rejects it; Spark is treated as Codex-only
|
||||
|
||||
```json5
|
||||
@@ -164,7 +163,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- Optional rotation: `ANTHROPIC_API_KEYS`, `ANTHROPIC_API_KEY_1`, `ANTHROPIC_API_KEY_2`, plus `OPENCLAW_LIVE_ANTHROPIC_KEY` (single override)
|
||||
- Example model: `anthropic/claude-opus-4-6`
|
||||
- CLI: `openclaw onboard --auth-choice token` (paste setup-token) or `openclaw models auth paste-token --provider anthropic`
|
||||
- Direct public Anthropic requests support the shared `/fast` toggle and `params.fastMode`, including API-key and OAuth-authenticated traffic sent to `api.anthropic.com`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
|
||||
- Direct API-key models support the shared `/fast` toggle and `params.fastMode`; OpenClaw maps that to Anthropic `service_tier` (`auto` vs `standard_only`)
|
||||
- Policy note: setup-token support is technical compatibility; Anthropic has blocked some subscription usage outside Claude Code in the past. Verify current Anthropic terms and decide based on your risk tolerance.
|
||||
- Recommendation: Anthropic API key auth is the safer, recommended path over subscription setup-token auth.
|
||||
|
||||
@@ -182,8 +181,7 @@ OpenClaw ships with the pi‑ai catalog. These providers require **no**
|
||||
- CLI: `openclaw onboard --auth-choice openai-codex` or `openclaw models auth login --provider openai-codex`
|
||||
- Default transport is `auto` (WebSocket-first, SSE fallback)
|
||||
- Override per model via `agents.defaults.models["openai-codex/<model>"].params.transport` (`"sse"`, `"websocket"`, or `"auto"`)
|
||||
- `params.serviceTier` is also forwarded on native Codex Responses requests (`chatgpt.com/backend-api`)
|
||||
- Shares the same `/fast` toggle and `params.fastMode` config as direct `openai/*`; OpenClaw maps that to `service_tier=priority`
|
||||
- Shares the same `/fast` toggle and `params.fastMode` config as direct `openai/*`
|
||||
- `openai-codex/gpt-5.3-codex-spark` remains available when the Codex OAuth catalog exposes it; entitlement-dependent
|
||||
- Policy note: OpenAI Codex OAuth is explicitly supported for external tools/workflows like OpenClaw.
|
||||
|
||||
|
||||
@@ -1,80 +1,121 @@
|
||||
---
|
||||
title: "Session Pruning"
|
||||
summary: "Trimming old tool results to keep context lean and caching efficient"
|
||||
summary: "Session pruning: tool-result trimming to reduce context bloat"
|
||||
read_when:
|
||||
- You want to reduce context growth from tool outputs
|
||||
- You want to understand Anthropic prompt cache optimization
|
||||
- You want to reduce LLM context growth from tool outputs
|
||||
- You are tuning agents.defaults.contextPruning
|
||||
---
|
||||
|
||||
# Session Pruning
|
||||
|
||||
Session pruning trims **old tool results** from the context before each LLM
|
||||
call. It reduces context bloat from accumulated tool outputs (exec results, file
|
||||
reads, search results) without touching your conversation messages.
|
||||
Session pruning trims **old tool results** from the in-memory context right before each LLM call. It does **not** rewrite the on-disk session history (`*.jsonl`).
|
||||
|
||||
<Info>
|
||||
Pruning is in-memory only -- it does not modify the on-disk session transcript.
|
||||
Your full history is always preserved.
|
||||
</Info>
|
||||
## When it runs
|
||||
|
||||
## Why it matters
|
||||
- When `mode: "cache-ttl"` is enabled and the last Anthropic call for the session is older than `ttl`.
|
||||
- Only affects the messages sent to the model for that request.
|
||||
- Only active for Anthropic API calls (and OpenRouter Anthropic models).
|
||||
- For best results, match `ttl` to your model `cacheRetention` policy (`short` = 5m, `long` = 1h).
|
||||
- After a prune, the TTL window resets so subsequent requests keep cache until `ttl` expires again.
|
||||
|
||||
Long sessions accumulate tool output that inflates the context window. This
|
||||
increases cost and can force [compaction](/concepts/compaction) sooner than
|
||||
necessary.
|
||||
## Smart defaults (Anthropic)
|
||||
|
||||
Pruning is especially valuable for **Anthropic prompt caching**. After the cache
|
||||
TTL expires, the next request re-caches the full prompt. Pruning reduces the
|
||||
cache-write size, directly lowering cost.
|
||||
- **OAuth or setup-token** profiles: enable `cache-ttl` pruning and set heartbeat to `1h`.
|
||||
- **API key** profiles: enable `cache-ttl` pruning, set heartbeat to `30m`, and default `cacheRetention: "short"` on Anthropic models.
|
||||
- If you set any of these values explicitly, OpenClaw does **not** override them.
|
||||
|
||||
## How it works
|
||||
## What this improves (cost + cache behavior)
|
||||
|
||||
1. Wait for the cache TTL to expire (default 5 minutes).
|
||||
2. Find old tool results (user and assistant messages are never touched).
|
||||
3. **Soft-trim** oversized results -- keep the head and tail, insert `...`.
|
||||
4. **Hard-clear** the rest -- replace with a placeholder.
|
||||
5. Reset the TTL so follow-up requests reuse the fresh cache.
|
||||
- **Why prune:** Anthropic prompt caching only applies within the TTL. If a session goes idle past the TTL, the next request re-caches the full prompt unless you trim it first.
|
||||
- **What gets cheaper:** pruning reduces the **cacheWrite** size for that first request after the TTL expires.
|
||||
- **Why the TTL reset matters:** once pruning runs, the cache window resets, so follow‑up requests can reuse the freshly cached prompt instead of re-caching the full history again.
|
||||
- **What it does not do:** pruning doesn’t add tokens or “double” costs; it only changes what gets cached on that first post‑TTL request.
|
||||
|
||||
## Smart defaults
|
||||
## What can be pruned
|
||||
|
||||
OpenClaw auto-enables pruning for Anthropic profiles:
|
||||
- Only `toolResult` messages.
|
||||
- User + assistant messages are **never** modified.
|
||||
- The last `keepLastAssistants` assistant messages are protected; tool results after that cutoff are not pruned.
|
||||
- If there aren’t enough assistant messages to establish the cutoff, pruning is skipped.
|
||||
- Tool results containing **image blocks** are skipped (never trimmed/cleared).
|
||||
|
||||
| Profile type | Pruning enabled | Heartbeat |
|
||||
| -------------------- | --------------- | --------- |
|
||||
| OAuth or setup-token | Yes | 1 hour |
|
||||
| API key | Yes | 30 min |
|
||||
## Context window estimation
|
||||
|
||||
If you set explicit values, OpenClaw does not override them.
|
||||
Pruning uses an estimated context window (chars ≈ tokens × 4). The base window is resolved in this order:
|
||||
|
||||
## Enable or disable
|
||||
1. `models.providers.*.models[].contextWindow` override.
|
||||
2. Model definition `contextWindow` (from the model registry).
|
||||
3. Default `200000` tokens.
|
||||
|
||||
Pruning is off by default for non-Anthropic providers. To enable:
|
||||
If `agents.defaults.contextTokens` is set, it is treated as a cap (min) on the resolved window.
|
||||
|
||||
## Mode
|
||||
|
||||
### cache-ttl
|
||||
|
||||
- Pruning only runs if the last Anthropic call is older than `ttl` (default `5m`).
|
||||
- When it runs: same soft-trim + hard-clear behavior as before.
|
||||
|
||||
## Soft vs hard pruning
|
||||
|
||||
- **Soft-trim**: only for oversized tool results.
|
||||
- Keeps head + tail, inserts `...`, and appends a note with the original size.
|
||||
- Skips results with image blocks.
|
||||
- **Hard-clear**: replaces the entire tool result with `hardClear.placeholder`.
|
||||
|
||||
## Tool selection
|
||||
|
||||
- `tools.allow` / `tools.deny` support `*` wildcards.
|
||||
- Deny wins.
|
||||
- Matching is case-insensitive.
|
||||
- Empty allow list => all tools allowed.
|
||||
|
||||
## Interaction with other limits
|
||||
|
||||
- Built-in tools already truncate their own output; session pruning is an extra layer that prevents long-running chats from accumulating too much tool output in the model context.
|
||||
- Compaction is separate: compaction summarizes and persists, pruning is transient per request. See [/concepts/compaction](/concepts/compaction).
|
||||
|
||||
## Defaults (when enabled)
|
||||
|
||||
- `ttl`: `"5m"`
|
||||
- `keepLastAssistants`: `3`
|
||||
- `softTrimRatio`: `0.3`
|
||||
- `hardClearRatio`: `0.5`
|
||||
- `minPrunableToolChars`: `50000`
|
||||
- `softTrim`: `{ maxChars: 4000, headChars: 1500, tailChars: 1500 }`
|
||||
- `hardClear`: `{ enabled: true, placeholder: "[Old tool result content cleared]" }`
|
||||
|
||||
## Examples
|
||||
|
||||
Default (off):
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: { defaults: { contextPruning: { mode: "off" } } },
|
||||
}
|
||||
```
|
||||
|
||||
Enable TTL-aware pruning:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: { defaults: { contextPruning: { mode: "cache-ttl", ttl: "5m" } } },
|
||||
}
|
||||
```
|
||||
|
||||
Restrict pruning to specific tools:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
contextPruning: { mode: "cache-ttl", ttl: "5m" },
|
||||
contextPruning: {
|
||||
mode: "cache-ttl",
|
||||
tools: { allow: ["exec", "read"], deny: ["*image*"] },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
To disable: set `mode: "off"`.
|
||||
|
||||
## Pruning vs compaction
|
||||
|
||||
| | Pruning | Compaction |
|
||||
| ---------- | ------------------ | ----------------------- |
|
||||
| **What** | Trims tool results | Summarizes conversation |
|
||||
| **Saved?** | No (per-request) | Yes (in transcript) |
|
||||
| **Scope** | Tool results only | Entire conversation |
|
||||
|
||||
They complement each other -- pruning keeps tool output lean between
|
||||
compaction cycles.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Compaction](/concepts/compaction) -- summarization-based context reduction
|
||||
- [Gateway Configuration](/gateway/configuration) -- all pruning config knobs
|
||||
(`contextPruning.*`)
|
||||
See config reference: [Gateway Configuration](/gateway/configuration)
|
||||
|
||||
@@ -1,84 +1,251 @@
|
||||
---
|
||||
summary: "Agent tools for listing sessions, reading history, and cross-session messaging"
|
||||
summary: "Agent session tools for listing sessions, fetching history, and sending cross-session messages"
|
||||
read_when:
|
||||
- You want to understand what session tools the agent has
|
||||
- You want to configure cross-session access or sub-agent spawning
|
||||
- Adding or modifying session tools
|
||||
title: "Session Tools"
|
||||
---
|
||||
|
||||
# Session Tools
|
||||
|
||||
OpenClaw gives agents tools to work across sessions -- listing conversations,
|
||||
reading history, sending messages to other sessions, and spawning sub-agents.
|
||||
Goal: small, hard-to-misuse tool set so agents can list sessions, fetch history, and send to another session.
|
||||
|
||||
## Available tools
|
||||
## Tool Names
|
||||
|
||||
| Tool | What it does |
|
||||
| ------------------ | ------------------------------------------------------- |
|
||||
| `sessions_list` | List sessions with optional filters (kind, recency) |
|
||||
| `sessions_history` | Read the transcript of a specific session |
|
||||
| `sessions_send` | Send a message to another session and optionally wait |
|
||||
| `sessions_spawn` | Spawn an isolated sub-agent session for background work |
|
||||
- `sessions_list`
|
||||
- `sessions_history`
|
||||
- `sessions_send`
|
||||
- `sessions_spawn`
|
||||
|
||||
## Listing and reading sessions
|
||||
## Key Model
|
||||
|
||||
`sessions_list` returns sessions with their key, kind, channel, model, token
|
||||
counts, and timestamps. Filter by kind (`main`, `group`, `cron`, `hook`,
|
||||
`node`) or recency (`activeMinutes`).
|
||||
- Main direct chat bucket is always the literal key `"main"` (resolved to the current agent’s main key).
|
||||
- Group chats use `agent:<agentId>:<channel>:group:<id>` or `agent:<agentId>:<channel>:channel:<id>` (pass the full key).
|
||||
- Cron jobs use `cron:<job.id>`.
|
||||
- Hooks use `hook:<uuid>` unless explicitly set.
|
||||
- Node sessions use `node-<nodeId>` unless explicitly set.
|
||||
|
||||
`sessions_history` fetches the conversation transcript for a specific session.
|
||||
By default, tool results are excluded -- pass `includeTools: true` to see them.
|
||||
`global` and `unknown` are reserved values and are never listed. If `session.scope = "global"`, we alias it to `main` for all tools so callers never see `global`.
|
||||
|
||||
Both tools accept either a **session key** (like `"main"`) or a **session ID**
|
||||
from a previous list call.
|
||||
## sessions_list
|
||||
|
||||
## Sending cross-session messages
|
||||
List sessions as an array of rows.
|
||||
|
||||
`sessions_send` delivers a message to another session and optionally waits for
|
||||
the response:
|
||||
Parameters:
|
||||
|
||||
- **Fire-and-forget:** set `timeoutSeconds: 0` to enqueue and return
|
||||
immediately.
|
||||
- **Wait for reply:** set a timeout and get the response inline.
|
||||
- `kinds?: string[]` filter: any of `"main" | "group" | "cron" | "hook" | "node" | "other"`
|
||||
- `limit?: number` max rows (default: server default, clamp e.g. 200)
|
||||
- `activeMinutes?: number` only sessions updated within N minutes
|
||||
- `messageLimit?: number` 0 = no messages (default 0); >0 = include last N messages
|
||||
|
||||
After the target responds, OpenClaw can run a **reply-back loop** where the
|
||||
agents alternate messages (up to 5 turns). The target agent can reply
|
||||
`REPLY_SKIP` to stop early.
|
||||
Behavior:
|
||||
|
||||
## Spawning sub-agents
|
||||
- `messageLimit > 0` fetches `chat.history` per session and includes the last N messages.
|
||||
- Tool results are filtered out in list output; use `sessions_history` for tool messages.
|
||||
- When running in a **sandboxed** agent session, session tools default to **spawned-only visibility** (see below).
|
||||
|
||||
`sessions_spawn` creates an isolated session for a background task. It is always
|
||||
non-blocking -- it returns immediately with a `runId` and `childSessionKey`.
|
||||
Row shape (JSON):
|
||||
|
||||
Key options:
|
||||
- `key`: session key (string)
|
||||
- `kind`: `main | group | cron | hook | node | other`
|
||||
- `channel`: `whatsapp | telegram | discord | signal | imessage | webchat | internal | unknown`
|
||||
- `displayName` (group display label if available)
|
||||
- `updatedAt` (ms)
|
||||
- `sessionId`
|
||||
- `model`, `contextTokens`, `totalTokens`
|
||||
- `thinkingLevel`, `verboseLevel`, `systemSent`, `abortedLastRun`
|
||||
- `sendPolicy` (session override if set)
|
||||
- `lastChannel`, `lastTo`
|
||||
- `deliveryContext` (normalized `{ channel, to, accountId }` when available)
|
||||
- `transcriptPath` (best-effort path derived from store dir + sessionId)
|
||||
- `messages?` (only when `messageLimit > 0`)
|
||||
|
||||
- `runtime: "subagent"` (default) or `"acp"` for external harness agents.
|
||||
- `model` and `thinking` overrides for the child session.
|
||||
- `thread: true` to bind the spawn to a chat thread (Discord, Slack, etc.).
|
||||
- `sandbox: "require"` to enforce sandboxing on the child.
|
||||
## sessions_history
|
||||
|
||||
Sub-agents get the full tool set minus session tools (no recursive spawning).
|
||||
After completion, an announce step posts the result to the requester's channel.
|
||||
Fetch transcript for one session.
|
||||
|
||||
For ACP-specific behavior, see [ACP Agents](/tools/acp-agents).
|
||||
Parameters:
|
||||
|
||||
## Visibility
|
||||
- `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)
|
||||
- `limit?: number` max messages (server clamps)
|
||||
- `includeTools?: boolean` (default false)
|
||||
|
||||
Session tools are scoped to limit what the agent can see:
|
||||
Behavior:
|
||||
|
||||
| Level | Scope |
|
||||
| ------- | ---------------------------------------- |
|
||||
| `self` | Only the current session |
|
||||
| `tree` | Current session + spawned sub-agents |
|
||||
| `agent` | All sessions for this agent |
|
||||
| `all` | All sessions (cross-agent if configured) |
|
||||
- `includeTools=false` filters `role: "toolResult"` messages.
|
||||
- Returns messages array in the raw transcript format.
|
||||
- When given a `sessionId`, OpenClaw resolves it to the corresponding session key (missing ids error).
|
||||
|
||||
Default is `tree`. Sandboxed sessions are clamped to `tree` regardless of
|
||||
config.
|
||||
## Gateway session history and live transcript APIs
|
||||
|
||||
## Further reading
|
||||
Control UI and gateway clients can use the lower level history and live transcript surfaces directly.
|
||||
|
||||
- [Session Management](/concepts/session) -- routing, lifecycle, maintenance
|
||||
- [ACP Agents](/tools/acp-agents) -- external harness spawning
|
||||
- [Multi-agent](/concepts/multi-agent) -- multi-agent architecture
|
||||
- [Gateway Configuration](/gateway/configuration) -- session tool config knobs
|
||||
HTTP:
|
||||
|
||||
- `GET /sessions/{sessionKey}/history`
|
||||
- Query params: `limit`, `cursor`, `includeTools=1`, `follow=1`
|
||||
- Unknown sessions return HTTP `404` with `error.type = "not_found"`
|
||||
- `follow=1` upgrades the response to an SSE stream of transcript updates for that session
|
||||
|
||||
WebSocket:
|
||||
|
||||
- `sessions.subscribe` subscribes to all session lifecycle and transcript events visible to the client
|
||||
- `sessions.messages.subscribe { key }` subscribes only to `session.message` events for one session
|
||||
- `sessions.messages.unsubscribe { key }` removes that targeted transcript subscription
|
||||
- `session.message` carries appended transcript messages plus live usage metadata when available
|
||||
- `sessions.changed` emits `phase: "message"` for transcript appends so session lists can refresh counters and previews
|
||||
|
||||
## sessions_send
|
||||
|
||||
Send a message into another session.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `sessionKey` (required; accepts session key or `sessionId` from `sessions_list`)
|
||||
- `message` (required)
|
||||
- `timeoutSeconds?: number` (default >0; 0 = fire-and-forget)
|
||||
|
||||
Behavior:
|
||||
|
||||
- `timeoutSeconds = 0`: enqueue and return `{ runId, status: "accepted" }`.
|
||||
- `timeoutSeconds > 0`: wait up to N seconds for completion, then return `{ runId, status: "ok", reply }`.
|
||||
- If wait times out: `{ runId, status: "timeout", error }`. Run continues; call `sessions_history` later.
|
||||
- If the run fails: `{ runId, status: "error", error }`.
|
||||
- Announce delivery runs after the primary run completes and is best-effort; `status: "ok"` does not guarantee the announce was delivered.
|
||||
- Waits via gateway `agent.wait` (server-side) so reconnects don't drop the wait.
|
||||
- Agent-to-agent message context is injected for the primary run.
|
||||
- Inter-session messages are persisted with `message.provenance.kind = "inter_session"` so transcript readers can distinguish routed agent instructions from external user input.
|
||||
- After the primary run completes, OpenClaw runs a **reply-back loop**:
|
||||
- Round 2+ alternates between requester and target agents.
|
||||
- Reply exactly `REPLY_SKIP` to stop the ping‑pong.
|
||||
- Max turns is `session.agentToAgent.maxPingPongTurns` (0–5, default 5).
|
||||
- Once the loop ends, OpenClaw runs the **agent‑to‑agent announce step** (target agent only):
|
||||
- Reply exactly `ANNOUNCE_SKIP` to stay silent.
|
||||
- Any other reply is sent to the target channel.
|
||||
- Announce step includes the original request + round‑1 reply + latest ping‑pong reply.
|
||||
|
||||
## Channel Field
|
||||
|
||||
- For groups, `channel` is the channel recorded on the session entry.
|
||||
- For direct chats, `channel` maps from `lastChannel`.
|
||||
- For cron/hook/node, `channel` is `internal`.
|
||||
- If missing, `channel` is `unknown`.
|
||||
|
||||
## Security / Send Policy
|
||||
|
||||
Policy-based blocking by channel/chat type (not per session id).
|
||||
|
||||
```json
|
||||
{
|
||||
"session": {
|
||||
"sendPolicy": {
|
||||
"rules": [
|
||||
{
|
||||
"match": { "channel": "discord", "chatType": "group" },
|
||||
"action": "deny"
|
||||
}
|
||||
],
|
||||
"default": "allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Runtime override (per session entry):
|
||||
|
||||
- `sendPolicy: "allow" | "deny"` (unset = inherit config)
|
||||
- Settable via `sessions.patch` or owner-only `/send on|off|inherit` (standalone message).
|
||||
|
||||
Enforcement points:
|
||||
|
||||
- `chat.send` / `agent` (gateway)
|
||||
- auto-reply delivery logic
|
||||
|
||||
## sessions_spawn
|
||||
|
||||
Spawn an isolated delegated session.
|
||||
|
||||
- Default runtime: OpenClaw sub-agent (`runtime: "subagent"`).
|
||||
- ACP harness sessions use `runtime: "acp"` and follow ACP-specific targeting/policy rules.
|
||||
- This section focuses on sub-agent behavior unless noted otherwise. For ACP-specific behavior, see [ACP Agents](/tools/acp-agents).
|
||||
|
||||
Parameters:
|
||||
|
||||
- `task` (required)
|
||||
- `runtime?` (`subagent|acp`; defaults to `subagent`)
|
||||
- `label?` (optional; used for logs/UI)
|
||||
- `agentId?` (optional)
|
||||
- `runtime: "subagent"`: target another OpenClaw agent id if allowed by `subagents.allowAgents`
|
||||
- `runtime: "acp"`: target an ACP harness id if allowed by `acp.allowedAgents`
|
||||
- `model?` (optional; overrides the sub-agent model; invalid values error)
|
||||
- `thinking?` (optional; overrides thinking level for the sub-agent run)
|
||||
- `runTimeoutSeconds?` (defaults to `agents.defaults.subagents.runTimeoutSeconds` when set, otherwise `0`; when set, aborts the sub-agent run after N seconds)
|
||||
- `thread?` (default false; request thread-bound routing for this spawn when supported by the channel/plugin)
|
||||
- `mode?` (`run|session`; defaults to `run`, but defaults to `session` when `thread=true`; `mode="session"` requires `thread=true`)
|
||||
- `cleanup?` (`delete|keep`, default `keep`)
|
||||
- `sandbox?` (`inherit|require`, default `inherit`; `require` rejects spawn unless the target child runtime is sandboxed)
|
||||
- `attachments?` (optional array of inline files; subagent runtime only, ACP rejects). Each entry: `{ name, content, encoding?: "utf8" | "base64", mimeType? }`. Files are materialized into the child workspace at `.openclaw/attachments/<uuid>/`. Returns a receipt with sha256 per file.
|
||||
- `attachAs?` (optional; `{ mountPath? }` hint reserved for future mount implementations)
|
||||
|
||||
Allowlist:
|
||||
|
||||
- `runtime: "subagent"`: `agents.list[].subagents.allowAgents` controls which OpenClaw agent ids are allowed via `agentId` (`["*"]` to allow any). Default: only the requester agent.
|
||||
- `runtime: "acp"`: `acp.allowedAgents` controls which ACP harness ids are allowed. This is a separate policy from `subagents.allowAgents`.
|
||||
- Sandbox inheritance guard: if the requester session is sandboxed, `sessions_spawn` rejects targets that would run unsandboxed.
|
||||
|
||||
Discovery:
|
||||
|
||||
- Use `agents_list` to discover allowed targets for `runtime: "subagent"`.
|
||||
- For `runtime: "acp"`, use configured ACP harness ids and `acp.allowedAgents`; `agents_list` does not list ACP harness targets.
|
||||
|
||||
Behavior:
|
||||
|
||||
- Starts a new `agent:<agentId>:subagent:<uuid>` session with `deliver: false`.
|
||||
- Sub-agents default to the full tool set **minus session tools** (configurable via `tools.subagents.tools`).
|
||||
- Sub-agents are not allowed to call `sessions_spawn` (no sub-agent → sub-agent spawning).
|
||||
- Always non-blocking: returns `{ status: "accepted", runId, childSessionKey }` immediately.
|
||||
- With `thread=true`, channel plugins can bind delivery/routing to a thread target (Discord support is controlled by `session.threadBindings.*` and `channels.discord.threadBindings.*`).
|
||||
- After completion, OpenClaw runs a sub-agent **announce step** and posts the result to the requester chat channel.
|
||||
- If the assistant final reply is empty, the latest `toolResult` from sub-agent history is included as `Result`.
|
||||
- Reply exactly `ANNOUNCE_SKIP` during the announce step to stay silent.
|
||||
- Announce replies are normalized to `Status`/`Result`/`Notes`; `Status` comes from runtime outcome (not model text).
|
||||
- Sub-agent sessions are auto-archived after `agents.defaults.subagents.archiveAfterMinutes` (default: 60).
|
||||
- Announce replies include a stats line (runtime, tokens, sessionKey/sessionId, transcript path, and optional cost).
|
||||
|
||||
## Sandbox Session Visibility
|
||||
|
||||
Session tools can be scoped to reduce cross-session access.
|
||||
|
||||
Default behavior:
|
||||
|
||||
- `tools.sessions.visibility` defaults to `tree` (current session + spawned subagent sessions).
|
||||
- For sandboxed sessions, `agents.defaults.sandbox.sessionToolsVisibility` can hard-clamp visibility.
|
||||
|
||||
Config:
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
sessions: {
|
||||
// "self" | "tree" | "agent" | "all"
|
||||
// default: "tree"
|
||||
visibility: "tree",
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
sandbox: {
|
||||
// default: "spawned"
|
||||
sessionToolsVisibility: "spawned", // or "all"
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `self`: only the current session key.
|
||||
- `tree`: current session + sessions spawned by the current session.
|
||||
- `agent`: any session belonging to the current agent id.
|
||||
- `all`: any session (cross-agent access still requires `tools.agentToAgent`).
|
||||
- When a session is sandboxed and `sessionToolsVisibility="spawned"`, OpenClaw clamps visibility to `tree` even if you set `tools.sessions.visibility="all"`.
|
||||
|
||||
@@ -1,113 +1,310 @@
|
||||
---
|
||||
summary: "How OpenClaw manages conversation sessions"
|
||||
summary: "Session management rules, keys, and persistence for chats"
|
||||
read_when:
|
||||
- You want to understand session routing and isolation
|
||||
- You want to configure DM scope for multi-user setups
|
||||
- Modifying session handling or storage
|
||||
title: "Session Management"
|
||||
---
|
||||
|
||||
# Session Management
|
||||
|
||||
OpenClaw organizes conversations into **sessions**. Each message is routed to a
|
||||
session based on where it came from -- DMs, group chats, cron jobs, etc.
|
||||
OpenClaw treats **one direct-chat session per agent** as primary. Direct chats collapse to `agent:<agentId>:<mainKey>` (default `main`), while group/channel chats get their own keys. `session.mainKey` is honored.
|
||||
|
||||
## How messages are routed
|
||||
Use `session.dmScope` to control how **direct messages** are grouped:
|
||||
|
||||
| Source | Behavior |
|
||||
| --------------- | ------------------------- |
|
||||
| Direct messages | Shared session by default |
|
||||
| Group chats | Isolated per group |
|
||||
| Rooms/channels | Isolated per room |
|
||||
| Cron jobs | Fresh session per run |
|
||||
| Webhooks | Isolated per hook |
|
||||
- `main` (default): all DMs share the main session for continuity.
|
||||
- `per-peer`: isolate by sender id across channels.
|
||||
- `per-channel-peer`: isolate by channel + sender (recommended for multi-user inboxes).
|
||||
- `per-account-channel-peer`: isolate by account + channel + sender (recommended for multi-account inboxes).
|
||||
Use `session.identityLinks` to map provider-prefixed peer ids to a canonical identity so the same person shares a DM session across channels when using `per-peer`, `per-channel-peer`, or `per-account-channel-peer`.
|
||||
|
||||
## DM isolation
|
||||
## Secure DM mode (recommended for multi-user setups)
|
||||
|
||||
By default, all DMs share one session for continuity. This is fine for
|
||||
single-user setups.
|
||||
> **Security Warning:** If your agent can receive DMs from **multiple people**, you should strongly consider enabling secure DM mode. Without it, all users share the same conversation context, which can leak private information between users.
|
||||
|
||||
<Warning>
|
||||
If multiple people can message your agent, enable DM isolation. Without it, all
|
||||
users share the same conversation context -- Alice's private messages would be
|
||||
visible to Bob.
|
||||
</Warning>
|
||||
**Example of the problem with default settings:**
|
||||
|
||||
**The fix:**
|
||||
- Alice (`<SENDER_A>`) messages your agent about a private topic (for example, a medical appointment)
|
||||
- Bob (`<SENDER_B>`) messages your agent asking "What were we talking about?"
|
||||
- Because both DMs share the same session, the model may answer Bob using Alice's prior context.
|
||||
|
||||
**The fix:** Set `dmScope` to isolate sessions per user:
|
||||
|
||||
```json5
|
||||
// ~/.openclaw/openclaw.json
|
||||
{
|
||||
session: {
|
||||
dmScope: "per-channel-peer", // isolate by channel + sender
|
||||
// Secure DM mode: isolate DM context per channel + sender.
|
||||
dmScope: "per-channel-peer",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Other options:
|
||||
**When to enable this:**
|
||||
|
||||
- `main` (default) -- all DMs share one session.
|
||||
- `per-peer` -- isolate by sender (across channels).
|
||||
- `per-channel-peer` -- isolate by channel + sender (recommended).
|
||||
- `per-account-channel-peer` -- isolate by account + channel + sender.
|
||||
- You have pairing approvals for more than one sender
|
||||
- You use a DM allowlist with multiple entries
|
||||
- You set `dmPolicy: "open"`
|
||||
- Multiple phone numbers or accounts can message your agent
|
||||
|
||||
<Tip>
|
||||
If the same person contacts you from multiple channels, use
|
||||
`session.identityLinks` to link their identities so they share one session.
|
||||
</Tip>
|
||||
Notes:
|
||||
|
||||
Verify your setup with `openclaw security audit`.
|
||||
- Default is `dmScope: "main"` for continuity (all DMs share the main session). This is fine for single-user setups.
|
||||
- Local CLI onboarding writes `session.dmScope: "per-channel-peer"` by default when unset (existing explicit values are preserved).
|
||||
- For multi-account inboxes on the same channel, prefer `per-account-channel-peer`.
|
||||
- If the same person contacts you on multiple channels, use `session.identityLinks` to collapse their DM sessions into one canonical identity.
|
||||
- You can verify your DM settings with `openclaw security audit` (see [security](/cli/security)).
|
||||
|
||||
## Session lifecycle
|
||||
## Gateway is the source of truth
|
||||
|
||||
Sessions are reused until they expire:
|
||||
All session state is **owned by the gateway** (the “master” OpenClaw). UI clients (macOS app, WebChat, etc.) must query the gateway for session lists and token counts instead of reading local files.
|
||||
|
||||
- **Daily reset** (default) -- new session at 4:00 AM local time on the gateway
|
||||
host.
|
||||
- **Idle reset** (optional) -- new session after a period of inactivity. Set
|
||||
`session.reset.idleMinutes`.
|
||||
- **Manual reset** -- type `/new` or `/reset` in chat. `/new <model>` also
|
||||
switches the model.
|
||||
|
||||
When both daily and idle resets are configured, whichever expires first wins.
|
||||
- In **remote mode**, the session store you care about lives on the remote gateway host, not your Mac.
|
||||
- Token counts shown in UIs come from the gateway’s store fields (`inputTokens`, `outputTokens`, `totalTokens`, `contextTokens`). Clients do not parse JSONL transcripts to “fix up” totals.
|
||||
|
||||
## Where state lives
|
||||
|
||||
All session state is owned by the **gateway**. UI clients query the gateway for
|
||||
session data.
|
||||
- On the **gateway host**:
|
||||
- Store file: `~/.openclaw/agents/<agentId>/sessions/sessions.json` (per agent).
|
||||
- Transcripts: `~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl` (Telegram topic sessions use `.../<SessionId>-topic-<threadId>.jsonl`).
|
||||
- The store is a map `sessionKey -> { sessionId, updatedAt, ... }`. Deleting entries is safe; they are recreated on demand.
|
||||
- Group entries may include `displayName`, `channel`, `subject`, `room`, and `space` to label sessions in UIs.
|
||||
- Session entries include `origin` metadata (label + routing hints) so UIs can explain where a session came from.
|
||||
- OpenClaw does **not** read legacy Pi/Tau session folders.
|
||||
|
||||
- **Store:** `~/.openclaw/agents/<agentId>/sessions/sessions.json`
|
||||
- **Transcripts:** `~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl`
|
||||
## Maintenance
|
||||
|
||||
## Session maintenance
|
||||
OpenClaw applies session-store maintenance to keep `sessions.json` and transcript artifacts bounded over time.
|
||||
|
||||
OpenClaw automatically bounds session storage over time. By default, it runs
|
||||
in `warn` mode (reports what would be cleaned). Set `session.maintenance.mode`
|
||||
to `"enforce"` for automatic cleanup:
|
||||
### Defaults
|
||||
|
||||
- `session.maintenance.mode`: `warn`
|
||||
- `session.maintenance.pruneAfter`: `30d`
|
||||
- `session.maintenance.maxEntries`: `500`
|
||||
- `session.maintenance.rotateBytes`: `10mb`
|
||||
- `session.maintenance.resetArchiveRetention`: defaults to `pruneAfter` (`30d`)
|
||||
- `session.maintenance.maxDiskBytes`: unset (disabled)
|
||||
- `session.maintenance.highWaterBytes`: defaults to `80%` of `maxDiskBytes` when budgeting is enabled
|
||||
|
||||
### How it works
|
||||
|
||||
Maintenance runs during session-store writes, and you can trigger it on demand with `openclaw sessions cleanup`.
|
||||
|
||||
- `mode: "warn"`: reports what would be evicted but does not mutate entries/transcripts.
|
||||
- `mode: "enforce"`: applies cleanup in this order:
|
||||
1. prune stale entries older than `pruneAfter`
|
||||
2. cap entry count to `maxEntries` (oldest first)
|
||||
3. archive transcript files for removed entries that are no longer referenced
|
||||
4. purge old `*.deleted.<timestamp>` and `*.reset.<timestamp>` archives by retention policy
|
||||
5. rotate `sessions.json` when it exceeds `rotateBytes`
|
||||
6. if `maxDiskBytes` is set, enforce disk budget toward `highWaterBytes` (oldest artifacts first, then oldest sessions)
|
||||
|
||||
### Performance caveat for large stores
|
||||
|
||||
Large session stores are common in high-volume setups. Maintenance work is write-path work, so very large stores can increase write latency.
|
||||
|
||||
What increases cost most:
|
||||
|
||||
- very high `session.maintenance.maxEntries` values
|
||||
- long `pruneAfter` windows that keep stale entries around
|
||||
- many transcript/archive artifacts in `~/.openclaw/agents/<agentId>/sessions/`
|
||||
- enabling disk budgets (`maxDiskBytes`) without reasonable pruning/cap limits
|
||||
|
||||
What to do:
|
||||
|
||||
- use `mode: "enforce"` in production so growth is bounded automatically
|
||||
- set both time and count limits (`pruneAfter` + `maxEntries`), not just one
|
||||
- set `maxDiskBytes` + `highWaterBytes` for hard upper bounds in large deployments
|
||||
- keep `highWaterBytes` meaningfully below `maxDiskBytes` (default is 80%)
|
||||
- run `openclaw sessions cleanup --dry-run --json` after config changes to verify projected impact before enforcing
|
||||
- for frequent active sessions, pass `--active-key` when running manual cleanup
|
||||
|
||||
### Customize examples
|
||||
|
||||
Use a conservative enforce policy:
|
||||
|
||||
```json5
|
||||
{
|
||||
session: {
|
||||
maintenance: {
|
||||
mode: "enforce",
|
||||
pruneAfter: "30d",
|
||||
maxEntries: 500,
|
||||
pruneAfter: "45d",
|
||||
maxEntries: 800,
|
||||
rotateBytes: "20mb",
|
||||
resetArchiveRetention: "14d",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Preview with `openclaw sessions cleanup --dry-run`.
|
||||
Enable a hard disk budget for the sessions directory:
|
||||
|
||||
## Inspecting sessions
|
||||
```json5
|
||||
{
|
||||
session: {
|
||||
maintenance: {
|
||||
mode: "enforce",
|
||||
maxDiskBytes: "1gb",
|
||||
highWaterBytes: "800mb",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `openclaw status` -- session store path and recent activity.
|
||||
- `openclaw sessions --json` -- all sessions (filter with `--active <minutes>`).
|
||||
- `/status` in chat -- context usage, model, and toggles.
|
||||
- `/context list` -- what is in the system prompt.
|
||||
Tune for larger installs (example):
|
||||
|
||||
## Further reading
|
||||
```json5
|
||||
{
|
||||
session: {
|
||||
maintenance: {
|
||||
mode: "enforce",
|
||||
pruneAfter: "14d",
|
||||
maxEntries: 2000,
|
||||
rotateBytes: "25mb",
|
||||
maxDiskBytes: "2gb",
|
||||
highWaterBytes: "1.6gb",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- [Session Pruning](/concepts/session-pruning) -- trimming tool results
|
||||
- [Compaction](/concepts/compaction) -- summarizing long conversations
|
||||
- [Session Tools](/concepts/session-tool) -- agent tools for cross-session work
|
||||
- [Session Management Deep Dive](/reference/session-management-compaction) --
|
||||
store schema, transcripts, send policy, origin metadata, and advanced config
|
||||
Preview or force maintenance from CLI:
|
||||
|
||||
```bash
|
||||
openclaw sessions cleanup --dry-run
|
||||
openclaw sessions cleanup --enforce
|
||||
```
|
||||
|
||||
## Session pruning
|
||||
|
||||
OpenClaw trims **old tool results** from the in-memory context right before LLM calls by default.
|
||||
This does **not** rewrite JSONL history. See [/concepts/session-pruning](/concepts/session-pruning).
|
||||
|
||||
## Pre-compaction memory flush
|
||||
|
||||
When a session nears auto-compaction, OpenClaw can run a **silent memory flush**
|
||||
turn that reminds the model to write durable notes to disk. This only runs when
|
||||
the workspace is writable. See [Memory](/concepts/memory) and
|
||||
[Compaction](/concepts/compaction).
|
||||
|
||||
## Mapping transports → session keys
|
||||
|
||||
- Direct chats follow `session.dmScope` (default `main`).
|
||||
- `main`: `agent:<agentId>:<mainKey>` (continuity across devices/channels).
|
||||
- Multiple phone numbers and channels can map to the same agent main key; they act as transports into one conversation.
|
||||
- `per-peer`: `agent:<agentId>:direct:<peerId>`.
|
||||
- `per-channel-peer`: `agent:<agentId>:<channel>:direct:<peerId>`.
|
||||
- `per-account-channel-peer`: `agent:<agentId>:<channel>:<accountId>:direct:<peerId>` (accountId defaults to `default`).
|
||||
- If `session.identityLinks` matches a provider-prefixed peer id (for example `telegram:123`), the canonical key replaces `<peerId>` so the same person shares a session across channels.
|
||||
- Group chats isolate state: `agent:<agentId>:<channel>:group:<id>` (rooms/channels use `agent:<agentId>:<channel>:channel:<id>`).
|
||||
- Telegram forum topics append `:topic:<threadId>` to the group id for isolation.
|
||||
- Legacy `group:<id>` keys are still recognized for migration.
|
||||
- Inbound contexts may still use `group:<id>`; the channel is inferred from `Provider` and normalized to the canonical `agent:<agentId>:<channel>:group:<id>` form.
|
||||
- Other sources:
|
||||
- Cron jobs: `cron:<job.id>` (isolated) or custom `session:<custom-id>` (persistent)
|
||||
- Webhooks: `hook:<uuid>` (unless explicitly set by the hook)
|
||||
- Node runs: `node-<nodeId>`
|
||||
|
||||
## Lifecycle
|
||||
|
||||
- Reset policy: sessions are reused until they expire, and expiry is evaluated on the next inbound message.
|
||||
- Daily reset: defaults to **4:00 AM local time on the gateway host**. A session is stale once its last update is earlier than the most recent daily reset time.
|
||||
- Idle reset (optional): `idleMinutes` adds a sliding idle window. When both daily and idle resets are configured, **whichever expires first** forces a new session.
|
||||
- Legacy idle-only: if you set `session.idleMinutes` without any `session.reset`/`resetByType` config, OpenClaw stays in idle-only mode for backward compatibility.
|
||||
- Per-type overrides (optional): `resetByType` lets you override the policy for `direct`, `group`, and `thread` sessions (thread = Slack/Discord threads, Telegram topics, Matrix threads when provided by the connector).
|
||||
- Per-channel overrides (optional): `resetByChannel` overrides the reset policy for a channel (applies to all session types for that channel and takes precedence over `reset`/`resetByType`).
|
||||
- Reset triggers: exact `/new` or `/reset` (plus any extras in `resetTriggers`) start a fresh session id and pass the remainder of the message through. `/new <model>` accepts a model alias, `provider/model`, or provider name (fuzzy match) to set the new session model. If `/new` or `/reset` is sent alone, OpenClaw runs a short “hello” greeting turn to confirm the reset.
|
||||
- Manual reset: delete specific keys from the store or remove the JSONL transcript; the next message recreates them.
|
||||
- Isolated cron jobs always mint a fresh `sessionId` per run (no idle reuse).
|
||||
|
||||
## Send policy (optional)
|
||||
|
||||
Block delivery for specific session types without listing individual ids.
|
||||
|
||||
```json5
|
||||
{
|
||||
session: {
|
||||
sendPolicy: {
|
||||
rules: [
|
||||
{ action: "deny", match: { channel: "discord", chatType: "group" } },
|
||||
{ action: "deny", match: { keyPrefix: "cron:" } },
|
||||
// Match the raw session key (including the `agent:<id>:` prefix).
|
||||
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
|
||||
],
|
||||
default: "allow",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Runtime override (owner only):
|
||||
|
||||
- `/send on` → allow for this session
|
||||
- `/send off` → deny for this session
|
||||
- `/send inherit` → clear override and use config rules
|
||||
Send these as standalone messages so they register.
|
||||
|
||||
## Configuration (optional rename example)
|
||||
|
||||
```json5
|
||||
// ~/.openclaw/openclaw.json
|
||||
{
|
||||
session: {
|
||||
scope: "per-sender", // keep group keys separate
|
||||
dmScope: "main", // DM continuity (set per-channel-peer/per-account-channel-peer for shared inboxes)
|
||||
identityLinks: {
|
||||
alice: ["telegram:123456789", "discord:987654321012345678"],
|
||||
},
|
||||
reset: {
|
||||
// Defaults: mode=daily, atHour=4 (gateway host local time).
|
||||
// If you also set idleMinutes, whichever expires first wins.
|
||||
mode: "daily",
|
||||
atHour: 4,
|
||||
idleMinutes: 120,
|
||||
},
|
||||
resetByType: {
|
||||
thread: { mode: "daily", atHour: 4 },
|
||||
direct: { mode: "idle", idleMinutes: 240 },
|
||||
group: { mode: "idle", idleMinutes: 120 },
|
||||
},
|
||||
resetByChannel: {
|
||||
discord: { mode: "idle", idleMinutes: 10080 },
|
||||
},
|
||||
resetTriggers: ["/new", "/reset"],
|
||||
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
|
||||
mainKey: "main",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Inspecting
|
||||
|
||||
- `openclaw status` — shows store path and recent sessions.
|
||||
- `openclaw sessions --json` — dumps every entry (filter with `--active <minutes>`).
|
||||
- `openclaw gateway call sessions.list --params '{}'` — fetch sessions from the running gateway (use `--url`/`--token` for remote gateway access).
|
||||
- Send `/status` as a standalone message in chat to see whether the agent is reachable, how much of the session context is used, current thinking/fast/verbose toggles, and when your WhatsApp web creds were last refreshed (helps spot relink needs).
|
||||
- Send `/context list` or `/context detail` to see what’s in the system prompt and injected workspace files (and the biggest context contributors).
|
||||
- Send `/stop` (or standalone abort phrases like `stop`, `stop action`, `stop run`, `stop openclaw`) to abort the current run, clear queued followups for that session, and stop any sub-agent runs spawned from it (the reply includes the stopped count).
|
||||
- Send `/compact` (optional instructions) as a standalone message to summarize older context and free up window space. See [/concepts/compaction](/concepts/compaction).
|
||||
- JSONL transcripts can be opened directly to review full turns.
|
||||
|
||||
## Tips
|
||||
|
||||
- Keep the primary key dedicated to 1:1 traffic; let groups keep their own keys.
|
||||
- When automating cleanup, delete individual keys instead of the whole store to preserve context elsewhere.
|
||||
|
||||
## Session origin metadata
|
||||
|
||||
Each session entry records where it came from (best-effort) in `origin`:
|
||||
|
||||
- `label`: human label (resolved from conversation label + group subject/channel)
|
||||
- `provider`: normalized channel id (including extensions)
|
||||
- `from`/`to`: raw routing ids from the inbound envelope
|
||||
- `accountId`: provider account id (when multi-account)
|
||||
- `threadId`: thread/topic id when the channel supports it
|
||||
The origin fields are populated for direct messages, channels, and groups. If a
|
||||
connector only updates delivery routing (for example, to keep a DM main session
|
||||
fresh), it should still provide inbound context so the session keeps its
|
||||
explainer metadata. Extensions can do this by sending `ConversationLabel`,
|
||||
`GroupSubject`, `GroupChannel`, `GroupSpace`, and `SenderName` in the inbound
|
||||
context and calling `recordSessionMetaFromInbound` (or passing the same context
|
||||
to `updateLastRoute`).
|
||||
|
||||
@@ -1032,16 +1032,7 @@
|
||||
"concepts/session",
|
||||
"concepts/session-pruning",
|
||||
"concepts/session-tool",
|
||||
{
|
||||
"group": "Memory",
|
||||
"pages": [
|
||||
"concepts/memory",
|
||||
"concepts/memory-builtin",
|
||||
"concepts/memory-qmd",
|
||||
"concepts/memory-honcho",
|
||||
"concepts/memory-search"
|
||||
]
|
||||
},
|
||||
"concepts/memory",
|
||||
"concepts/compaction"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -906,7 +906,7 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- Also used as fallback routing when the selected/default model cannot accept image input.
|
||||
- `imageGenerationModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
- Used by the shared image-generation capability and any future tool/plugin surface that generates images.
|
||||
- Typical values: `google/gemini-3-pro-image-preview` for native Gemini image generation, `fal/fal-ai/flux/dev` for fal, or `openai/gpt-image-1` for OpenAI Images.
|
||||
- Typical values: `google/gemini-3-pro-image-preview` for the native Nano Banana-style flow, `fal/fal-ai/flux/dev` for fal, or `openai/gpt-image-1` for OpenAI Images.
|
||||
- If you select a provider/model directly, configure the matching provider auth/API key too (for example `GEMINI_API_KEY` or `GOOGLE_API_KEY` for `google/*`, `OPENAI_API_KEY` for `openai/*`, `FAL_KEY` for `fal/*`).
|
||||
- If omitted, `image_generate` can still infer a best-effort provider default from compatible auth-backed image-generation providers.
|
||||
- `pdfModel`: accepts either a string (`"provider/model"`) or an object (`{ primary, fallbacks }`).
|
||||
@@ -914,13 +914,11 @@ Time format in system prompt. Default: `auto` (OS preference).
|
||||
- If omitted, the PDF tool falls back to `imageModel`, then to best-effort provider defaults.
|
||||
- `pdfMaxBytesMb`: default PDF size limit for the `pdf` tool when `maxBytesMb` is not passed at call time.
|
||||
- `pdfMaxPages`: default maximum pages considered by extraction fallback mode in the `pdf` tool.
|
||||
- `verboseDefault`: default verbose level for agents. Values: `"off"`, `"on"`, `"full"`. Default: `"off"`.
|
||||
- `elevatedDefault`: default elevated-output level for agents. Values: `"off"`, `"on"`, `"ask"`, `"full"`. Default: `"on"`.
|
||||
- `model.primary`: format `provider/model` (e.g. `anthropic/claude-opus-4-6`). If you omit the provider, OpenClaw assumes `anthropic` (deprecated).
|
||||
- `models`: the configured model catalog and allowlist for `/model`. Each entry can include `alias` (shortcut) and `params` (provider-specific, for example `temperature`, `maxTokens`, `cacheRetention`, `context1m`).
|
||||
- `params` merge precedence (config): `agents.defaults.models["provider/model"].params` is the base, then `agents.list[].params` (matching agent id) overrides by key.
|
||||
- Config writers that mutate these fields (for example `/models set`, `/models set-image`, and fallback add/remove commands) save canonical object form and preserve existing fallback lists when possible.
|
||||
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 4.
|
||||
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 1.
|
||||
|
||||
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
|
||||
|
||||
@@ -1001,7 +999,7 @@ Periodic heartbeat runs.
|
||||
}
|
||||
```
|
||||
|
||||
- `every`: duration string (ms/s/m/h). Default: `30m` (API-key auth) or `1h` (OAuth auth). Set to `0m` to disable.
|
||||
- `every`: duration string (ms/s/m/h). Default: `30m`.
|
||||
- `suppressToolErrorWarnings`: when true, suppresses tool error warning payloads during heartbeat runs.
|
||||
- `directPolicy`: direct/DM delivery policy. `allow` (default) permits direct-target delivery. `block` suppresses direct-target delivery and emits `reason=dm-blocked`.
|
||||
- `lightContext`: when true, heartbeat runs use lightweight bootstrap context and keep only `HEARTBEAT.md` from workspace bootstrap files.
|
||||
@@ -1127,8 +1125,6 @@ See [Streaming](/concepts/streaming) for behavior + chunking details.
|
||||
|
||||
See [Typing Indicators](/concepts/typing-indicators).
|
||||
|
||||
<a id="agentsdefaultssandbox"></a>
|
||||
|
||||
### `agents.defaults.sandbox`
|
||||
|
||||
Optional sandboxing for the embedded agent. See [Sandboxing](/gateway/sandboxing) for the full guide.
|
||||
@@ -1616,9 +1612,6 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
|
||||
|
||||
<Accordion title="Session field details">
|
||||
|
||||
- **`scope`**: base session grouping strategy for group-chat contexts.
|
||||
- `per-sender` (default): each sender gets an isolated session within a channel context.
|
||||
- `global`: all participants in a channel context share a single session (use only when shared context is intended).
|
||||
- **`dmScope`**: how DMs are grouped.
|
||||
- `main`: all DMs share the main session.
|
||||
- `per-peer`: isolate by sender id across channels.
|
||||
@@ -1631,7 +1624,6 @@ See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for preceden
|
||||
- If parent `totalTokens` is above this value, OpenClaw starts a fresh thread session instead of inheriting parent transcript history.
|
||||
- Set `0` to disable this guard and always allow parent forking.
|
||||
- **`mainKey`**: legacy field. Runtime now always uses `"main"` for the main direct-chat bucket.
|
||||
- **`agentToAgent.maxPingPongTurns`**: maximum reply-back turns between agents during agent-to-agent exchanges (integer, range: `0`–`5`). `0` disables ping-pong chaining.
|
||||
- **`sendPolicy`**: match by `channel`, `chatType` (`direct|group|channel`, with legacy `dm` alias), `keyPrefix`, or `rawKeyPrefix`. First deny wins.
|
||||
- **`maintenance`**: session-store cleanup + retention controls.
|
||||
- `mode`: `warn` emits warnings only; `enforce` applies cleanup.
|
||||
@@ -2067,7 +2059,7 @@ Notes:
|
||||
- File permissions are `0700` for directories and `0600` for files.
|
||||
- Cleanup follows the `cleanup` policy: `delete` always removes attachments; `keep` retains them only when `retainOnSessionKeep: true`.
|
||||
|
||||
### `agents.defaults.subagents`
|
||||
### `tools.subagents`
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -2075,7 +2067,7 @@ Notes:
|
||||
defaults: {
|
||||
subagents: {
|
||||
model: "minimax/MiniMax-M2.7",
|
||||
maxConcurrent: 8,
|
||||
maxConcurrent: 1,
|
||||
runTimeoutSeconds: 900,
|
||||
archiveAfterMinutes: 60,
|
||||
},
|
||||
@@ -2092,7 +2084,7 @@ Notes:
|
||||
|
||||
## Custom providers and base URLs
|
||||
|
||||
OpenClaw uses the built-in model catalog. Add custom providers via `models.providers` in config or `~/.openclaw/agents/<agentId>/agent/models.json`.
|
||||
OpenClaw uses the pi-coding-agent model catalog. Add custom providers via `models.providers` in config or `~/.openclaw/agents/<agentId>/agent/models.json`.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -2121,7 +2113,7 @@ OpenClaw uses the built-in model catalog. Add custom providers via `models.provi
|
||||
```
|
||||
|
||||
- Use `authHeader: true` + `headers` for custom auth needs.
|
||||
- Override agent config root with `OPENCLAW_AGENT_DIR` (or `PI_CODING_AGENT_DIR`, a legacy environment variable alias).
|
||||
- Override agent config root with `OPENCLAW_AGENT_DIR` (or `PI_CODING_AGENT_DIR`).
|
||||
- Merge precedence for matching provider IDs:
|
||||
- Non-empty agent `models.json` `baseUrl` values win.
|
||||
- Non-empty agent `apiKey` values win only when that provider is not SecretRef-managed in current config/auth-profile context.
|
||||
@@ -2652,50 +2644,6 @@ Convenience flags: `--dev` (uses `~/.openclaw-dev` + port `19001`), `--profile <
|
||||
|
||||
See [Multiple Gateways](/gateway/multiple-gateways).
|
||||
|
||||
### `gateway.tls`
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
tls: {
|
||||
enabled: false,
|
||||
autoGenerate: false,
|
||||
certPath: "/etc/openclaw/tls/server.crt",
|
||||
keyPath: "/etc/openclaw/tls/server.key",
|
||||
caPath: "/etc/openclaw/tls/ca-bundle.crt",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `enabled`: enables TLS termination at the gateway listener (HTTPS/WSS) (default: `false`).
|
||||
- `autoGenerate`: auto-generates a local self-signed cert/key pair when explicit files are not configured; for local/dev use only.
|
||||
- `certPath`: filesystem path to the TLS certificate file.
|
||||
- `keyPath`: filesystem path to the TLS private key file; keep permission-restricted.
|
||||
- `caPath`: optional CA bundle path for client verification or custom trust chains.
|
||||
|
||||
### `gateway.reload`
|
||||
|
||||
```json5
|
||||
{
|
||||
gateway: {
|
||||
reload: {
|
||||
mode: "hybrid", // off | restart | hot | hybrid
|
||||
debounceMs: 500,
|
||||
deferralTimeoutMs: 300000,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `mode`: controls how config edits are applied at runtime.
|
||||
- `"off"`: ignore live edits; changes require an explicit restart.
|
||||
- `"restart"`: always restart the gateway process on config change.
|
||||
- `"hot"`: apply changes in-process without restarting.
|
||||
- `"hybrid"` (default): try hot reload first; fall back to restart if required.
|
||||
- `debounceMs`: debounce window in ms before config changes are applied (non-negative integer).
|
||||
- `deferralTimeoutMs`: maximum time in ms to wait for in-flight operations before forcing a restart (default: `300000` = 5 minutes).
|
||||
|
||||
---
|
||||
|
||||
## Hooks
|
||||
@@ -2978,26 +2926,6 @@ Notes:
|
||||
- See [OAuth](/concepts/oauth).
|
||||
- Secrets runtime behavior and `audit/configure/apply` tooling: [Secrets Management](/gateway/secrets).
|
||||
|
||||
### `auth.cooldowns`
|
||||
|
||||
```json5
|
||||
{
|
||||
auth: {
|
||||
cooldowns: {
|
||||
billingBackoffHours: 5,
|
||||
billingBackoffHoursByProvider: { anthropic: 3, openai: 8 },
|
||||
billingMaxHours: 24,
|
||||
failureWindowHours: 24,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `billingBackoffHours`: base backoff in hours when a profile fails due to billing/insufficient credits (default: `5`).
|
||||
- `billingBackoffHoursByProvider`: optional per-provider overrides for billing backoff hours.
|
||||
- `billingMaxHours`: cap in hours for billing backoff exponential growth (default: `24`).
|
||||
- `failureWindowHours`: rolling window in hours used for backoff counters (default: `24`).
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
@@ -3018,128 +2946,6 @@ Notes:
|
||||
- Default log file: `/tmp/openclaw/openclaw-YYYY-MM-DD.log`.
|
||||
- Set `logging.file` for a stable path.
|
||||
- `consoleLevel` bumps to `debug` when `--verbose`.
|
||||
- `maxFileBytes`: maximum log file size in bytes before writes are suppressed (positive integer; default: `524288000` = 500 MB). Use external log rotation for production deployments.
|
||||
|
||||
---
|
||||
|
||||
## Diagnostics
|
||||
|
||||
```json5
|
||||
{
|
||||
diagnostics: {
|
||||
enabled: true,
|
||||
flags: ["telegram.*"],
|
||||
stuckSessionWarnMs: 30000,
|
||||
|
||||
otel: {
|
||||
enabled: false,
|
||||
endpoint: "https://otel-collector.example.com:4318",
|
||||
protocol: "http/protobuf", // http/protobuf | grpc
|
||||
headers: { "x-tenant-id": "my-org" },
|
||||
serviceName: "openclaw-gateway",
|
||||
traces: true,
|
||||
metrics: true,
|
||||
logs: false,
|
||||
sampleRate: 1.0,
|
||||
flushIntervalMs: 5000,
|
||||
},
|
||||
|
||||
cacheTrace: {
|
||||
enabled: false,
|
||||
includeMessages: true,
|
||||
includePrompt: true,
|
||||
includeSystem: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `enabled`: master toggle for instrumentation output (default: `true`).
|
||||
- `flags`: array of flag strings enabling targeted log output (supports wildcards like `"telegram.*"` or `"*"`).
|
||||
- `stuckSessionWarnMs`: age threshold in ms for emitting stuck-session warnings while a session remains in processing state.
|
||||
- `otel.enabled`: enables the OpenTelemetry export pipeline (default: `false`).
|
||||
- `otel.endpoint`: collector URL for OTel export.
|
||||
- `otel.protocol`: `"http/protobuf"` (default) or `"grpc"`.
|
||||
- `otel.headers`: extra HTTP/gRPC metadata headers sent with OTel export requests.
|
||||
- `otel.serviceName`: service name for resource attributes.
|
||||
- `otel.traces` / `otel.metrics` / `otel.logs`: enable trace, metrics, or log export.
|
||||
- `otel.sampleRate`: trace sampling rate `0`–`1`.
|
||||
- `otel.flushIntervalMs`: periodic telemetry flush interval in ms.
|
||||
- `cacheTrace.enabled`: log cache trace snapshots for embedded runs (default: `false`).
|
||||
- `cacheTrace.includeMessages` / `includePrompt` / `includeSystem`: control what is included in cache trace output (all default: `true`).
|
||||
|
||||
---
|
||||
|
||||
## Update
|
||||
|
||||
```json5
|
||||
{
|
||||
update: {
|
||||
channel: "stable", // stable | beta | dev
|
||||
checkOnStart: true,
|
||||
|
||||
auto: {
|
||||
enabled: false,
|
||||
stableDelayHours: 6,
|
||||
stableJitterHours: 12,
|
||||
betaCheckIntervalHours: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `channel`: release channel for npm/git installs — `"stable"`, `"beta"`, or `"dev"`.
|
||||
- `checkOnStart`: check for npm updates when the gateway starts (default: `true`).
|
||||
- `auto.enabled`: enable background auto-update for package installs (default: `false`).
|
||||
- `auto.stableDelayHours`: minimum delay in hours before stable-channel auto-apply (default: `6`; max: `168`).
|
||||
- `auto.stableJitterHours`: extra stable-channel rollout spread window in hours (default: `12`; max: `168`).
|
||||
- `auto.betaCheckIntervalHours`: how often beta-channel checks run in hours (default: `1`; max: `24`).
|
||||
|
||||
---
|
||||
|
||||
## ACP
|
||||
|
||||
```json5
|
||||
{
|
||||
acp: {
|
||||
enabled: false,
|
||||
dispatch: { enabled: true },
|
||||
backend: "acpx",
|
||||
defaultAgent: "main",
|
||||
allowedAgents: ["main", "ops"],
|
||||
maxConcurrentSessions: 10,
|
||||
|
||||
stream: {
|
||||
coalesceIdleMs: 50,
|
||||
maxChunkChars: 1000,
|
||||
repeatSuppression: true,
|
||||
deliveryMode: "live", // live | final_only
|
||||
hiddenBoundarySeparator: "paragraph", // none | space | newline | paragraph
|
||||
maxOutputChars: 50000,
|
||||
maxSessionUpdateChars: 500,
|
||||
},
|
||||
|
||||
runtime: {
|
||||
ttlMinutes: 30,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `enabled`: global ACP feature gate (default: `false`).
|
||||
- `dispatch.enabled`: independent gate for ACP session turn dispatch (default: `true`). Set `false` to keep ACP commands available while blocking execution.
|
||||
- `backend`: default ACP runtime backend id (must match a registered ACP runtime plugin).
|
||||
- `defaultAgent`: fallback ACP target agent id when spawns do not specify an explicit target.
|
||||
- `allowedAgents`: allowlist of agent ids permitted for ACP runtime sessions; empty means no additional restriction.
|
||||
- `maxConcurrentSessions`: maximum concurrently active ACP sessions.
|
||||
- `stream.coalesceIdleMs`: idle flush window in ms for streamed text.
|
||||
- `stream.maxChunkChars`: maximum chunk size before splitting streamed block projection.
|
||||
- `stream.repeatSuppression`: suppress repeated status/tool lines per turn (default: `true`).
|
||||
- `stream.deliveryMode`: `"live"` streams incrementally; `"final_only"` buffers until turn terminal events.
|
||||
- `stream.hiddenBoundarySeparator`: separator before visible text after hidden tool events (default: `"paragraph"`).
|
||||
- `stream.maxOutputChars`: maximum assistant output characters projected per ACP turn.
|
||||
- `stream.maxSessionUpdateChars`: maximum characters for projected ACP status/update lines.
|
||||
- `runtime.ttlMinutes`: idle TTL in minutes for ACP session workers before eligible cleanup.
|
||||
|
||||
---
|
||||
|
||||
@@ -3183,7 +2989,29 @@ Metadata written by CLI guided setup flows (`onboard`, `configure`, `doctor`):
|
||||
|
||||
## Identity
|
||||
|
||||
See `agents.list` identity fields under [Agent defaults](#agent-defaults).
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
list: [
|
||||
{
|
||||
id: "main",
|
||||
identity: {
|
||||
name: "Samantha",
|
||||
theme: "helpful sloth",
|
||||
emoji: "🦥",
|
||||
avatar: "avatars/samantha.png",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Written by the macOS onboarding assistant. Derives defaults:
|
||||
|
||||
- `messages.ackReaction` from `identity.emoji` (falls back to 👀)
|
||||
- `mentionPatterns` from `identity.name`/`identity.emoji`
|
||||
- `avatar` accepts: workspace-relative path, `http(s)` URL, or `data:` URI
|
||||
|
||||
---
|
||||
|
||||
@@ -3235,48 +3063,6 @@ Current builds no longer include the TCP bridge. Nodes connect over the Gateway
|
||||
- `webhookToken`: bearer token used for cron webhook POST delivery (`delivery.mode = "webhook"`), if omitted no auth header is sent.
|
||||
- `webhook`: deprecated legacy fallback webhook URL (http/https) used only for stored jobs that still have `notify: true`.
|
||||
|
||||
### `cron.retry`
|
||||
|
||||
```json5
|
||||
{
|
||||
cron: {
|
||||
retry: {
|
||||
maxAttempts: 3,
|
||||
backoffMs: [30000, 60000, 300000],
|
||||
retryOn: ["rate_limit", "overloaded", "network", "timeout", "server_error"],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `maxAttempts`: maximum retries for one-shot jobs on transient errors (default: `3`; range: `0`–`10`).
|
||||
- `backoffMs`: array of backoff delays in ms for each retry attempt (default: `[30000, 60000, 300000]`; 1–10 entries).
|
||||
- `retryOn`: error types that trigger retries — `"rate_limit"`, `"overloaded"`, `"network"`, `"timeout"`, `"server_error"`. Omit to retry all transient types.
|
||||
|
||||
Applies only to one-shot cron jobs. Recurring jobs use separate failure handling.
|
||||
|
||||
### `cron.failureAlert`
|
||||
|
||||
```json5
|
||||
{
|
||||
cron: {
|
||||
failureAlert: {
|
||||
enabled: false,
|
||||
after: 3,
|
||||
cooldownMs: 3600000,
|
||||
mode: "announce",
|
||||
accountId: "main",
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- `enabled`: enable failure alerts for cron jobs (default: `false`).
|
||||
- `after`: consecutive failures before an alert fires (positive integer, min: `1`).
|
||||
- `cooldownMs`: minimum milliseconds between repeated alerts for the same job (non-negative integer).
|
||||
- `mode`: delivery mode — `"announce"` sends via a channel message; `"webhook"` posts to the configured webhook.
|
||||
- `accountId`: optional account or channel id to scope alert delivery.
|
||||
|
||||
See [Cron Jobs](/automation/cron-jobs).
|
||||
|
||||
---
|
||||
|
||||
@@ -63,7 +63,6 @@ openclaw channels status --probe
|
||||
<Note>
|
||||
Gateway config reload watches the active config file path (resolved from profile/state defaults, or `OPENCLAW_CONFIG_PATH` when set).
|
||||
Default mode is `gateway.reload.mode="hybrid"`.
|
||||
After the first successful load, the running process serves the active in-memory config snapshot; successful reload swaps that snapshot atomically.
|
||||
</Note>
|
||||
|
||||
## Runtime model
|
||||
|
||||
@@ -195,12 +195,6 @@ The Gateway treats these as **claims** and enforces server-side allowlists.
|
||||
- Operator clients resolve by calling `exec.approval.resolve` (requires `operator.approvals` scope).
|
||||
- For `host=node`, `exec.approval.request` must include `systemRunPlan` (canonical `argv`/`cwd`/`rawCommand`/session metadata). Requests missing `systemRunPlan` are rejected.
|
||||
|
||||
## Agent delivery fallback
|
||||
|
||||
- `agent` requests can include `deliver=true` to request outbound delivery.
|
||||
- `bestEffortDeliver=false` keeps strict behavior: unresolved or internal-only delivery targets return `INVALID_REQUEST`.
|
||||
- `bestEffortDeliver=true` allows fallback to session-only execution when no external deliverable route can be resolved (for example internal/webchat sessions or ambiguous multi-channel configs).
|
||||
|
||||
## Versioning
|
||||
|
||||
- `PROTOCOL_VERSION` lives in `src/gateway/protocol/schema.ts`.
|
||||
|
||||
@@ -21,7 +21,6 @@ Secrets are resolved into an in-memory runtime snapshot.
|
||||
- Startup fails fast when an effectively active SecretRef cannot be resolved.
|
||||
- Reload uses atomic swap: full success, or keep the last-known-good snapshot.
|
||||
- Runtime requests read from the active in-memory snapshot only.
|
||||
- After the first successful config activation/load, runtime code paths keep reading that active in-memory snapshot until a successful reload swaps it.
|
||||
- Outbound delivery paths also read from that active snapshot (for example Discord reply/thread delivery and Telegram action sends); they do not re-resolve SecretRefs on each send.
|
||||
|
||||
This keeps secret-provider outages off hot request paths.
|
||||
@@ -289,39 +288,6 @@ Optional per-id errors:
|
||||
}
|
||||
```
|
||||
|
||||
## MCP server environment variables
|
||||
|
||||
MCP server env vars configured via `plugins.entries.acpx.config.mcpServers` support SecretInput. This keeps API keys and tokens out of plaintext config:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
acpx: {
|
||||
enabled: true,
|
||||
config: {
|
||||
mcpServers: {
|
||||
github: {
|
||||
command: "npx",
|
||||
args: ["-y", "@modelcontextprotocol/server-github"],
|
||||
env: {
|
||||
GITHUB_PERSONAL_ACCESS_TOKEN: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "MCP_GITHUB_PAT",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Plaintext string values still work. Env-template refs like `${MCP_SERVER_API_KEY}` and SecretRef objects are resolved during gateway activation before the MCP server process is spawned. As with other SecretRef surfaces, unresolved refs only block activation when the `acpx` plugin is effectively active.
|
||||
|
||||
## Sandbox SSH auth material
|
||||
|
||||
The core `ssh` sandbox backend also supports SecretRefs for SSH auth material:
|
||||
|
||||
@@ -7,13 +7,10 @@ title: "Security"
|
||||
|
||||
# Security
|
||||
|
||||
<Warning>
|
||||
**Personal assistant trust model:** this guidance assumes one trusted operator boundary per gateway (single-user/personal assistant model).
|
||||
OpenClaw is **not** a hostile multi-tenant security boundary for multiple adversarial users sharing one agent/gateway.
|
||||
If you need mixed-trust or adversarial-user operation, split trust boundaries (separate gateway + credentials, ideally separate OS users/hosts).
|
||||
</Warning>
|
||||
|
||||
**On this page:** [Trust model](#scope-first-personal-assistant-security-model) | [Quick audit](#quick-check-openclaw-security-audit) | [Hardened baseline](#hardened-baseline-in-60-seconds) | [DM access model](#dm-access-model-pairing--allowlist--open--disabled) | [Configuration hardening](#configuration-hardening-examples) | [Incident response](#incident-response)
|
||||
> [!WARNING]
|
||||
> **Personal assistant trust model:** this guidance assumes one trusted operator boundary per gateway (single-user/personal assistant model).
|
||||
> OpenClaw is **not** a hostile multi-tenant security boundary for multiple adversarial users sharing one agent/gateway.
|
||||
> If you need mixed-trust or adversarial-user operation, split trust boundaries (separate gateway + credentials, ideally separate OS users/hosts).
|
||||
|
||||
## Scope first: personal assistant security model
|
||||
|
||||
@@ -49,17 +46,34 @@ OpenClaw is both a product and an experiment: you’re wiring frontier-model beh
|
||||
|
||||
Start with the smallest access that still works, then widen it as you gain confidence.
|
||||
|
||||
### Deployment and host trust
|
||||
## Deployment assumption (important)
|
||||
|
||||
OpenClaw assumes the host and config boundary are trusted:
|
||||
|
||||
- If someone can modify Gateway host state/config (`~/.openclaw`, including `openclaw.json`), treat them as a trusted operator.
|
||||
- Running one Gateway for multiple mutually untrusted/adversarial operators is **not a recommended setup**.
|
||||
- For mixed-trust teams, split trust boundaries with separate gateways (or at minimum separate OS users/hosts).
|
||||
- OpenClaw can run multiple gateway instances on one machine, but recommended operations favor clean trust-boundary separation.
|
||||
- Recommended default: one user per machine/host (or VPS), one gateway for that user, and one or more agents in that gateway.
|
||||
- Inside one Gateway instance, authenticated operator access is a trusted control-plane role, not a per-user tenant role.
|
||||
- If multiple users want OpenClaw, use one VPS/host per user.
|
||||
|
||||
### Practical consequence (operator trust boundary)
|
||||
|
||||
Inside one Gateway instance, authenticated operator access is a trusted control-plane role, not a per-user tenant role.
|
||||
|
||||
- Operators with read/control-plane access can inspect gateway session metadata/history by design.
|
||||
- Session identifiers (`sessionKey`, session IDs, labels) are routing selectors, not authorization tokens.
|
||||
- If several people can message one tool-enabled agent, each of them can steer that same permission set. Per-user session/memory isolation helps privacy, but does not convert a shared agent into per-user host authorization.
|
||||
- Example: expecting per-operator isolation for methods like `sessions.list`, `sessions.preview`, or `chat.history` is outside this model.
|
||||
- If you need adversarial-user isolation, run separate gateways per trust boundary.
|
||||
- Multiple gateways on one machine are technically possible, but not the recommended baseline for multi-user isolation.
|
||||
|
||||
## Personal assistant model (not a multi-tenant bus)
|
||||
|
||||
OpenClaw is designed as a personal assistant security model: one trusted operator boundary, potentially many agents.
|
||||
|
||||
- If several people can message one tool-enabled agent, each of them can steer that same permission set.
|
||||
- Per-user session/memory isolation helps privacy, but does not convert a shared agent into per-user host authorization.
|
||||
- If users may be adversarial to each other, run separate gateways (or separate OS users/hosts) per trust boundary.
|
||||
|
||||
### Shared Slack workspace: real risk
|
||||
|
||||
@@ -167,7 +181,7 @@ If more than one person can DM your bot:
|
||||
- Never combine shared DMs with broad tool access.
|
||||
- This hardens cooperative/shared inboxes, but is not designed as hostile co-tenant isolation when users share host/config write access.
|
||||
|
||||
## What the audit checks (high level)
|
||||
### What the audit checks (high level)
|
||||
|
||||
- **Inbound access** (DM policies, group policies, allowlists): can strangers trigger the bot?
|
||||
- **Tool blast radius** (elevated tools + open rooms): could prompt injection turn into shell/file/network actions?
|
||||
@@ -177,7 +191,7 @@ If more than one person can DM your bot:
|
||||
- **Local disk hygiene** (permissions, symlinks, config includes, “synced folder” paths).
|
||||
- **Plugins** (extensions exist without an explicit allowlist).
|
||||
- **Policy drift/misconfig** (sandbox docker settings configured but sandbox mode off; ineffective `gateway.nodes.denyCommands` patterns because matching is exact command-name only (for example `system.run`) and does not inspect shell text; dangerous `gateway.nodes.allowCommands` entries; global `tools.profile="minimal"` overridden by per-agent profiles; extension plugin tools reachable under permissive tool policy).
|
||||
- **Runtime expectation drift** (for example assuming implicit exec still means `sandbox` when `tools.exec.host` now defaults to `auto`, or explicitly setting `tools.exec.host="sandbox"` while sandbox mode is off).
|
||||
- **Runtime expectation drift** (for example `tools.exec.host="sandbox"` while sandbox mode is off, which now fails closed because no sandbox runtime is available).
|
||||
- **Model hygiene** (warn when configured models look legacy; not a hard block).
|
||||
|
||||
If you run `--deep`, OpenClaw also attempts a best-effort live Gateway probe.
|
||||
@@ -197,7 +211,7 @@ Use this when auditing access or deciding what to back up:
|
||||
- **File-backed secrets payload (optional)**: `~/.openclaw/secrets.json`
|
||||
- **Legacy OAuth import**: `~/.openclaw/credentials/oauth.json`
|
||||
|
||||
## Security audit checklist
|
||||
## Security Audit Checklist
|
||||
|
||||
When the audit prints findings, treat this as a priority order:
|
||||
|
||||
@@ -490,7 +504,6 @@ Treat the snippet above as **secure DM mode**:
|
||||
- Default: `session.dmScope: "main"` (all DMs share one session for continuity).
|
||||
- Local CLI onboarding default: writes `session.dmScope: "per-channel-peer"` when unset (keeps existing explicit values).
|
||||
- Secure DM mode: `session.dmScope: "per-channel-peer"` (each channel+sender pair gets an isolated DM context).
|
||||
- Cross-channel peer isolation: `session.dmScope: "per-peer"` (each sender gets one session across all channels of the same type).
|
||||
|
||||
If you run multiple accounts on the same channel, use `per-account-channel-peer` instead. If the same person contacts you on multiple channels, use `session.identityLinks` to collapse those DM sessions into one canonical identity. See [Session Management](/concepts/session) and [Configuration](/gateway/configuration).
|
||||
|
||||
@@ -521,7 +534,7 @@ Even with strong system prompts, **prompt injection is not solved**. System prom
|
||||
- Prefer mention gating in groups; avoid “always-on” bots in public rooms.
|
||||
- Treat links, attachments, and pasted instructions as hostile by default.
|
||||
- Run sensitive tool execution in a sandbox; keep secrets out of the agent’s reachable filesystem.
|
||||
- Note: sandboxing is opt-in. If sandbox mode is off, implicit `host=auto` resolves to the gateway host. Explicit `host=sandbox` still fails closed because no sandbox runtime is available. Set `host=gateway` if you want that behavior to be explicit in config.
|
||||
- Note: sandboxing is opt-in. If sandbox mode is off, `host=sandbox` fails closed even though tools.exec.host defaults to sandbox. To run on the gateway host, set `host=gateway` and configure exec approvals.
|
||||
- Limit high-risk tools (`exec`, `browser`, `web_fetch`, `web_search`) to trusted agents or explicit allowlists.
|
||||
- If you allowlist interpreters (`python`, `node`, `ruby`, `perl`, `php`, `lua`, `osascript`), enable `tools.exec.strictInlineEval` so inline eval forms still need explicit approval.
|
||||
- **Model choice matters:** older/smaller/legacy models are significantly less robust against prompt injection and tool misuse. For tool-enabled agents, use the strongest latest-generation, instruction-hardened model available.
|
||||
@@ -589,8 +602,6 @@ Recommendations:
|
||||
- When running small models, **enable sandboxing for all sessions** and **disable web_search/web_fetch/browser** unless inputs are tightly controlled.
|
||||
- For chat-only personal assistants with trusted input and no tools, smaller models are usually fine.
|
||||
|
||||
<a id="reasoning-verbose-output-in-groups"></a>
|
||||
|
||||
## Reasoning & verbose output in groups
|
||||
|
||||
`/reasoning` and `/verbose` can expose internal reasoning or tool output that
|
||||
@@ -905,20 +916,22 @@ Details: [Logging](/gateway/logging)
|
||||
|
||||
In group chats, only respond when explicitly mentioned.
|
||||
|
||||
### 3) Separate numbers (WhatsApp, Signal, Telegram)
|
||||
### 3. Separate Numbers
|
||||
|
||||
For phone-number-based channels, consider running your AI on a separate phone number from your personal one:
|
||||
Consider running your AI on a separate phone number from your personal one:
|
||||
|
||||
- Personal number: Your conversations stay private
|
||||
- Bot number: AI handles these, with appropriate boundaries
|
||||
|
||||
### 4) Read-only mode (via sandbox + tools)
|
||||
### 4. Read-Only Mode (Today, via sandbox + tools)
|
||||
|
||||
You can build a read-only profile by combining:
|
||||
You can already build a read-only profile by combining:
|
||||
|
||||
- `agents.defaults.sandbox.workspaceAccess: "ro"` (or `"none"` for no workspace access)
|
||||
- tool allow/deny lists that block `write`, `edit`, `apply_patch`, `exec`, `process`, etc.
|
||||
|
||||
We may add a single `readOnlyMode` flag later to simplify this configuration.
|
||||
|
||||
Additional hardening options:
|
||||
|
||||
- `tools.exec.applyPatch.workspaceOnly: true` (default): ensures `apply_patch` cannot write/delete outside the workspace directory even when sandboxing is off. Set to `false` only if you intentionally want `apply_patch` to touch files outside the workspace.
|
||||
|
||||
@@ -287,8 +287,6 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
See what changed:
|
||||
[https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md](https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md)
|
||||
|
||||
For install one-liners and the difference between beta and dev, see the accordion below.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="How do I install the beta version and what is the difference between beta and dev?">
|
||||
@@ -587,12 +585,11 @@ Quick answers plus deeper troubleshooting for real-world setups (local dev, VPS,
|
||||
|
||||
</Accordion>
|
||||
|
||||
<a id="why-am-i-seeing-http-429-ratelimiterror-from-anthropic"></a>
|
||||
<Accordion title="Why am I seeing HTTP 429 rate_limit_error from Anthropic?">
|
||||
That means your **Anthropic quota/rate limit** is exhausted for the current window. If you
|
||||
use a **Claude subscription** (setup-token), wait for the window to
|
||||
reset or upgrade your plan. If you use an **Anthropic API key**, check the Anthropic Console
|
||||
for usage/billing and raise limits as needed.
|
||||
<Accordion title="Why am I seeing HTTP 429 rate_limit_error from Anthropic?">
|
||||
That means your **Anthropic quota/rate limit** is exhausted for the current window. If you
|
||||
use a **Claude subscription** (setup-token), wait for the window to
|
||||
reset or upgrade your plan. If you use an **Anthropic API key**, check the Anthropic Console
|
||||
for usage/billing and raise limits as needed.
|
||||
|
||||
If the message is specifically:
|
||||
`Extra usage is required for long context requests`, the request is trying to use
|
||||
@@ -1309,7 +1306,7 @@ for usage/billing and raise limits as needed.
|
||||
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Remote mode: where is the session store?">
|
||||
<Accordion title="I'm in remote mode - where is the session store?">
|
||||
Session state is owned by the **gateway host**. If you're in remote mode, the session store you care about is on the remote machine, not your local laptop. See [Session management](/concepts/session).
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -1783,10 +1780,9 @@ for usage/billing and raise limits as needed.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do sessions reset automatically if I never send /new?">
|
||||
Sessions can expire after `session.idleMinutes`, but this is **disabled by default** (default **0**).
|
||||
Set it to a positive value to enable idle expiry. When enabled, the **next**
|
||||
message after the idle period starts a fresh session id for that chat key.
|
||||
This does not delete transcripts - it just starts a new session.
|
||||
Yes. Sessions expire after `session.idleMinutes` (default **60**). The **next**
|
||||
message starts a fresh session id for that chat key. This does not delete
|
||||
transcripts - it just starts a new session.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -1889,7 +1885,7 @@ for usage/billing and raise limits as needed.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Why am I getting heartbeat messages every 30 minutes?">
|
||||
Heartbeats run every **30m** by default (**1h** when using OAuth auth). Tune or disable them:
|
||||
Heartbeats run every **30m** by default. Tune or disable them:
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -2089,16 +2085,14 @@ for usage/billing and raise limits as needed.
|
||||
|
||||
```
|
||||
/model sonnet
|
||||
/model haiku
|
||||
/model opus
|
||||
/model gpt
|
||||
/model gpt-mini
|
||||
/model gemini
|
||||
/model gemini-flash
|
||||
/model gemini-flash-lite
|
||||
```
|
||||
|
||||
These are the built-in aliases. Custom aliases can be added via `agents.defaults.models`.
|
||||
|
||||
You can list available models with `/model`, `/model list`, or `/model status`.
|
||||
|
||||
`/model` (and `/model list`) shows a compact, numbered picker. Select by number:
|
||||
@@ -2133,8 +2127,8 @@ for usage/billing and raise limits as needed.
|
||||
<Accordion title="Can I use GPT 5.2 for daily tasks and Codex 5.3 for coding?">
|
||||
Yes. Set one as default and switch as needed:
|
||||
|
||||
- **Quick switch (per session):** `/model gpt-5.4` for daily tasks, `/model openai-codex/gpt-5.4` for coding with Codex OAuth.
|
||||
- **Default + switch:** set `agents.defaults.model.primary` to `openai/gpt-5.4`, then switch to `openai-codex/gpt-5.4` when coding (or the other way around).
|
||||
- **Quick switch (per session):** `/model gpt-5.2` for daily tasks, `/model openai-codex/gpt-5.4` for coding with Codex OAuth.
|
||||
- **Default + switch:** set `agents.defaults.model.primary` to `openai/gpt-5.2`, then switch to `openai-codex/gpt-5.4` when coding (or the other way around).
|
||||
- **Sub-agents:** route coding tasks to sub-agents with a different default model.
|
||||
|
||||
See [Models](/concepts/models) and [Slash commands](/tools/slash-commands).
|
||||
@@ -2191,7 +2185,7 @@ for usage/billing and raise limits as needed.
|
||||
model: { primary: "minimax/MiniMax-M2.7" },
|
||||
models: {
|
||||
"minimax/MiniMax-M2.7": { alias: "minimax" },
|
||||
"openai/gpt-5.4": { alias: "gpt" },
|
||||
"openai/gpt-5.2": { alias: "gpt" },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2958,18 +2952,23 @@ Related: [/concepts/oauth](/concepts/oauth) (OAuth flows, token storage, multi-a
|
||||
|
||||
```json5
|
||||
{
|
||||
tools: {
|
||||
message: {
|
||||
crossContext: {
|
||||
allowAcrossProviders: true,
|
||||
marker: { enabled: true, prefix: "[from {channel}] " },
|
||||
agents: {
|
||||
defaults: {
|
||||
tools: {
|
||||
message: {
|
||||
crossContext: {
|
||||
allowAcrossProviders: true,
|
||||
marker: { enabled: true, prefix: "[from {channel}] " },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Restart the gateway after editing config.
|
||||
Restart the gateway after editing config. If you only want this for a single
|
||||
agent, set it under `agents.list[].tools.message` instead.
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
---
|
||||
summary: "ClawDock shell helpers for Docker-based OpenClaw installs"
|
||||
read_when:
|
||||
- You run OpenClaw with Docker often and want shorter day-to-day commands
|
||||
- You want a helper layer for dashboard, logs, token setup, and pairing flows
|
||||
title: "ClawDock"
|
||||
---
|
||||
|
||||
# ClawDock
|
||||
|
||||
ClawDock is a small shell-helper layer for Docker-based OpenClaw installs.
|
||||
|
||||
It gives you short commands like `clawdock-start`, `clawdock-dashboard`, and `clawdock-fix-token` instead of longer `docker compose ...` invocations.
|
||||
|
||||
If you have not set up Docker yet, start with [Docker](/install/docker).
|
||||
|
||||
## Install
|
||||
|
||||
Use the canonical helper path:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.clawdock && curl -sL https://raw.githubusercontent.com/openclaw/openclaw/main/scripts/clawdock/clawdock-helpers.sh -o ~/.clawdock/clawdock-helpers.sh
|
||||
echo 'source ~/.clawdock/clawdock-helpers.sh' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
If you previously installed ClawDock from `scripts/shell-helpers/clawdock-helpers.sh`, reinstall from the new `scripts/clawdock/clawdock-helpers.sh` path. The old raw GitHub path was removed.
|
||||
|
||||
## What you get
|
||||
|
||||
### Basic operations
|
||||
|
||||
| Command | Description |
|
||||
| ------------------ | ---------------------- |
|
||||
| `clawdock-start` | Start the gateway |
|
||||
| `clawdock-stop` | Stop the gateway |
|
||||
| `clawdock-restart` | Restart the gateway |
|
||||
| `clawdock-status` | Check container status |
|
||||
| `clawdock-logs` | Follow gateway logs |
|
||||
|
||||
### Container access
|
||||
|
||||
| Command | Description |
|
||||
| ------------------------- | --------------------------------------------- |
|
||||
| `clawdock-shell` | Open a shell inside the gateway container |
|
||||
| `clawdock-cli <command>` | Run OpenClaw CLI commands in Docker |
|
||||
| `clawdock-exec <command>` | Execute an arbitrary command in the container |
|
||||
|
||||
### Web UI and pairing
|
||||
|
||||
| Command | Description |
|
||||
| ----------------------- | ---------------------------- |
|
||||
| `clawdock-dashboard` | Open the Control UI URL |
|
||||
| `clawdock-devices` | List pending device pairings |
|
||||
| `clawdock-approve <id>` | Approve a pairing request |
|
||||
|
||||
### Setup and maintenance
|
||||
|
||||
| Command | Description |
|
||||
| -------------------- | ------------------------------------------------ |
|
||||
| `clawdock-fix-token` | Configure the gateway token inside the container |
|
||||
| `clawdock-update` | Pull, rebuild, and restart |
|
||||
| `clawdock-rebuild` | Rebuild the Docker image only |
|
||||
| `clawdock-clean` | Remove containers and volumes |
|
||||
|
||||
### Utilities
|
||||
|
||||
| Command | Description |
|
||||
| ---------------------- | --------------------------------------- |
|
||||
| `clawdock-health` | Run a gateway health check |
|
||||
| `clawdock-token` | Print the gateway token |
|
||||
| `clawdock-cd` | Jump to the OpenClaw project directory |
|
||||
| `clawdock-config` | Open `~/.openclaw` |
|
||||
| `clawdock-show-config` | Print config files with redacted values |
|
||||
| `clawdock-workspace` | Open the workspace directory |
|
||||
|
||||
## First-time flow
|
||||
|
||||
```bash
|
||||
clawdock-start
|
||||
clawdock-fix-token
|
||||
clawdock-dashboard
|
||||
```
|
||||
|
||||
If the browser says pairing is required:
|
||||
|
||||
```bash
|
||||
clawdock-devices
|
||||
clawdock-approve <request-id>
|
||||
```
|
||||
|
||||
## Config and secrets
|
||||
|
||||
ClawDock works with the same Docker config split described in [Docker](/install/docker):
|
||||
|
||||
- `<project>/.env` for Docker-specific values like image name, ports, and the gateway token
|
||||
- `~/.openclaw/.env` for provider keys and bot tokens
|
||||
- `~/.openclaw/openclaw.json` for behavior config
|
||||
|
||||
Use `clawdock-show-config` when you want to inspect those files quickly. It redacts `.env` values in its printed output.
|
||||
|
||||
## Related pages
|
||||
|
||||
- [Docker](/install/docker)
|
||||
- [Docker VM Runtime](/install/docker-vm-runtime)
|
||||
- [Updating](/install/updating)
|
||||
@@ -48,7 +48,7 @@ update **without** changing your persisted channel:
|
||||
|
||||
```bash
|
||||
# Install a specific version
|
||||
openclaw update --tag 2026.3.30-beta.1
|
||||
openclaw update --tag 2026.3.29-beta.1
|
||||
|
||||
# Install from the beta dist-tag (one-off, does not persist)
|
||||
openclaw update --tag beta
|
||||
@@ -57,7 +57,7 @@ openclaw update --tag beta
|
||||
openclaw update --tag main
|
||||
|
||||
# Install a specific npm package spec
|
||||
openclaw update --tag openclaw@2026.3.30-beta.1
|
||||
openclaw update --tag openclaw@2026.3.29-beta.1
|
||||
```
|
||||
|
||||
Notes:
|
||||
@@ -75,7 +75,7 @@ Preview what `openclaw update` would do without making changes:
|
||||
```bash
|
||||
openclaw update --dry-run
|
||||
openclaw update --channel beta --dry-run
|
||||
openclaw update --tag 2026.3.30-beta.1 --dry-run
|
||||
openclaw update --tag 2026.3.29-beta.1 --dry-run
|
||||
openclaw update --dry-run --json
|
||||
```
|
||||
|
||||
|
||||
@@ -187,15 +187,13 @@ and rolling file logs under `/tmp/openclaw/`.
|
||||
For easier day-to-day Docker management, install `ClawDock`:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.clawdock && curl -sL https://raw.githubusercontent.com/openclaw/openclaw/main/scripts/clawdock/clawdock-helpers.sh -o ~/.clawdock/clawdock-helpers.sh
|
||||
mkdir -p ~/.clawdock && curl -sL https://raw.githubusercontent.com/openclaw/openclaw/main/scripts/shell-helpers/clawdock-helpers.sh -o ~/.clawdock/clawdock-helpers.sh
|
||||
echo 'source ~/.clawdock/clawdock-helpers.sh' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
If you installed ClawDock from the older `scripts/shell-helpers/clawdock-helpers.sh` raw path, rerun the install command above so your local helper file tracks the new location.
|
||||
|
||||
Then use `clawdock-start`, `clawdock-stop`, `clawdock-dashboard`, etc. Run
|
||||
`clawdock-help` for all commands.
|
||||
See [ClawDock](/install/clawdock) for the full helper guide.
|
||||
See the [`ClawDock` Helper README](https://github.com/openclaw/openclaw/blob/main/scripts/shell-helpers/README.md).
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Enable agent sandbox for Docker gateway">
|
||||
|
||||
@@ -58,8 +58,6 @@ If install succeeds but `openclaw` is not found in a new terminal, see [Node.js
|
||||
|
||||
---
|
||||
|
||||
<a id="installsh"></a>
|
||||
|
||||
## install.sh
|
||||
|
||||
<Tip>
|
||||
@@ -172,8 +170,6 @@ The script exits with code `2` for invalid method selection or invalid `--instal
|
||||
|
||||
---
|
||||
|
||||
<a id="install-clish"></a>
|
||||
|
||||
## install-cli.sh
|
||||
|
||||
<Info>
|
||||
@@ -252,8 +248,6 @@ Designed for environments where you want everything under a local prefix (defaul
|
||||
|
||||
---
|
||||
|
||||
<a id="installps1"></a>
|
||||
|
||||
## install.ps1
|
||||
|
||||
### Flow (install.ps1)
|
||||
|
||||
@@ -165,8 +165,6 @@ openclaw devices list \
|
||||
--token "$(sed -n 's/^OPENCLAW_GATEWAY_TOKEN=//p' ~/.openclaw/.env | head -n1)"
|
||||
```
|
||||
|
||||
<a id="podman--tailscale"></a>
|
||||
|
||||
## Podman + Tailscale
|
||||
|
||||
For HTTPS or remote browser access, follow the main Tailscale docs.
|
||||
|
||||
@@ -316,15 +316,13 @@ The headless node host exposes `system.run`, `system.which`, and `system.execApp
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
openclaw nodes run --node <idOrNameOrIp> -- echo "Hello from mac node"
|
||||
openclaw nodes notify --node <idOrNameOrIp> --title "Ping" --body "Gateway ready"
|
||||
openclaw nodes invoke --node <idOrNameOrIp> --command system.which --params '{"name":"git"}'
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `system.run` returns stdout/stderr/exit code in the payload.
|
||||
- Shell execution now goes through the `exec` tool with `host=node`; `nodes` remains the direct-RPC surface for explicit node commands.
|
||||
- `nodes invoke` does not expose `system.run` or `system.run.prepare`; those stay on the exec path only.
|
||||
- `system.notify` respects notification permission state on the macOS app.
|
||||
- Unrecognized node `platform` / `deviceFamily` metadata uses a conservative default allowlist that excludes `system.run` and `system.which`. If you intentionally need those commands for an unknown platform, add them explicitly via `gateway.nodes.allowCommands`.
|
||||
- `system.run` supports `--cwd`, `--env KEY=VAL`, `--command-timeout`, and `--needs-screen-recording`.
|
||||
|
||||
@@ -288,11 +288,13 @@ contracts for models, speech, media understanding, and web search, a vendor can
|
||||
own all of its surfaces in one place:
|
||||
|
||||
```ts
|
||||
import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import type { OpenClawPluginDefinition } from "openclaw/plugin-sdk";
|
||||
import {
|
||||
buildOpenAISpeechProvider,
|
||||
createPluginBackedWebSearchProvider,
|
||||
describeImageWithModel,
|
||||
transcribeOpenAiCompatibleAudio,
|
||||
} from "openclaw/plugin-sdk/media-understanding";
|
||||
} from "openclaw/plugin-sdk";
|
||||
|
||||
const plugin: OpenClawPluginDefinition = {
|
||||
id: "exampleai",
|
||||
@@ -303,10 +305,12 @@ const plugin: OpenClawPluginDefinition = {
|
||||
// auth/model catalog/runtime hooks
|
||||
});
|
||||
|
||||
api.registerSpeechProvider({
|
||||
id: "exampleai",
|
||||
// vendor speech config — implement the SpeechProviderPlugin interface directly
|
||||
});
|
||||
api.registerSpeechProvider(
|
||||
buildOpenAISpeechProvider({
|
||||
id: "exampleai",
|
||||
// vendor speech config
|
||||
}),
|
||||
);
|
||||
|
||||
api.registerMediaUnderstandingProvider({
|
||||
id: "exampleai",
|
||||
@@ -474,13 +478,9 @@ At startup, OpenClaw does roughly this:
|
||||
`slots`, `load.paths`)
|
||||
5. decide enablement for each candidate
|
||||
6. load enabled native modules via jiti
|
||||
7. call native `register(api)` (or `activate(api)` — a legacy alias) hooks and collect registrations into the plugin registry
|
||||
7. call native `register(api)` hooks and collect registrations into the plugin registry
|
||||
8. expose the registry to commands/runtime surfaces
|
||||
|
||||
<Note>
|
||||
`activate` is a legacy alias for `register` — the loader resolves whichever is present (`def.register ?? def.activate`) and calls it at the same point. All bundled plugins use `register`; prefer `register` for new plugins.
|
||||
</Note>
|
||||
|
||||
The safety gates happen **before** runtime execution. Candidates are blocked
|
||||
when the entry escapes the plugin root, the path is world-writable, or path
|
||||
ownership looks suspicious for non-bundled plugins.
|
||||
@@ -744,6 +744,7 @@ api.registerProvider({
|
||||
`huggingface`, `kimi-coding`, `modelstudio`, `nvidia`, `qianfan`,
|
||||
`synthetic`, `together`, `venice`, `vercel-ai-gateway`, and `volcengine` use
|
||||
`catalog` only.
|
||||
- Qwen portal uses `catalog`, `auth`, and `refreshOAuth`.
|
||||
- MiniMax and Xiaomi use `catalog` plus usage hooks because their `/usage`
|
||||
behavior is plugin-owned even though inference still runs through the shared
|
||||
transports.
|
||||
@@ -908,22 +909,6 @@ Notes:
|
||||
- Use web-search providers for vendor-specific search transports.
|
||||
- `api.runtime.webSearch.*` is the preferred shared surface for feature/channel plugins that need search behavior without depending on the agent tool wrapper.
|
||||
|
||||
### `api.runtime.imageGeneration`
|
||||
|
||||
```ts
|
||||
const result = await api.runtime.imageGeneration.generate({
|
||||
config: api.config,
|
||||
args: { prompt: "A friendly lobster mascot", size: "1024x1024" },
|
||||
});
|
||||
|
||||
const providers = api.runtime.imageGeneration.listProviders({
|
||||
config: api.config,
|
||||
});
|
||||
```
|
||||
|
||||
- `generate(...)`: generate an image using the configured image-generation provider chain.
|
||||
- `listProviders(...)`: list available image-generation providers and their capabilities.
|
||||
|
||||
## Gateway HTTP routes
|
||||
|
||||
Plugins can expose HTTP endpoints with `api.registerHttpRoute(...)`.
|
||||
@@ -951,7 +936,7 @@ Route fields:
|
||||
|
||||
Notes:
|
||||
|
||||
- `api.registerHttpHandler(...)` was removed and will cause a plugin-load error. Use `api.registerHttpRoute(...)` instead.
|
||||
- `api.registerHttpHandler(...)` is obsolete. Use `api.registerHttpRoute(...)`.
|
||||
- Plugin routes must declare `auth` explicitly.
|
||||
- Exact `path + match` conflicts are rejected unless `replaceExisting: true`, and one plugin cannot replace another plugin's route.
|
||||
- Overlapping routes with different `auth` levels are rejected. Keep `exact`/`prefix` fallthrough chains on the same auth level only.
|
||||
@@ -1019,10 +1004,8 @@ Compatibility note:
|
||||
helper is only needed by a bundled extension, keep it behind the extension's
|
||||
local `api.js` or `runtime-api.js` seam instead of promoting it into
|
||||
`openclaw/plugin-sdk/<extension>`.
|
||||
- New shared helper seams should be generic, not channel-branded. Shared target
|
||||
parsing belongs on `openclaw/plugin-sdk/channel-targets`; channel-specific
|
||||
internals stay behind the owning plugin's local `api.js` or `runtime-api.js`
|
||||
seam.
|
||||
- Channel-branded bundled bars stay private unless they are explicitly added
|
||||
back to the public contract.
|
||||
- Capability-specific subpaths such as `image-generation`,
|
||||
`media-understanding`, and `speech` exist because bundled/native plugins use
|
||||
them today. Their presence does not by itself mean every exported helper is a
|
||||
@@ -1177,7 +1160,7 @@ directory after symlink resolution. Entries that escape the package directory ar
|
||||
rejected.
|
||||
|
||||
Security note: `openclaw plugins install` installs plugin dependencies with
|
||||
`npm install --omit=dev --ignore-scripts` (no lifecycle scripts, no dev dependencies at runtime). Keep plugin dependency
|
||||
`npm install --ignore-scripts` (no lifecycle scripts). Keep plugin dependency
|
||||
trees "pure JS/TS" and avoid packages that require `postinstall` builds.
|
||||
|
||||
Optional: `openclaw.setupEntry` can point at a lightweight setup-only module.
|
||||
@@ -1258,7 +1241,7 @@ registry export). Drop a JSON file at one of:
|
||||
|
||||
Or point `OPENCLAW_PLUGIN_CATALOG_PATHS` (or `OPENCLAW_MPM_CATALOG_PATHS`) at
|
||||
one or more JSON files (comma/semicolon/`PATH`-delimited). Each file should
|
||||
contain `{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`. The parser also accepts `"packages"` or `"plugins"` as legacy aliases for the `"entries"` key.
|
||||
contain `{ "entries": [ { "name": "@scope/pkg", "openclaw": { "channel": {...}, "install": {...} } } ] }`.
|
||||
|
||||
## Context engine plugins
|
||||
|
||||
|
||||
@@ -52,15 +52,7 @@ and provider plugins have dedicated guides linked above.
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.3.24-beta.2",
|
||||
"minGatewayVersion": "2026.3.24-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.3.24-beta.2",
|
||||
"pluginSdkVersion": "2026.3.24-beta.2"
|
||||
}
|
||||
"extensions": ["./index.ts"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -79,8 +71,7 @@ and provider plugins have dedicated guides linked above.
|
||||
</CodeGroup>
|
||||
|
||||
Every plugin needs a manifest, even with no config. See
|
||||
[Manifest](/plugins/manifest) for the full schema. The canonical ClawHub
|
||||
publish snippets live in `docs/snippets/plugin-publish/`.
|
||||
[Manifest](/plugins/manifest) for the full schema.
|
||||
|
||||
</Step>
|
||||
|
||||
@@ -116,16 +107,13 @@ and provider plugins have dedicated guides linked above.
|
||||
|
||||
<Step title="Test and publish">
|
||||
|
||||
**External plugins:** validate and publish with ClawHub, then install:
|
||||
**External plugins:** publish to [ClawHub](/tools/clawhub) or npm, then install:
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/your-plugin --dry-run
|
||||
clawhub package publish your-org/your-plugin
|
||||
openclaw plugins install clawhub:@myorg/openclaw-my-plugin
|
||||
openclaw plugins install @myorg/openclaw-my-plugin
|
||||
```
|
||||
|
||||
OpenClaw also checks ClawHub before npm for bare package specs like
|
||||
`@myorg/openclaw-my-plugin`.
|
||||
OpenClaw checks ClawHub first, then falls back to npm.
|
||||
|
||||
**In-repo plugins:** place under the bundled plugin workspace tree — automatically discovered.
|
||||
|
||||
@@ -162,8 +150,6 @@ Hook guard semantics to keep in mind:
|
||||
- `before_tool_call`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_tool_call`: `{ block: false }` is treated as no decision.
|
||||
- `before_tool_call`: `{ requireApproval: true }` pauses agent execution and prompts the user for approval via the exec approval overlay, Telegram buttons, Discord interactions, or the `/approve` command on any channel.
|
||||
- `before_install`: `{ block: true }` is terminal and stops lower-priority handlers.
|
||||
- `before_install`: `{ block: false }` is treated as no decision.
|
||||
- `message_sending`: `{ cancel: true }` is terminal and stops lower-priority handlers.
|
||||
- `message_sending`: `{ cancel: false }` is treated as no decision.
|
||||
|
||||
|
||||
@@ -72,110 +72,13 @@ is detected but not yet wired.
|
||||
|
||||
### Supported now
|
||||
|
||||
| Feature | How it maps | Applies to |
|
||||
| ------------- | ------------------------------------------------------------------------------------------- | -------------- |
|
||||
| Skill content | Bundle skill roots load as normal OpenClaw skills | All formats |
|
||||
| Commands | `commands/` and `.cursor/commands/` treated as skill roots | Claude, Cursor |
|
||||
| Hook packs | OpenClaw-style `HOOK.md` + `handler.ts` layouts | Codex |
|
||||
| MCP tools | Bundle MCP config merged into embedded Pi settings; supported stdio and HTTP servers loaded | All formats |
|
||||
| Settings | Claude `settings.json` imported as embedded Pi defaults | Claude |
|
||||
|
||||
#### Skill content
|
||||
|
||||
- bundle skill roots load as normal OpenClaw skill roots
|
||||
- Claude `commands` roots are treated as additional skill roots
|
||||
- Cursor `.cursor/commands` roots are treated as additional skill roots
|
||||
|
||||
This means Claude markdown command files work through the normal OpenClaw skill
|
||||
loader. Cursor command markdown works through the same path.
|
||||
|
||||
#### Hook packs
|
||||
|
||||
- bundle hook roots work **only** when they use the normal OpenClaw hook-pack
|
||||
layout. Today this is primarily the Codex-compatible case:
|
||||
- `HOOK.md`
|
||||
- `handler.ts` or `handler.js`
|
||||
|
||||
#### MCP for Pi
|
||||
|
||||
- enabled bundles can contribute MCP server config
|
||||
- OpenClaw merges bundle MCP config into the effective embedded Pi settings as
|
||||
`mcpServers`
|
||||
- OpenClaw exposes supported bundle MCP tools during embedded Pi agent turns by
|
||||
launching stdio servers or connecting to HTTP servers
|
||||
- project-local Pi settings still apply after bundle defaults, so workspace
|
||||
settings can override bundle MCP entries when needed
|
||||
|
||||
##### Transports
|
||||
|
||||
MCP servers can use stdio or HTTP transport:
|
||||
|
||||
**Stdio** launches a child process:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"my-server": {
|
||||
"command": "node",
|
||||
"args": ["server.js"],
|
||||
"env": { "PORT": "3000" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**HTTP** connects to a running MCP server over `sse` by default, or `streamable-http` when requested:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"my-server": {
|
||||
"url": "http://localhost:3100/mcp",
|
||||
"transport": "streamable-http",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${MY_SECRET_TOKEN}"
|
||||
},
|
||||
"connectionTimeoutMs": 30000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- `transport` may be set to `"streamable-http"` or `"sse"`; when omitted, OpenClaw uses `sse`
|
||||
- only `http:` and `https:` URL schemes are allowed
|
||||
- `headers` values support `${ENV_VAR}` interpolation
|
||||
- a server entry with both `command` and `url` is rejected
|
||||
- URL credentials (userinfo and query params) are redacted from tool
|
||||
descriptions and logs
|
||||
- `connectionTimeoutMs` overrides the default 30-second connection timeout for
|
||||
both stdio and HTTP transports
|
||||
|
||||
##### Tool naming
|
||||
|
||||
OpenClaw registers bundle MCP tools with provider-safe names in the form
|
||||
`serverName__toolName`. For example, a server keyed `"vigil-harbor"` exposing a
|
||||
`memory_search` tool registers as `vigil-harbor__memory_search`.
|
||||
|
||||
- characters outside `A-Za-z0-9_-` are replaced with `-`
|
||||
- server prefixes are capped at 30 characters
|
||||
- full tool names are capped at 64 characters
|
||||
- empty server names fall back to `mcp`
|
||||
- colliding sanitized names are disambiguated with numeric suffixes
|
||||
|
||||
#### Embedded Pi settings
|
||||
|
||||
- Claude `settings.json` is imported as default embedded Pi settings when the
|
||||
bundle is enabled
|
||||
- OpenClaw sanitizes shell override keys before applying them
|
||||
|
||||
Sanitized keys:
|
||||
|
||||
- `shellPath`
|
||||
- `shellCommandPrefix`
|
||||
| Feature | How it maps | Applies to |
|
||||
| ------------- | ---------------------------------------------------------------------------------------------------- | -------------- |
|
||||
| Skill content | Bundle skill roots load as normal OpenClaw skills | All formats |
|
||||
| Commands | `commands/` and `.cursor/commands/` treated as skill roots | Claude, Cursor |
|
||||
| Hook packs | OpenClaw-style `HOOK.md` + `handler.ts` layouts | Codex |
|
||||
| MCP tools | Bundle MCP config merged into embedded Pi settings; supported stdio servers launched as subprocesses | All formats |
|
||||
| Settings | Claude `settings.json` imported as embedded Pi defaults | Claude |
|
||||
|
||||
### Detected but not executed
|
||||
|
||||
|
||||
@@ -34,23 +34,9 @@ shared `message` tool in core. Your plugin owns:
|
||||
Core owns the shared message tool, prompt wiring, session bookkeeping, and
|
||||
dispatch.
|
||||
|
||||
## Approvals and channel capabilities
|
||||
|
||||
Most channel plugins do not need approval-specific code.
|
||||
|
||||
- Core owns same-chat `/approve`, shared approval button payloads, and generic fallback delivery.
|
||||
- Use `auth.authorizeActorAction` or `auth.getActionAvailabilityState` only when approval auth differs from normal chat auth.
|
||||
- Use `outbound.shouldSuppressLocalPayloadPrompt` or `outbound.beforeDeliverPayload` for channel-specific payload lifecycle behavior such as hiding duplicate local approval prompts or sending typing indicators before delivery.
|
||||
- Use `approvals.delivery` only for native approval routing or fallback suppression.
|
||||
- Use `approvals.render` only when a channel truly needs custom approval payloads instead of the shared renderer.
|
||||
- If a channel can infer stable owner-like DM identities from existing config, use `createResolvedApproverActionAuthAdapter` from `openclaw/plugin-sdk/approval-runtime` to restrict same-chat `/approve` without adding approval-specific core logic.
|
||||
|
||||
For Slack, Matrix, Microsoft Teams, and similar chat channels, the default path is usually enough: core handles approvals and the plugin just exposes normal outbound and auth capabilities.
|
||||
|
||||
## Walkthrough
|
||||
|
||||
<Steps>
|
||||
<a id="step-1-package-and-manifest"></a>
|
||||
<Step title="Package and manifest">
|
||||
Create the standard plugin files. The `channel` field in `package.json` is
|
||||
what makes this a channel plugin:
|
||||
@@ -228,7 +214,7 @@ For Slack, Matrix, Microsoft Teams, and similar chat channels, the default path
|
||||
name: "Acme Chat",
|
||||
description: "Acme Chat channel plugin",
|
||||
plugin: acmeChatPlugin,
|
||||
registerCliMetadata(api) {
|
||||
registerFull(api) {
|
||||
api.registerCli(
|
||||
({ program }) => {
|
||||
program
|
||||
@@ -246,17 +232,11 @@ For Slack, Matrix, Microsoft Teams, and similar chat channels, the default path
|
||||
},
|
||||
);
|
||||
},
|
||||
registerFull(api) {
|
||||
api.registerGatewayMethod(/* ... */);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Put channel-owned CLI descriptors in `registerCliMetadata(...)` so OpenClaw
|
||||
can show them in root help without activating the full channel runtime,
|
||||
while normal full loads still pick up the same descriptors for real command
|
||||
registration. Keep `registerFull(...)` for runtime-only work.
|
||||
`defineChannelPluginEntry` handles the registration-mode split automatically. See
|
||||
`defineChannelPluginEntry` handles the setup/full registration split
|
||||
automatically. See
|
||||
[Entry Points](/plugins/sdk-entrypoints#definechannelpluginentry) for all
|
||||
options.
|
||||
|
||||
@@ -312,9 +292,8 @@ For Slack, Matrix, Microsoft Teams, and similar chat channels, the default path
|
||||
|
||||
</Step>
|
||||
|
||||
<a id="step-6-test"></a>
|
||||
<Step title="Test">
|
||||
Write colocated tests in `src/channel.test.ts`:
|
||||
<Step title="Test">
|
||||
Write colocated tests in `src/channel.test.ts`:
|
||||
|
||||
```typescript src/channel.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: "Entry Points"
|
||||
summary: "Reference for definePluginEntry, defineChannelPluginEntry, and defineSetupPluginEntry"
|
||||
read_when:
|
||||
- You need the exact type signature of definePluginEntry or defineChannelPluginEntry
|
||||
- You want to understand registration mode (full vs setup vs CLI metadata)
|
||||
- You want to understand registration mode (full vs setup)
|
||||
- You are looking up entry point options
|
||||
---
|
||||
|
||||
@@ -61,8 +61,7 @@ export default definePluginEntry({
|
||||
**Import:** `openclaw/plugin-sdk/core`
|
||||
|
||||
Wraps `definePluginEntry` with channel-specific wiring. Automatically calls
|
||||
`api.registerChannel({ plugin })`, exposes an optional root-help CLI metadata
|
||||
seam, and gates `registerFull` on registration mode.
|
||||
`api.registerChannel({ plugin })` and gates `registerFull` on registration mode.
|
||||
|
||||
```typescript
|
||||
import { defineChannelPluginEntry } from "openclaw/plugin-sdk/core";
|
||||
@@ -73,40 +72,30 @@ export default defineChannelPluginEntry({
|
||||
description: "Short summary",
|
||||
plugin: myChannelPlugin,
|
||||
setRuntime: setMyRuntime,
|
||||
registerCliMetadata(api) {
|
||||
api.registerCli(/* ... */);
|
||||
},
|
||||
registerFull(api) {
|
||||
api.registerCli(/* ... */);
|
||||
api.registerGatewayMethod(/* ... */);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
| Field | Type | Required | Default |
|
||||
| --------------------- | ---------------------------------------------------------------- | -------- | ------------------- |
|
||||
| `id` | `string` | Yes | — |
|
||||
| `name` | `string` | Yes | — |
|
||||
| `description` | `string` | Yes | — |
|
||||
| `plugin` | `ChannelPlugin` | Yes | — |
|
||||
| `configSchema` | `OpenClawPluginConfigSchema \| () => OpenClawPluginConfigSchema` | No | Empty object schema |
|
||||
| `setRuntime` | `(runtime: PluginRuntime) => void` | No | — |
|
||||
| `registerCliMetadata` | `(api: OpenClawPluginApi) => void` | No | — |
|
||||
| `registerFull` | `(api: OpenClawPluginApi) => void` | No | — |
|
||||
| Field | Type | Required | Default |
|
||||
| -------------- | ---------------------------------------------------------------- | -------- | ------------------- |
|
||||
| `id` | `string` | Yes | — |
|
||||
| `name` | `string` | Yes | — |
|
||||
| `description` | `string` | Yes | — |
|
||||
| `plugin` | `ChannelPlugin` | Yes | — |
|
||||
| `configSchema` | `OpenClawPluginConfigSchema \| () => OpenClawPluginConfigSchema` | No | Empty object schema |
|
||||
| `setRuntime` | `(runtime: PluginRuntime) => void` | No | — |
|
||||
| `registerFull` | `(api: OpenClawPluginApi) => void` | No | — |
|
||||
|
||||
- `setRuntime` is called during registration so you can store the runtime reference
|
||||
(typically via `createPluginRuntimeStore`). It is skipped during CLI metadata
|
||||
capture.
|
||||
- `registerCliMetadata` runs during both `api.registrationMode === "cli-metadata"`
|
||||
and `api.registrationMode === "full"`.
|
||||
Use it as the canonical place for channel-owned CLI descriptors so root help
|
||||
stays non-activating while normal CLI command registration remains compatible
|
||||
with full plugin loads.
|
||||
(typically via `createPluginRuntimeStore`).
|
||||
- `registerFull` only runs when `api.registrationMode === "full"`. It is skipped
|
||||
during setup-only loading.
|
||||
- For plugin-owned root CLI commands, prefer `api.registerCli(..., { descriptors: [...] })`
|
||||
when you want the command to stay lazy-loaded without disappearing from the
|
||||
root CLI parse tree. For channel plugins, prefer registering those descriptors
|
||||
from `registerCliMetadata(...)` and keep `registerFull(...)` focused on runtime-only work.
|
||||
root CLI parse tree.
|
||||
|
||||
## `defineSetupPluginEntry`
|
||||
|
||||
@@ -134,22 +123,17 @@ unconfigured, or when deferred loading is enabled. See
|
||||
| `"full"` | Normal gateway startup | Everything |
|
||||
| `"setup-only"` | Disabled/unconfigured channel | Channel registration only |
|
||||
| `"setup-runtime"` | Setup flow with runtime available | Channel + lightweight runtime |
|
||||
| `"cli-metadata"` | Root help / CLI metadata capture | CLI descriptors only |
|
||||
|
||||
`defineChannelPluginEntry` handles this split automatically. If you use
|
||||
`definePluginEntry` directly for a channel, check mode yourself:
|
||||
|
||||
```typescript
|
||||
register(api) {
|
||||
if (api.registrationMode === "cli-metadata" || api.registrationMode === "full") {
|
||||
api.registerCli(/* ... */);
|
||||
if (api.registrationMode === "cli-metadata") return;
|
||||
}
|
||||
|
||||
api.registerChannel({ plugin: myPlugin });
|
||||
if (api.registrationMode !== "full") return;
|
||||
|
||||
// Heavy runtime-only registrations
|
||||
api.registerCli(/* ... */);
|
||||
api.registerService(/* ... */);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -68,8 +68,10 @@ subpaths is in `scripts/lib/plugin-sdk-entrypoints.json`.
|
||||
| --- | --- |
|
||||
| `plugin-sdk/cli-backend` | CLI backend defaults + watchdog constants |
|
||||
| `plugin-sdk/provider-auth` | `createProviderApiKeyAuthMethod`, `ensureApiKeyFromOptionEnvOrPrompt`, `upsertAuthProfile` |
|
||||
| `plugin-sdk/provider-models` | Legacy compat provider model aliases; prefer provider-specific subpaths or `plugin-sdk/provider-model-shared` |
|
||||
| `plugin-sdk/provider-model-shared` | `normalizeModelCompat` |
|
||||
| `plugin-sdk/provider-catalog-shared` | `findCatalogTemplate`, `buildSingleProviderApiKeyCatalog` |
|
||||
| `plugin-sdk/provider-catalog` | Legacy compat provider builder aliases; prefer provider-specific subpaths or `plugin-sdk/provider-catalog-shared` |
|
||||
| `plugin-sdk/provider-usage` | `fetchClaudeUsage` and similar |
|
||||
| `plugin-sdk/provider-stream` | Stream wrapper types |
|
||||
| `plugin-sdk/provider-onboard` | Onboarding config patch helpers |
|
||||
@@ -230,27 +232,25 @@ AI CLI backend such as `claude-cli` or `codex-cli`.
|
||||
|
||||
- `before_tool_call`: returning `{ block: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
|
||||
- `before_tool_call`: returning `{ block: false }` is treated as no decision (same as omitting `block`), not as an override.
|
||||
- `before_install`: returning `{ block: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
|
||||
- `before_install`: returning `{ block: false }` is treated as no decision (same as omitting `block`), not as an override.
|
||||
- `message_sending`: returning `{ cancel: true }` is terminal. Once any handler sets it, lower-priority handlers are skipped.
|
||||
- `message_sending`: returning `{ cancel: false }` is treated as no decision (same as omitting `cancel`), not as an override.
|
||||
|
||||
### API object fields
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------------------ | ------------------------- | ---------------------------------------------------------------- |
|
||||
| `api.id` | `string` | Plugin id |
|
||||
| `api.name` | `string` | Display name |
|
||||
| `api.version` | `string?` | Plugin version (optional) |
|
||||
| `api.description` | `string?` | Plugin description (optional) |
|
||||
| `api.source` | `string` | Plugin source path |
|
||||
| `api.rootDir` | `string?` | Plugin root directory (optional) |
|
||||
| `api.config` | `OpenClawConfig` | Current config snapshot |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | Plugin-specific config from `plugins.entries.<id>.config` |
|
||||
| `api.runtime` | `PluginRuntime` | [Runtime helpers](/plugins/sdk-runtime) |
|
||||
| `api.logger` | `PluginLogger` | Scoped logger (`debug`, `info`, `warn`, `error`) |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | `"full"`, `"setup-only"`, `"setup-runtime"`, or `"cli-metadata"` |
|
||||
| `api.resolvePath(input)` | `(string) => string` | Resolve path relative to plugin root |
|
||||
| Field | Type | Description |
|
||||
| ------------------------ | ------------------------- | --------------------------------------------------------- |
|
||||
| `api.id` | `string` | Plugin id |
|
||||
| `api.name` | `string` | Display name |
|
||||
| `api.version` | `string?` | Plugin version (optional) |
|
||||
| `api.description` | `string?` | Plugin description (optional) |
|
||||
| `api.source` | `string` | Plugin source path |
|
||||
| `api.rootDir` | `string?` | Plugin root directory (optional) |
|
||||
| `api.config` | `OpenClawConfig` | Current config snapshot |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | Plugin-specific config from `plugins.entries.<id>.config` |
|
||||
| `api.runtime` | `PluginRuntime` | [Runtime helpers](/plugins/sdk-runtime) |
|
||||
| `api.logger` | `PluginLogger` | Scoped logger (`debug`, `info`, `warn`, `error`) |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | `"full"`, `"setup-only"`, or `"setup-runtime"` |
|
||||
| `api.resolvePath(input)` | `(string) => string` | Resolve path relative to plugin root |
|
||||
|
||||
## Internal module convention
|
||||
|
||||
@@ -270,13 +270,6 @@ my-plugin/
|
||||
`./runtime-api.ts`. The SDK path is the external contract only.
|
||||
</Warning>
|
||||
|
||||
<Warning>
|
||||
Extension production code should also avoid `openclaw/plugin-sdk/<other-plugin>`
|
||||
imports. If a helper is truly shared, promote it to a neutral SDK subpath
|
||||
such as `openclaw/plugin-sdk/speech`, `.../provider-model-shared`, or another
|
||||
capability-oriented surface instead of coupling two plugins together.
|
||||
</Warning>
|
||||
|
||||
## Related
|
||||
|
||||
- [Entry Points](/plugins/sdk-entrypoints) — `definePluginEntry` and `defineChannelPluginEntry` options
|
||||
|
||||
@@ -23,7 +23,6 @@ API key auth, and dynamic model resolution.
|
||||
## Walkthrough
|
||||
|
||||
<Steps>
|
||||
<a id="step-1-package-and-manifest"></a>
|
||||
<Step title="Package and manifest">
|
||||
<CodeGroup>
|
||||
```json package.json
|
||||
@@ -33,15 +32,7 @@ API key auth, and dynamic model resolution.
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"],
|
||||
"providers": ["acme-ai"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.3.24-beta.2",
|
||||
"minGatewayVersion": "2026.3.24-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.3.24-beta.2",
|
||||
"pluginSdkVersion": "2026.3.24-beta.2"
|
||||
}
|
||||
"providers": ["acme-ai"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -77,9 +68,7 @@ API key auth, and dynamic model resolution.
|
||||
</CodeGroup>
|
||||
|
||||
The manifest declares `providerAuthEnvVars` so OpenClaw can detect
|
||||
credentials without loading your plugin runtime. If you publish the
|
||||
provider on ClawHub, those `openclaw.compat` and `openclaw.build` fields
|
||||
are required in `package.json`.
|
||||
credentials without loading your plugin runtime.
|
||||
|
||||
</Step>
|
||||
|
||||
@@ -320,7 +309,6 @@ API key auth, and dynamic model resolution.
|
||||
</Step>
|
||||
|
||||
<Step title="Add extra capabilities (optional)">
|
||||
<a id="step-5-add-extra-capabilities"></a>
|
||||
A provider plugin can register speech, media understanding, image
|
||||
generation, and web search alongside text inference:
|
||||
|
||||
@@ -362,7 +350,6 @@ API key auth, and dynamic model resolution.
|
||||
</Step>
|
||||
|
||||
<Step title="Test">
|
||||
<a id="step-6-test"></a>
|
||||
```typescript src/provider.test.ts
|
||||
import { describe, it, expect } from "vitest";
|
||||
// Export your provider config object from index.ts or a dedicated file
|
||||
@@ -396,18 +383,6 @@ API key auth, and dynamic model resolution.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
## Publish to ClawHub
|
||||
|
||||
Provider plugins publish the same way as any other external code plugin:
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/your-plugin --dry-run
|
||||
clawhub package publish your-org/your-plugin
|
||||
```
|
||||
|
||||
Do not use the legacy skill-only publish alias here; plugin packages should use
|
||||
`clawhub package publish`.
|
||||
|
||||
## File structure
|
||||
|
||||
```
|
||||
|
||||
@@ -330,15 +330,15 @@ export function tryGetRuntime() {
|
||||
|
||||
Beyond `api.runtime`, the API object also provides:
|
||||
|
||||
| Field | Type | Description |
|
||||
| ------------------------ | ------------------------- | ---------------------------------------------------------------- |
|
||||
| `api.id` | `string` | Plugin id |
|
||||
| `api.name` | `string` | Plugin display name |
|
||||
| `api.config` | `OpenClawConfig` | Current config snapshot |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | Plugin-specific config from `plugins.entries.<id>.config` |
|
||||
| `api.logger` | `PluginLogger` | Scoped logger (`debug`, `info`, `warn`, `error`) |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | `"full"`, `"setup-only"`, `"setup-runtime"`, or `"cli-metadata"` |
|
||||
| `api.resolvePath(input)` | `(string) => string` | Resolve a path relative to the plugin root |
|
||||
| Field | Type | Description |
|
||||
| ------------------------ | ------------------------- | --------------------------------------------------------- |
|
||||
| `api.id` | `string` | Plugin id |
|
||||
| `api.name` | `string` | Plugin display name |
|
||||
| `api.config` | `OpenClawConfig` | Current config snapshot |
|
||||
| `api.pluginConfig` | `Record<string, unknown>` | Plugin-specific config from `plugins.entries.<id>.config` |
|
||||
| `api.logger` | `PluginLogger` | Scoped logger (`debug`, `info`, `warn`, `error`) |
|
||||
| `api.registrationMode` | `PluginRegistrationMode` | `"full"`, `"setup-only"`, or `"setup-runtime"` |
|
||||
| `api.resolvePath(input)` | `(string) => string` | Resolve a path relative to the plugin root |
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
@@ -43,31 +43,20 @@ your plugin provides:
|
||||
}
|
||||
```
|
||||
|
||||
**Provider plugin / ClawHub publish baseline:**
|
||||
**Provider plugin:**
|
||||
|
||||
```json openclaw-clawhub-package.json
|
||||
```json
|
||||
{
|
||||
"name": "@myorg/openclaw-my-plugin",
|
||||
"name": "@myorg/openclaw-my-provider",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.3.24-beta.2",
|
||||
"minGatewayVersion": "2026.3.24-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.3.24-beta.2",
|
||||
"pluginSdkVersion": "2026.3.24-beta.2"
|
||||
}
|
||||
"providers": ["my-provider"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you publish the plugin externally on ClawHub, those `compat` and `build`
|
||||
fields are required. The canonical publish snippets live in
|
||||
`docs/snippets/plugin-publish/`.
|
||||
|
||||
### `openclaw` fields
|
||||
|
||||
| Field | Type | Description |
|
||||
@@ -158,18 +147,6 @@ Even plugins with no config must ship a schema. An empty schema is valid:
|
||||
|
||||
See [Plugin Manifest](/plugins/manifest) for the full schema reference.
|
||||
|
||||
## ClawHub publishing
|
||||
|
||||
For plugin packages, use the package-specific ClawHub command:
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/your-plugin --dry-run
|
||||
clawhub package publish your-org/your-plugin
|
||||
```
|
||||
|
||||
The legacy skill-only publish alias is for skills. Plugin packages should
|
||||
always use `clawhub package publish`.
|
||||
|
||||
## Setup entry
|
||||
|
||||
The `setup-entry.ts` file is a lightweight alternative to `index.ts` that
|
||||
|
||||
@@ -47,7 +47,7 @@ openclaw onboard --anthropic-api-key "$ANTHROPIC_API_KEY"
|
||||
|
||||
## Fast mode (Anthropic API)
|
||||
|
||||
OpenClaw's shared `/fast` toggle also supports direct public Anthropic traffic, including API-key and OAuth-authenticated requests sent to `api.anthropic.com`.
|
||||
OpenClaw's shared `/fast` toggle also supports direct Anthropic API-key traffic.
|
||||
|
||||
- `/fast on` maps to `service_tier: "auto"`
|
||||
- `/fast off` maps to `service_tier: "standard_only"`
|
||||
@@ -69,8 +69,8 @@ OpenClaw's shared `/fast` toggle also supports direct public Anthropic traffic,
|
||||
|
||||
Important limits:
|
||||
|
||||
- This is **API-key only**. Anthropic setup-token / OAuth auth does not honor OpenClaw fast-mode tier injection.
|
||||
- OpenClaw only injects Anthropic service tiers for direct `api.anthropic.com` requests. If you route `anthropic/*` through a proxy or gateway, `/fast` leaves `service_tier` untouched.
|
||||
- Explicit Anthropic `serviceTier` or `service_tier` model params override the `/fast` default when both are set.
|
||||
- Anthropic reports the effective tier on the response under `usage.service_tier`. On accounts without Priority Tier capacity, `service_tier: "auto"` may still resolve to `standard`.
|
||||
|
||||
## Prompt caching (Anthropic API)
|
||||
|
||||
@@ -159,11 +159,11 @@ OpenAI docs describe warm-up as optional. OpenClaw enables it by default for
|
||||
}
|
||||
```
|
||||
|
||||
### OpenAI and Codex priority processing
|
||||
### OpenAI priority processing
|
||||
|
||||
OpenAI's API exposes priority processing via `service_tier=priority`. In
|
||||
OpenClaw, set `agents.defaults.models["<provider>/<model>"].params.serviceTier`
|
||||
to pass that field through on native OpenAI/Codex Responses endpoints.
|
||||
OpenClaw, set `agents.defaults.models["openai/<model>"].params.serviceTier` to
|
||||
pass that field through on direct `openai/*` Responses requests.
|
||||
|
||||
```json5
|
||||
{
|
||||
@@ -175,11 +175,6 @@ to pass that field through on native OpenAI/Codex Responses endpoints.
|
||||
serviceTier: "priority",
|
||||
},
|
||||
},
|
||||
"openai-codex/gpt-5.4": {
|
||||
params: {
|
||||
serviceTier: "priority",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -188,16 +183,6 @@ to pass that field through on native OpenAI/Codex Responses endpoints.
|
||||
|
||||
Supported values are `auto`, `default`, `flex`, and `priority`.
|
||||
|
||||
OpenClaw forwards `params.serviceTier` to both direct `openai/*` Responses
|
||||
requests and `openai-codex/*` Codex Responses requests when those models point
|
||||
at the native OpenAI/Codex endpoints.
|
||||
|
||||
Important behavior:
|
||||
|
||||
- direct `openai/*` must target `api.openai.com`
|
||||
- `openai-codex/*` must target `chatgpt.com/backend-api`
|
||||
- if you route either provider through another base URL or proxy, OpenClaw leaves `service_tier` untouched
|
||||
|
||||
### OpenAI fast mode
|
||||
|
||||
OpenClaw exposes a shared fast-mode toggle for both `openai/*` and
|
||||
@@ -206,12 +191,11 @@ OpenClaw exposes a shared fast-mode toggle for both `openai/*` and
|
||||
- Chat/UI: `/fast status|on|off`
|
||||
- Config: `agents.defaults.models["<provider>/<model>"].params.fastMode`
|
||||
|
||||
When fast mode is enabled, OpenClaw maps it to OpenAI priority processing:
|
||||
When fast mode is enabled, OpenClaw applies a low-latency OpenAI profile:
|
||||
|
||||
- direct `openai/*` Responses calls to `api.openai.com` send `service_tier = "priority"`
|
||||
- `openai-codex/*` Responses calls to `chatgpt.com/backend-api` also send `service_tier = "priority"`
|
||||
- existing payload `service_tier` values are preserved
|
||||
- fast mode does not rewrite `reasoning` or `text.verbosity`
|
||||
- `reasoning.effort = "low"` when the payload does not already specify reasoning
|
||||
- `text.verbosity = "low"` when the payload does not already specify verbosity
|
||||
- `service_tier = "priority"` for direct `openai/*` Responses calls to `api.openai.com`
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ background.
|
||||
|
||||
## Recommended: Model Studio (Alibaba Cloud Coding Plan)
|
||||
|
||||
Use [Model Studio](/providers/qwen_modelstudio) for officially supported access to
|
||||
Use [Model Studio](/providers/modelstudio) for officially supported access to
|
||||
Qwen models (Qwen 3.5 Plus, GLM-4.7, Kimi K2.5, and more).
|
||||
|
||||
```bash
|
||||
@@ -30,4 +30,4 @@ openclaw onboard --auth-choice modelstudio-api-key
|
||||
openclaw onboard --auth-choice modelstudio-api-key-cn
|
||||
```
|
||||
|
||||
See [Model Studio](/providers/qwen_modelstudio) for full setup details.
|
||||
See [Model Studio](/providers/modelstudio) for full setup details.
|
||||
|
||||
@@ -10,7 +10,7 @@ read_when:
|
||||
|
||||
OpenClaw has three public release lanes:
|
||||
|
||||
- stable: tagged releases that publish to npm `latest` and mirror the same version onto `beta` unless `beta` already points at a newer prerelease
|
||||
- stable: tagged releases that publish to npm `latest`
|
||||
- beta: prerelease tags that publish to npm `beta`
|
||||
- dev: the moving head of `main`
|
||||
|
||||
@@ -24,8 +24,8 @@ OpenClaw has three public release lanes:
|
||||
- Git tag: `vYYYY.M.D-beta.N`
|
||||
- Do not zero-pad month or day
|
||||
- `latest` means the current stable npm release
|
||||
- `beta` means the current beta install target, which may point to either the active prerelease or the latest promoted stable build
|
||||
- Stable and stable correction releases publish to npm `latest` and also retag npm `beta` to that same non-beta version after promotion, unless `beta` already points at a newer prerelease
|
||||
- `beta` means the current prerelease npm release
|
||||
- Stable correction releases also publish to npm `latest`
|
||||
- Every OpenClaw release ships the npm package and macOS app together
|
||||
|
||||
## Release cadence
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Memory configuration reference"
|
||||
summary: "All configuration knobs for memory search, embedding providers, QMD, hybrid search, and multimodal indexing"
|
||||
summary: "Full configuration reference for OpenClaw memory search, embedding providers, QMD backend, hybrid search, and multimodal memory"
|
||||
read_when:
|
||||
- You want to configure memory search providers or embedding models
|
||||
- You want to set up the QMD backend
|
||||
@@ -10,350 +10,710 @@ read_when:
|
||||
|
||||
# Memory configuration reference
|
||||
|
||||
This page lists every configuration knob for OpenClaw memory search. For
|
||||
conceptual overviews, see:
|
||||
This page covers the full configuration surface for OpenClaw memory search. For
|
||||
the conceptual overview (file layout, memory tools, when to write memory, and the
|
||||
automatic flush), see [Memory](/concepts/memory).
|
||||
|
||||
- [Memory Overview](/concepts/memory) -- how memory works
|
||||
- [Builtin Engine](/concepts/memory-builtin) -- default SQLite backend
|
||||
- [QMD Engine](/concepts/memory-qmd) -- local-first sidecar
|
||||
- [Memory Search](/concepts/memory-search) -- search pipeline and tuning
|
||||
## Memory search defaults
|
||||
|
||||
All memory search settings live under `agents.defaults.memorySearch` in
|
||||
`openclaw.json` unless noted otherwise.
|
||||
- Enabled by default.
|
||||
- Watches memory files for changes (debounced).
|
||||
- Configure memory search under `agents.defaults.memorySearch` (not top-level
|
||||
`memorySearch`).
|
||||
- `memorySearch.provider` and `memorySearch.fallback` accept **adapter ids**
|
||||
registered by the active memory plugin.
|
||||
- The default `memory-core` plugin registers these built-in adapter ids:
|
||||
`local`, `openai`, `gemini`, `voyage`, `mistral`, and `ollama`.
|
||||
- With the default `memory-core` plugin, if `memorySearch.provider` is not set,
|
||||
OpenClaw auto-selects:
|
||||
1. `local` if a `memorySearch.local.modelPath` is configured and the file exists.
|
||||
2. `openai` if an OpenAI key can be resolved.
|
||||
3. `gemini` if a Gemini key can be resolved.
|
||||
4. `voyage` if a Voyage key can be resolved.
|
||||
5. `mistral` if a Mistral key can be resolved.
|
||||
6. Otherwise memory search stays disabled until configured.
|
||||
- Local mode uses node-llama-cpp and may require `pnpm approve-builds`.
|
||||
- Uses sqlite-vec (when available) to accelerate vector search inside SQLite.
|
||||
- With the default `memory-core` plugin, `memorySearch.provider = "ollama"` is
|
||||
also supported for local/self-hosted Ollama embeddings (`/api/embeddings`),
|
||||
but it is not auto-selected.
|
||||
|
||||
---
|
||||
Remote embeddings **require** an API key for the embedding provider. OpenClaw
|
||||
resolves keys from auth profiles, `models.providers.*.apiKey`, or environment
|
||||
variables. Codex OAuth only covers chat/completions and does **not** satisfy
|
||||
embeddings for memory search. For Gemini, use `GEMINI_API_KEY` or
|
||||
`models.providers.google.apiKey`. For Voyage, use `VOYAGE_API_KEY` or
|
||||
`models.providers.voyage.apiKey`. For Mistral, use `MISTRAL_API_KEY` or
|
||||
`models.providers.mistral.apiKey`. Ollama typically does not require a real API
|
||||
key (a placeholder like `OLLAMA_API_KEY=ollama-local` is enough when needed by
|
||||
local policy).
|
||||
When using a custom OpenAI-compatible endpoint,
|
||||
set `memorySearch.remote.apiKey` (and optional `memorySearch.remote.headers`).
|
||||
|
||||
## Provider selection
|
||||
## QMD backend (experimental)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------- | --------- | ---------------- | -------------------------------------------------------------------------------- |
|
||||
| `provider` | `string` | auto-detected | Embedding adapter ID: `openai`, `gemini`, `voyage`, `mistral`, `ollama`, `local` |
|
||||
| `model` | `string` | provider default | Embedding model name |
|
||||
| `fallback` | `string` | `"none"` | Fallback adapter ID when the primary fails |
|
||||
| `enabled` | `boolean` | `true` | Enable or disable memory search |
|
||||
Set `memory.backend = "qmd"` to swap the built-in SQLite indexer for
|
||||
[QMD](https://github.com/tobi/qmd): a local-first search sidecar that combines
|
||||
BM25 + vectors + reranking. Markdown stays the source of truth; OpenClaw shells
|
||||
out to QMD for retrieval. Key points:
|
||||
|
||||
### Auto-detection order
|
||||
### Prerequisites
|
||||
|
||||
When `provider` is not set, OpenClaw selects the first available:
|
||||
- Disabled by default. Opt in per-config (`memory.backend = "qmd"`).
|
||||
- Install the QMD CLI separately (`bun install -g https://github.com/tobi/qmd` or grab
|
||||
a release) and make sure the `qmd` binary is on the gateway's `PATH`.
|
||||
- QMD needs an SQLite build that allows extensions (`brew install sqlite` on
|
||||
macOS).
|
||||
- QMD runs fully locally via Bun + `node-llama-cpp` and auto-downloads GGUF
|
||||
models from HuggingFace on first use (no separate Ollama daemon required).
|
||||
- The gateway runs QMD in a self-contained XDG home under
|
||||
`~/.openclaw/agents/<agentId>/qmd/` by setting `XDG_CONFIG_HOME` and
|
||||
`XDG_CACHE_HOME`.
|
||||
- OS support: macOS and Linux work out of the box once Bun + SQLite are
|
||||
installed. Windows is best supported via WSL2.
|
||||
|
||||
1. `local` -- if `memorySearch.local.modelPath` is configured and the file exists.
|
||||
2. `openai` -- if an OpenAI key can be resolved.
|
||||
3. `gemini` -- if a Gemini key can be resolved.
|
||||
4. `voyage` -- if a Voyage key can be resolved.
|
||||
5. `mistral` -- if a Mistral key can be resolved.
|
||||
### How the sidecar runs
|
||||
|
||||
`ollama` is supported but not auto-detected (set it explicitly).
|
||||
- The gateway writes a self-contained QMD home under
|
||||
`~/.openclaw/agents/<agentId>/qmd/` (config + cache + sqlite DB).
|
||||
- Collections are created via `qmd collection add` from `memory.qmd.paths`
|
||||
(plus default workspace memory files), then `qmd update` + `qmd embed` run
|
||||
on boot and on a configurable interval (`memory.qmd.update.interval`,
|
||||
default 5 m).
|
||||
- The gateway now initializes the QMD manager on startup, so periodic update
|
||||
timers are armed even before the first `memory_search` call.
|
||||
- Boot refresh now runs in the background by default so chat startup is not
|
||||
blocked; set `memory.qmd.update.waitForBootSync = true` to keep the previous
|
||||
blocking behavior.
|
||||
- Searches run via `memory.qmd.searchMode` (default `qmd search --json`; also
|
||||
supports `vsearch` and `query`). If the selected mode rejects flags on your
|
||||
QMD build, OpenClaw retries with `qmd query`. If QMD fails or the binary is
|
||||
missing, OpenClaw automatically falls back to the builtin SQLite manager so
|
||||
memory tools keep working.
|
||||
- OpenClaw does not expose QMD embed batch-size tuning today; batch behavior is
|
||||
controlled by QMD itself.
|
||||
- **First search may be slow**: QMD may download local GGUF models (reranker/query
|
||||
expansion) on the first `qmd query` run.
|
||||
- OpenClaw sets `XDG_CONFIG_HOME`/`XDG_CACHE_HOME` automatically when it runs QMD.
|
||||
- If you want to pre-download models manually (and warm the same index OpenClaw
|
||||
uses), run a one-off query with the agent's XDG dirs.
|
||||
|
||||
### API key resolution
|
||||
OpenClaw's QMD state lives under your **state dir** (defaults to `~/.openclaw`).
|
||||
You can point `qmd` at the exact same index by exporting the same XDG vars
|
||||
OpenClaw uses:
|
||||
|
||||
Remote embeddings require an API key. OpenClaw resolves from:
|
||||
auth profiles, `models.providers.*.apiKey`, or environment variables.
|
||||
```bash
|
||||
# Pick the same state dir OpenClaw uses
|
||||
STATE_DIR="${OPENCLAW_STATE_DIR:-$HOME/.openclaw}"
|
||||
|
||||
| Provider | Env var | Config key |
|
||||
| -------- | ------------------------------ | --------------------------------- |
|
||||
| OpenAI | `OPENAI_API_KEY` | `models.providers.openai.apiKey` |
|
||||
| Gemini | `GEMINI_API_KEY` | `models.providers.google.apiKey` |
|
||||
| Voyage | `VOYAGE_API_KEY` | `models.providers.voyage.apiKey` |
|
||||
| Mistral | `MISTRAL_API_KEY` | `models.providers.mistral.apiKey` |
|
||||
| Ollama | `OLLAMA_API_KEY` (placeholder) | -- |
|
||||
export XDG_CONFIG_HOME="$STATE_DIR/agents/main/qmd/xdg-config"
|
||||
export XDG_CACHE_HOME="$STATE_DIR/agents/main/qmd/xdg-cache"
|
||||
|
||||
Codex OAuth covers chat/completions only and does not satisfy embedding
|
||||
requests.
|
||||
# (Optional) force an index refresh + embeddings
|
||||
qmd update
|
||||
qmd embed
|
||||
|
||||
---
|
||||
# Warm up / trigger first-time model downloads
|
||||
qmd query "test" -c memory-root --json >/dev/null 2>&1
|
||||
```
|
||||
|
||||
## Remote endpoint config
|
||||
### Config surface (`memory.qmd.*`)
|
||||
|
||||
For custom OpenAI-compatible endpoints or overriding provider defaults:
|
||||
- `command` (default `qmd`): override the executable path.
|
||||
- `searchMode` (default `search`): pick which QMD command backs
|
||||
`memory_search` (`search`, `vsearch`, `query`).
|
||||
- `includeDefaultMemory` (default `true`): auto-index `MEMORY.md` + `memory/**/*.md`.
|
||||
- `paths[]`: add extra directories/files (`path`, optional `pattern`, optional
|
||||
stable `name`).
|
||||
- `sessions`: opt into session JSONL indexing (`enabled`, `retentionDays`,
|
||||
`exportDir`).
|
||||
- `update`: controls refresh cadence and maintenance execution:
|
||||
(`interval`, `debounceMs`, `onBoot`, `waitForBootSync`, `embedInterval`,
|
||||
`commandTimeoutMs`, `updateTimeoutMs`, `embedTimeoutMs`).
|
||||
- `limits`: clamp recall payload (`maxResults`, `maxSnippetChars`,
|
||||
`maxInjectedChars`, `timeoutMs`).
|
||||
- `scope`: same schema as [`session.sendPolicy`](/gateway/configuration-reference#session).
|
||||
Default is DM-only (`deny` all, `allow` direct chats); loosen it to surface QMD
|
||||
hits in groups/channels.
|
||||
- `match.keyPrefix` matches the **normalized** session key (lowercased, with any
|
||||
leading `agent:<id>:` stripped). Example: `discord:channel:`.
|
||||
- `match.rawKeyPrefix` matches the **raw** session key (lowercased), including
|
||||
`agent:<id>:`. Example: `agent:main:discord:`.
|
||||
- Legacy: `match.keyPrefix: "agent:..."` is still treated as a raw-key prefix,
|
||||
but prefer `rawKeyPrefix` for clarity.
|
||||
- When `scope` denies a search, OpenClaw logs a warning with the derived
|
||||
`channel`/`chatType` so empty results are easier to debug.
|
||||
- Snippets sourced outside the workspace show up as
|
||||
`qmd/<collection>/<relative-path>` in `memory_search` results; `memory_get`
|
||||
understands that prefix and reads from the configured QMD collection root.
|
||||
- When `memory.qmd.sessions.enabled = true`, OpenClaw exports sanitized session
|
||||
transcripts (User/Assistant turns) into a dedicated QMD collection under
|
||||
`~/.openclaw/agents/<id>/qmd/sessions/`, so `memory_search` can recall recent
|
||||
conversations without touching the builtin SQLite index.
|
||||
- `memory_search` snippets now include a `Source: <path#line>` footer when
|
||||
`memory.citations` is `auto`/`on`; set `memory.citations = "off"` to keep
|
||||
the path metadata internal (the agent still receives the path for
|
||||
`memory_get`, but the snippet text omits the footer and the system prompt
|
||||
warns the agent not to cite it).
|
||||
|
||||
| Key | Type | Description |
|
||||
| ---------------- | -------- | -------------------------------------------------- |
|
||||
| `remote.baseUrl` | `string` | Custom API base URL |
|
||||
| `remote.apiKey` | `string` | Override API key |
|
||||
| `remote.headers` | `object` | Extra HTTP headers (merged with provider defaults) |
|
||||
### QMD example
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
remote: {
|
||||
baseUrl: "https://api.example.com/v1/",
|
||||
apiKey: "YOUR_KEY",
|
||||
},
|
||||
},
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
citations: "auto",
|
||||
qmd: {
|
||||
includeDefaultMemory: true,
|
||||
update: { interval: "5m", debounceMs: 15000 },
|
||||
limits: { maxResults: 6, timeoutMs: 4000 },
|
||||
scope: {
|
||||
default: "deny",
|
||||
rules: [
|
||||
{ action: "allow", match: { chatType: "direct" } },
|
||||
// Normalized session-key prefix (strips `agent:<id>:`).
|
||||
{ action: "deny", match: { keyPrefix: "discord:channel:" } },
|
||||
// Raw session-key prefix (includes `agent:<id>:`).
|
||||
{ action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
|
||||
]
|
||||
},
|
||||
},
|
||||
paths: [
|
||||
{ name: "docs", path: "~/notes", pattern: "**/*.md" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
### Citations and fallback
|
||||
|
||||
## Gemini-specific config
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------------------- | -------- | ---------------------- | ------------------------------------------ |
|
||||
| `model` | `string` | `gemini-embedding-001` | Also supports `gemini-embedding-2-preview` |
|
||||
| `outputDimensionality` | `number` | `3072` | For Embedding 2: 768, 1536, or 3072 |
|
||||
|
||||
<Warning>
|
||||
Changing model or `outputDimensionality` triggers an automatic full reindex.
|
||||
</Warning>
|
||||
|
||||
---
|
||||
|
||||
## Local embedding config
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --------------------- | -------- | ---------------------- | ------------------------------- |
|
||||
| `local.modelPath` | `string` | auto-downloaded | Path to GGUF model file |
|
||||
| `local.modelCacheDir` | `string` | node-llama-cpp default | Cache dir for downloaded models |
|
||||
|
||||
Default model: `embeddinggemma-300m-qat-Q8_0.gguf` (~0.6 GB, auto-downloaded).
|
||||
Requires native build: `pnpm approve-builds` then `pnpm rebuild node-llama-cpp`.
|
||||
|
||||
---
|
||||
|
||||
## Hybrid search config
|
||||
|
||||
All under `memorySearch.query.hybrid`:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --------------------- | --------- | ------- | ---------------------------------- |
|
||||
| `enabled` | `boolean` | `true` | Enable hybrid BM25 + vector search |
|
||||
| `vectorWeight` | `number` | `0.7` | Weight for vector scores (0-1) |
|
||||
| `textWeight` | `number` | `0.3` | Weight for BM25 scores (0-1) |
|
||||
| `candidateMultiplier` | `number` | `4` | Candidate pool size multiplier |
|
||||
|
||||
### MMR (diversity)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------- | --------- | ------- | ------------------------------------ |
|
||||
| `mmr.enabled` | `boolean` | `false` | Enable MMR re-ranking |
|
||||
| `mmr.lambda` | `number` | `0.7` | 0 = max diversity, 1 = max relevance |
|
||||
|
||||
### Temporal decay (recency)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------------------------- | --------- | ------- | ------------------------- |
|
||||
| `temporalDecay.enabled` | `boolean` | `false` | Enable recency boost |
|
||||
| `temporalDecay.halfLifeDays` | `number` | `30` | Score halves every N days |
|
||||
|
||||
Evergreen files (`MEMORY.md`, non-dated files in `memory/`) are never decayed.
|
||||
|
||||
### Full example
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
query: {
|
||||
hybrid: {
|
||||
vectorWeight: 0.7,
|
||||
textWeight: 0.3,
|
||||
mmr: { enabled: true, lambda: 0.7 },
|
||||
temporalDecay: { enabled: true, halfLifeDays: 30 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
- `memory.citations` applies regardless of backend (`auto`/`on`/`off`).
|
||||
- When `qmd` runs, we tag `status().backend = "qmd"` so diagnostics show which
|
||||
engine served the results. If the QMD subprocess exits or JSON output can't be
|
||||
parsed, the search manager logs a warning and returns the builtin provider
|
||||
(existing Markdown embeddings) until QMD recovers.
|
||||
|
||||
## Additional memory paths
|
||||
|
||||
| Key | Type | Description |
|
||||
| ------------ | ---------- | ---------------------------------------- |
|
||||
| `extraPaths` | `string[]` | Additional directories or files to index |
|
||||
If you want to index Markdown files outside the default workspace layout, add
|
||||
explicit paths:
|
||||
|
||||
```json5
|
||||
{
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
extraPaths: ["../team-docs", "/srv/shared-notes"],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Paths can be absolute or workspace-relative. Directories are scanned
|
||||
recursively for `.md` files. Symlinks are ignored.
|
||||
Notes:
|
||||
|
||||
---
|
||||
- Paths can be absolute or workspace-relative.
|
||||
- Directories are scanned recursively for `.md` files.
|
||||
- By default, only Markdown files are indexed.
|
||||
- If `memorySearch.multimodal.enabled = true`, OpenClaw also indexes supported image/audio files under `extraPaths` only. Default memory roots (`MEMORY.md`, `memory.md`, `memory/**/*.md`) stay Markdown-only.
|
||||
- Symlinks are ignored (files or directories).
|
||||
|
||||
## Multimodal memory (Gemini)
|
||||
## Multimodal memory files (Gemini image + audio)
|
||||
|
||||
Index images and audio alongside Markdown using Gemini Embedding 2:
|
||||
OpenClaw can index image and audio files from `memorySearch.extraPaths` when using Gemini embedding 2:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------------- | ---------- | ---------- | -------------------------------------- |
|
||||
| `multimodal.enabled` | `boolean` | `false` | Enable multimodal indexing |
|
||||
| `multimodal.modalities` | `string[]` | -- | `["image"]`, `["audio"]`, or `["all"]` |
|
||||
| `multimodal.maxFileBytes` | `number` | `10000000` | Max file size for indexing |
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "gemini",
|
||||
model: "gemini-embedding-2-preview",
|
||||
extraPaths: ["assets/reference", "voice-notes"],
|
||||
multimodal: {
|
||||
enabled: true,
|
||||
modalities: ["image", "audio"], // or ["all"]
|
||||
maxFileBytes: 10000000
|
||||
},
|
||||
remote: {
|
||||
apiKey: "YOUR_GEMINI_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Only applies to files in `extraPaths`. Default memory roots stay Markdown-only.
|
||||
Requires `gemini-embedding-2-preview`. `fallback` must be `"none"`.
|
||||
Notes:
|
||||
|
||||
Supported formats: `.jpg`, `.jpeg`, `.png`, `.webp`, `.gif`, `.heic`, `.heif`
|
||||
(images); `.mp3`, `.wav`, `.ogg`, `.opus`, `.m4a`, `.aac`, `.flac` (audio).
|
||||
- Multimodal memory is currently supported only for `gemini-embedding-2-preview`.
|
||||
- Multimodal indexing applies only to files discovered through `memorySearch.extraPaths`.
|
||||
- Supported modalities in this phase: image and audio.
|
||||
- `memorySearch.fallback` must stay `"none"` while multimodal memory is enabled.
|
||||
- Matching image/audio file bytes are uploaded to the configured Gemini embedding endpoint during indexing.
|
||||
- Supported image extensions: `.jpg`, `.jpeg`, `.png`, `.webp`, `.gif`, `.heic`, `.heif`.
|
||||
- Supported audio extensions: `.mp3`, `.wav`, `.ogg`, `.opus`, `.m4a`, `.aac`, `.flac`.
|
||||
- Search queries remain text, but Gemini can compare those text queries against indexed image/audio embeddings.
|
||||
- `memory_get` still reads Markdown only; binary files are searchable but not returned as raw file contents.
|
||||
|
||||
---
|
||||
## Gemini embeddings (native)
|
||||
|
||||
Set the provider to `gemini` to use the Gemini embeddings API directly:
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "gemini",
|
||||
model: "gemini-embedding-001",
|
||||
remote: {
|
||||
apiKey: "YOUR_GEMINI_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `remote.baseUrl` is optional (defaults to the Gemini API base URL).
|
||||
- `remote.headers` lets you add extra headers if needed.
|
||||
- Default model: `gemini-embedding-001`.
|
||||
- `gemini-embedding-2-preview` is also supported: 8192 token limit and configurable dimensions (768 / 1536 / 3072, default 3072).
|
||||
|
||||
### Gemini Embedding 2 (preview)
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "gemini",
|
||||
model: "gemini-embedding-2-preview",
|
||||
outputDimensionality: 3072, // optional: 768, 1536, or 3072 (default)
|
||||
remote: {
|
||||
apiKey: "YOUR_GEMINI_API_KEY"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Re-index required:** Switching from `gemini-embedding-001` (768 dimensions)
|
||||
> to `gemini-embedding-2-preview` (3072 dimensions) changes the vector size. The same is true if you
|
||||
> change `outputDimensionality` between 768, 1536, and 3072.
|
||||
> OpenClaw will automatically reindex when it detects a model or dimension change.
|
||||
|
||||
## Custom OpenAI-compatible endpoint
|
||||
|
||||
If you want to use a custom OpenAI-compatible endpoint (OpenRouter, vLLM, or a proxy),
|
||||
you can use the `remote` configuration with the OpenAI provider:
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
remote: {
|
||||
baseUrl: "https://api.example.com/v1/",
|
||||
apiKey: "YOUR_OPENAI_COMPAT_API_KEY",
|
||||
headers: { "X-Custom-Header": "value" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you don't want to set an API key, use `memorySearch.provider = "local"` or set
|
||||
`memorySearch.fallback = "none"`.
|
||||
|
||||
### Fallbacks
|
||||
|
||||
- `memorySearch.fallback` can be any registered memory embedding adapter id, or `none`.
|
||||
- With the default `memory-core` plugin, valid built-in fallback ids are `openai`, `gemini`, `voyage`, `mistral`, `ollama`, and `local`.
|
||||
- The fallback provider is only used when the primary embedding provider fails.
|
||||
|
||||
### Batch indexing
|
||||
|
||||
- Disabled by default. Set `agents.defaults.memorySearch.remote.batch.enabled = true` to enable batch indexing for providers whose adapter exposes batch support.
|
||||
- Default behavior waits for batch completion; tune `remote.batch.wait`, `remote.batch.pollIntervalMs`, and `remote.batch.timeoutMinutes` if needed.
|
||||
- Set `remote.batch.concurrency` to control how many batch jobs we submit in parallel (default: 2).
|
||||
- With the default `memory-core` plugin, batch indexing is available for `openai`, `gemini`, and `voyage`.
|
||||
- Gemini batch jobs use the async embeddings batch endpoint and require Gemini Batch API availability.
|
||||
|
||||
Why OpenAI batch is fast and cheap:
|
||||
|
||||
- For large backfills, OpenAI is typically the fastest option we support because we can submit many embedding requests in a single batch job and let OpenAI process them asynchronously.
|
||||
- OpenAI offers discounted pricing for Batch API workloads, so large indexing runs are usually cheaper than sending the same requests synchronously.
|
||||
- See the OpenAI Batch API docs and pricing for details:
|
||||
- [https://platform.openai.com/docs/api-reference/batch](https://platform.openai.com/docs/api-reference/batch)
|
||||
- [https://platform.openai.com/pricing](https://platform.openai.com/pricing)
|
||||
|
||||
Config example:
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
fallback: "openai",
|
||||
remote: {
|
||||
batch: { enabled: true, concurrency: 2 }
|
||||
},
|
||||
sync: { watch: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## How the memory tools work
|
||||
|
||||
- `memory_search` semantically searches Markdown chunks (~400 token target, 80-token overlap) from `MEMORY.md` + `memory/**/*.md`. It returns snippet text (capped ~700 chars), file path, line range, score, provider/model, and whether we fell back from local to remote embeddings. No full file payload is returned.
|
||||
- `memory_get` reads a specific memory Markdown file (workspace-relative), optionally from a starting line and for N lines. Paths outside `MEMORY.md` / `memory/` are rejected.
|
||||
- Both tools are enabled only when `memorySearch.enabled` resolves true for the agent.
|
||||
|
||||
## What gets indexed (and when)
|
||||
|
||||
- File type: Markdown only (`MEMORY.md`, `memory/**/*.md`).
|
||||
- Index storage: per-agent SQLite at `~/.openclaw/memory/<agentId>.sqlite` (configurable via `agents.defaults.memorySearch.store.path`, supports `{agentId}` token).
|
||||
- Freshness: watcher on `MEMORY.md` + `memory/` marks the index dirty (debounce 1.5s). Sync is scheduled on session start, on search, or on an interval and runs asynchronously. Session transcripts use delta thresholds to trigger background sync.
|
||||
- Reindex triggers: the index stores the embedding **provider/model + endpoint fingerprint + chunking params**. If any of those change, OpenClaw automatically resets and reindexes the entire store.
|
||||
|
||||
## Hybrid search (BM25 + vector)
|
||||
|
||||
When enabled, OpenClaw combines:
|
||||
|
||||
- **Vector similarity** (semantic match, wording can differ)
|
||||
- **BM25 keyword relevance** (exact tokens like IDs, env vars, code symbols)
|
||||
|
||||
If full-text search is unavailable on your platform, OpenClaw falls back to vector-only search.
|
||||
|
||||
### Why hybrid
|
||||
|
||||
Vector search is great at "this means the same thing":
|
||||
|
||||
- "Mac Studio gateway host" vs "the machine running the gateway"
|
||||
- "debounce file updates" vs "avoid indexing on every write"
|
||||
|
||||
But it can be weak at exact, high-signal tokens:
|
||||
|
||||
- IDs (`a828e60`, `b3b9895a...`)
|
||||
- code symbols (`memorySearch.query.hybrid`)
|
||||
- error strings ("sqlite-vec unavailable")
|
||||
|
||||
BM25 (full-text) is the opposite: strong at exact tokens, weaker at paraphrases.
|
||||
Hybrid search is the pragmatic middle ground: **use both retrieval signals** so you get
|
||||
good results for both "natural language" queries and "needle in a haystack" queries.
|
||||
|
||||
### How we merge results (the current design)
|
||||
|
||||
Implementation sketch:
|
||||
|
||||
1. Retrieve a candidate pool from both sides:
|
||||
|
||||
- **Vector**: top `maxResults * candidateMultiplier` by cosine similarity.
|
||||
- **BM25**: top `maxResults * candidateMultiplier` by FTS5 BM25 rank (lower is better).
|
||||
|
||||
2. Convert BM25 rank into a 0..1-ish score:
|
||||
|
||||
- `textScore = 1 / (1 + max(0, bm25Rank))`
|
||||
|
||||
3. Union candidates by chunk id and compute a weighted score:
|
||||
|
||||
- `finalScore = vectorWeight * vectorScore + textWeight * textScore`
|
||||
|
||||
Notes:
|
||||
|
||||
- `vectorWeight` + `textWeight` is normalized to 1.0 in config resolution, so weights behave as percentages.
|
||||
- If embeddings are unavailable (or the provider returns a zero-vector), we still run BM25 and return keyword matches.
|
||||
- If FTS5 can't be created, we keep vector-only search (no hard failure).
|
||||
- **CJK support**: FTS5 uses configurable trigram tokenization with a short-substring fallback so Chinese, Japanese, and Korean text is searchable without breaking mixed-length queries. CJK-heavy text is also weighted correctly during chunk size estimation, and surrogate-pair characters are preserved during fine splits.
|
||||
|
||||
This isn't "IR-theory perfect", but it's simple, fast, and tends to improve recall/precision on real notes.
|
||||
If we want to get fancier later, common next steps are Reciprocal Rank Fusion (RRF) or score normalization
|
||||
(min/max or z-score) before mixing.
|
||||
|
||||
### Post-processing pipeline
|
||||
|
||||
After merging vector and keyword scores, two optional post-processing stages
|
||||
refine the result list before it reaches the agent:
|
||||
|
||||
```
|
||||
Vector + Keyword -> Weighted Merge -> Temporal Decay -> Sort -> MMR -> Top-K Results
|
||||
```
|
||||
|
||||
Both stages are **off by default** and can be enabled independently.
|
||||
|
||||
### MMR re-ranking (diversity)
|
||||
|
||||
When hybrid search returns results, multiple chunks may contain similar or overlapping content.
|
||||
For example, searching for "home network setup" might return five nearly identical snippets
|
||||
from different daily notes that all mention the same router configuration.
|
||||
|
||||
**MMR (Maximal Marginal Relevance)** re-ranks the results to balance relevance with diversity,
|
||||
ensuring the top results cover different aspects of the query instead of repeating the same information.
|
||||
|
||||
How it works:
|
||||
|
||||
1. Results are scored by their original relevance (vector + BM25 weighted score).
|
||||
2. MMR iteratively selects results that maximize: `lambda x relevance - (1-lambda) x max_similarity_to_selected`.
|
||||
3. Similarity between results is measured using Jaccard text similarity on tokenized content.
|
||||
|
||||
The `lambda` parameter controls the trade-off:
|
||||
|
||||
- `lambda = 1.0` -- pure relevance (no diversity penalty)
|
||||
- `lambda = 0.0` -- maximum diversity (ignores relevance)
|
||||
- Default: `0.7` (balanced, slight relevance bias)
|
||||
|
||||
**Example -- query: "home network setup"**
|
||||
|
||||
Given these memory files:
|
||||
|
||||
```
|
||||
memory/2026-02-10.md -> "Configured Omada router, set VLAN 10 for IoT devices"
|
||||
memory/2026-02-08.md -> "Configured Omada router, moved IoT to VLAN 10"
|
||||
memory/2026-02-05.md -> "Set up AdGuard DNS on 192.168.10.2"
|
||||
memory/network.md -> "Router: Omada ER605, AdGuard: 192.168.10.2, VLAN 10: IoT"
|
||||
```
|
||||
|
||||
Without MMR -- top 3 results:
|
||||
|
||||
```
|
||||
1. memory/2026-02-10.md (score: 0.92) <- router + VLAN
|
||||
2. memory/2026-02-08.md (score: 0.89) <- router + VLAN (near-duplicate!)
|
||||
3. memory/network.md (score: 0.85) <- reference doc
|
||||
```
|
||||
|
||||
With MMR (lambda=0.7) -- top 3 results:
|
||||
|
||||
```
|
||||
1. memory/2026-02-10.md (score: 0.92) <- router + VLAN
|
||||
2. memory/network.md (score: 0.85) <- reference doc (diverse!)
|
||||
3. memory/2026-02-05.md (score: 0.78) <- AdGuard DNS (diverse!)
|
||||
```
|
||||
|
||||
The near-duplicate from Feb 8 drops out, and the agent gets three distinct pieces of information.
|
||||
|
||||
**When to enable:** If you notice `memory_search` returning redundant or near-duplicate snippets,
|
||||
especially with daily notes that often repeat similar information across days.
|
||||
|
||||
### Temporal decay (recency boost)
|
||||
|
||||
Agents with daily notes accumulate hundreds of dated files over time. Without decay,
|
||||
a well-worded note from six months ago can outrank yesterday's update on the same topic.
|
||||
|
||||
**Temporal decay** applies an exponential multiplier to scores based on the age of each result,
|
||||
so recent memories naturally rank higher while old ones fade:
|
||||
|
||||
```
|
||||
decayedScore = score x e^(-lambda x ageInDays)
|
||||
```
|
||||
|
||||
where `lambda = ln(2) / halfLifeDays`.
|
||||
|
||||
With the default half-life of 30 days:
|
||||
|
||||
- Today's notes: **100%** of original score
|
||||
- 7 days ago: **~84%**
|
||||
- 30 days ago: **50%**
|
||||
- 90 days ago: **12.5%**
|
||||
- 180 days ago: **~1.6%**
|
||||
|
||||
**Evergreen files are never decayed:**
|
||||
|
||||
- `MEMORY.md` (root memory file)
|
||||
- Non-dated files in `memory/` (e.g., `memory/projects.md`, `memory/network.md`)
|
||||
- These contain durable reference information that should always rank normally.
|
||||
|
||||
**Dated daily files** (`memory/YYYY-MM-DD.md`) use the date extracted from the filename.
|
||||
Other sources (e.g., session transcripts) fall back to file modification time (`mtime`).
|
||||
|
||||
**Example -- query: "what's Rod's work schedule?"**
|
||||
|
||||
Given these memory files (today is Feb 10):
|
||||
|
||||
```
|
||||
memory/2025-09-15.md -> "Rod works Mon-Fri, standup at 10am, pairing at 2pm" (148 days old)
|
||||
memory/2026-02-10.md -> "Rod has standup at 14:15, 1:1 with Zeb at 14:45" (today)
|
||||
memory/2026-02-03.md -> "Rod started new team, standup moved to 14:15" (7 days old)
|
||||
```
|
||||
|
||||
Without decay:
|
||||
|
||||
```
|
||||
1. memory/2025-09-15.md (score: 0.91) <- best semantic match, but stale!
|
||||
2. memory/2026-02-10.md (score: 0.82)
|
||||
3. memory/2026-02-03.md (score: 0.80)
|
||||
```
|
||||
|
||||
With decay (halfLife=30):
|
||||
|
||||
```
|
||||
1. memory/2026-02-10.md (score: 0.82 x 1.00 = 0.82) <- today, no decay
|
||||
2. memory/2026-02-03.md (score: 0.80 x 0.85 = 0.68) <- 7 days, mild decay
|
||||
3. memory/2025-09-15.md (score: 0.91 x 0.03 = 0.03) <- 148 days, nearly gone
|
||||
```
|
||||
|
||||
The stale September note drops to the bottom despite having the best raw semantic match.
|
||||
|
||||
**When to enable:** If your agent has months of daily notes and you find that old,
|
||||
stale information outranks recent context. A half-life of 30 days works well for
|
||||
daily-note-heavy workflows; increase it (e.g., 90 days) if you reference older notes frequently.
|
||||
|
||||
### Hybrid search configuration
|
||||
|
||||
Both features are configured under `memorySearch.query.hybrid`:
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
query: {
|
||||
hybrid: {
|
||||
enabled: true,
|
||||
vectorWeight: 0.7,
|
||||
textWeight: 0.3,
|
||||
candidateMultiplier: 4,
|
||||
// Diversity: reduce redundant results
|
||||
mmr: {
|
||||
enabled: true, // default: false
|
||||
lambda: 0.7 // 0 = max diversity, 1 = max relevance
|
||||
},
|
||||
// Recency: boost newer memories
|
||||
temporalDecay: {
|
||||
enabled: true, // default: false
|
||||
halfLifeDays: 30 // score halves every 30 days
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can enable either feature independently:
|
||||
|
||||
- **MMR only** -- useful when you have many similar notes but age doesn't matter.
|
||||
- **Temporal decay only** -- useful when recency matters but your results are already diverse.
|
||||
- **Both** -- recommended for agents with large, long-running daily note histories.
|
||||
|
||||
## Embedding cache
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------ | --------- | ------- | -------------------------------- |
|
||||
| `cache.enabled` | `boolean` | `false` | Cache chunk embeddings in SQLite |
|
||||
| `cache.maxEntries` | `number` | `50000` | Max cached embeddings |
|
||||
OpenClaw can cache **chunk embeddings** in SQLite so reindexing and frequent updates (especially session transcripts) don't re-embed unchanged text.
|
||||
|
||||
Prevents re-embedding unchanged text during reindex or transcript updates.
|
||||
Config:
|
||||
|
||||
---
|
||||
|
||||
## Batch indexing
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ----------------------------- | --------- | ------- | -------------------------- |
|
||||
| `remote.batch.enabled` | `boolean` | `false` | Enable batch embedding API |
|
||||
| `remote.batch.concurrency` | `number` | `2` | Parallel batch jobs |
|
||||
| `remote.batch.wait` | `boolean` | `true` | Wait for batch completion |
|
||||
| `remote.batch.pollIntervalMs` | `number` | -- | Poll interval |
|
||||
| `remote.batch.timeoutMinutes` | `number` | -- | Batch timeout |
|
||||
|
||||
Available for `openai`, `gemini`, and `voyage`. OpenAI batch is typically
|
||||
fastest and cheapest for large backfills.
|
||||
|
||||
---
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
cache: {
|
||||
enabled: true,
|
||||
maxEntries: 50000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Session memory search (experimental)
|
||||
|
||||
Index session transcripts and surface them via `memory_search`:
|
||||
You can optionally index **session transcripts** and surface them via `memory_search`.
|
||||
This is gated behind an experimental flag.
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ----------------------------- | ---------- | ------------ | --------------------------------------- |
|
||||
| `experimental.sessionMemory` | `boolean` | `false` | Enable session indexing |
|
||||
| `sources` | `string[]` | `["memory"]` | Add `"sessions"` to include transcripts |
|
||||
| `sync.sessions.deltaBytes` | `number` | `100000` | Byte threshold for reindex |
|
||||
| `sync.sessions.deltaMessages` | `number` | `50` | Message threshold for reindex |
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
experimental: { sessionMemory: true },
|
||||
sources: ["memory", "sessions"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Session indexing is opt-in and runs asynchronously. Results can be slightly
|
||||
stale. Session logs live on disk, so treat filesystem access as the trust
|
||||
boundary.
|
||||
Notes:
|
||||
|
||||
---
|
||||
- Session indexing is **opt-in** (off by default).
|
||||
- Session updates are debounced and **indexed asynchronously** once they cross delta thresholds (best-effort).
|
||||
- `memory_search` never blocks on indexing; results can be slightly stale until background sync finishes.
|
||||
- Results still include snippets only; `memory_get` remains limited to memory files.
|
||||
- Session indexing is isolated per agent (only that agent's session logs are indexed).
|
||||
- Session logs live on disk (`~/.openclaw/agents/<agentId>/sessions/*.jsonl`). Any process/user with filesystem access can read them, so treat disk access as the trust boundary. For stricter isolation, run agents under separate OS users or hosts.
|
||||
|
||||
Delta thresholds (defaults shown):
|
||||
|
||||
```json5
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
sync: {
|
||||
sessions: {
|
||||
deltaBytes: 100000, // ~100 KB
|
||||
deltaMessages: 50 // JSONL lines
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## SQLite vector acceleration (sqlite-vec)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ---------------------------- | --------- | ------- | --------------------------------- |
|
||||
| `store.vector.enabled` | `boolean` | `true` | Use sqlite-vec for vector queries |
|
||||
| `store.vector.extensionPath` | `string` | bundled | Override sqlite-vec path |
|
||||
When the sqlite-vec extension is available, OpenClaw stores embeddings in a
|
||||
SQLite virtual table (`vec0`) and performs vector distance queries in the
|
||||
database. This keeps search fast without loading every embedding into JS.
|
||||
|
||||
When sqlite-vec is unavailable, OpenClaw falls back to in-process cosine
|
||||
similarity automatically.
|
||||
|
||||
---
|
||||
|
||||
## Index storage
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| --------------------- | -------- | ------------------------------------- | ------------------------------------------- |
|
||||
| `store.path` | `string` | `~/.openclaw/memory/{agentId}.sqlite` | Index location (supports `{agentId}` token) |
|
||||
| `store.fts.tokenizer` | `string` | `unicode61` | FTS5 tokenizer (`unicode61` or `trigram`) |
|
||||
|
||||
---
|
||||
|
||||
## QMD backend config
|
||||
|
||||
Set `memory.backend = "qmd"` to enable. All QMD settings live under
|
||||
`memory.qmd`:
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------------ | --------- | -------- | -------------------------------------------- |
|
||||
| `command` | `string` | `qmd` | QMD executable path |
|
||||
| `searchMode` | `string` | `search` | Search command: `search`, `vsearch`, `query` |
|
||||
| `includeDefaultMemory` | `boolean` | `true` | Auto-index `MEMORY.md` + `memory/**/*.md` |
|
||||
| `paths[]` | `array` | -- | Extra paths: `{ name, path, pattern? }` |
|
||||
| `sessions.enabled` | `boolean` | `false` | Index session transcripts |
|
||||
| `sessions.retentionDays` | `number` | -- | Transcript retention |
|
||||
| `sessions.exportDir` | `string` | -- | Export directory |
|
||||
|
||||
### Update schedule
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------------- | --------- | ------- | ------------------------------------- |
|
||||
| `update.interval` | `string` | `5m` | Refresh interval |
|
||||
| `update.debounceMs` | `number` | `15000` | Debounce file changes |
|
||||
| `update.onBoot` | `boolean` | `true` | Refresh on startup |
|
||||
| `update.waitForBootSync` | `boolean` | `false` | Block startup until refresh completes |
|
||||
| `update.embedInterval` | `string` | -- | Separate embed cadence |
|
||||
| `update.commandTimeoutMs` | `number` | -- | Timeout for QMD commands |
|
||||
|
||||
### Limits
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------------- | -------- | ------- | -------------------------- |
|
||||
| `limits.maxResults` | `number` | `6` | Max search results |
|
||||
| `limits.maxSnippetChars` | `number` | -- | Clamp snippet length |
|
||||
| `limits.maxInjectedChars` | `number` | -- | Clamp total injected chars |
|
||||
| `limits.timeoutMs` | `number` | `4000` | Search timeout |
|
||||
|
||||
### Scope
|
||||
|
||||
Controls which sessions can receive QMD search results. Same schema as
|
||||
[`session.sendPolicy`](/gateway/configuration-reference#session):
|
||||
Configuration (optional):
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
qmd: {
|
||||
scope: {
|
||||
default: "deny",
|
||||
rules: [{ action: "allow", match: { chatType: "direct" } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
store: {
|
||||
vector: {
|
||||
enabled: true,
|
||||
extensionPath: "/path/to/sqlite-vec"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Default is DM-only. `match.keyPrefix` matches the normalized session key;
|
||||
`match.rawKeyPrefix` matches the raw key including `agent:<id>:`.
|
||||
Notes:
|
||||
|
||||
### Citations
|
||||
- `enabled` defaults to true; when disabled, search falls back to in-process
|
||||
cosine similarity over stored embeddings.
|
||||
- If the sqlite-vec extension is missing or fails to load, OpenClaw logs the
|
||||
error and continues with the JS fallback (no vector table).
|
||||
- `extensionPath` overrides the bundled sqlite-vec path (useful for custom builds
|
||||
or non-standard install locations).
|
||||
|
||||
`memory.citations` applies to all backends:
|
||||
## Local embedding auto-download
|
||||
|
||||
| Value | Behavior |
|
||||
| ---------------- | --------------------------------------------------- |
|
||||
| `auto` (default) | Include `Source: <path#line>` footer in snippets |
|
||||
| `on` | Always include footer |
|
||||
| `off` | Omit footer (path still passed to agent internally) |
|
||||
- Default local embedding model: `hf:ggml-org/embeddinggemma-300m-qat-q8_0-GGUF/embeddinggemma-300m-qat-Q8_0.gguf` (~0.6 GB).
|
||||
- When `memorySearch.provider = "local"`, `node-llama-cpp` resolves `modelPath`; if the GGUF is missing it **auto-downloads** to the cache (or `local.modelCacheDir` if set), then loads it. Downloads resume on retry.
|
||||
- Native build requirement: run `pnpm approve-builds`, pick `node-llama-cpp`, then `pnpm rebuild node-llama-cpp`.
|
||||
- Fallback: if local setup fails and `memorySearch.fallback = "openai"`, we automatically switch to remote embeddings (`openai/text-embedding-3-small` unless overridden) and record the reason.
|
||||
|
||||
### Full QMD example
|
||||
## Custom OpenAI-compatible endpoint example
|
||||
|
||||
```json5
|
||||
{
|
||||
memory: {
|
||||
backend: "qmd",
|
||||
citations: "auto",
|
||||
qmd: {
|
||||
includeDefaultMemory: true,
|
||||
update: { interval: "5m", debounceMs: 15000 },
|
||||
limits: { maxResults: 6, timeoutMs: 4000 },
|
||||
scope: {
|
||||
default: "deny",
|
||||
rules: [{ action: "allow", match: { chatType: "direct" } }],
|
||||
},
|
||||
paths: [{ name: "docs", path: "~/notes", pattern: "**/*.md" }],
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: "openai",
|
||||
model: "text-embedding-3-small",
|
||||
remote: {
|
||||
baseUrl: "https://api.example.com/v1/",
|
||||
apiKey: "YOUR_REMOTE_API_KEY",
|
||||
headers: {
|
||||
"X-Organization": "org-id",
|
||||
"X-Project": "project-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `remote.*` takes precedence over `models.providers.openai.*`.
|
||||
- `remote.headers` merge with OpenAI headers; remote wins on key conflicts. Omit `remote.headers` to use the OpenAI defaults.
|
||||
|
||||
@@ -23,8 +23,6 @@ If you want a higher-level overview first, start with:
|
||||
|
||||
- [/concepts/session](/concepts/session)
|
||||
- [/concepts/compaction](/concepts/compaction)
|
||||
- [/concepts/memory](/concepts/memory)
|
||||
- [/concepts/memory-search](/concepts/memory-search)
|
||||
- [/concepts/session-pruning](/concepts/session-pruning)
|
||||
- [/reference/transcript-hygiene](/reference/transcript-hygiene)
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ make <target>
|
||||
|
||||
See also: `docs/gateway-exposure-matrix.md` in the models repo.
|
||||
|
||||
### Node exec pipeline (highest-risk capability)
|
||||
### Nodes.run pipeline (highest-risk capability)
|
||||
|
||||
**Claim:** `exec host=node` requires (a) node command allowlist plus declared commands and (b) live approval when configured; approvals are tokenized to prevent replay (in the model).
|
||||
**Claim:** `nodes.run` requires (a) node command allowlist plus declared commands and (b) live approval when configured; approvals are tokenized to prevent replay (in the model).
|
||||
|
||||
- Green runs:
|
||||
- `make nodes-pipeline`
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"id": "my-plugin",
|
||||
"name": "My Plugin",
|
||||
"description": "Adds a custom tool to OpenClaw",
|
||||
"configSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "@myorg/openclaw-my-plugin",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.3.24-beta.2",
|
||||
"minGatewayVersion": "2026.3.24-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.3.24-beta.2",
|
||||
"pluginSdkVersion": "2026.3.24-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -672,44 +672,6 @@ Notes:
|
||||
|
||||
See [Plugins](/tools/plugin).
|
||||
|
||||
### Automatic dependency install
|
||||
|
||||
When you install OpenClaw globally with `npm install -g openclaw`, the acpx
|
||||
runtime dependencies (platform-specific binaries) are installed automatically
|
||||
via a postinstall hook. If the automatic install fails, the gateway still starts
|
||||
normally and reports the missing dependency through `openclaw acp doctor`.
|
||||
|
||||
### Plugin tools MCP bridge
|
||||
|
||||
By default, ACPX sessions do **not** expose OpenClaw plugin-registered tools to
|
||||
the ACP harness.
|
||||
|
||||
If you want ACP agents such as Codex or Claude Code to call installed
|
||||
OpenClaw plugin tools such as memory recall/store, enable the dedicated bridge:
|
||||
|
||||
```bash
|
||||
openclaw config set plugins.entries.acpx.config.pluginToolsMcpBridge true
|
||||
```
|
||||
|
||||
What this does:
|
||||
|
||||
- Injects a built-in MCP server named `openclaw-plugin-tools` into ACPX session
|
||||
bootstrap.
|
||||
- Exposes plugin tools already registered by installed and enabled OpenClaw
|
||||
plugins.
|
||||
- Keeps the feature explicit and default-off.
|
||||
|
||||
Security and trust notes:
|
||||
|
||||
- This expands the ACP harness tool surface.
|
||||
- ACP agents get access only to plugin tools already active in the gateway.
|
||||
- Treat this as the same trust boundary as letting those plugins execute in
|
||||
OpenClaw itself.
|
||||
- Review installed plugins before enabling it.
|
||||
|
||||
Custom `mcpServers` still work as before. The built-in plugin-tools bridge is an
|
||||
additional opt-in convenience, not a replacement for generic MCP server config.
|
||||
|
||||
## Permission configuration
|
||||
|
||||
ACP sessions run non-interactively — there is no TTY to approve or deny file-write and shell-exec permission prompts. The acpx plugin provides two config keys that control how permissions are handled:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
summary: "ClawHub guide: public registry, native OpenClaw install flows, and ClawHub CLI workflows"
|
||||
read_when:
|
||||
- Introducing ClawHub to new users
|
||||
- Installing, searching, or publishing skills or plugins
|
||||
- Installing, searching, or publishing skills
|
||||
- Explaining ClawHub CLI flags and sync behavior
|
||||
title: "ClawHub"
|
||||
---
|
||||
@@ -46,7 +46,7 @@ metadata so later `update` calls can stay on ClawHub.
|
||||
|
||||
## What ClawHub is
|
||||
|
||||
- A public registry for OpenClaw skills and plugins.
|
||||
- A public registry for OpenClaw skills.
|
||||
- A versioned store of skill bundles and metadata.
|
||||
- A discovery surface for search, tags, and usage signals.
|
||||
|
||||
@@ -201,23 +201,15 @@ List:
|
||||
|
||||
- `clawhub list` (reads `.clawhub/lock.json`)
|
||||
|
||||
Publish skills:
|
||||
Publish:
|
||||
|
||||
- `clawhub skill publish <path>`
|
||||
- `clawhub publish <path>`
|
||||
- `--slug <slug>`: Skill slug.
|
||||
- `--name <name>`: Display name.
|
||||
- `--version <version>`: Semver version.
|
||||
- `--changelog <text>`: Changelog text (can be empty).
|
||||
- `--tags <tags>`: Comma-separated tags (default: `latest`).
|
||||
|
||||
Publish plugins:
|
||||
|
||||
- `clawhub package publish <source>`
|
||||
- `<source>` can be a local folder, `owner/repo`, `owner/repo@ref`, or a GitHub URL.
|
||||
- `--dry-run`: Build the exact publish plan without uploading anything.
|
||||
- `--json`: Emit machine-readable output for CI.
|
||||
- `--source-repo`, `--source-commit`, `--source-ref`: Optional overrides when auto-detection is not enough.
|
||||
|
||||
Delete/undelete (owner/admin only):
|
||||
|
||||
- `clawhub delete <slug> --yes`
|
||||
@@ -259,7 +251,7 @@ clawhub update --all
|
||||
For a single skill folder:
|
||||
|
||||
```bash
|
||||
clawhub skill publish ./my-skill --slug my-skill --name "My Skill" --version 1.0.0 --tags latest
|
||||
clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.0.0 --tags latest
|
||||
```
|
||||
|
||||
To scan and back up many skills at once:
|
||||
@@ -268,36 +260,6 @@ To scan and back up many skills at once:
|
||||
clawhub sync --all
|
||||
```
|
||||
|
||||
### Publish a plugin from GitHub
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/your-plugin --dry-run
|
||||
clawhub package publish your-org/your-plugin
|
||||
clawhub package publish your-org/your-plugin@v1.0.0
|
||||
clawhub package publish https://github.com/your-org/your-plugin
|
||||
```
|
||||
|
||||
Code plugins must include the required OpenClaw metadata in `package.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@myorg/openclaw-my-plugin",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"openclaw": {
|
||||
"extensions": ["./index.ts"],
|
||||
"compat": {
|
||||
"pluginApi": ">=2026.3.24-beta.2",
|
||||
"minGatewayVersion": "2026.3.24-beta.2"
|
||||
},
|
||||
"build": {
|
||||
"openclawVersion": "2026.3.24-beta.2",
|
||||
"pluginSdkVersion": "2026.3.24-beta.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced details (technical)
|
||||
|
||||
### Versioning and tags
|
||||
|
||||
@@ -315,15 +315,6 @@ When approvals are required, the exec tool returns immediately with an approval
|
||||
correlate later system events (`Exec finished` / `Exec denied`). If no decision arrives before the
|
||||
timeout, the request is treated as an approval timeout and surfaced as a denial reason.
|
||||
|
||||
### Followup delivery behavior
|
||||
|
||||
After an approved async exec finishes, OpenClaw sends a followup `agent` turn to the same session.
|
||||
|
||||
- If a valid external delivery target exists (deliverable channel plus target `to`), followup delivery uses that channel.
|
||||
- In webchat-only or internal-session flows with no external target, followup delivery stays session-only (`deliver: false`).
|
||||
- If a caller explicitly requests strict external delivery with no resolvable external channel, the request fails with `INVALID_REQUEST`.
|
||||
- If `bestEffortDeliver` is enabled and no external channel can be resolved, delivery is downgraded to session-only instead of failing.
|
||||
|
||||
The confirmation dialog includes:
|
||||
|
||||
- command + args
|
||||
@@ -396,43 +387,24 @@ independent config under `approvals.plugin`. Enabling or disabling one does not
|
||||
The config shape is identical to `approvals.exec`: `enabled`, `mode`, `agentFilter`,
|
||||
`sessionFilter`, and `targets` work the same way.
|
||||
|
||||
Channels that support shared interactive replies render the same approval buttons for both exec and
|
||||
plugin approvals. Channels without shared interactive UI fall back to plain text with `/approve`
|
||||
instructions.
|
||||
Channels that support interactive exec approval buttons (such as Telegram) also render buttons for
|
||||
plugin approvals. Channels without adapter support fall back to plain text with `/approve` instructions.
|
||||
|
||||
### Same-chat approvals on any channel
|
||||
### Built-in chat approval clients
|
||||
|
||||
When an exec or plugin approval request originates from a deliverable chat surface, the same chat
|
||||
can now approve it with `/approve` by default. This applies to channels such as Slack, Matrix, and
|
||||
Microsoft Teams in addition to the existing Web UI and terminal UI flows.
|
||||
|
||||
This shared text-command path uses the normal channel auth model for that conversation. If the
|
||||
originating chat can already send commands and receive replies, approval requests no longer need a
|
||||
separate native delivery adapter just to stay pending.
|
||||
|
||||
Discord and Telegram also support same-chat `/approve`, but those channels still use their
|
||||
resolved approver list for authorization even when native approval delivery is disabled.
|
||||
|
||||
### Native approval delivery
|
||||
|
||||
Discord and Telegram can also act as native approval-delivery adapters with channel-specific config.
|
||||
Discord and Telegram can also act as explicit exec approval clients with channel-specific config.
|
||||
|
||||
- Discord: `channels.discord.execApprovals.*`
|
||||
- Telegram: `channels.telegram.execApprovals.*`
|
||||
|
||||
These native delivery adapters are opt-in. They add DM routing and channel fanout on top of the
|
||||
shared same-chat `/approve` flow and the shared approval buttons.
|
||||
These clients are opt-in. If a channel does not have exec approvals enabled, OpenClaw does not treat
|
||||
that channel as an approval surface just because the conversation happened there.
|
||||
|
||||
Shared behavior:
|
||||
|
||||
- Slack, Matrix, Microsoft Teams, and similar deliverable chats use the normal channel auth model
|
||||
for same-chat `/approve`
|
||||
- for Discord and Telegram, only resolved approvers can approve or deny
|
||||
- Discord and Telegram approvers can be explicit (`execApprovals.approvers`) or inferred from existing owner config (`allowFrom`, plus direct-message `defaultTo` where supported)
|
||||
- only configured approvers can approve or deny
|
||||
- the requester does not need to be an approver
|
||||
- the originating chat can approve directly with `/approve` when that chat already supports commands and replies
|
||||
- when channel delivery is enabled, approval prompts include the command text
|
||||
- pending exec approvals expire after 30 minutes by default
|
||||
- if no operator UI or configured approval client can accept the request, the prompt falls back to `askFallback`
|
||||
|
||||
Telegram defaults to approver DMs (`target: "dm"`). You can switch to `channel` or `both` when you
|
||||
@@ -471,14 +443,6 @@ These are posted to the agent’s session after the node reports the event.
|
||||
Gateway-host exec approvals emit the same lifecycle events when the command finishes (and optionally when running longer than the threshold).
|
||||
Approval-gated execs reuse the approval id as the `runId` in these messages for easy correlation.
|
||||
|
||||
## Denied approval behavior
|
||||
|
||||
When an async exec approval is denied, OpenClaw prevents the agent from reusing
|
||||
output from any earlier run of the same command in the session. The denial reason
|
||||
is passed with explicit guidance that no command output is available, which stops
|
||||
the agent from claiming there is new output or repeating the denied command with
|
||||
stale results from a prior successful run.
|
||||
|
||||
## Implications
|
||||
|
||||
- **full** is powerful; prefer allowlists when possible.
|
||||
|
||||
@@ -21,7 +21,7 @@ Background sessions are scoped per agent; `process` only sees sessions from the
|
||||
- `background` (bool): background immediately
|
||||
- `timeout` (seconds, default 1800): kill on expiry
|
||||
- `pty` (bool): run in a pseudo-terminal when available (TTY-only CLIs, coding agents, terminal UIs)
|
||||
- `host` (`auto | sandbox | gateway | node`): where to execute
|
||||
- `host` (`sandbox | gateway | node`): where to execute
|
||||
- `security` (`deny | allowlist | full`): enforcement mode for `gateway`/`node`
|
||||
- `ask` (`off | on-miss | always`): approval prompts for `gateway`/`node`
|
||||
- `node` (string): node id/name for `host=node`
|
||||
@@ -29,12 +29,11 @@ Background sessions are scoped per agent; `process` only sees sessions from the
|
||||
|
||||
Notes:
|
||||
|
||||
- `host` defaults to `auto`: sandbox when sandbox runtime is active for the session, otherwise gateway.
|
||||
- `host` defaults to `sandbox`.
|
||||
- `elevated` forces `host=gateway`; it is only available when elevated access is enabled for the current session/provider.
|
||||
- `gateway`/`node` approvals are controlled by `~/.openclaw/exec-approvals.json`.
|
||||
- `node` requires a paired node (companion app or headless node host).
|
||||
- If multiple nodes are available, set `exec.node` or `tools.exec.node` to select one.
|
||||
- `exec host=node` is the only shell-execution path for nodes; the legacy `nodes.run` wrapper has been removed.
|
||||
- On non-Windows hosts, exec uses `SHELL` when set; if `SHELL` is `fish`, it prefers `bash` (or `sh`)
|
||||
from `PATH` to avoid fish-incompatible scripts, then falls back to `SHELL` if neither exists.
|
||||
- On Windows hosts, exec prefers PowerShell 7 (`pwsh`) discovery (Program Files, ProgramW6432, then PATH),
|
||||
@@ -42,8 +41,8 @@ Notes:
|
||||
- Host execution (`gateway`/`node`) rejects `env.PATH` and loader overrides (`LD_*`/`DYLD_*`) to
|
||||
prevent binary hijacking or injected code.
|
||||
- OpenClaw sets `OPENCLAW_SHELL=exec` in the spawned command environment (including PTY and sandbox execution) so shell/profile rules can detect exec-tool context.
|
||||
- Important: sandboxing is **off by default**. If sandboxing is off, implicit `host=auto`
|
||||
resolves to `gateway`. Explicit `host=sandbox` still fails closed instead of silently
|
||||
- Important: sandboxing is **off by default**. If sandboxing is off and exec resolves to
|
||||
`host=sandbox` (including the implicit default), exec fails closed instead of silently
|
||||
running on the gateway host. Enable sandboxing or use `host=gateway` with approvals.
|
||||
- Script preflight checks (for common Python/Node shell-syntax mistakes) only inspect files inside the
|
||||
effective `workdir` boundary. If a script path resolves outside `workdir`, preflight is skipped for
|
||||
@@ -53,7 +52,7 @@ Notes:
|
||||
|
||||
- `tools.exec.notifyOnExit` (default: true): when true, backgrounded exec sessions enqueue a system event and request a heartbeat on exit.
|
||||
- `tools.exec.approvalRunningNoticeMs` (default: 10000): emit a single “running” notice when an approval-gated exec runs longer than this (0 disables).
|
||||
- `tools.exec.host` (default: `auto`; resolves to `sandbox` when sandbox runtime is active, `gateway` otherwise)
|
||||
- `tools.exec.host` (default: `sandbox`)
|
||||
- `tools.exec.security` (default: `deny` for sandbox, `allowlist` for gateway + node when unset)
|
||||
- `tools.exec.ask` (default: `on-miss`)
|
||||
- `tools.exec.node` (default: unset)
|
||||
@@ -105,7 +104,7 @@ Send `/exec` with no arguments to show the current values.
|
||||
Example:
|
||||
|
||||
```
|
||||
/exec host=auto security=allowlist ask=on-miss node=mac-1
|
||||
/exec host=gateway security=allowlist ask=on-miss node=mac-1
|
||||
```
|
||||
|
||||
## Authorization model
|
||||
|
||||
@@ -263,8 +263,6 @@ Hook guard behavior for typed lifecycle hooks:
|
||||
|
||||
- `before_tool_call`: `{ block: true }` is terminal; lower-priority handlers are skipped.
|
||||
- `before_tool_call`: `{ block: false }` is a no-op and does not clear an earlier block.
|
||||
- `before_install`: `{ block: true }` is terminal; lower-priority handlers are skipped.
|
||||
- `before_install`: `{ block: false }` is a no-op and does not clear an earlier block.
|
||||
- `message_sending`: `{ cancel: true }` is terminal; lower-priority handlers are skipped.
|
||||
- `message_sending`: `{ cancel: false }` is a no-op and does not clear an earlier cancel.
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ Skills can also refresh mid-session when the skills watcher is enabled or when a
|
||||
|
||||
## Remote macOS nodes (Linux gateway)
|
||||
|
||||
If the Gateway is running on Linux but a **macOS node** is connected **with `system.run` allowed** (Exec approvals security not set to `deny`), OpenClaw can treat macOS-only skills as eligible when the required binaries are present on that node. The agent should execute those skills via the `exec` tool with `host=node`.
|
||||
If the Gateway is running on Linux but a **macOS node** is connected **with `system.run` allowed** (Exec approvals security not set to `deny`), OpenClaw can treat macOS-only skills as eligible when the required binaries are present on that node. The agent should execute those skills via the `nodes` tool (typically `nodes.run`).
|
||||
|
||||
This relies on the node reporting its command support and on a bin probe via `system.run`. If the macOS node goes offline later, the skills remain visible; invocations may fail until the node reconnects.
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ Text + native (when enabled):
|
||||
- `/verbose on|full|off` (alias: `/v`)
|
||||
- `/reasoning on|off|stream` (alias: `/reason`; when on, sends a separate message prefixed `Reasoning:`; `stream` = Telegram draft only)
|
||||
- `/elevated on|off|ask|full` (alias: `/elev`; `full` skips exec approvals)
|
||||
- `/exec host=<auto|sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>` (send `/exec` to show current)
|
||||
- `/exec host=<sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>` (send `/exec` to show current)
|
||||
- `/model <name>` (alias: `/models`; or `/<alias>` from `agents.defaults.models.*.alias`)
|
||||
- `/queue <mode>` (plus options like `debounce:2s cap:25 drop:summarize`; send `/queue` to see current settings)
|
||||
- `/bash <command>` (host-only; alias for `! <command>`; requires `commands.bash: true` + `tools.elevated` allowlists)
|
||||
@@ -143,7 +143,6 @@ Notes:
|
||||
- ACP command reference and runtime behavior: [ACP Agents](/tools/acp-agents).
|
||||
- `/verbose` is meant for debugging and extra visibility; keep it **off** in normal use.
|
||||
- `/fast on|off` persists a session override. Use the Sessions UI `inherit` option to clear it and fall back to config defaults.
|
||||
- `/fast` is provider-specific: OpenAI/OpenAI Codex map it to `service_tier=priority` on native Responses endpoints, while direct public Anthropic requests, including OAuth-authenticated traffic sent to `api.anthropic.com`, map it to `service_tier=auto` or `standard_only`. See [OpenAI](/providers/openai) and [Anthropic](/providers/anthropic).
|
||||
- Tool failure summaries are still shown when relevant, but detailed failure text is only included when `/verbose` is `on` or `full`.
|
||||
- `/reasoning` (and `/verbose`) are risky in group settings: they may reveal internal reasoning or tool output you did not intend to expose. Prefer leaving them off, especially in group chats.
|
||||
- **Fast path:** command-only messages from allowlisted senders are handled immediately (bypass queue + model).
|
||||
|
||||
@@ -54,10 +54,10 @@ title: "Thinking Levels"
|
||||
3. Per-agent default (`agents.list[].fastModeDefault`)
|
||||
4. Per-model config: `agents.defaults.models["<provider>/<model>"].params.fastMode`
|
||||
5. Fallback: `off`
|
||||
- For `openai/*`, fast mode maps to OpenAI priority processing by sending `service_tier=priority` on supported Responses requests.
|
||||
- For `openai-codex/*`, fast mode sends the same `service_tier=priority` flag on Codex Responses. OpenClaw keeps one shared `/fast` toggle across both auth paths.
|
||||
- For direct public `anthropic/*` requests, including OAuth-authenticated traffic sent to `api.anthropic.com`, fast mode maps to Anthropic service tiers: `/fast on` sets `service_tier=auto`, `/fast off` sets `service_tier=standard_only`.
|
||||
- Explicit Anthropic `serviceTier` / `service_tier` model params override the fast-mode default when both are set. OpenClaw still skips Anthropic service-tier injection for non-Anthropic proxy base URLs.
|
||||
- For `openai/*`, fast mode applies the OpenAI fast profile: `service_tier=priority` when supported, plus low reasoning effort and low text verbosity.
|
||||
- For `openai-codex/*`, fast mode applies the same low-latency profile on Codex Responses. OpenClaw keeps one shared `/fast` toggle across both auth paths.
|
||||
- For direct `anthropic/*` API-key requests, fast mode maps to Anthropic service tiers: `/fast on` sets `service_tier=auto`, `/fast off` sets `service_tier=standard_only`.
|
||||
- Anthropic fast mode is API-key only. OpenClaw skips Anthropic service-tier injection for Claude setup-token / OAuth auth and for non-Anthropic proxy base URLs.
|
||||
|
||||
## Verbose directives (/verbose or /v)
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ Prefer localhost, Tailscale Serve, or an SSH tunnel.
|
||||
- If `gateway.auth.token` is configured as a SecretRef and is unresolved in your current shell, `openclaw dashboard` still prints a non-tokenized URL plus actionable auth setup guidance.
|
||||
- **Not localhost**: use Tailscale Serve (tokenless for Control UI/WebSocket if `gateway.auth.allowTailscale: true`, assumes trusted gateway host; HTTP APIs still need token/password), tailnet bind with a token, or an SSH tunnel. See [Web surfaces](/web).
|
||||
|
||||
<a id="if-you-see-unauthorized-1008"></a>
|
||||
|
||||
## If you see "unauthorized" / 1008
|
||||
|
||||
- Ensure the gateway is reachable (local: `openclaw status`; remote: SSH tunnel `ssh -N -L 18789:127.0.0.1:18789 user@host` then open `http://127.0.0.1:18789/`).
|
||||
|
||||
@@ -1048,6 +1048,7 @@ heartbeat 控制(Gateway 网关 RPC)。
|
||||
- `nodes reject <requestId>`
|
||||
- `nodes rename --node <id|name|ip> --name <displayName>`
|
||||
- `nodes invoke --node <id|name|ip> --command <command> [--params <json>] [--invoke-timeout <ms>] [--idempotency-key <key>]`
|
||||
- `nodes run --node <id|name|ip> [--cwd <path>] [--env KEY=VAL] [--command-timeout <ms>] [--needs-screen-recording] [--invoke-timeout <ms>] <command...>`(mac 节点或无头 node host)
|
||||
- `nodes notify --node <id|name|ip> [--title <text>] [--body <text>] [--sound <name>] [--priority <passive|active|timeSensitive>] [--delivery <system|overlay|auto>] [--invoke-timeout <ms>]`(仅 mac)
|
||||
|
||||
相机:
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
---
|
||||
read_when:
|
||||
- 你正在管理已配对节点(相机、屏幕、画布)
|
||||
- 你正在管理已配对的节点(摄像头、屏幕、画布)
|
||||
- 你需要批准请求或调用节点命令
|
||||
summary: "`openclaw nodes` 的 CLI 参考(列表/状态/批准/调用、相机/画布/屏幕)"
|
||||
title: 节点
|
||||
summary: "`openclaw nodes` 的 CLI 参考(列表/状态/批准/调用,摄像头/画布/屏幕)"
|
||||
title: nodes
|
||||
x-i18n:
|
||||
generated_at: "2026-03-29T23:52:03Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 91d16fba3c12c0cce5e585a7f5072a831de3e10928b2c34bdbf126b3b718e0c3
|
||||
generated_at: "2026-02-03T10:04:26Z"
|
||||
model: claude-opus-4-5
|
||||
provider: pi
|
||||
source_hash: 23da6efdd659a82dbbc4afd18eb4ab1020a2892f69c28d610f912c8a799f734c
|
||||
source_path: cli/nodes.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# `openclaw nodes`
|
||||
|
||||
管理已配对节点(设备)并调用节点能力。
|
||||
管理已配对的节点(设备)并调用节点功能。
|
||||
|
||||
相关内容:
|
||||
|
||||
- 节点概览:[节点](/nodes)
|
||||
- 相机:[相机节点](/nodes/camera)
|
||||
- 节点概述:[节点](/nodes)
|
||||
- 摄像头:[摄像头节点](/nodes/camera)
|
||||
- 图像:[图像节点](/nodes/images)
|
||||
|
||||
常用选项:
|
||||
通用选项:
|
||||
|
||||
- `--url`, `--token`, `--timeout`, `--json`
|
||||
- `--url`、`--token`、`--timeout`、`--json`
|
||||
|
||||
## 常用命令
|
||||
|
||||
@@ -40,23 +40,41 @@ openclaw nodes status --connected
|
||||
openclaw nodes status --last-connected 24h
|
||||
```
|
||||
|
||||
`nodes list` 会打印待处理/已配对表格。已配对行包含距离最近一次连接的时间(上次连接)。
|
||||
使用 `--connected` 仅显示当前已连接的节点。使用 `--last-connected <duration>` 将
|
||||
结果筛选为在某个时长内连接过的节点(例如 `24h`、`7d`)。
|
||||
`nodes list` 打印待处理/已配对表格。已配对行包含最近连接时长(Last Connect)。
|
||||
使用 `--connected` 仅显示当前已连接的节点。使用 `--last-connected <duration>`
|
||||
筛选在指定时间段内连接过的节点(例如 `24h`、`7d`)。
|
||||
|
||||
## 调用
|
||||
## 调用 / 运行
|
||||
|
||||
```bash
|
||||
openclaw nodes invoke --node <id|name|ip> --command <command> --params <json>
|
||||
openclaw nodes run --node <id|name|ip> <command...>
|
||||
openclaw nodes run --raw "git status"
|
||||
openclaw nodes run --agent main --node <id|name|ip> --raw "git status"
|
||||
```
|
||||
|
||||
调用标志:
|
||||
|
||||
- `--params <json>`:JSON 对象字符串(默认 `{}`)。
|
||||
- `--invoke-timeout <ms>`:节点调用超时时间(默认 `15000`)。
|
||||
- `--invoke-timeout <ms>`:节点调用超时(默认 `15000`)。
|
||||
- `--idempotency-key <key>`:可选的幂等键。
|
||||
- 此处不允许使用 `system.run` 和 `system.run.prepare`;如需在命令行中执行命令,请使用带 `host=node` 的 `exec` 工具。
|
||||
|
||||
要在节点上以命令行方式执行命令,请使用带 `host=node` 的 `exec` 工具,而不是 `openclaw nodes run`。
|
||||
`nodes` CLI 现在以能力为中心:通过 `nodes invoke` 直接进行远程过程调用,以及配对、相机、
|
||||
屏幕、位置、画布和通知。
|
||||
### Exec 风格默认值
|
||||
|
||||
`nodes run` 与模型的 exec 行为一致(默认值 + 审批):
|
||||
|
||||
- 读取 `tools.exec.*`(以及 `agents.list[].tools.exec.*` 覆盖)。
|
||||
- 在调用 `system.run` 前使用 exec 审批(`exec.approval.request`)。
|
||||
- 当设置了 `tools.exec.node` 时可省略 `--node`。
|
||||
- 需要支持 `system.run` 的节点(macOS 配套应用或无头节点主机)。
|
||||
|
||||
标志:
|
||||
|
||||
- `--cwd <path>`:工作目录。
|
||||
- `--env <key=val>`:环境变量覆盖(可重复)。
|
||||
- `--command-timeout <ms>`:命令超时。
|
||||
- `--invoke-timeout <ms>`:节点调用超时(默认 `30000`)。
|
||||
- `--needs-screen-recording`:要求屏幕录制权限。
|
||||
- `--raw <command>`:运行 shell 字符串(`/bin/sh -lc` 或 `cmd.exe /c`)。
|
||||
- `--agent <id>`:智能体范围的审批/白名单(默认为已配置的智能体)。
|
||||
- `--ask <off|on-miss|always>`、`--security <deny|allowlist|full>`:覆盖选项。
|
||||
|
||||
@@ -275,15 +275,13 @@ macOS 节点暴露 `system.run`、`system.notify` 和 `system.execApprovals.get/
|
||||
示例:
|
||||
|
||||
```bash
|
||||
openclaw nodes run --node <idOrNameOrIp> -- echo "Hello from mac node"
|
||||
openclaw nodes notify --node <idOrNameOrIp> --title "Ping" --body "Gateway ready"
|
||||
openclaw nodes invoke --node <idOrNameOrIp> --command system.which --params '{"name":"git"}'
|
||||
```
|
||||
|
||||
注意事项:
|
||||
|
||||
- `system.run` 在负载中返回 stdout/stderr/退出码。
|
||||
- Shell 执行现在统一走带 `host=node` 的 `exec` 工具;`nodes` 保持为显式节点命令的直接 RPC 表面。
|
||||
- `nodes invoke` 不暴露 `system.run` 或 `system.run.prepare`;这些仅保留在 `exec` 路径上。
|
||||
- `system.notify` 遵守 macOS 应用上的通知权限状态。
|
||||
- `system.run` 支持 `--cwd`、`--env KEY=VAL`、`--command-timeout` 和 `--needs-screen-recording`。
|
||||
- `system.notify` 支持 `--priority <passive|active|timeSensitive>` 和 `--delivery <system|overlay|auto>`。
|
||||
|
||||
@@ -69,9 +69,9 @@ make <target>
|
||||
|
||||
另见:模型仓库中的 `docs/gateway-exposure-matrix.md`。
|
||||
|
||||
### 节点 exec 管道(最高风险能力)
|
||||
### Nodes.run 管道(最高风险能力)
|
||||
|
||||
**声明:** `exec host=node` 需要(a)节点命令允许列表加上声明的命令以及(b)配置时的实时批准;批准被令牌化以防止重放(在模型中)。
|
||||
**声明:** `nodes.run` 需要(a)节点命令允许列表加上声明的命令以及(b)配置时的实时批准;批准被令牌化以防止重放(在模型中)。
|
||||
|
||||
- 绿色运行:
|
||||
- `make nodes-pipeline`
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
read_when:
|
||||
- 向新用户介绍 ClawHub
|
||||
- 安装、搜索或发布 Skills 或插件
|
||||
- 安装、搜索或发布 Skills
|
||||
- 说明 ClawHub CLI 标志和同步行为
|
||||
summary: ClawHub 指南:公共 Skills / 插件注册中心与 CLI 工作流
|
||||
summary: ClawHub 指南:公共 Skills 注册中心 + CLI 工作流
|
||||
title: ClawHub
|
||||
x-i18n:
|
||||
generated_at: "2026-02-01T21:42:32Z"
|
||||
@@ -16,9 +16,9 @@ x-i18n:
|
||||
|
||||
# ClawHub
|
||||
|
||||
ClawHub 是 **OpenClaw 的公共 Skills 与插件注册中心**。你可以在网页应用中浏览资源,也可以使用 CLI 来搜索、安装、更新和发布 Skills / 插件。
|
||||
ClawHub 是 **OpenClaw 的公共 Skills 注册中心**。它是一项免费服务:所有 Skills 都是公开的、开放的,所有人都可以查看、共享和复用。Skills 就是一个包含 `SKILL.md` 文件(以及辅助文本文件)的文件夹。你可以在网页应用中浏览 Skills,也可以使用 CLI 来搜索、安装、更新和发布 Skills。
|
||||
|
||||
网站:[clawhub.ai](https://clawhub.ai)
|
||||
网站:[clawhub.com](https://clawhub.com)
|
||||
|
||||
## 适用人群(新手友好)
|
||||
|
||||
@@ -112,22 +112,15 @@ pnpm add -g clawhub
|
||||
|
||||
- `clawhub list`(读取 `.clawhub/lock.json`)
|
||||
|
||||
发布 Skills:
|
||||
发布:
|
||||
|
||||
- `clawhub skill publish <path>`
|
||||
- `clawhub publish <path>`
|
||||
- `--slug <slug>`:Skills 标识符。
|
||||
- `--name <name>`:显示名称。
|
||||
- `--version <version>`:语义化版本号。
|
||||
- `--changelog <text>`:变更日志文本(可以为空)。
|
||||
- `--tags <tags>`:逗号分隔的标签(默认:`latest`)。
|
||||
|
||||
发布插件:
|
||||
|
||||
- `clawhub package publish <source>`
|
||||
- `<source>` 可以是本地文件夹、`owner/repo`、`owner/repo@ref` 或 GitHub URL。
|
||||
- `--dry-run`:只生成发布计划,不实际上传。
|
||||
- `--json`:为 CI 输出结构化 JSON。
|
||||
|
||||
删除/恢复(仅所有者/管理员):
|
||||
|
||||
- `clawhub delete <slug> --yes`
|
||||
@@ -169,7 +162,7 @@ clawhub update --all
|
||||
对于单个 Skills 文件夹:
|
||||
|
||||
```bash
|
||||
clawhub skill publish ./my-skill --slug my-skill --name "My Skill" --version 1.0.0 --tags latest
|
||||
clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.0.0 --tags latest
|
||||
```
|
||||
|
||||
一次扫描并备份多个 Skills:
|
||||
@@ -178,15 +171,6 @@ clawhub skill publish ./my-skill --slug my-skill --name "My Skill" --version 1.0
|
||||
clawhub sync --all
|
||||
```
|
||||
|
||||
### 从 GitHub 发布插件
|
||||
|
||||
```bash
|
||||
clawhub package publish your-org/your-plugin --dry-run
|
||||
clawhub package publish your-org/your-plugin
|
||||
clawhub package publish your-org/your-plugin@v1.0.0
|
||||
clawhub package publish https://github.com/your-org/your-plugin
|
||||
```
|
||||
|
||||
## 高级详情(技术性)
|
||||
|
||||
### 版本管理和标签
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
---
|
||||
title: Diffs
|
||||
summary: 为智能体提供的只读 diff 查看器和文件渲染器(可选插件工具)
|
||||
read_when:
|
||||
- 你希望智能体将代码或 Markdown 编辑以 diff 形式展示
|
||||
- 你需要可在画布中使用的查看器 URL 或渲染后的 diff 文件
|
||||
- 你需要带安全默认值的受控临时 diff 制品
|
||||
x-i18n:
|
||||
source_path: tools/diffs.md
|
||||
source_hash: a9f0c2da0fe2729a7267a83037101901879a9687da58ad05cba6026145d54ae0
|
||||
provider: openai
|
||||
model: gpt-5.4
|
||||
workflow: 15
|
||||
generated_at: "2026-03-29T23:41:59Z"
|
||||
---
|
||||
|
||||
# Diffs
|
||||
|
||||
`diffs` 是一个可选插件工具,带有简短的内置系统引导和配套技能,可将变更内容转换为供智能体使用的只读 diff 制品。
|
||||
|
||||
它接受以下两种输入之一:
|
||||
|
||||
- `before` 和 `after` 文本
|
||||
- 统一格式的 `patch`
|
||||
|
||||
可返回:
|
||||
|
||||
- 用于画布展示的 Gateway 网关查看器 URL
|
||||
- 用于消息投递的渲染文件路径(PNG 或 PDF)
|
||||
- 在一次调用中同时返回两种输出
|
||||
|
||||
启用后,插件会在系统提示空间中注入简洁的使用引导,同时还提供一个详细技能供智能体在需要更完整指令时使用。
|
||||
|
||||
## 快速开始
|
||||
|
||||
1. 启用插件。
|
||||
2. 使用 `diffs`,设置 `mode: "view"` 以走画布优先流程。
|
||||
3. 使用 `diffs`,设置 `mode: "file"` 以走聊天文件投递流程。
|
||||
4. 使用 `diffs`,设置 `mode: "both"` 以同时获取两种制品。
|
||||
|
||||
## 启用插件
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
diffs: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 禁用内置系统引导
|
||||
|
||||
如果你想保留 `diffs` 工具但禁用其内置系统提示引导,将 `plugins.entries.diffs.hooks.allowPromptInjection` 设为 `false`:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
diffs: {
|
||||
enabled: true,
|
||||
hooks: {
|
||||
allowPromptInjection: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
这会阻止 diffs 插件的 `before_prompt_build` 钩子,同时保留插件、工具和配套技能可用。
|
||||
|
||||
如果你想同时禁用引导和工具,请直接禁用插件。
|
||||
|
||||
## 典型智能体工作流
|
||||
|
||||
1. 智能体调用 `diffs`。
|
||||
2. 智能体读取 `details` 字段。
|
||||
3. 智能体执行以下操作之一:
|
||||
- 用 `canvas present` 打开 `details.viewerUrl`
|
||||
- 用 `message` 工具通过 `path` 或 `filePath` 发送 `details.filePath`
|
||||
- 两者都做
|
||||
|
||||
## 输入示例
|
||||
|
||||
前后文本:
|
||||
|
||||
```json
|
||||
{
|
||||
"before": "# Hello\n\nOne",
|
||||
"after": "# Hello\n\nTwo",
|
||||
"path": "docs/example.md",
|
||||
"mode": "view"
|
||||
}
|
||||
```
|
||||
|
||||
补丁:
|
||||
|
||||
```json
|
||||
{
|
||||
"patch": "diff --git a/src/example.ts b/src/example.ts\n--- a/src/example.ts\n+++ b/src/example.ts\n@@ -1 +1 @@\n-const x = 1;\n+const x = 2;\n",
|
||||
"mode": "both"
|
||||
}
|
||||
```
|
||||
|
||||
## 工具输入参考
|
||||
|
||||
除特别说明外,所有字段均为可选:
|
||||
|
||||
- `before`(`string`):原始文本。省略 `patch` 时需与 `after` 一起提供。
|
||||
- `after`(`string`):更新后文本。省略 `patch` 时需与 `before` 一起提供。
|
||||
- `patch`(`string`):统一 diff 文本。与 `before` 和 `after` 互斥。
|
||||
- `path`(`string`):前后对比模式下的显示文件名。
|
||||
- `lang`(`string`):前后对比模式下的语言覆盖提示。
|
||||
- `title`(`string`):查看器标题覆盖。
|
||||
- `mode`(`"view" | "file" | "both"`):输出模式。默认为插件默认值 `defaults.mode`。
|
||||
已弃用别名:`"image"` 的行为与 `"file"` 相同,出于向后兼容仍然接受。
|
||||
- `theme`(`"light" | "dark"`):查看器主题。默认为插件默认值 `defaults.theme`。
|
||||
- `layout`(`"unified" | "split"`):diff 布局。默认为插件默认值 `defaults.layout`。
|
||||
- `expandUnchanged`(`boolean`):在有完整上下文时展开未变更部分。仅限单次调用选项,不是插件默认值键。
|
||||
- `fileFormat`(`"png" | "pdf"`):渲染文件格式。默认为插件默认值 `defaults.fileFormat`。
|
||||
- `fileQuality`(`"standard" | "hq" | "print"`):PNG 或 PDF 渲染的质量预设。
|
||||
- `fileScale`(`number`):设备缩放覆盖(`1`-`4`)。
|
||||
- `fileMaxWidth`(`number`):最大渲染宽度(CSS 像素,`640`-`2400`)。
|
||||
- `ttlSeconds`(`number`):查看器制品 TTL(秒)。默认 1800,最大 21600。
|
||||
- `baseUrl`(`string`):查看器 URL 来源覆盖。必须为 `http` 或 `https`,不含查询参数或哈希。
|
||||
|
||||
验证和限制:
|
||||
|
||||
- `before` 和 `after` 各最大 512 KiB。
|
||||
- `patch` 最大 2 MiB。
|
||||
- `path` 最大 2048 字节。
|
||||
- `lang` 最大 128 字节。
|
||||
- `title` 最大 1024 字节。
|
||||
- 补丁复杂度上限:最多 128 个文件,总计 120000 行。
|
||||
- `patch` 和 `before` 或 `after` 不能同时使用。
|
||||
- 渲染文件安全限制(适用于 PNG 和 PDF):
|
||||
- `fileQuality: "standard"`:最大 8 MP(8,000,000 渲染像素)。
|
||||
- `fileQuality: "hq"`:最大 14 MP(14,000,000 渲染像素)。
|
||||
- `fileQuality: "print"`:最大 24 MP(24,000,000 渲染像素)。
|
||||
- PDF 还有最多 50 页的限制。
|
||||
|
||||
## 输出详情约定
|
||||
|
||||
工具在 `details` 下返回结构化元数据。
|
||||
|
||||
创建查看器的模式共享字段:
|
||||
|
||||
- `artifactId`
|
||||
- `viewerUrl`
|
||||
- `viewerPath`
|
||||
- `title`
|
||||
- `expiresAt`
|
||||
- `inputKind`
|
||||
- `fileCount`
|
||||
- `mode`
|
||||
- `context`(可用时包含 `agentId`、`sessionId`、`messageChannel`、`agentAccountId`)
|
||||
|
||||
渲染 PNG 或 PDF 时的文件字段:
|
||||
|
||||
- `artifactId`
|
||||
- `expiresAt`
|
||||
- `filePath`
|
||||
- `path`(与 `filePath` 相同,兼容 `message` 工具)
|
||||
- `fileBytes`
|
||||
- `fileFormat`
|
||||
- `fileQuality`
|
||||
- `fileScale`
|
||||
- `fileMaxWidth`
|
||||
|
||||
模式行为汇总:
|
||||
|
||||
- `mode: "view"`:仅查看器字段。
|
||||
- `mode: "file"`:仅文件字段,无查看器制品。
|
||||
- `mode: "both"`:查看器字段加文件字段。如果文件渲染失败,查看器仍然返回并附带 `fileError`。
|
||||
|
||||
## 折叠的未变更部分
|
||||
|
||||
- 查看器可以显示类似 `N unmodified lines` 的行。
|
||||
- 这些行上的展开控件是有条件的,并非每种输入都保证提供。
|
||||
- 当渲染的 diff 具有可展开的上下文数据时会出现展开控件,这在前后对比输入中很常见。
|
||||
- 对于许多统一补丁输入,省略的上下文主体在解析后的补丁块中不可用,因此该行可能没有展开控件。这是预期行为。
|
||||
- `expandUnchanged` 仅在存在可展开上下文时生效。
|
||||
|
||||
## 插件默认值
|
||||
|
||||
在 `~/.openclaw/openclaw.json` 中设置插件全局默认值:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
diffs: {
|
||||
enabled: true,
|
||||
config: {
|
||||
defaults: {
|
||||
fontFamily: "Fira Code",
|
||||
fontSize: 15,
|
||||
lineSpacing: 1.6,
|
||||
layout: "unified",
|
||||
showLineNumbers: true,
|
||||
diffIndicators: "bars",
|
||||
wordWrap: true,
|
||||
background: true,
|
||||
theme: "dark",
|
||||
fileFormat: "png",
|
||||
fileQuality: "standard",
|
||||
fileScale: 2,
|
||||
fileMaxWidth: 960,
|
||||
mode: "both",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
支持的默认值:
|
||||
|
||||
- `fontFamily`
|
||||
- `fontSize`
|
||||
- `lineSpacing`
|
||||
- `layout`
|
||||
- `showLineNumbers`
|
||||
- `diffIndicators`
|
||||
- `wordWrap`
|
||||
- `background`
|
||||
- `theme`
|
||||
- `fileFormat`
|
||||
- `fileQuality`
|
||||
- `fileScale`
|
||||
- `fileMaxWidth`
|
||||
- `mode`
|
||||
|
||||
显式工具参数会覆盖这些默认值。
|
||||
|
||||
## 安全配置
|
||||
|
||||
- `security.allowRemoteViewer`(`boolean`,默认 `false`)
|
||||
- `false`:拒绝非本地回环请求访问查看器路由。
|
||||
- `true`:如果令牌化路径有效,则允许远程查看器。
|
||||
|
||||
示例:
|
||||
|
||||
```json5
|
||||
{
|
||||
plugins: {
|
||||
entries: {
|
||||
diffs: {
|
||||
enabled: true,
|
||||
config: {
|
||||
security: {
|
||||
allowRemoteViewer: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## 制品生命周期和存储
|
||||
|
||||
- 制品存储在临时子目录下:`$TMPDIR/openclaw-diffs`。
|
||||
- 查看器制品元数据包含:
|
||||
- 随机制品 ID(20 个十六进制字符)
|
||||
- 随机令牌(48 个十六进制字符)
|
||||
- `createdAt` 和 `expiresAt`
|
||||
- 存储的 `viewer.html` 路径
|
||||
- 未指定时默认查看器 TTL 为 30 分钟。
|
||||
- 可接受的最大查看器 TTL 为 6 小时。
|
||||
- 清理会在制品创建后择机运行。
|
||||
- 过期的制品会被删除。
|
||||
- 当元数据缺失时,回退清理会移除超过 24 小时的陈旧文件夹。
|
||||
|
||||
## 查看器 URL 和网络行为
|
||||
|
||||
查看器路由:
|
||||
|
||||
- `/plugins/diffs/view/{artifactId}/{token}`
|
||||
|
||||
查看器资源:
|
||||
|
||||
- `/plugins/diffs/assets/viewer.js`
|
||||
- `/plugins/diffs/assets/viewer-runtime.js`
|
||||
|
||||
URL 构建行为:
|
||||
|
||||
- 如果提供了 `baseUrl`,会在严格校验后使用。
|
||||
- 没有 `baseUrl` 时,查看器 URL 默认使用本地回环 `127.0.0.1`。
|
||||
- 如果 Gateway 网关绑定模式为 `custom` 且设置了 `gateway.customBindHost`,则使用该主机。
|
||||
|
||||
`baseUrl` 规则:
|
||||
|
||||
- 必须以 `http://` 或 `https://` 开头。
|
||||
- 查询参数和哈希会被拒绝。
|
||||
- 允许使用来源加可选基础路径。
|
||||
|
||||
## 安全模型
|
||||
|
||||
查看器加固:
|
||||
|
||||
- 默认仅限本地回环。
|
||||
- 带严格 ID 和令牌验证的令牌化查看器路径。
|
||||
- 查看器响应 CSP:
|
||||
- `default-src 'none'`
|
||||
- 脚本和资源仅来自 self
|
||||
- 无出站 `connect-src`
|
||||
- 启用远程访问时的未命中节流:
|
||||
- 60 秒内 40 次失败
|
||||
- 60 秒锁定(`429 Too Many Requests`)
|
||||
|
||||
文件渲染加固:
|
||||
|
||||
- 截图浏览器请求路由默认拒绝。
|
||||
- 仅允许来自 `http://127.0.0.1/plugins/diffs/assets/*` 的本地查看器资源。
|
||||
- 外部网络请求被阻止。
|
||||
|
||||
## 文件模式的浏览器要求
|
||||
|
||||
`mode: "file"` 和 `mode: "both"` 需要兼容 Chromium 的浏览器。
|
||||
|
||||
解析顺序:
|
||||
|
||||
1. OpenClaw 配置中的 `browser.executablePath`。
|
||||
2. 环境变量:
|
||||
- `OPENCLAW_BROWSER_EXECUTABLE_PATH`
|
||||
- `BROWSER_EXECUTABLE_PATH`
|
||||
- `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH`
|
||||
3. 平台命令或路径发现回退。
|
||||
|
||||
常见失败文本:
|
||||
|
||||
- `Diff PNG/PDF rendering requires a Chromium-compatible browser...`
|
||||
|
||||
通过安装 Chrome、Chromium、Edge 或 Brave 修复,或设置上述可执行路径选项之一。
|
||||
|
||||
## 故障排除
|
||||
|
||||
输入校验错误:
|
||||
|
||||
- `Provide patch or both before and after text.`
|
||||
- 同时提供 `before` 和 `after`,或提供 `patch`。
|
||||
- `Provide either patch or before/after input, not both.`
|
||||
- 不要混用输入模式。
|
||||
- `Invalid baseUrl: ...`
|
||||
- 使用带可选路径的 `http(s)` 来源,不含查询参数或哈希。
|
||||
- `{field} exceeds maximum size (...)`
|
||||
- 减小有效负载大小。
|
||||
- 大型补丁被拒绝
|
||||
- 减少补丁文件数或总行数。
|
||||
|
||||
查看器访问问题:
|
||||
|
||||
- 查看器 URL 默认解析到 `127.0.0.1`。
|
||||
- 对于远程访问场景,可以:
|
||||
- 在每次工具调用时传入 `baseUrl`,或
|
||||
- 使用 `gateway.bind=custom` 和 `gateway.customBindHost`
|
||||
- 仅在确实需要外部查看器访问时启用 `security.allowRemoteViewer`。
|
||||
|
||||
未变更行没有展开按钮:
|
||||
|
||||
- 当补丁输入未携带可展开上下文时可能出现此情况。
|
||||
- 这是预期行为,不表示查看器故障。
|
||||
|
||||
制品未找到:
|
||||
|
||||
- 制品因 TTL 过期。
|
||||
- 令牌或路径已更改。
|
||||
- 清理移除了陈旧数据。
|
||||
|
||||
## 操作指南
|
||||
|
||||
- 对于本地画布中的交互式审阅,优先使用 `mode: "view"`。
|
||||
- 对于需要附件的出站聊天渠道,优先使用 `mode: "file"`。
|
||||
- 除非你的部署需要远程查看器 URL,否则保持 `allowRemoteViewer` 禁用。
|
||||
- 对于敏感 diff,设置明确且较短的 `ttlSeconds`。
|
||||
- 在不需要时避免在 diff 输入中发送密钥。
|
||||
- 如果你的渠道会大幅压缩图片,例如 Telegram 或 WhatsApp,优先使用 PDF 输出(`fileFormat: "pdf"`)。
|
||||
|
||||
Diff 渲染引擎:
|
||||
|
||||
- 由 [Diffs](https://diffs.com) 提供支持。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [工具概览](/tools)
|
||||
- [插件](/tools/plugin)
|
||||
- [浏览器](/tools/browser)
|
||||
@@ -329,6 +329,7 @@ OpenClaw 为 browser、canvas、nodes 和 cron 暴露**一流的智能体工具*
|
||||
- `status`、`describe`
|
||||
- `pending`、`approve`、`reject`(配对)
|
||||
- `notify`(macOS `system.notify`)
|
||||
- `run`(macOS `system.run`)
|
||||
- `camera_snap`、`camera_clip`、`screen_record`
|
||||
- `location_get`
|
||||
|
||||
@@ -338,9 +339,9 @@ OpenClaw 为 browser、canvas、nodes 和 cron 暴露**一流的智能体工具*
|
||||
- 图像返回图像块 + `MEDIA:<path>`。
|
||||
- 视频返回 `FILE:<path>`(mp4)。
|
||||
- 位置返回 JSON 负载(lat/lon/accuracy/timestamp)。
|
||||
- 节点 shell 执行现在统一通过带 `host=node` 的 `exec` 工具;`nodes` 保持为显式节点命令的 RPC 表面。
|
||||
- `run` 参数:`command` argv 数组;可选的 `cwd`、`env`(`KEY=VAL`)、`commandTimeoutMs`、`invokeTimeoutMs`、`needsScreenRecording`。
|
||||
|
||||
示例(节点能力):
|
||||
示例(`run`):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -495,11 +496,11 @@ Canvas 渲染:
|
||||
|
||||
1. `nodes` → `status`
|
||||
2. 在选定的节点上 `describe`
|
||||
3. `notify` / `invoke` / `camera_snap` / `screen_record`
|
||||
3. `notify` / `run` / `camera_snap` / `screen_record`
|
||||
|
||||
## 安全性
|
||||
|
||||
- 避免直接 `system.run`;仅在用户明确同意时使用带 `host=node` 的 `exec` 工具。
|
||||
- 避免直接 `system.run`;仅在用户明确同意时使用 `nodes` → `run`。
|
||||
- 尊重用户对摄像头/屏幕捕获的同意。
|
||||
- 在调用媒体命令前使用 `status/describe` 确保权限。
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ OpenClaw 在**会话开始时**对有资格的 Skills 进行快照,并在同
|
||||
|
||||
## 远程 macOS 节点(Linux Gateway 网关)
|
||||
|
||||
如果 Gateway 网关运行在 Linux 上但连接了一个**允许 `system.run` 的 macOS 节点**(Exec 批准安全设置未设为 `deny`),当所需的二进制文件存在于该节点上时,OpenClaw 可以将仅限 macOS 的 Skills 视为有资格。智能体应通过带 `host=node` 的 `exec` 工具执行这些 Skills。
|
||||
如果 Gateway 网关运行在 Linux 上但连接了一个**允许 `system.run` 的 macOS 节点**(Exec 批准安全设置未设为 `deny`),当所需的二进制文件存在于该节点上时,OpenClaw 可以将仅限 macOS 的 Skills 视为有资格。智能体应通过 `nodes` 工具(通常是 `nodes.run`)执行这些 Skills。
|
||||
|
||||
这依赖于节点报告其命令支持以及通过 `system.run` 进行的二进制文件探测。如果 macOS 节点稍后离线,Skills 仍然可见;调用可能会失败,直到节点重新连接。
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
"type": "string",
|
||||
"enum": ["deny", "fail"]
|
||||
},
|
||||
"pluginToolsMcpBridge": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"strictWindowsCmdWrapper": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -88,11 +85,6 @@
|
||||
"label": "Non-Interactive Permission Policy",
|
||||
"help": "acpx policy when interactive permission prompts are unavailable."
|
||||
},
|
||||
"pluginToolsMcpBridge": {
|
||||
"label": "Plugin Tools MCP Bridge",
|
||||
"help": "Default off. When enabled, inject the built-in OpenClaw plugin-tools MCP server into ACPX sessions so ACP agents can call plugin-registered tools.",
|
||||
"advanced": true
|
||||
},
|
||||
"strictWindowsCmdWrapper": {
|
||||
"label": "Strict Windows cmd Wrapper",
|
||||
"help": "Enabled by default. On Windows, reject unresolved .cmd/.bat wrappers instead of shell fallback. Disable only for compatibility with non-standard wrappers.",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/acpx",
|
||||
"version": "2026.3.30",
|
||||
"version": "2026.3.29",
|
||||
"description": "OpenClaw ACP runtime backend via acpx",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -9,12 +9,10 @@ import {
|
||||
} from "../../../test/helpers/bundled-plugin-paths.js";
|
||||
import {
|
||||
ACPX_BUNDLED_BIN,
|
||||
ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME,
|
||||
ACPX_PINNED_VERSION,
|
||||
createAcpxPluginConfigSchema,
|
||||
resolveAcpxPluginRoot,
|
||||
resolveAcpxPluginConfig,
|
||||
resolvePluginToolsMcpServerConfig,
|
||||
} from "./config.js";
|
||||
|
||||
describe("acpx plugin config parsing", () => {
|
||||
@@ -77,8 +75,6 @@ describe("acpx plugin config parsing", () => {
|
||||
expect(resolved.allowPluginLocalInstall).toBe(true);
|
||||
expect(resolved.stripProviderAuthEnvVars).toBe(true);
|
||||
expect(resolved.cwd).toBe(path.resolve("/tmp/workspace"));
|
||||
expect(resolved.pluginToolsMcpBridge).toBe(false);
|
||||
expect(resolved.mcpServers).toEqual({});
|
||||
expect(resolved.strictWindowsCmdWrapper).toBe(true);
|
||||
});
|
||||
|
||||
@@ -174,79 +170,6 @@ describe("acpx plugin config parsing", () => {
|
||||
expect(parsed.success).toBe(false);
|
||||
});
|
||||
|
||||
it("injects the built-in plugin-tools MCP server only when explicitly enabled", () => {
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "acpx-plugin-tools-dist-"));
|
||||
const pluginRoot = path.join(repoRoot, "extensions", "acpx");
|
||||
const distEntry = path.join(repoRoot, "dist", "mcp", "plugin-tools-serve.js");
|
||||
try {
|
||||
fs.mkdirSync(path.join(pluginRoot, "src"), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(distEntry), { recursive: true });
|
||||
fs.writeFileSync(path.join(pluginRoot, "package.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "openclaw.plugin.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "src", "config.ts"), "// test\n", "utf8");
|
||||
fs.writeFileSync(distEntry, "// built entry\n", "utf8");
|
||||
|
||||
const resolved = resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
pluginToolsMcpBridge: true,
|
||||
},
|
||||
workspaceDir: repoRoot,
|
||||
moduleUrl: pathToFileURL(path.join(pluginRoot, "src", "config.ts")).href,
|
||||
});
|
||||
|
||||
expect(resolved.pluginToolsMcpBridge).toBe(true);
|
||||
expect(resolved.mcpServers[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME]).toEqual({
|
||||
command: process.execPath,
|
||||
args: [distEntry],
|
||||
});
|
||||
} finally {
|
||||
fs.rmSync(repoRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("falls back to the source plugin-tools MCP server entry when dist is absent", () => {
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "acpx-plugin-tools-src-"));
|
||||
const pluginRoot = path.join(repoRoot, "extensions", "acpx");
|
||||
const sourceConfigUrl = pathToFileURL(path.join(pluginRoot, "src", "config.ts")).href;
|
||||
try {
|
||||
fs.mkdirSync(path.join(pluginRoot, "src"), { recursive: true });
|
||||
fs.mkdirSync(path.join(repoRoot, "src", "mcp"), { recursive: true });
|
||||
fs.writeFileSync(path.join(pluginRoot, "package.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "openclaw.plugin.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "src", "config.ts"), "// test\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
path.join(repoRoot, "src", "mcp", "plugin-tools-serve.ts"),
|
||||
"// test\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
expect(resolvePluginToolsMcpServerConfig(sourceConfigUrl)).toEqual({
|
||||
command: process.execPath,
|
||||
args: ["--import", "tsx", path.join(repoRoot, "src", "mcp", "plugin-tools-serve.ts")],
|
||||
});
|
||||
} finally {
|
||||
fs.rmSync(repoRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects reserved MCP server name collisions when the plugin-tools bridge is enabled", () => {
|
||||
expect(() =>
|
||||
resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
pluginToolsMcpBridge: true,
|
||||
mcpServers: {
|
||||
[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME]: {
|
||||
command: "node",
|
||||
},
|
||||
},
|
||||
},
|
||||
workspaceDir: "/tmp/workspace",
|
||||
}),
|
||||
).toThrow(
|
||||
`mcpServers.${ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME} is reserved when pluginToolsMcpBridge=true`,
|
||||
);
|
||||
});
|
||||
|
||||
it("accepts strictWindowsCmdWrapper override", () => {
|
||||
const resolved = resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
|
||||
@@ -12,7 +12,6 @@ export const ACPX_NON_INTERACTIVE_POLICIES = ["deny", "fail"] as const;
|
||||
export type AcpxNonInteractivePermissionPolicy = (typeof ACPX_NON_INTERACTIVE_POLICIES)[number];
|
||||
|
||||
export const ACPX_VERSION_ANY = "any";
|
||||
export const ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME = "openclaw-plugin-tools";
|
||||
const ACPX_BIN_NAME = process.platform === "win32" ? "acpx.cmd" : "acpx";
|
||||
|
||||
function isAcpxPluginRoot(dir: string): boolean {
|
||||
@@ -91,7 +90,6 @@ export type AcpxPluginConfig = {
|
||||
cwd?: string;
|
||||
permissionMode?: AcpxPermissionMode;
|
||||
nonInteractivePermissions?: AcpxNonInteractivePermissionPolicy;
|
||||
pluginToolsMcpBridge?: boolean;
|
||||
strictWindowsCmdWrapper?: boolean;
|
||||
timeoutSeconds?: number;
|
||||
queueOwnerTtlSeconds?: number;
|
||||
@@ -107,7 +105,6 @@ export type ResolvedAcpxPluginConfig = {
|
||||
cwd: string;
|
||||
permissionMode: AcpxPermissionMode;
|
||||
nonInteractivePermissions: AcpxNonInteractivePermissionPolicy;
|
||||
pluginToolsMcpBridge: boolean;
|
||||
strictWindowsCmdWrapper: boolean;
|
||||
timeoutSeconds?: number;
|
||||
queueOwnerTtlSeconds: number;
|
||||
@@ -158,7 +155,6 @@ const AcpxPluginConfigSchema = z.strictObject({
|
||||
error: `nonInteractivePermissions must be one of: ${ACPX_NON_INTERACTIVE_POLICIES.join(", ")}`,
|
||||
})
|
||||
.optional(),
|
||||
pluginToolsMcpBridge: z.boolean({ error: "pluginToolsMcpBridge must be a boolean" }).optional(),
|
||||
strictWindowsCmdWrapper: z
|
||||
.boolean({ error: "strictWindowsCmdWrapper must be a boolean" })
|
||||
.optional(),
|
||||
@@ -212,57 +208,6 @@ function resolveConfiguredCommand(params: { configured?: string; workspaceDir?:
|
||||
return configured;
|
||||
}
|
||||
|
||||
function resolveOpenClawRoot(currentRoot: string): string {
|
||||
if (
|
||||
path.basename(currentRoot) === "acpx" &&
|
||||
path.basename(path.dirname(currentRoot)) === "extensions"
|
||||
) {
|
||||
const parent = path.dirname(path.dirname(currentRoot));
|
||||
if (path.basename(parent) === "dist") {
|
||||
return path.dirname(parent);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
return path.resolve(currentRoot, "..");
|
||||
}
|
||||
|
||||
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");
|
||||
if (fs.existsSync(distEntry)) {
|
||||
return {
|
||||
command: process.execPath,
|
||||
args: [distEntry],
|
||||
};
|
||||
}
|
||||
const sourceEntry = path.join(openClawRoot, "src", "mcp", "plugin-tools-serve.ts");
|
||||
return {
|
||||
command: process.execPath,
|
||||
args: ["--import", "tsx", sourceEntry],
|
||||
};
|
||||
}
|
||||
|
||||
function resolveConfiguredMcpServers(params: {
|
||||
mcpServers?: Record<string, McpServerConfig>;
|
||||
pluginToolsMcpBridge: boolean;
|
||||
moduleUrl?: string;
|
||||
}): Record<string, McpServerConfig> {
|
||||
const resolved = { ...(params.mcpServers ?? {}) };
|
||||
if (!params.pluginToolsMcpBridge) {
|
||||
return resolved;
|
||||
}
|
||||
if (resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME]) {
|
||||
throw new Error(
|
||||
`mcpServers.${ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME} is reserved when pluginToolsMcpBridge=true`,
|
||||
);
|
||||
}
|
||||
resolved[ACPX_PLUGIN_TOOLS_MCP_SERVER_NAME] = resolvePluginToolsMcpServerConfig(params.moduleUrl);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function createAcpxPluginConfigSchema(): OpenClawPluginConfigSchema {
|
||||
return buildPluginConfigSchema(AcpxPluginConfigSchema);
|
||||
}
|
||||
@@ -282,7 +227,6 @@ export function toAcpMcpServers(mcpServers: Record<string, McpServerConfig>): Ac
|
||||
export function resolveAcpxPluginConfig(params: {
|
||||
rawConfig: unknown;
|
||||
workspaceDir?: string;
|
||||
moduleUrl?: string;
|
||||
}): ResolvedAcpxPluginConfig {
|
||||
const parsed = parseAcpxPluginConfig(params.rawConfig);
|
||||
if (!parsed.ok) {
|
||||
@@ -303,12 +247,6 @@ export function resolveAcpxPluginConfig(params: {
|
||||
? undefined
|
||||
: (configuredExpectedVersion ?? (allowPluginLocalInstall ? ACPX_PINNED_VERSION : undefined));
|
||||
const installCommand = buildAcpxLocalInstallCommand(expectedVersion ?? ACPX_PINNED_VERSION);
|
||||
const pluginToolsMcpBridge = normalized.pluginToolsMcpBridge === true;
|
||||
const mcpServers = resolveConfiguredMcpServers({
|
||||
mcpServers: normalized.mcpServers,
|
||||
pluginToolsMcpBridge,
|
||||
moduleUrl: params.moduleUrl,
|
||||
});
|
||||
|
||||
return {
|
||||
command,
|
||||
@@ -320,11 +258,10 @@ export function resolveAcpxPluginConfig(params: {
|
||||
permissionMode: normalized.permissionMode ?? DEFAULT_PERMISSION_MODE,
|
||||
nonInteractivePermissions:
|
||||
normalized.nonInteractivePermissions ?? DEFAULT_NON_INTERACTIVE_POLICY,
|
||||
pluginToolsMcpBridge,
|
||||
strictWindowsCmdWrapper:
|
||||
normalized.strictWindowsCmdWrapper ?? DEFAULT_STRICT_WINDOWS_CMD_WRAPPER,
|
||||
timeoutSeconds: normalized.timeoutSeconds,
|
||||
queueOwnerTtlSeconds: normalized.queueOwnerTtlSeconds ?? DEFAULT_QUEUE_OWNER_TTL_SECONDS,
|
||||
mcpServers,
|
||||
mcpServers: normalized.mcpServers ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { runAcpRuntimeAdapterContract } from "openclaw/plugin-sdk/testing";
|
||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { resolveAcpxPluginConfig } from "./config.js";
|
||||
import { runAcpRuntimeAdapterContract } from "../../../src/acp/runtime/adapter-contract.testkit.js";
|
||||
import { AcpxRuntime, decodeAcpxRuntimeHandleState } from "./runtime.js";
|
||||
import {
|
||||
cleanupMockRuntimeFixtures,
|
||||
@@ -27,7 +24,6 @@ beforeAll(async () => {
|
||||
cwd: process.cwd(),
|
||||
permissionMode: "approve-reads",
|
||||
nonInteractivePermissions: "fail",
|
||||
pluginToolsMcpBridge: false,
|
||||
strictWindowsCmdWrapper: true,
|
||||
queueOwnerTtlSeconds: 0.1,
|
||||
mcpServers: {},
|
||||
@@ -616,72 +612,6 @@ describe("AcpxRuntime", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("routes ACPX commands through the built-in plugin-tools bridge only when explicitly enabled", async () => {
|
||||
process.env.MOCK_ACPX_CONFIG_SHOW_AGENTS = JSON.stringify({
|
||||
codex: {
|
||||
command: "npx custom-codex-acp",
|
||||
},
|
||||
});
|
||||
const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-acpx-plugin-tools-"));
|
||||
const pluginRoot = path.join(repoRoot, "extensions", "acpx");
|
||||
const distEntry = path.join(repoRoot, "dist", "mcp", "plugin-tools-serve.js");
|
||||
try {
|
||||
fs.mkdirSync(path.join(pluginRoot, "src"), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(distEntry), { recursive: true });
|
||||
fs.writeFileSync(path.join(pluginRoot, "package.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "openclaw.plugin.json"), "{}\n", "utf8");
|
||||
fs.writeFileSync(path.join(pluginRoot, "src", "config.ts"), "// test\n", "utf8");
|
||||
fs.writeFileSync(distEntry, "// built entry\n", "utf8");
|
||||
|
||||
const fixture = await createMockRuntimeFixture();
|
||||
const runtime = new AcpxRuntime(
|
||||
resolveAcpxPluginConfig({
|
||||
rawConfig: {
|
||||
command: fixture.config.command,
|
||||
pluginToolsMcpBridge: true,
|
||||
},
|
||||
workspaceDir: repoRoot,
|
||||
moduleUrl: pathToFileURL(path.join(pluginRoot, "src", "config.ts")).href,
|
||||
}),
|
||||
{ logger: NOOP_LOGGER },
|
||||
);
|
||||
|
||||
await runtime.ensureSession({
|
||||
sessionKey: "agent:codex:acp:plugin-tools-bridge",
|
||||
agent: "codex",
|
||||
mode: "persistent",
|
||||
});
|
||||
|
||||
const logs = await readMockRuntimeLogEntries(fixture.logPath);
|
||||
const ensureArgs = (logs.find((entry) => entry.kind === "ensure")?.args as string[]) ?? [];
|
||||
const agentFlagIndex = ensureArgs.indexOf("--agent");
|
||||
expect(agentFlagIndex).toBeGreaterThanOrEqual(0);
|
||||
const rawAgentCommand = ensureArgs[agentFlagIndex + 1];
|
||||
expect(rawAgentCommand).toContain("mcp-proxy.mjs");
|
||||
const payloadMatch = rawAgentCommand.match(/--payload\s+([A-Za-z0-9_-]+)/);
|
||||
expect(payloadMatch?.[1]).toBeDefined();
|
||||
const payload = JSON.parse(
|
||||
Buffer.from(String(payloadMatch?.[1]), "base64url").toString("utf8"),
|
||||
) as {
|
||||
targetCommand: string;
|
||||
mcpServers: Array<{ name: string; command: string; args: string[] }>;
|
||||
};
|
||||
expect(payload.targetCommand).toContain("custom-codex-acp");
|
||||
expect(payload.mcpServers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "openclaw-plugin-tools",
|
||||
command: process.execPath,
|
||||
args: [distEntry],
|
||||
}),
|
||||
]),
|
||||
);
|
||||
} finally {
|
||||
delete process.env.MOCK_ACPX_CONFIG_SHOW_AGENTS;
|
||||
fs.rmSync(repoRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("does not pass unknown agent ids through acpx --agent when MCP servers are configured", async () => {
|
||||
const { runtime, logPath } = await createMockRuntimeFixture({
|
||||
mcpServers: {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AcpRuntimeError } from "openclaw/plugin-sdk/acp-runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { AcpRuntimeError } from "../../../src/acp/runtime/errors.js";
|
||||
import {
|
||||
__testing,
|
||||
getAcpRuntimeBackend,
|
||||
requireAcpRuntimeBackend,
|
||||
} from "openclaw/plugin-sdk/acp-runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
} from "../../../src/acp/runtime/registry.js";
|
||||
import type { AcpRuntime, OpenClawPluginServiceContext } from "../runtime-api.js";
|
||||
import { ACPX_BUNDLED_BIN, ACPX_PINNED_VERSION } from "./config.js";
|
||||
import { createAcpxRuntimeService } from "./service.js";
|
||||
|
||||
@@ -357,7 +357,6 @@ export async function createMockRuntimeFixture(params?: {
|
||||
cwd: dir,
|
||||
permissionMode: params?.permissionMode ?? "approve-all",
|
||||
nonInteractivePermissions: "fail",
|
||||
pluginToolsMcpBridge: false,
|
||||
strictWindowsCmdWrapper: true,
|
||||
queueOwnerTtlSeconds: params?.queueOwnerTtlSeconds ?? 0.1,
|
||||
mcpServers: params?.mcpServers ?? {},
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
{
|
||||
"name": "@openclaw/amazon-bedrock-provider",
|
||||
"version": "2026.3.30",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Amazon Bedrock provider plugin",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-bedrock": "3.1018.0"
|
||||
},
|
||||
"openclaw": {
|
||||
"bundle": {
|
||||
"stageRuntimeDependencies": true
|
||||
},
|
||||
"extensions": [
|
||||
"./index.ts"
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-vertex-provider",
|
||||
"version": "2026.3.30",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic Vertex provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@openclaw/anthropic-provider",
|
||||
"version": "2026.3.30",
|
||||
"version": "2026.3.29",
|
||||
"private": true,
|
||||
"description": "OpenClaw Anthropic provider plugin",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "@openclaw/bluebubbles",
|
||||
"version": "2026.3.30",
|
||||
"version": "2026.3.29",
|
||||
"description": "OpenClaw BlueBubbles channel plugin",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"openclaw": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"openclaw": ">=2026.3.30"
|
||||
"openclaw": ">=2026.3.29"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"openclaw": {
|
||||
@@ -39,7 +39,7 @@
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/bluebubbles",
|
||||
"defaultChoice": "npm",
|
||||
"minHostVersion": ">=2026.3.30"
|
||||
"minHostVersion": ">=2026.3.29"
|
||||
},
|
||||
"release": {
|
||||
"publishToNpm": true
|
||||
|
||||
@@ -102,11 +102,6 @@ export const bluebubblesPlugin: ChannelPlugin<ResolvedBlueBubblesAccount, BlueBu
|
||||
},
|
||||
conversationBindings: {
|
||||
supportsCurrentConversationBinding: true,
|
||||
createManager: ({ cfg, accountId }) =>
|
||||
createBlueBubblesConversationBindingManager({
|
||||
cfg,
|
||||
accountId: accountId ?? undefined,
|
||||
}),
|
||||
},
|
||||
actions: bluebubblesMessageActions,
|
||||
bindings: {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { setDefaultChannelPluginRegistryForTests } from "../../../src/commands/channel-test-helpers.js";
|
||||
import type { OpenClawConfig } from "../../../src/config/config.js";
|
||||
import {
|
||||
__testing as sessionBindingTesting,
|
||||
registerSessionBindingAdapter,
|
||||
} from "openclaw/plugin-sdk/conversation-runtime";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
} from "../../../src/infra/outbound/session-binding-service.js";
|
||||
import { resolveBlueBubblesConversationRoute } from "./conversation-route.js";
|
||||
|
||||
const baseCfg = {
|
||||
@@ -16,6 +17,7 @@ const baseCfg = {
|
||||
describe("resolveBlueBubblesConversationRoute", () => {
|
||||
beforeEach(() => {
|
||||
sessionBindingTesting.resetSessionBindingAdaptersForTests();
|
||||
setDefaultChannelPluginRegistryForTests();
|
||||
});
|
||||
|
||||
it("lets runtime BlueBubbles conversation bindings override default routing", () => {
|
||||
|
||||
@@ -150,7 +150,7 @@ export function createBlueBubblesDebounceRegistry(params: {
|
||||
const balloonBundleId = msg.balloonBundleId?.trim();
|
||||
const associatedMessageGuid = msg.associatedMessageGuid?.trim();
|
||||
if (balloonBundleId && associatedMessageGuid) {
|
||||
return `bluebubbles:${account.accountId}:msg:${associatedMessageGuid}`;
|
||||
return `bluebubbles:${account.accountId}:balloon:${associatedMessageGuid}`;
|
||||
}
|
||||
|
||||
const messageId = msg.messageId?.trim();
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createBlueBubblesMonitorTestRuntime,
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
|
||||
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { fetchBlueBubblesHistory } from "./history.js";
|
||||
import { createBlueBubblesDebounceRegistry } from "./monitor-debounce.js";
|
||||
@@ -24,12 +30,6 @@ import {
|
||||
setBlueBubblesParticipantContactDepsForTest,
|
||||
} from "./participant-contact-names.js";
|
||||
import type { OpenClawConfig, PluginRuntime } from "./runtime-api.js";
|
||||
import {
|
||||
createBlueBubblesMonitorTestRuntime,
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "./test-support/monitor-test-support.js";
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("./send.js", () => ({
|
||||
@@ -850,68 +850,6 @@ describe("BlueBubbles webhook monitor", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("coalesces URL text with URL balloon webhook events by associatedMessageGuid", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const core = createMockRuntime();
|
||||
installTimingAwareInboundDebouncer(core);
|
||||
const processMessage = vi.fn().mockResolvedValue(undefined);
|
||||
const registry = createBlueBubblesDebounceRegistry({ processMessage });
|
||||
const account = createMockAccount();
|
||||
const target = {
|
||||
account,
|
||||
config: {},
|
||||
runtime: { log: vi.fn(), error: vi.fn() },
|
||||
core,
|
||||
path: "/bluebubbles-webhook",
|
||||
};
|
||||
const debouncer = registry.getOrCreateDebouncer(target);
|
||||
|
||||
const messageId = "url-msg-1";
|
||||
const chatGuid = "iMessage;-;+15551234567";
|
||||
const url = "https://github.com/bitfocus/companion/issues/4047";
|
||||
|
||||
await debouncer.enqueue({
|
||||
message: createDebounceTestMessage({
|
||||
chatGuid,
|
||||
text: url,
|
||||
messageId,
|
||||
}),
|
||||
target,
|
||||
});
|
||||
|
||||
await vi.advanceTimersByTimeAsync(300);
|
||||
|
||||
await debouncer.enqueue({
|
||||
message: createDebounceTestMessage({
|
||||
chatGuid,
|
||||
text: url,
|
||||
messageId: "url-balloon-1",
|
||||
balloonBundleId: "com.apple.messages.URLBalloonProvider",
|
||||
associatedMessageGuid: messageId,
|
||||
}),
|
||||
target,
|
||||
});
|
||||
|
||||
expect(processMessage).not.toHaveBeenCalled();
|
||||
|
||||
await vi.advanceTimersByTimeAsync(600);
|
||||
|
||||
expect(processMessage).toHaveBeenCalledTimes(1);
|
||||
expect(processMessage).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
text: url,
|
||||
messageId,
|
||||
balloonBundleId: undefined,
|
||||
}),
|
||||
target,
|
||||
);
|
||||
expect(target.runtime.error).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("skips null-text entries during flush and still delivers the valid message", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
createBlueBubblesMonitorTestRuntime,
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "../../../test/helpers/plugins/bluebubbles-monitor.js";
|
||||
import type { ResolvedBlueBubblesAccount } from "./accounts.js";
|
||||
import { fetchBlueBubblesHistory } from "./history.js";
|
||||
import { handleBlueBubblesWebhookRequest, resolveBlueBubblesMessageId } from "./monitor.js";
|
||||
@@ -21,12 +27,6 @@ import {
|
||||
type WebhookRequestParams,
|
||||
} from "./monitor.webhook.test-helpers.js";
|
||||
import type { OpenClawConfig, PluginRuntime } from "./runtime-api.js";
|
||||
import {
|
||||
createBlueBubblesMonitorTestRuntime,
|
||||
EMPTY_DISPATCH_RESULT,
|
||||
resetBlueBubblesMonitorTestState,
|
||||
type DispatchReplyParams,
|
||||
} from "./test-support/monitor-test-support.js";
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("./send.js", () => ({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createEmptyPluginRegistry } from "openclaw/plugin-sdk/testing";
|
||||
import { setActivePluginRegistry } from "openclaw/plugin-sdk/testing";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { createEmptyPluginRegistry } from "../../../src/plugins/registry.js";
|
||||
import { setActivePluginRegistry } from "../../../src/plugins/runtime.js";
|
||||
import type { WebhookTarget } from "./monitor-shared.js";
|
||||
import { registerBlueBubblesWebhookTarget } from "./monitor.js";
|
||||
import type { OpenClawConfig } from "./runtime-api.js";
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user