mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:00:50 +00:00
feat(plugins): add harness tool result middleware (#71021)
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
c4a62f081d0b9fcfd5e76a843547411bba0fdc129c1c143e7f4c4f6294b040b9 plugin-sdk-api-baseline.json
|
||||
a62c9aea45d5694a851380ff6b35b7fb2ffd9fc4dfa3f0c567a8e1c97094475e plugin-sdk-api-baseline.jsonl
|
||||
b758a1c5503c08325113e0d6c9f1ac2db5a5fd9992a3902706ebe0f0dbbc1213 plugin-sdk-api-baseline.json
|
||||
2c9d0a00e526dcd47d131261b8ceddd8e59faa8530b129d108a3721a4cbcbea7 plugin-sdk-api-baseline.jsonl
|
||||
|
||||
@@ -160,7 +160,7 @@ A single plugin can register any number of capabilities via the `api` object:
|
||||
| Video generation | `api.registerVideoGenerationProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
|
||||
| Web fetch | `api.registerWebFetchProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
|
||||
| Web search | `api.registerWebSearchProvider(...)` | [Provider Plugins](/plugins/sdk-provider-plugins#step-5-add-extra-capabilities) |
|
||||
| Embedded Pi extension | `api.registerEmbeddedExtensionFactory(...)` | [SDK Overview](/plugins/sdk-overview#registration-api) |
|
||||
| Tool-result middleware | `api.registerAgentToolResultMiddleware(...)` | [SDK Overview](/plugins/sdk-overview#registration-api) |
|
||||
| Agent tools | `api.registerTool(...)` | Below |
|
||||
| Custom commands | `api.registerCommand(...)` | [Entry Points](/plugins/sdk-entrypoints) |
|
||||
| Plugin hooks | `api.on(...)` | [Plugin hooks](/plugins/hooks) |
|
||||
@@ -170,10 +170,11 @@ A single plugin can register any number of capabilities via the `api` object:
|
||||
|
||||
For the full registration API, see [SDK Overview](/plugins/sdk-overview#registration-api).
|
||||
|
||||
Use `api.registerEmbeddedExtensionFactory(...)` when a plugin needs Pi-native
|
||||
embedded-runner hooks such as async `tool_result` rewriting before the final
|
||||
tool result message is emitted. Prefer regular OpenClaw plugin hooks when the
|
||||
work does not need Pi extension timing.
|
||||
Use `api.registerAgentToolResultMiddleware(...)` when a plugin needs async
|
||||
tool-result rewriting before the model sees the output. Declare the targeted
|
||||
harnesses in `contracts.agentToolResultMiddleware`, for example
|
||||
`["pi", "codex-app-server"]`. Prefer regular OpenClaw plugin hooks when the
|
||||
work does not need pre-model tool-result timing.
|
||||
|
||||
If your plugin registers custom gateway RPC methods, keep them on a
|
||||
plugin-specific prefix. Core admin namespaces (`config.*`,
|
||||
|
||||
@@ -25,11 +25,11 @@ These are in-process OpenClaw hooks, not Codex `hooks.json` command hooks:
|
||||
- `before_message_write` for mirrored transcript records
|
||||
- `agent_end`
|
||||
|
||||
Bundled plugins can also register a Codex app-server extension factory to add
|
||||
async `tool_result` middleware. That middleware runs for OpenClaw dynamic tools
|
||||
after OpenClaw executes the tool and before the result is returned to Codex. It
|
||||
is separate from the public `tool_result_persist` plugin hook, which transforms
|
||||
OpenClaw-owned transcript tool-result writes.
|
||||
Plugins can also register harness-neutral tool-result middleware to rewrite
|
||||
OpenClaw dynamic tool results after OpenClaw executes the tool and before the
|
||||
result is returned to Codex. This is separate from the public
|
||||
`tool_result_persist` plugin hook, which transforms OpenClaw-owned transcript
|
||||
tool-result writes.
|
||||
|
||||
The harness is off by default. New configs should keep OpenAI model refs
|
||||
canonical as `openai/gpt-*` and explicitly force
|
||||
|
||||
@@ -396,7 +396,7 @@ read without importing the plugin runtime.
|
||||
```json
|
||||
{
|
||||
"contracts": {
|
||||
"embeddedExtensionFactories": ["pi"],
|
||||
"agentToolResultMiddleware": ["pi", "codex-app-server"],
|
||||
"externalAuthProviders": ["acme-ai"],
|
||||
"speechProviders": ["openai"],
|
||||
"realtimeTranscriptionProviders": ["openai"],
|
||||
@@ -414,20 +414,26 @@ read without importing the plugin runtime.
|
||||
|
||||
Each list is optional:
|
||||
|
||||
| Field | Type | What it means |
|
||||
| -------------------------------- | ---------- | ----------------------------------------------------------------- |
|
||||
| `embeddedExtensionFactories` | `string[]` | Embedded runtime ids a bundled plugin may register factories for. |
|
||||
| `externalAuthProviders` | `string[]` | Provider ids whose external auth profile hook this plugin owns. |
|
||||
| `speechProviders` | `string[]` | Speech provider ids this plugin owns. |
|
||||
| `realtimeTranscriptionProviders` | `string[]` | Realtime-transcription provider ids this plugin owns. |
|
||||
| `realtimeVoiceProviders` | `string[]` | Realtime-voice provider ids this plugin owns. |
|
||||
| `memoryEmbeddingProviders` | `string[]` | Memory embedding provider ids this plugin owns. |
|
||||
| `mediaUnderstandingProviders` | `string[]` | Media-understanding provider ids this plugin owns. |
|
||||
| `imageGenerationProviders` | `string[]` | Image-generation provider ids this plugin owns. |
|
||||
| `videoGenerationProviders` | `string[]` | Video-generation provider ids this plugin owns. |
|
||||
| `webFetchProviders` | `string[]` | Web-fetch provider ids this plugin owns. |
|
||||
| `webSearchProviders` | `string[]` | Web-search provider ids this plugin owns. |
|
||||
| `tools` | `string[]` | Agent tool names this plugin owns for bundled contract checks. |
|
||||
| Field | Type | What it means |
|
||||
| -------------------------------- | ---------- | ---------------------------------------------------------------- |
|
||||
| `embeddedExtensionFactories` | `string[]` | Deprecated embedded extension factory ids. |
|
||||
| `agentToolResultMiddleware` | `string[]` | Harness ids this plugin may register tool-result middleware for. |
|
||||
| `externalAuthProviders` | `string[]` | Provider ids whose external auth profile hook this plugin owns. |
|
||||
| `speechProviders` | `string[]` | Speech provider ids this plugin owns. |
|
||||
| `realtimeTranscriptionProviders` | `string[]` | Realtime-transcription provider ids this plugin owns. |
|
||||
| `realtimeVoiceProviders` | `string[]` | Realtime-voice provider ids this plugin owns. |
|
||||
| `memoryEmbeddingProviders` | `string[]` | Memory embedding provider ids this plugin owns. |
|
||||
| `mediaUnderstandingProviders` | `string[]` | Media-understanding provider ids this plugin owns. |
|
||||
| `imageGenerationProviders` | `string[]` | Image-generation provider ids this plugin owns. |
|
||||
| `videoGenerationProviders` | `string[]` | Video-generation provider ids this plugin owns. |
|
||||
| `webFetchProviders` | `string[]` | Web-fetch provider ids this plugin owns. |
|
||||
| `webSearchProviders` | `string[]` | Web-search provider ids this plugin owns. |
|
||||
| `tools` | `string[]` | Agent tool names this plugin owns for bundled contract checks. |
|
||||
|
||||
`contracts.embeddedExtensionFactories` is retained for bundled compatibility
|
||||
code that still needs direct Pi embedded-runner events. New tool-result
|
||||
transforms should declare `contracts.agentToolResultMiddleware` and register
|
||||
with `api.registerAgentToolResultMiddleware(...)` instead.
|
||||
|
||||
Provider plugins that implement `resolveExternalAuthProfiles` should declare
|
||||
`contracts.externalAuthProviders`. Plugins without the declaration still run
|
||||
|
||||
@@ -144,14 +144,20 @@ OpenClaw requires Codex app-server `0.118.0` or newer. The Codex plugin checks
|
||||
the app-server initialize handshake and blocks older or unversioned servers so
|
||||
OpenClaw only runs against the protocol surface it has been tested with.
|
||||
|
||||
### Codex app-server tool-result middleware
|
||||
### Tool-result middleware
|
||||
|
||||
Bundled plugins can also attach Codex app-server-specific `tool_result`
|
||||
middleware through `api.registerCodexAppServerExtensionFactory(...)` when their
|
||||
manifest declares `contracts.embeddedExtensionFactories: ["codex-app-server"]`.
|
||||
This is the trusted-plugin seam for async tool-result transforms that need to
|
||||
run inside the native Codex harness before the tool output is projected back
|
||||
into the OpenClaw transcript.
|
||||
Plugins can attach harness-neutral tool-result middleware through
|
||||
`api.registerAgentToolResultMiddleware(...)` when their manifest declares the
|
||||
targeted harness ids in `contracts.agentToolResultMiddleware`. This is the seam
|
||||
for async tool-result transforms that must run before PI or Codex feeds tool
|
||||
output back into the model.
|
||||
|
||||
Legacy bundled plugins can still use
|
||||
`api.registerCodexAppServerExtensionFactory(...)` for Codex app-server-only
|
||||
middleware, but new result transforms should use the harness-neutral API.
|
||||
The Pi-only `api.registerEmbeddedExtensionFactory(...)` hook is deprecated for
|
||||
tool-result transforms; keep it only for bundled compatibility code that still
|
||||
needs direct Pi embedded-runner events.
|
||||
|
||||
### Native Codex harness mode
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ sidebarTitle: "Migrate to SDK"
|
||||
read_when:
|
||||
- You see the OPENCLAW_PLUGIN_SDK_COMPAT_DEPRECATED warning
|
||||
- You see the OPENCLAW_EXTENSION_API_DEPRECATED warning
|
||||
- You use api.registerEmbeddedExtensionFactory
|
||||
- You are updating a plugin to the modern plugin architecture
|
||||
- You maintain an external OpenClaw plugin
|
||||
---
|
||||
@@ -23,8 +24,10 @@ anything they needed from a single entry point:
|
||||
new plugin architecture was being built.
|
||||
- **`openclaw/extension-api`** — a bridge that gave plugins direct access to
|
||||
host-side helpers like the embedded agent runner.
|
||||
- **`api.registerEmbeddedExtensionFactory(...)`** — a Pi-only bundled extension
|
||||
hook that could observe embedded-runner events such as `tool_result`.
|
||||
|
||||
Both surfaces are now **deprecated**. They still work at runtime, but new
|
||||
These surfaces are now **deprecated**. They still work at runtime, but new
|
||||
plugins must not use them, and existing plugins should migrate before the next
|
||||
major release removes them.
|
||||
|
||||
@@ -87,6 +90,41 @@ releases.
|
||||
## How to migrate
|
||||
|
||||
<Steps>
|
||||
<Step title="Migrate Pi tool-result extensions to middleware">
|
||||
Replace Pi-only `api.registerEmbeddedExtensionFactory(...)` tool-result
|
||||
handlers with harness-neutral middleware.
|
||||
|
||||
```typescript
|
||||
// Before: Pi-only compatibility hook
|
||||
api.registerEmbeddedExtensionFactory((pi) => {
|
||||
pi.on("tool_result", async (event) => {
|
||||
return compactToolResult(event);
|
||||
});
|
||||
});
|
||||
|
||||
// After: Pi and Codex app-server dynamic tools
|
||||
api.registerAgentToolResultMiddleware(async (event) => {
|
||||
return compactToolResult(event);
|
||||
}, {
|
||||
harnesses: ["pi", "codex-app-server"],
|
||||
});
|
||||
```
|
||||
|
||||
Update the plugin manifest at the same time:
|
||||
|
||||
```json
|
||||
{
|
||||
"contracts": {
|
||||
"agentToolResultMiddleware": ["pi", "codex-app-server"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Keep `contracts.embeddedExtensionFactories` only for bundled compatibility
|
||||
code that still needs direct Pi embedded-runner events.
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Migrate approval-native handlers to capability facts">
|
||||
Approval-capable channel plugins now expose native approval behavior through
|
||||
`approvalCapability.nativeRuntime` plus the shared runtime-context registry.
|
||||
|
||||
@@ -99,7 +99,8 @@ methods:
|
||||
| `api.registerCli(registrar, opts?)` | CLI subcommand |
|
||||
| `api.registerService(service)` | Background service |
|
||||
| `api.registerInteractiveHandler(registration)` | Interactive handler |
|
||||
| `api.registerEmbeddedExtensionFactory(factory)` | Pi embedded-runner extension factory |
|
||||
| `api.registerAgentToolResultMiddleware(...)` | Harness tool-result middleware |
|
||||
| `api.registerEmbeddedExtensionFactory(factory)` | Deprecated PI extension factory |
|
||||
| `api.registerMemoryPromptSupplement(builder)` | Additive memory-adjacent prompt section |
|
||||
| `api.registerMemoryCorpusSupplement(adapter)` | Additive memory search/read corpus |
|
||||
|
||||
@@ -110,15 +111,22 @@ methods:
|
||||
plugin-owned methods.
|
||||
</Note>
|
||||
|
||||
<Accordion title="When to use registerEmbeddedExtensionFactory">
|
||||
Use `api.registerEmbeddedExtensionFactory(...)` when a plugin needs Pi-native
|
||||
event timing during OpenClaw embedded runs — for example async `tool_result`
|
||||
rewrites that must happen before the final tool-result message is emitted.
|
||||
<Accordion title="When to use tool-result middleware">
|
||||
Use `api.registerAgentToolResultMiddleware(...)` when a plugin needs to
|
||||
rewrite a tool result after execution and before the harness feeds that
|
||||
result back into the model. This is the harness-neutral seam for async output
|
||||
reducers such as tokenjuice.
|
||||
|
||||
This is a bundled-plugin seam today: only bundled plugins may register one,
|
||||
and they must declare `contracts.embeddedExtensionFactories: ["pi"]` in
|
||||
`openclaw.plugin.json`. Keep normal OpenClaw plugin hooks for everything that
|
||||
does not require that lower-level seam.
|
||||
Plugins must declare `contracts.agentToolResultMiddleware` for each targeted
|
||||
harness, for example `["pi", "codex-app-server"]`. Keep normal OpenClaw
|
||||
plugin hooks for work that does not need pre-model tool-result timing.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Legacy Pi extension factories">
|
||||
`api.registerEmbeddedExtensionFactory(...)` is deprecated. It remains a
|
||||
compatibility seam for bundled plugins that still need direct Pi
|
||||
embedded-runner events. New tool-result transforms should use
|
||||
`api.registerAgentToolResultMiddleware(...)` instead.
|
||||
</Accordion>
|
||||
|
||||
### Gateway discovery registration
|
||||
|
||||
@@ -13,8 +13,9 @@ tool results after the command has already run.
|
||||
It changes the returned `tool_result`, not the command itself. Tokenjuice does
|
||||
not rewrite shell input, rerun commands, or change exit codes.
|
||||
|
||||
Today this applies to Pi embedded runs, where tokenjuice hooks the embedded
|
||||
`tool_result` path and trims the output that goes back into the session.
|
||||
Today this applies to PI embedded runs and OpenClaw dynamic tools in the Codex
|
||||
app-server harness. Tokenjuice hooks OpenClaw's tool-result middleware and
|
||||
trims the output before it goes back into the active harness session.
|
||||
|
||||
## Enable the plugin
|
||||
|
||||
|
||||
Reference in New Issue
Block a user