Skip to content

Commit 427c102

Browse files
committed
fix(gateway): honor plugin suppression + drop provider-gated tools in raw coding factory
Address bot review on PR #63919: [P2] Plugin suppression bypass — `createOpenClawCodingToolsRaw()` previously delegated to the full coding factory which unconditionally appended `createOpenClawTools(...)` and `listChannelAgentTools(...)`. The resolver explicitly requests core-only resolution for known core tool requests (`disablePluginTools: true`), but that intent was lost — a "core-only" HTTP request could still re-enter plugin resolution via the coding factory. Added `disablePluginTools` flag to `createOpenClawCodingTools` options that skips both plugin spreads. Raw factory always sets it. Independent of the existing `includeCoreTools` flag — keeps core coding tools materialized, only suppresses plugin loading. [P2] Provider-gated tools without context — `apply_patch` is provider-gated to OpenAI-family models. The /tools/invoke HTTP surface has no session-bound model context, so allowlisting `apply_patch` would silently produce nothing (resolver reports tool unavailable). Added `excludeProviderGatedTools` flag that drops provider-gated tools at construction time. Raw factory always sets it. Documented in tools-invoke HTTP API docs that `apply_patch` is no longer materializable via this surface even if allowlisted. [P3] Docs + changelog — updated `docs/gateway/tools-invoke-http-api.md` to reflect the new default deny entries (`process`, `write`, `edit`) and added a "Coding tools available via HTTP" section. Added Unreleased changelog entry describing the new feature and contract. Local tests: 99/99 in tools-invoke-http suites pass on private fork (legacy fork has pre-existing typebox dep issue unrelated to this change).
1 parent 004d2c7 commit 427c102

3 files changed

Lines changed: 48 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
1010

1111
### Changes
1212

13+
- Gateway/tools-invoke: wire coding primitive tools (`read`, plus opt-in `write`/`edit`/`exec`/`process`) into the HTTP `/tools/invoke` endpoint via a new unwrapped factory `createOpenClawCodingToolsRaw()`. Enables deterministic automation flows (linting, tests, browser capture) without a full LLM round-trip. The new factory honors the resolver's plugin-suppression intent end-to-end and excludes provider-gated tools (`apply_patch`) that cannot be safely materialized without session-bound model context. The default HTTP deny list now also includes the canonical mutating tool names `write`, `edit`, and `process`, so they require explicit `gateway.tools.allow` opt-in. Refs #37131. Thanks @simonusa.
1314
- Plugins/active-memory: skip session-store channel entries that contain `:` when resolving the recall subagent's channel, so QQ c2c agent IDs (e.g. `c2c:10D4F7C2…`) and other scoped conversation IDs do not reach bundled-plugin `dirName` validation and crash the recall run. The same guard already applied to explicit `channelId` params (#76704); this extends it to store-derived channels. (#77396) Thanks @hclsys.
1415
- Models/auth: add `openclaw models auth list [--provider <id>] [--json]` so users can inspect saved per-agent auth profiles without dumping secrets or hitting the old “too many arguments” path. Thanks @vincentkoc.
1516
- Control UI/header: show the active agent name in dashboard breadcrumbs without adding the current session key, keeping non-chat views oriented without crowding the topbar.

docs/gateway/tools-invoke-http-api.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,24 @@ Gateway HTTP also applies a hard deny list by default (even if session policy al
106106
- `exec` — direct command execution (RCE surface)
107107
- `spawn` — arbitrary child process creation (RCE surface)
108108
- `shell` — shell command execution (RCE surface)
109-
- `fs_write` — arbitrary file mutation on the host
109+
- `process` — background process orchestration; sibling of exec (RCE surface via shell)
110+
- `write` — canonical workspace write tool; arbitrary file mutation on the host
111+
- `edit` — canonical workspace edit tool; arbitrary file mutation on the host
112+
- `fs_write` — arbitrary file mutation on the host (legacy/alternate name)
110113
- `fs_delete` — arbitrary file deletion on the host
111114
- `fs_move` — arbitrary file move/rename on the host
112-
- `apply_patch` — patch application can rewrite arbitrary files
115+
- `apply_patch` — patch application can rewrite arbitrary files (note: not currently materializable via `/tools/invoke` even if allowlisted, since this surface has no session-bound model context and `apply_patch` is provider-gated to OpenAI-family models)
113116
- `sessions_spawn` — session orchestration; spawning agents remotely is RCE
114117
- `sessions_send` — cross-session message injection
115118
- `cron` — persistent automation control plane
116119
- `gateway` — gateway control plane; prevents reconfiguration via HTTP
117120
- `nodes` — node command relay can reach system.run on paired hosts
118121
- `whatsapp_login` — interactive setup requiring terminal QR scan; hangs on HTTP
119122

123+
### Coding tools available via HTTP
124+
125+
The default coding-primitive tools (`read`, plus the deny-listed `write`/`edit`/`exec`/`process` if allowlisted) are reachable here for deterministic automation use cases that don't need a full LLM round-trip (linting, tests, browser capture, etc.). They are constructed via the unwrapped factory `createOpenClawCodingToolsRaw()`, which omits plugin-capable tools and provider-gated tools — the resolver's plugin-suppression intent is honored end-to-end.
126+
120127
You can customize this deny list via `gateway.tools`:
121128

122129
```json5

src/agents/pi-tools.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,23 @@ export function createOpenClawCodingTools(options?: {
311311
* after wrapping, dropping symbol-keyed unwrap markers.
312312
*/
313313
skipBeforeToolCallHook?: boolean;
314+
/**
315+
* Skip the appended `createOpenClawTools(...)` plugin-capable tool block AND
316+
* the channel-defined agent tools. Used by the /tools/invoke HTTP surface so
317+
* the resolver's `disablePluginTools` intent is honored end-to-end — without
318+
* this, a "core-only" HTTP request would still re-enter plugin resolution
319+
* via the coding factory. Independent of `includeCoreTools`: keeps the core
320+
* coding tools (read/write/edit/exec/process) materialized, only suppresses
321+
* plugin-loading.
322+
*/
323+
disablePluginTools?: boolean;
324+
/**
325+
* Skip materializing tools that require model/provider context to construct
326+
* (currently `apply_patch`, gated to OpenAI providers). Used by the
327+
* /tools/invoke HTTP surface where no model context is available — without
328+
* this, allowlisting a provider-gated tool would silently produce nothing.
329+
*/
330+
excludeProviderGatedTools?: boolean;
314331
/**
315332
* Provider of the currently selected model (used for provider-specific tool quirks).
316333
* Example: "anthropic", "openai", "google", "openai-codex".
@@ -500,6 +517,7 @@ export function createOpenClawCodingTools(options?: {
500517
// (tools.fs.workspaceOnly is a separate umbrella flag for read/write/edit/apply_patch.)
501518
const applyPatchWorkspaceOnly = workspaceOnly || applyPatchConfig?.workspaceOnly !== false;
502519
const applyPatchEnabled =
520+
!options?.excludeProviderGatedTools &&
503521
applyPatchConfig?.enabled !== false &&
504522
isOpenAIProvider(options?.modelProvider) &&
505523
isApplyPatchAllowedForModel({
@@ -706,8 +724,13 @@ export function createOpenClawCodingTools(options?: {
706724
...(execTool ? [execTool as unknown as AnyAgentTool] : []),
707725
...(processTool ? [processTool as unknown as AnyAgentTool] : []),
708726
// Channel docking: include channel-defined agent tools (login, etc.).
709-
...(includeCoreTools ? listChannelAgentTools({ cfg: options?.config }) : []),
710-
...(includeCoreTools
727+
...(includeCoreTools && !options?.disablePluginTools
728+
? listChannelAgentTools({ cfg: options?.config })
729+
: []),
730+
// Plugin-capable OpenClaw tools (channel/messaging/skill plugins). Skipped
731+
// when `disablePluginTools` is set so a "core-only" caller (e.g. /tools/invoke
732+
// HTTP surface) does not re-enter plugin resolution via the coding factory.
733+
...(includeCoreTools && !options?.disablePluginTools
711734
? createOpenClawTools({
712735
sandboxBrowserBridgeUrl: sandbox?.browser?.bridgeUrl,
713736
allowHostBrowserControl: sandbox ? sandbox.browserAllowHostControl : true,
@@ -883,5 +906,17 @@ export function createOpenClawCodingTools(options?: {
883906
export function createOpenClawCodingToolsRaw(
884907
options?: Parameters<typeof createOpenClawCodingTools>[0],
885908
): AnyAgentTool[] {
886-
return createOpenClawCodingTools({ ...options, skipBeforeToolCallHook: true });
909+
return createOpenClawCodingTools({
910+
...options,
911+
skipBeforeToolCallHook: true,
912+
// The /tools/invoke HTTP surface has no session-bound model context, so
913+
// provider-gated tools (apply_patch is OpenAI-only) cannot be safely
914+
// materialized here. Drop them rather than letting allowlist opt-in
915+
// silently produce nothing.
916+
excludeProviderGatedTools: true,
917+
// The resolver already gates plugin loading (`disablePluginTools`) for
918+
// core-only HTTP requests; honor that here so the coding factory does not
919+
// re-enter plugin resolution via the appended createOpenClawTools(...) block.
920+
disablePluginTools: true,
921+
});
887922
}

0 commit comments

Comments
 (0)