[FRD] Installed-plugin opt-in for registerTrustedToolPolicy + registerAgentToolResultMiddleware
Status: Draft (ByteDesk-internal). Ready to file at openclaw/openclaw issues.
Author: ByteDesk Platform team (companion to BDP-1207, follow-up from BDP-1198 in bytedesk-platform).
Reference: docs/plugins/hooks.md, docs/plugins/building-plugins.md, docs/plugins/manifest.md.
Problem
The OpenClaw plugin SDK currently restricts two host-trusted registration surfaces to bundled plugins only. When an installed plugin (e.g. one loaded via plugins.load.paths or openclaw plugins install) calls them, the gateway emits warnings and skips registration:
api.registerTrustedToolPolicy(...) → only bundled plugins can register trusted tool policies
api.registerAgentToolResultMiddleware(...) → only bundled plugins can register agent tool result middleware
This is a reasonable default — these surfaces run with host trust ahead of the standard before_tool_call / after_tool_call hook chain — but it leaves serious gaps for downstream platforms that ship their own first-party plugins outside the OpenClaw image.
ByteDesk hit this on bytedesk-workflow-harness, an installed plugin that implements a workflow control plane on top of OpenClaw primitives. BDP-1198 had to fall back to the public hook chain to keep functionality:
- Trusted-policy fallback →
before_tool_call hook for the workflow-harness engineering exec-policy / per-cascade budget gate.
- Tool-result-middleware fallback →
after_tool_call hook for workflow correlation-id tagging and artifact capture.
The fallbacks work, but they have two real costs:
- Wrong execution order.
before_tool_call hooks for installed plugins run after all bundled-plugin trusted policies. The workflow-harness engineering policy is meant to be the per-cascade budget/exec-policy gate for harness-owned cascades, and ordering matters for deny-decisions.
- Reduced surface for middleware.
after_tool_call hooks cannot mutate the model-visible tool output the way registerAgentToolResultMiddleware can — only the artifact bag. Workflow-correlation tagging into model-visible context is therefore not possible from an installed plugin today.
The hard side-step is bundling our plugin into the OpenClaw image, which tightens coupling between OpenClaw releases and the downstream platform release. We'd prefer not to do that just to escape an SDK gate.
Requested upstream primitive
An explicit, manifest-declared opt-in for both surfaces, available to installed plugins. Two equivalent shapes — either works, but manifest-field is preferred because it keeps the trust posture declarative and reviewable at install time:
Option A — manifest field on openclaw.plugin.json (preferred)
Two new optional contract fields, mirroring the existing contracts.tools[] pattern:
{
"id": "bytedesk-workflow-harness",
"contracts": {
"tools": ["bytedesk_workflow_*"],
"trustedToolPolicies": ["bytedesk-workflow"],
"agentToolResultMiddleware": ["bytedesk-workflow"]
}
}
Semantics:
- Each entry is a policy / middleware id the plugin promises to register at runtime.
- The gateway records the declaration at plugin discovery (before code load), exactly like
contracts.tools[].
- At
api.registerTrustedToolPolicy(id, ...) / api.registerAgentToolResultMiddleware(id, ...) call time, the gateway accepts the registration iff id appears in the matching manifest array.
- Undeclared policies/middleware continue to log the existing warning and skip registration — the existing bundled-plugin path is unchanged.
This matches the spirit of "if OpenClaw must know it before loading plugin code, put it in openclaw.plugin.json" from manifest.md.
Option B — gateway-config allowlist
Equivalent opt-in via gateway config rather than plugin manifest:
{
plugins: {
entries: {
"bytedesk-workflow-harness": {
enabled: true,
trustedToolPolicy: true,
agentToolResultMiddleware: true,
},
},
},
}
Same runtime gate, decision lives in the operator's config instead of the plugin's manifest. Acceptable as a complement (operator override) but should not be the only opt-in path — manifest declarations are reviewable by openclaw plugins inspect before install.
Either option — ordering guarantee
Document the ordering relationship between installed-plugin trusted policies and bundled-plugin trusted policies. The downstream-useful semantics are:
- Bundled-plugin trusted policies run first (existing behavior).
- Installed-plugin trusted policies run next, in plugin-load order.
- Standard
before_tool_call / after_tool_call hooks run last (existing behavior).
This preserves the host-trust default while giving installed plugins a slot that's strictly stronger than the public hook chain.
Out of scope for this FRD
- Letting installed plugins register
registerNodeInvokePolicy host-trusted surfaces — separate trust posture, separate review.
- Letting installed plugins extend
registerSessionExtension defaults — already allowed; just needs the namespace + description fields documented in hooks.md (BDP-1198 path A handled the local-side warning).
- Cross-plugin policy composition / merge rules — installed plugins each register their own id; collisions on id should be a load error, not a merge.
Docs locations to update upstream
If the primitive lands, the following files in openclaw/openclaw should pick up the opt-in surface in the same change:
| File |
What to add |
docs/plugins/manifest.md |
New contracts.trustedToolPolicies[] + contracts.agentToolResultMiddleware[] fields, alongside the existing contracts.tools[] row in the top-level schema table. |
docs/plugins/building-plugins.md |
A short "Trusted surfaces from installed plugins" section showing the manifest declaration + the api.registerTrustedToolPolicy(...) call, plus the ordering guarantee. |
docs/plugins/hooks.md |
Update the registerTrustedToolPolicy and registerAgentToolResultMiddleware reference rows to note the installed-plugin opt-in and link to the manifest section. |
docs/cli/plugins.md |
Update openclaw plugins inspect --runtime --json output description to surface declared trustedToolPolicies / agentToolResultMiddleware so operators can audit before enabling. |
Downstream design we're holding back
ByteDesk's bytedesk-workflow-harness plugin currently ships the hook-based fallback in:
bytedesk-openclaw/plugins/bytedesk-workflow-harness/src/harness/trusted-tool-policy.ts
bytedesk-openclaw/plugins/bytedesk-workflow-harness/src/harness/tool-result-middleware.ts
bytedesk-openclaw/plugins/bytedesk-workflow-harness/src/exec-policy/registry.ts
When the opt-in lands, each will prefer api.registerTrustedToolPolicy(...) / api.registerAgentToolResultMiddleware(...) and fall back to the hook chain only when the gate isn't configured — so we stay forward-compatible with older OpenClaw releases.
Why not just bundle the plugin
Bundling bytedesk-workflow-harness into the OpenClaw image side-steps the restriction but tightens coupling between every OpenClaw release and the workflow-harness release. The harness already targets a published pluginApi / minGatewayVersion range and ships its own lifecycle. The installed-plugin opt-in lets the host trust posture stay explicit without forcing the bundle.
Asked of upstream
- Confirm whether the bundled-only restriction on these two surfaces is a deliberate long-term trust posture or a default that predates installed-plugin maturity.
- If extending: pick Option A (manifest) or Option B (gateway config) — or accept both — and scope a single PR that adds the manifest fields, the runtime gate, and the docs updates listed above.
- If not extending: state that explicitly so downstream platforms can plan around bundling or hook-only fallbacks.
Companion ByteDesk artifacts
- BDP-1198 — landed Path A (cleared session-extension + nodeInvokePolicy warnings; trusted-policy + middleware fallbacks via hooks).
- BDP-1207 — this FRD; tracks the upstream ask.
docs/workflow-harness.md — the workflow-harness design that motivates the ask (see "Trusted policy" + "Decorator" pattern rows).
[FRD] Installed-plugin opt-in for
registerTrustedToolPolicy+registerAgentToolResultMiddlewareProblem
The OpenClaw plugin SDK currently restricts two host-trusted registration surfaces to bundled plugins only. When an installed plugin (e.g. one loaded via
plugins.load.pathsoropenclaw plugins install) calls them, the gateway emits warnings and skips registration:api.registerTrustedToolPolicy(...)→only bundled plugins can register trusted tool policiesapi.registerAgentToolResultMiddleware(...)→only bundled plugins can register agent tool result middlewareThis is a reasonable default — these surfaces run with host trust ahead of the standard
before_tool_call/after_tool_callhook chain — but it leaves serious gaps for downstream platforms that ship their own first-party plugins outside the OpenClaw image.ByteDesk hit this on
bytedesk-workflow-harness, an installed plugin that implements a workflow control plane on top of OpenClaw primitives. BDP-1198 had to fall back to the public hook chain to keep functionality:before_tool_callhook for the workflow-harness engineering exec-policy / per-cascade budget gate.after_tool_callhook for workflow correlation-id tagging and artifact capture.The fallbacks work, but they have two real costs:
before_tool_callhooks for installed plugins run after all bundled-plugin trusted policies. The workflow-harness engineering policy is meant to be the per-cascade budget/exec-policy gate for harness-owned cascades, and ordering matters for deny-decisions.after_tool_callhooks cannot mutate the model-visible tool output the wayregisterAgentToolResultMiddlewarecan — only the artifact bag. Workflow-correlation tagging into model-visible context is therefore not possible from an installed plugin today.The hard side-step is bundling our plugin into the OpenClaw image, which tightens coupling between OpenClaw releases and the downstream platform release. We'd prefer not to do that just to escape an SDK gate.
Requested upstream primitive
An explicit, manifest-declared opt-in for both surfaces, available to installed plugins. Two equivalent shapes — either works, but manifest-field is preferred because it keeps the trust posture declarative and reviewable at install time:
Option A — manifest field on
openclaw.plugin.json(preferred)Two new optional contract fields, mirroring the existing
contracts.tools[]pattern:{ "id": "bytedesk-workflow-harness", "contracts": { "tools": ["bytedesk_workflow_*"], "trustedToolPolicies": ["bytedesk-workflow"], "agentToolResultMiddleware": ["bytedesk-workflow"] } }Semantics:
contracts.tools[].api.registerTrustedToolPolicy(id, ...)/api.registerAgentToolResultMiddleware(id, ...)call time, the gateway accepts the registration iffidappears in the matching manifest array.This matches the spirit of "if OpenClaw must know it before loading plugin code, put it in
openclaw.plugin.json" frommanifest.md.Option B — gateway-config allowlist
Equivalent opt-in via gateway config rather than plugin manifest:
Same runtime gate, decision lives in the operator's config instead of the plugin's manifest. Acceptable as a complement (operator override) but should not be the only opt-in path — manifest declarations are reviewable by
openclaw plugins inspectbefore install.Either option — ordering guarantee
Document the ordering relationship between installed-plugin trusted policies and bundled-plugin trusted policies. The downstream-useful semantics are:
before_tool_call/after_tool_callhooks run last (existing behavior).This preserves the host-trust default while giving installed plugins a slot that's strictly stronger than the public hook chain.
Out of scope for this FRD
registerNodeInvokePolicyhost-trusted surfaces — separate trust posture, separate review.registerSessionExtensiondefaults — already allowed; just needs thenamespace+descriptionfields documented inhooks.md(BDP-1198 path A handled the local-side warning).Docs locations to update upstream
If the primitive lands, the following files in
openclaw/openclawshould pick up the opt-in surface in the same change:docs/plugins/manifest.mdcontracts.trustedToolPolicies[]+contracts.agentToolResultMiddleware[]fields, alongside the existingcontracts.tools[]row in the top-level schema table.docs/plugins/building-plugins.mdapi.registerTrustedToolPolicy(...)call, plus the ordering guarantee.docs/plugins/hooks.mdregisterTrustedToolPolicyandregisterAgentToolResultMiddlewarereference rows to note the installed-plugin opt-in and link to the manifest section.docs/cli/plugins.mdopenclaw plugins inspect --runtime --jsonoutput description to surface declaredtrustedToolPolicies/agentToolResultMiddlewareso operators can audit before enabling.Downstream design we're holding back
ByteDesk's
bytedesk-workflow-harnessplugin currently ships the hook-based fallback in:bytedesk-openclaw/plugins/bytedesk-workflow-harness/src/harness/trusted-tool-policy.tsbytedesk-openclaw/plugins/bytedesk-workflow-harness/src/harness/tool-result-middleware.tsbytedesk-openclaw/plugins/bytedesk-workflow-harness/src/exec-policy/registry.tsWhen the opt-in lands, each will prefer
api.registerTrustedToolPolicy(...)/api.registerAgentToolResultMiddleware(...)and fall back to the hook chain only when the gate isn't configured — so we stay forward-compatible with older OpenClaw releases.Why not just bundle the plugin
Bundling
bytedesk-workflow-harnessinto the OpenClaw image side-steps the restriction but tightens coupling between every OpenClaw release and the workflow-harness release. The harness already targets a publishedpluginApi/minGatewayVersionrange and ships its own lifecycle. The installed-plugin opt-in lets the host trust posture stay explicit without forcing the bundle.Asked of upstream
Companion ByteDesk artifacts
docs/workflow-harness.md— the workflow-harness design that motivates the ask (see "Trusted policy" + "Decorator" pattern rows).