Summary
Plugins cannot invoke existing host gateway RPCs (status, models.list, etc.) — PluginRuntime exposes only the curated subagent.{run, waitForRun, getSessionMessages, getSession, deleteSession} methods. Plugins can REGISTER gateway methods via registerGatewayMethod but cannot CALL existing ones.
Problem to solve
The gap was discovered during an adversarial audit of Martian-Engineering/lossless-claw#516 (temporal-spine LCM). Commit 38fc1eb attempted to wire smart detailLevel auto-pick by reading host runtime status — callGateway({method:"status"}) and callGateway({method:"models.list"}). Both calls always throw because the plugin's callGateway switch falls through default: throw "Unsupported gateway method..." — the plugin-sdk surface gives plugins no way to reach the host's getStatusSummary() or listModels(). The author wrote // No new openclaw-side wiring required. in the commit message; that claim is wrong, and the feature shipped inert until an audit caught it.
The host RPCs themselves DO exist — src/gateway/server-methods/health.ts:30 (status) and src/gateway/server-methods/models.ts:13 (models.list). They're registered in BASE_METHODS and called by the TUI today (src/tui/gateway-chat.ts:240/:244). But there's no plugin-sdk path to them.
Proposed solution
Add a curated read-only RPC accessor surface on PluginRuntime:
runtime.gateway: {
/** Return the host's StatusSummary (same shape as /status command). */
getStatus(opts?: { includeSensitive?: boolean }): Promise<StatusSummary>;
/** Return the host's filtered model catalog. */
listModels(): Promise<{ models: ModelCatalogEntry[] }>;
}
Or, less curated, a general allowlisted runtime.gateway.request(method, params) that enforces a method allowlist (initially: status, models.list).
Either shape solves the immediate use case. Curated accessors are easier to reason about for replay determinism (frozen-snapshot semantics per accessor); a generic request() requires per-method snapshot handling.
The StatusSummary and ModelCatalogEntry types should be exported from the plugin-sdk public surface (separate issue forthcoming on type export hygiene).
Alternatives considered
- Plugins build their own host integration — what lossless-claw did (and why it shipped inert). Doesn't scale.
- Expose
gatewayClient.request to plugins — too broad; gives plugins ability to call any RPC including mutating ones (agent.cancel, etc.). The curated read-only accessors are safer.
- Tier-1-only workaround — what lossless-claw#516 ended up doing post-audit: derive context window from runtime config synchronously, skip the host RPC. Loses the per-session live usage signal.
Impact
- Affected: any plugin that wants to read host runtime state for tuning behavior. Today: lossless-claw (auto-detail-level), potentially smarter-claw, future plan-mode plugins.
- Severity: blocks a class of features. Not a crash bug, but it's a footgun — committed code looks correct in isolation and silently degrades without it.
- Frequency: every plugin run that tries to read host state.
Evidence
- lossless-claw audit investigation report:
audit/pr516/INVESTIGATION-design-calls.md lines 49-53 (in the lossless-claw repo, pr516-audit-fixes branch — gitignored, but the fact-finding cites are upstream).
- Plugin-sdk public surface:
src/plugins/runtime/types.ts:53-66 (in-host) and dist/plugin-sdk/plugins/runtime/types.d.ts:132+ (consumed by plugins).
- StatusSummary shape that plugins currently can't see:
src/commands/status.summary.ts:218-296.
Notes
This issue was discovered during an adversarial audit of lossless-claw#516. Three independent agents (running in parallel) caught the inertness; the host-side fix is ~50-100 LOC + tests. Happy to draft a PR if there's interest in the curated-accessor approach.
Summary
Plugins cannot invoke existing host gateway RPCs (
status,models.list, etc.) —PluginRuntimeexposes only the curatedsubagent.{run, waitForRun, getSessionMessages, getSession, deleteSession}methods. Plugins can REGISTER gateway methods viaregisterGatewayMethodbut cannot CALL existing ones.Problem to solve
The gap was discovered during an adversarial audit of Martian-Engineering/lossless-claw#516 (temporal-spine LCM). Commit
38fc1ebattempted to wire smartdetailLevelauto-pick by reading host runtime status —callGateway({method:"status"})andcallGateway({method:"models.list"}). Both calls always throw because the plugin'scallGatewayswitch falls throughdefault: throw "Unsupported gateway method..."— the plugin-sdk surface gives plugins no way to reach the host'sgetStatusSummary()orlistModels(). The author wrote// No new openclaw-side wiring required.in the commit message; that claim is wrong, and the feature shipped inert until an audit caught it.The host RPCs themselves DO exist —
src/gateway/server-methods/health.ts:30(status) andsrc/gateway/server-methods/models.ts:13(models.list). They're registered inBASE_METHODSand called by the TUI today (src/tui/gateway-chat.ts:240/:244). But there's no plugin-sdk path to them.Proposed solution
Add a curated read-only RPC accessor surface on
PluginRuntime:Or, less curated, a general allowlisted
runtime.gateway.request(method, params)that enforces a method allowlist (initially:status,models.list).Either shape solves the immediate use case. Curated accessors are easier to reason about for replay determinism (frozen-snapshot semantics per accessor); a generic
request()requires per-method snapshot handling.The
StatusSummaryandModelCatalogEntrytypes should be exported from the plugin-sdk public surface (separate issue forthcoming on type export hygiene).Alternatives considered
gatewayClient.requestto plugins — too broad; gives plugins ability to call any RPC including mutating ones (agent.cancel, etc.). The curated read-only accessors are safer.Impact
Evidence
audit/pr516/INVESTIGATION-design-calls.mdlines 49-53 (in the lossless-claw repo,pr516-audit-fixesbranch — gitignored, but the fact-finding cites are upstream).src/plugins/runtime/types.ts:53-66(in-host) anddist/plugin-sdk/plugins/runtime/types.d.ts:132+(consumed by plugins).src/commands/status.summary.ts:218-296.Notes
This issue was discovered during an adversarial audit of lossless-claw#516. Three independent agents (running in parallel) caught the inertness; the host-side fix is ~50-100 LOC + tests. Happy to draft a PR if there's interest in the curated-accessor approach.