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 byplugins.dirs[]Specify directory - A plugin must have
plugin.json, and point to the entry file viamainPoint 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
- One independent directory per plugin (
<plugins-root>/<plugin-id>/)。 - Single-file mode is prohibited: you cannot just put one
index.tsand be done. - The entry file only assembles; it does not contain core business logic.
- Business is split by responsibility into
src/commands.ts、src/rpc-methods.ts、src/hooks.ts、src/services.ts、src/types.ts。 - 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
- First determine that it is a Gateway plugin requirement (not a pi extension).
- Read the directory specification:
references/plugin-directory-architecture.md - Read the SDK interface:
references/sdk-capabilities.md - Read the RPC capability mapping:
references/rpc-capabilities.md - If model capabilities are involved, read:
references/model-control-pattern.md - Use the scaffold to generate the plugin directory, then fill in the business logic.
- In
pi-gateway.json, configureplugins.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
GatewayPluginApidoes not directly exposegetAvailableModels, so it is recommended to reuse the gateway's built-inmodels.list//api/models。 - If the plugin must connect directly to RPC internally: first extend
GatewayPluginApi+createPluginApi+RpcClientmapping, seereferences/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
- AbortSignal handling: Check
signal.abortedto support cancellation - PartialResult structure: Contains the
contentarray and optionaldetailsmetadata - Visual feedback: Use
…or another indicator to show that there is more content - 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.jsonand 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