Plugins

Plugin Actions

Understand command, api, execute, and allowWhenDisabled 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

When an action is called by a Session tool, its parameters also include the explicit execution_context for that Session Turn. It may be absent for direct CLI, HTTP, or scheduler calls. Use execution_context.workspace_env and execution_context.agent_systems for the effective Step configuration; context.workspace_env is the current Workspace snapshot and context.instructions contains Agent instructions.

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

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 action execution is designed around the PluginCommandContext shape.

Practically, you can think of it as:

  • a stable minimal execution context for plugin work

It includes the current Agent id, project root, logger, and resolved env. Product configuration for a Plugin is passed by the upstream host when constructing the Plugin instance; actions do not read or persist it through their context.

A slightly fuller example

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