> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clarkos.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Building Custom Plugins

> Extend your agent with custom capabilities

## Overview

Plugins are the primary way to extend ClarkOS agents. They hook into the tick lifecycle to add custom behavior—posting to social media, fetching data, executing trades, or anything else.

## Plugin Interface

Every plugin implements this interface:

| Property       | Required | Description                         |
| -------------- | -------- | ----------------------------------- |
| `name`         | Yes      | Unique identifier                   |
| `version`      | Yes      | Semver version string               |
| `description`  | No       | Human-readable description          |
| `dependencies` | No       | Other plugins that must load first  |
| `init()`       | No       | Called once when agent starts       |
| `cleanup()`    | No       | Called when agent stops             |
| `onTick()`     | No       | Called after every tick             |
| `actions`      | No       | Callable functions exposed to agent |

**Reference:** `Plugin` interface in [`src/plugins/types.ts`](https://github.com/clarkOS/clark/blob/main/example/convex/src/plugins/types.ts)

## Basic Plugin

```typescript theme={null}
import type { Plugin } from "./src/plugins";

export const loggerPlugin: Plugin = {
  name: "logger",
  version: "1.0.0",

  onTick(context) {
    console.log(`Tick: mood=${context.state.mood}, health=${context.state.health}`);
  }
};
```

## Factory Pattern (Recommended)

Use a factory function for configurable plugins:

```typescript theme={null}
interface MyPluginConfig {
  enabled: boolean;
  cooldownMs: number;
}

export function createMyPlugin(config: MyPluginConfig): Plugin {
  let lastAction = 0;

  return {
    name: "my-plugin",
    version: "1.0.0",

    onTick(context) {
      if (!config.enabled) return;
      if (Date.now() - lastAction < config.cooldownMs) return;

      // Do something
      lastAction = Date.now();
    }
  };
}
```

## Lifecycle Hooks

### init()

Called once when the plugin is registered. Use for setup:

* Establish connections
* Validate configuration
* Load initial state

### cleanup()

Called when the agent stops. Use for teardown:

* Close connections
* Flush buffers
* Save state

### onTick()

Called after every tick with full context. Use for reactive behavior:

* Check state conditions before acting
* Access memories and knowledge
* Trigger external actions

**Reference:** `TickContext` in [`src/core/types.ts`](https://github.com/clarkOS/clark/blob/main/example/convex/src/core/types.ts)

## Plugin Actions

Expose callable functions via the `actions` property:

```typescript theme={null}
actions: {
  async getData(params, agent) {
    return { success: true, data: "..." };
  },

  async doSomething(params, agent) {
    return { success: true };
  }
}
```

Call actions from code:

```typescript theme={null}
const result = await agent.executeAction("my-plugin", "getData", { query: "..." });
```

## Plugin Dependencies

Declare dependencies to ensure load order:

```typescript theme={null}
export const enhancedPlugin: Plugin = {
  name: "enhanced",
  dependencies: ["base-plugin", "analytics"],

  init(agent) {
    const base = agent.getPlugin("base-plugin");
    // base is guaranteed to exist
  }
};
```

**Reference:** `sortPluginsByDependencies()` in [`src/plugins/loader.ts`](https://github.com/clarkOS/clark/blob/main/example/convex/src/plugins/loader.ts) handles topological sorting

## Registering Plugins

```typescript theme={null}
const agent = new Agent({
  backend,
  plugins: [loggerPlugin, createMyPlugin({ enabled: true, cooldownMs: 60000 })]
});

// Or add later
agent.use(anotherPlugin);
```

## Error Handling

Never let plugin errors crash the agent:

```typescript theme={null}
onTick(context) {
  try {
    await this.riskyOperation();
  } catch (error) {
    console.error("Plugin error:", error);
    // Don't re-throw—let agent continue
  }
}
```

## Testing Plugins

Use the `MemoryBackend` for testing without a real database:

```typescript theme={null}
import { MemoryBackend } from "./src/backend/memory";

const backend = new MemoryBackend();
const agent = new Agent({ backend, plugins: [myPlugin] });

await agent.tick();
// Assert expected behavior
```

**Reference:** `MemoryBackend` in [`src/backend/memory.ts`](https://github.com/clarkOS/clark/blob/main/example/convex/src/backend/memory.ts)

## Best Practices

* **Single responsibility**: One plugin, one purpose
* **Graceful failures**: Log errors, don't throw
* **Respect rate limits**: Track API usage, implement cooldowns
* **Configurable**: Use factory functions with config objects
* **Testable**: Use dependency injection

## Next Steps

<CardGroup cols={2}>
  <Card title="Official Plugins" href="/plugins/official">
    Example plugins to learn from.
  </Card>

  <Card title="Plugin SDK Reference" href="/api-reference/sdk/plugins">
    Complete API documentation.
  </Card>
</CardGroup>
