Skip to content

feat(plugins): add sessions.spawn and rateLimit to plugin runtime#16558

Draft
Zephyr-Blessed wants to merge 2 commits intoopenclaw:mainfrom
Zephyr-Blessed:feat/plugin-session-spawn
Draft

feat(plugins): add sessions.spawn and rateLimit to plugin runtime#16558
Zephyr-Blessed wants to merge 2 commits intoopenclaw:mainfrom
Zephyr-Blessed:feat/plugin-session-spawn

Conversation

@Zephyr-Blessed
Copy link

Summary

Exposes two new capabilities on the plugin runtime:

1. api.runtime.sessions.spawn()

Plugins can spawn isolated agent sessions with restricted tool policies. This enables plugins to process external requests (webhooks, A2A messages, inter-agent communication) safely — the plugin controls which tools each session gets.

const result = await api.runtime.sessions.spawn({
  message: "Check my calendar for free slots this week",
  systemPrompt: "You are handling a request from a trusted friend's agent. Only use calendar tools.",
  toolPolicy: {
    allow: ["calendar", "web_search"],
    deny: ["exec", "write", "message"],
  },
  timeoutSeconds: 60,
  label: "a2a-calendar-check",
});

Uses the existing subagent infrastructure (callGatewayagent handler). No new execution machinery — just exposing what sessions_spawn already does internally, but accessible to plugins.

Built-in safety guardrails:

  • Max 10 concurrent plugin-spawned sessions
  • Max 20 spawns per minute (global across all plugins)
  • Existing tools.subagents.tools.allow/deny config applies to all spawned sessions

2. api.runtime.rateLimit.check()

In-memory sliding-window rate limiter. Zero dependencies. Plugins provide the key (IP, sender URL, agent ID) and limits.

if (!api.runtime.rateLimit.check(`ip:${remoteAddress}`, { maxRequests: 30, windowMs: 60_000 })) {
  return res.writeHead(429).end("Rate limited");
}

Use case: A2A (Agent-to-Agent) protocol

We're building an A2A plugin that enables OpenClaw agents to communicate with other agents via Google's A2A protocol. A friend's agent can ask your agent to check your calendar and book a meeting — but a stranger's agent gets chat-only access with no tools.

This requires plugins to be able to route external messages through the LLM with controlled tool access, which isn't currently possible.

Discussion: Config gating?

We considered adding a config toggle (e.g. plugins.allowSessionSpawn: true) but decided against it because:

  • No existing PluginRuntime method requires config opt-in
  • Plugins are treated as trusted code ("treat them as trusted code" per docs)
  • The existing tools.subagents.tools config already controls spawned session tool access
  • Plugin installation itself (plugins.allow/deny) is the trust gate

Question for maintainers: Would you prefer a config gate here? We're happy to add one if the team feels it's warranted. The hardcoded concurrency/rate limits provide basic safety regardless.

Files changed

File Change
src/plugins/runtime/types.ts New types: PluginSessionSpawnOptions, PluginSessionSpawnResult, RateLimitOptions
src/plugins/runtime/index.ts Implementation: spawnPluginSession, rateLimitCheck/Reset, concurrency guards
src/plugin-sdk/index.ts Export new types
docs/tools/plugin.md Documentation with examples
src/plugins/runtime/rate-limit.test.ts Rate limiter tests

Note on tool policy override

The current implementation passes toolPolicy via sessions.patch before spawning. If sessions.patch doesn't support toolPolicy yet, the session falls back to the default subagent tool policy (which already denies sessions_spawn, gateway, cron, memory_*, etc.). The extraSystemPrompt provides additional guidance to the LLM.

A follow-up PR could add toolPolicyOverride to the agent gateway method for runtime-enforced per-session tool restrictions.


This PR is part of the OpenClaw A2A plugin project — enabling inter-agent communication via Google's A2A protocol.

Plugins can now:

1. Spawn isolated agent sessions with restricted tool policies:
   `api.runtime.sessions.spawn({ message, systemPrompt, toolPolicy })`
   - Enables plugins to process external requests (A2A, webhooks) safely
   - Tool policy controls which tools the session can use
   - Supports model override, timeout, and labeling
   - Uses existing subagent infrastructure (callGateway + agent handler)

2. Rate-limit incoming requests with a sliding-window limiter:
   `api.runtime.rateLimit.check(key, { maxRequests, windowMs })`
   - In-memory, zero dependencies
   - Plugin provides the key (IP, sender URL, agent ID)
   - Protects against token/cost abuse from external callers

Use case: A2A (Agent-to-Agent) protocol plugins that receive messages
from external agents and need to process them with controlled permissions.
A friend's agent can read your calendar; a stranger gets chat-only.

Files changed:
- src/plugins/runtime/types.ts — New types for session spawn + rate limit
- src/plugins/runtime/index.ts — Implementation
- src/plugin-sdk/index.ts — Export new types
- docs/tools/plugin.md — Documentation with examples
- src/plugins/runtime/rate-limit.test.ts — Rate limiter tests
Prevents runaway costs from buggy or malicious plugins:
- Max 10 concurrent plugin-spawned sessions
- Max 20 spawns per minute (global across all plugins)
- Both are hardcoded safeguards, not config — matches the pattern
  that plugins are trusted code but mistakes happen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments