Plugins

Plugin Actions

Understand command, api, execute, execution context, and cooperative timeouts in plugin actions

Plugin Actions

Plugin actions are the most direct explicit capability surface of a plugin.

An action is mainly built from:

  • allowWhenDisabled
  • command
  • api
  • execute

The core layer is execute

Minimal example:

actions: {
  status: {
    execute: async ({ context }) => {
      return {
        success: true,
        data: {
          workspace_path: context.workspace_path,
        },
      };
    },
  },
}

execute is responsible for:

  • receiving structured payload
  • running the business logic
  • returning success / data / error / message

Every Action call receives two separate readonly inputs:

  • context: stable Agent and Workspace capabilities such as identity, files, Shell, logger, web services, sessions, and plugins
  • execution: per-call identity, cancellation, optional Session scope, and the effective Step snapshot

execution.call_id matches the Tool Call ID for model tools. execution.session contains session_id, turn_id, and optional interactions for Session calls. execution.snapshot contains effective workspace_env and agent_systems. Direct CLI, HTTP, and scheduler calls receive a runtime-generated call_id and cancellation signal without a Session scope. Pass execution.abort_signal to network requests, polling, and other long-running operations.

command

command handles CLI-side mapping.

It is a good place to define:

  • help text
  • commander arguments and options
  • how CLI input becomes structured payload

api

api handles HTTP-side input mapping.

It is a good place to define:

  • method
  • path
  • how an HTTP request becomes structured payload

timeout_ms

An Action may declare a cooperative timeout:

actions: {
  search: {
    timeout_ms: 30_000,
    execute: async ({ execution }) => {
      const response = await fetch("https://example.com", {
        signal: execution.abort_signal,
      });
      return { success: true, data: { status: response.status } };
    },
  },
}

The timeout aborts execution.abort_signal. This is cooperative: the Action must pass the signal to the underlying operation. A business failure does not change the Plugin lifecycle state.

allowWhenDisabled

This is one of the easiest plugin-specific details to miss.

It means:

  • this action is still allowed even if the plugin is currently disabled

Typical cases:

  • status
  • install
  • configure
  • models

In other words, the actions you often need precisely because the plugin is not yet fully usable.

Without allowWhenDisabled: true, normal actions on a disabled plugin are blocked.

What context does execute receive

Plugin Actions deliberately separate stable capabilities from per-call state. Read Agent and Workspace services from context; read call identity, cancellation, Session interaction, and the effective Step snapshot from execution. Product configuration is passed by the upstream host when constructing the Plugin instance.

A slightly fuller example

actions: {
  use: {
    allowWhenDisabled: true,
    command: {
      description: "switch the default provider",
      mapInput({ args }) {
        return {
          provider: String(args[0] || ""),
        };
      },
    },
    execute: async ({ input }) => {
      const provider = String((input as { provider?: unknown }).provider || "").trim();
      if (!provider) {
        return {
          success: false,
          error: "provider is required",
          message: "provider is required",
        };
      }
      return {
        success: true,
        data: { provider },
      };
    },
  },
}