SuperAPI AI Directory

pi-gateway-plugin-dev

Coding & development

View repository

Updated 10-15 21:36

About this skill

Pi Gateway Plugin Dev skill

Used to develop plugins in gateway mode , not pi extension。

Applicable boundaries

  • The goal is to extend the connection-layer capabilities of pi-gateway (message channels, HTTP/WS, commands, services)
  • The plugin loading location is ~/.pi/gateway/plugins/ or the directory specified by plugins.dirs[] Specify directory
  • A plugin must have plugin.json, and point to the entry file via main Point to the entry file

Do not use this skill in the following scenarios:

  • Modifying Agent thinking, prompts, or tool execution behavior (that is the responsibility of ~/.pi/agent/extensions )
  • Features that run only in the CLI/TUI and do not go through the gateway

Mandatory specifications

  1. One independent directory per plugin (<plugins-root>/<plugin-id>/)。
  2. Single-file mode is prohibited: you cannot just put one index.ts and be done.
  3. The entry file only assembles; it does not contain core business logic.
  4. Business is split by responsibility into src/commands.ts、src/rpc-methods.ts、src/hooks.ts、src/services.ts、src/types.ts。
  5. Plugins are loaded only in the gateway plugin mechanism, and are not placed in ~/.pi/agent/extensions/。

Standard directory

<plugins-root>/<plugin-id>/
  plugin.json
  src/
    index.ts
    types.ts
    commands.ts
    rpc-methods.ts
    hooks.ts
    services.ts

Workflow

  1. First determine that it is a Gateway plugin requirement (not a pi extension).
  2. Read the directory specification:references/plugin-directory-architecture.md
  3. Read the SDK interface:references/sdk-capabilities.md
  4. Read the RPC capability mapping:references/rpc-capabilities.md
  5. If model capabilities are involved, read:references/model-control-pattern.md
  6. Use the scaffold to generate the plugin directory, then fill in the business logic.
  7. In pi-gateway.json , configure plugins.dirs[] and start it to verify.

Scaffold command

bash skills/pi-gateway-plugin-dev/scripts/new-plugin.sh ~/.pi/gateway/plugins model-control "Model Control"

After generation, you will get a complete multi-file plugin directory, and it will not degrade into a single index.ts。

Model capability implementation strategy

  • Switch model: use GatewayPluginApi.setModel(sessionKey, provider, modelId)。
  • Adjust thinking intensity: use GatewayPluginApi.setThinkingLevel(sessionKey, level)。
  • List models: the current GatewayPluginApi does not directly expose getAvailableModels, so it is recommended to reuse the gateway's built-in models.list / /api/models。
  • If the plugin must connect directly to RPC internally: first extend GatewayPluginApi + createPluginApi + RpcClient mapping, see references/rpc-capabilities.md 's "extension path".

Tool Streaming support

pi-gateway tools support streaming execution, updating progress in real time via the onPartialResult callback:

Tool execution method signature

async execute(
  toolCallId: string,
  args: unknown,
  signal: AbortSignal,
  onPartialResult?: (partial: ToolResult) => void
): Promise<ToolResult>

Implement a streaming tool

// src/tools/my-streaming-tool.ts
export function createMyStreamingTool() {
  return {
    name: "my_streaming_tool",
    parameters: Type.Object({
      text: Type.String(),
      stream: Type.Optional(Type.Boolean({ default: true })),
    }),
    async execute(
      _toolCallId: string,
      params: unknown,
      signal: AbortSignal,
      onPartialResult?: (partial: ToolResult) => void
    ) {
      const { text, stream } = params as { text: string; stream?: boolean };
      
      if (!stream) {
        // 非流式:直接返回结果
        return { content: [{ type: "text", text }] };
      }
      
      // 流式:分块处理,每块调用 onPartialResult
      const chunks = splitIntoChunks(text, 80);
      let accumulated = "";
      
      for (let i = 0; i < chunks.length; i++) {
        if (signal?.aborted) {
          return { content: [{ type: "text", text: "Aborted" }], details: { error: true } };
        }
        
        accumulated += chunks[i];
        
        // 报告进度
        if (onPartialResult) {
          onPartialResult({
            content: [{ type: "text", text: accumulated + (i < chunks.length - 1 ? "…" : "") }],
            details: { progress: i + 1, total: chunks.length },
          });
        }
        
        // 模拟处理延迟
        await new Promise((resolve) => setTimeout(resolve, 100));
      }
      
      return {
        content: [{ type: "text", text: accumulated }],
        details: { completed: true, chunks: chunks.length },
      };
    },
  };
}

Key points

  1. AbortSignal handling: Check signal.aborted to support cancellation
  2. PartialResult structure: Contains the content array and optional details metadata
  3. Visual feedback: Use … or another indicator to show that there is more content
  4. Progressive updates: Call onPartialResultafter each stage is completed; do not wait until everything is complete

Reference implementation

View the streaming implementation of the send_message tool:

  • extensions/gateway-tools/send-message.ts

Delivery requirements

  • Output the plugin directory tree
  • Output plugin.json and key source files
  • Explain how to load it in gateway mode (plugins.dirs[])
  • Provide the minimal verification command (at least covering "list models/switch models" or the corresponding capability)
  • (Optional) If the tool supports streaming, provide a streaming call example