src — plugin-sdk
src — plugin-sdk
The src/plugin-sdk module is the public-facing API for developing extensions for Code Buddy. It provides a set of type-safe helper functions and interfaces that simplify the creation of various plugin types: general-purpose plugins, channel integrations, LLM providers, and AI tools. Its primary goal is to offer a consistent, declarative, and robust developer experience for extending Code Buddy's capabilities.
This module acts as a facade, re-exporting and wrapping core internal types and functionalities from other Code Buddy modules (like ../plugins, ../channels, ../providers, ../context) into a developer-friendly SDK.
Module Structure
The plugin-sdk is organized into several sub-modules, each focusing on a specific aspect of plugin development:
core.ts: Foundational types and the primarydefinePluginhelper.channel.ts: Interfaces and helpers for creating channel integrations.llm.ts: Interfaces and helpers for integrating new Large Language Models.tool.ts: Interfaces and helpers for defining AI-callable tools.testing.ts: Utilities for unit testing plugins.index.ts: The main barrel export, consolidating all public SDK components.
graph TD
subgraph Plugin SDK
A[index.ts] --> B(core.ts)
A --> C(channel.ts)
A --> D(llm.ts)
A --> E(tool.ts)
A --> F(testing.ts)
end
subgraph Internal Code Buddy Modules
G[../plugins/types.js]
H[../channels/core.js]
I[../providers/types.js]
J[../context/context-engine.js]
end
B --> G
C --> H
D --> G
D --> I
F --> G
F --> I
F --> E
F --> D
A --> J
Figure 1: High-level dependencies within the Plugin SDK and to internal Code Buddy modules.
Core Plugin Development (src/plugin-sdk/core.ts)
This module provides the fundamental building blocks for any Code Buddy plugin.
Key Concepts
Plugin: The base interface for all Code Buddy plugins, definingactivateanddeactivatemethods.PluginManifest: Metadata about the plugin, including itsid,name,version, anddescription.PluginContext: An object passed to theactivatemethod, providing access to runtime services such as:logger: For logging messages.config: Plugin-specific configuration values.dataDir: A persistent directory for plugin data.registerTool(tool): To register AI tools.registerCommand(command): To register user-invokable commands.registerProvider(provider): To register various service providers (e.g., LLM providers).registerContextEngine(engine): To register a custom context assembly engine.PluginLifecycle: An optional interface defining hooks that allow plugins to perform actions before or after their main activation/deactivation logic:onBeforeActivate?(): Called beforeactivate. Can returnfalseto abort activation.onAfterActivate?(): Called after successfulactivate.onBeforeDeactivate?(): Called beforedeactivate.onAfterDeactivate?(): Called afterdeactivate.
definePlugin Function
The definePlugin function is the primary entry point for defining a general-purpose plugin. It takes a DefinePluginConfig object, which bundles the manifest, optional lifecycle hooks, and the main activate and deactivate functions. This function handles the orchestration of lifecycle hooks around your core logic, ensuring a consistent activation and deactivation flow.
import { definePlugin } from 39;@phuetz/code-buddy/plugin-sdk/core39;;
export default definePlugin({
manifest: {
id: 39;my-first-plugin39;,
name: 39;My First Plugin39;,
version: 39;1.0.039;,
description: 39;A simple example plugin.39;,
permissions: [39;read_config39;],
},
lifecycle: {
onBeforeActivate: async () => {
console.log(39;Preparing to activate...39;);
return true; class="hl-cmt">// Allow activation to proceed
},
onAfterActivate: () => {
console.log(39;Plugin fully activated!39;);
},
},
async activate(ctx) {
ctx.logger.info(39;My First Plugin activated!39;);
class="hl-cmt">// Access config: ctx.config.mySetting
class="hl-cmt">// Register a tool: ctx.registerTool(...)
},
async deactivate() {
console.log(39;My First Plugin deactivated.39;);
class="hl-cmt">// Clean up resources
},
});
Channel Plugins (src/plugin-sdk/channel.ts)
This module facilitates the creation of plugins that enable Code Buddy to communicate with new messaging platforms.
Key Concepts
ChannelPlugin: The core interface for any channel plugin. Implementations must provide methods for:connect(): Establish a connection to the messaging platform.disconnect(): Close the connection.send(message: OutboundMessage): Send a message from Code Buddy to the platform.getStatus(): Report the current connection status.onMessage(handler: (message: InboundMessage) => void): Register a callback to handle incoming messages from the platform.isUserAllowed?(userId: string): Optional method to check if a user is authorized.describeMessageTool?(): Optional method to describe message-action tools exposed by the channel (Native Engine v2026.3.12 alignment).ChannelPluginConfig: Defines the configuration structure for a channel, includingtype,enabled,token,webhookUrl, and various access control lists (allowedUsers,allowedChannels).ChannelMessageToolDescription: An interface for describing message-action tools exposed by a channel, aligning with Native Engine specifications for tool discovery.- Message Types: Re-exports
InboundMessage,OutboundMessage,DeliveryResult,ChannelStatus,ChannelType, and other related types from../channels/core.jsfor convenience.
defineChannel Function
The defineChannel function is a type-safe helper to create a ChannelPlugin instance. It takes a DefineChannelConfig object, which requires implementations for all ChannelPlugin methods.
import { defineChannel, ChannelStatus } from 39;@phuetz/code-buddy/plugin-sdk/channel39;;
let messageHandler: ((message: InboundMessage) => void) | undefined;
let isConnected = false;
const myWebChatChannel = defineChannel({
type: 39;webchat39;,
async connect() {
class="hl-cmt">// Simulate connection setup
console.log(39;WebChat channel connecting...39;);
await new Promise(resolve => setTimeout(resolve, 500));
isConnected = true;
console.log(39;WebChat channel connected.39;);
class="hl-cmt">// In a real scenario, you39;d set up a WebSocket server or similar
},
async disconnect() {
console.log(39;WebChat channel disconnecting...39;);
isConnected = false;
class="hl-cmt">// Clean up server/connections
},
async send(message) {
console.log(`Sending message to webchat: ${message.text}`);
class="hl-cmt">// Simulate sending to connected clients
return { success: true, timestamp: new Date() };
},
getStatus(): ChannelStatus {
return { type: 39;webchat39;, connected: isConnected, authenticated: true };
},
onMessage(handler) {
messageHandler = handler;
class="hl-cmt">// In a real scenario, wire up your incoming message events to call `handler`
class="hl-cmt">// Example: simulate an incoming message after 2 seconds
setTimeout(() => {
if (messageHandler) {
messageHandler({
id: 39;msg-12339;,
channelId: 39;webchat-general39;,
sender: { id: 39;user-abc39;, name: 39;Web User39; },
text: 39;Hello Code Buddy!39;,
timestamp: new Date(),
type: 39;text39;,
});
}
}, 2000);
},
isUserAllowed(userId: string): boolean {
return userId.startsWith(39;user-39;); class="hl-cmt">// Only allow users with 39;user-39; prefix
},
});
LLM Provider Plugins (src/plugin-sdk/llm.ts)
This module provides the API for integrating new Large Language Models (LLMs) into Code Buddy, allowing the AI agent to use different model backends.
Key Concepts
LLMProviderPlugin: Extends the basePluginProviderinterface with LLM-specific methods:type: 'llm': Identifies this as an LLM provider.chat(messages: LLMMessage[], options?: LLMChatOptions): The primary method for conversational interactions.complete?(prompt: string, options?: LLMChatOptions): Optional method for simple text generation.listModels(): Returns a list of available models from this provider.getModelInfo?(modelName: string): Optional method to get detailed information for a specific model.ModelInfo: Describes an LLM model, including itsname,contextWindow,maxOutput, optionalpricing, and capabilities likesupportsVisionorsupportsToolCalling.LLMChatOptions: Defines parameters for LLM interactions, such astemperature,maxTokens,stopsequences, andmodelselection.
defineLLMProvider Function
The defineLLMProvider function is a type-safe helper to create an LLMProviderPlugin. It requires an id, name, initialize, chat, and listModels implementation, along with optional shutdown, complete, and getModelInfo.
import { defineLLMProvider } from 39;@phuetz/code-buddy/plugin-sdk/llm39;;
import type { LLMMessage } from 39;@phuetz/code-buddy/providers/types39;; class="hl-cmt">// Internal type
const myLLMProvider = defineLLMProvider({
id: 39;my-custom-llm39;,
name: 39;My Custom LLM Provider39;,
priority: 10, class="hl-cmt">// Higher priority means preferred
config: {
apiKey: process.env.MY_LLM_API_KEY,
},
async initialize() {
if (!this.config?.apiKey) {
throw new Error(39;My LLM Provider requires an API key.39;);
}
console.log(39;My LLM Provider initialized.39;);
},
async shutdown() {
console.log(39;My LLM Provider shutting down.39;);
},
async chat(messages: LLMMessage[], options) {
console.log(`Chat request for model: ${options?.model || 39;default39;}`);
class="hl-cmt">// Simulate API call to your LLM
const lastMessage = messages[messages.length - 1];
return `Echo: ${lastMessage.content}`;
},
async listModels() {
return [
{ name: 39;my-llm/fast-model39;, contextWindow: 8192, maxOutput: 1024 },
{ name: 39;my-llm/large-model39;, contextWindow: 32768, maxOutput: 4096, supportsToolCalling: true },
];
},
async getModelInfo(modelName: string) {
const models = await this.listModels();
return models.find(m => m.name === modelName);
},
});
Tool Plugins (src/plugin-sdk/tool.ts)
This module provides the API for defining tools that the AI agent can invoke to perform specific actions or retrieve information.
Key Concepts
ToolPlugin: An interface representing a collection ofToolDefinitions.ToolDefinition: Describes a single tool, including:name: A unique, snake\_case identifier (e.g.,fetch_weather).description: A human-readable description, crucial for the LLM to understand when to use the tool.parameters: AParametersSchema(JSON Schema) defining the tool's arguments.readOnly?: A boolean indicating if the tool is read-only (safe for parallel execution).tags?: Optional tags for categorization and RAG selection.execute(args: Record: The function that performs the tool's action.) ToolResult: The standardized return type fortool.execute(), indicatingsuccess,output(for the LLM),error, and optionalmetadata.ParametersSchema: Defines the JSON Schema for tool arguments, following the OpenAI function calling format. It includestype: 'object',properties(a map ofParameterProperty), andrequiredfields.
defineToolPlugin Function
The defineToolPlugin function is a helper to define a tool plugin. It takes an id, name, and an array of ToolDefinitions. It also includes validation to ensure unique tool names within the plugin.
import { defineToolPlugin } from 39;@phuetz/code-buddy/plugin-sdk/tool39;;
export default defineToolPlugin({
id: 39;weather-tools39;,
name: 39;Weather Information Tools39;,
tools: [
{
name: 39;get_current_weather39;,
description: 39;Get the current weather conditions for a specified city.39;,
parameters: {
type: 39;object39;,
properties: {
city: {
type: 39;string39;,
description: 39;The name of the city.39;,
},
unit: {
type: 39;string39;,
enum: [39;celsius39;, 39;fahrenheit39;],
default: 39;celsius39;,
description: 39;The unit of temperature.39;,
},
},
required: [39;city39;],
},
readOnly: true,
async execute({ city, unit }) {
try {
class="hl-cmt">// Simulate API call to a weather service
const temperature = Math.floor(Math.random() * 15) + 15; class="hl-cmt">// 15-30°C
const conditions = [39;sunny39;, 39;cloudy39;, 39;rainy39;][Math.floor(Math.random() * 3)];
const tempUnit = unit === 39;fahrenheit39; ? 39;F39; : 39;C39;;
const displayTemp = unit === 39;fahrenheit39; ? (temperature * 9/5) + 32 : temperature;
return {
success: true,
output: `The weather in ${city} is ${conditions} with a temperature of ${displayTemp}°${tempUnit}.`,
metadata: { city, unit, temperature: displayTemp, conditions },
};
} catch (error: any) {
return { success: false, error: `Failed to fetch weather: ${error.message}` };
}
},
},
],
});
Advanced Topics
Context Engine Alignment (delegateCompactionToRuntime)
The delegateCompactionToRuntime() function is a sentinel, primarily for documentation and future runtime hooks. It signals to the Code Buddy runtime that a plugin's context engine (if it provides one) does not handle its own context compaction. This means the runtime should apply its built-in auto-compaction logic before calling the engine's assemble() method.
Plugins that provide custom context engines should set ownsCompaction = false (or omit it) and can use this function to explicitly state this intent. This aligns with the "Native Engine v2026.3.13-1" specification.
import { delegateCompactionToRuntime } from 39;@phuetz/code-buddy/plugin-sdk39;;
import type { ContextEngine, AssembleResult } from 39;@phuetz/code-buddy/plugin-sdk39;;
class MyCustomContextEngine implements ContextEngine {
readonly ownsCompaction = delegateCompactionToRuntime().ownsCompaction; class="hl-cmt">// Explicitly delegate
async assemble(messages: any[], tools: any[], maxTokens: number): Promise<AssembleResult> {
class="hl-cmt">// The runtime will have already compacted `messages` if ownsCompaction is false
class="hl-cmt">// Your logic here focuses on assembling the final prompt
return {
messages: messages,
tokens: messages.length * 4, class="hl-cmt">// Simplified token count
truncated: false,
};
}
}
Testing Utilities (src/plugin-sdk/testing.ts)
This module provides mock objects and assertion helpers for unit testing plugins without depending on the full Code Buddy runtime.
Key Components
createMockPluginContext(overrides?: MockPluginContextOptions): MockPluginContext:- Creates a mock
PluginContextinstance. - It tracks all registered tools, commands, and providers, allowing you to assert that your plugin correctly registers its components during activation.
- Includes a
MockLoggerfor inspecting log calls. - Example:
import { createMockPluginContext } from 39;@phuetz/code-buddy/plugin-sdk/testing39;;
import myToolPlugin from 39;./my-tool-plugin39;; class="hl-cmt">// Your plugin
describe(39;My Tool Plugin39;, () => {
it(39;should register its tools correctly39;, async () => {
const ctx = createMockPluginContext();
await myToolPlugin.activate(ctx); class="hl-cmt">// Assuming tool plugins have an activate method or are directly registered
expect(ctx.registeredTools).toHaveLength(1);
expect(ctx.registeredTools[0].name).toBe(39;get_current_weather39;);
expect(ctx.logger.messages).toContainEqual(
expect.objectContaining({ level: 39;info39;, message: 39;Plugin activated!39; })
);
});
});
createMockLLMProvider(options?: MockLLMProviderOptions): MockLLMProviderInstance:- Generates a mock
LLMProviderPlugin. - Can be configured with canned responses for
chat()andcomplete()methods. - Tracks all calls made to these methods, enabling assertions on LLM interactions.
- Example:
import { createMockLLMProvider } from 39;@phuetz/code-buddy/plugin-sdk/testing39;;
describe(39;Mock LLM Provider39;, () => {
it(39;should return canned responses and track calls39;, async () => {
const provider = createMockLLMProvider({
responses: [39;First response39;, 39;Second response39;],
});
const result1 = await provider.chat([{ role: 39;user39;, content: 39;Hi39; }]);
expect(result1).toBe(39;First response39;);
expect(provider.chatCalls).toHaveLength(1);
const result2 = await provider.chat([{ role: 39;user39;, content: 39;How are you?39; }]);
expect(result2).toBe(39;Second response39;);
expect(provider.chatCalls).toHaveLength(2);
});
});
assertToolResult(result: ToolResult, expected: Partial:): void - A utility function to assert the properties of a
ToolResult. - Checks
success,output(partial string match), anderror(partial string match), throwing descriptive errors on mismatches. - Example:
import { assertToolResult } from 39;@phuetz/code-buddy/plugin-sdk/testing39;;
import { ToolResult } from 39;@phuetz/code-buddy/plugin-sdk/tool39;;
describe(39;assertToolResult39;, () => {
it(39;should assert successful tool results39;, () => {
const result: ToolResult = { success: true, output: 39;Data fetched: 12339; };
assertToolResult(result, { success: true, output: 39;Data fetched39; });
});
it(39;should assert failed tool results39;, () => {
const result: ToolResult = { success: false, error: 39;Network error39; };
expect(() => assertToolResult(result, { success: true })).toThrow(/Expected tool result success to be true, got false/);
assertToolResult(result, { success: false, error: 39;Network39; });
});
});
MockLogger: An interface and implementation for a logger that records all messages, allowing tests to verify log output. This is integrated intoMockPluginContext.
These testing utilities are crucial for maintaining the quality and reliability of plugins by enabling isolated and efficient unit testing.