Skip to content

[Feature]: plugin-sdk surface for invoking host gateway RPCs (status, models.list, etc.) #76756

@100yenadmin

Description

@100yenadmin

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions