Plugins
Plugins are swappable, shape-conforming components. Built-in and user-provided plugins follow the exact same shapes — the plugin registry auto-discovers both on startup.
Plugin Shape
Every plugin has a top-level Plugin record:
pub type Plugin {
Plugin(
name: String,
description: String,
plugin_type: PluginType,
supervised: Bool,
start: fn() -> Result(Nil, String),
stop: fn() -> Result(Nil, String),
health: fn() -> Result(String, String),
)
}The plugin_type determines which sub-type shape the plugin must also implement.
Plugin Types
Tools
Things the agent can do. Each tool implements the Tool shape:
pub type Tool {
Tool(
name: String,
description: String,
parameters: JsonSchema,
execute: fn(Map(String, JsonValue)) -> Result(String, String),
parallel_safe: Bool,
)
}Built-in tools:
| Tool | Module | Description |
|---|---|---|
bash | plugins/tools/bash/ | Shell command execution (sandboxed) |
web | plugins/tools/web/ | Web page fetching (SSRF-hardened) |
browser | plugins/tools/browser/ | Playwright browser automation (6 actions) |
code | plugins/tools/code/ | Python/Node/Bash code executor |
memory | plugins/tools/memory/ | Memory CRUD (SQLite-backed) |
session_search | plugins/tools/session_search/ | Search past conversations |
cron | plugins/tools/cron/ | Cron job management |
send_message | plugins/tools/gateways/telegram/ | Send Telegram messages |
Gateways
Channels the agent communicates through. Each gateway implements the Gateway shape:
pub type Gateway {
Gateway(
name: String,
gateway_type: GatewayType,
start: fn() -> Result(Nil, String),
stop: fn() -> Result(Nil, String),
health: fn() -> Result(String, String),
)
}Built-in gateways:
- Telegram — real Telegram bot using telega's OTP supervision tree with long-polling
- TUI — terminal UI for the CLI REPL
Hooks
Lifecycle callbacks that fire at specific points in the conversation loop.
Built-in hooks:
- Context Compressor — summarizes middle conversation messages when the context window fills up
- Reflection — post-turn memory consolidation (nudge engine)
- Tool Guardrails — circuit breaker for detecting and interrupting stuck tool loops
Memory Plugins
Persistence backends for agent memory.
Built-in memory plugins:
- File Memory — file-based memory storage
Writing a Custom Plugin
Custom Tool
Create a module at ~/.agent/tools/my_tool.gleam:
import plugins/shapes
pub fn plugin() -> shapes.Plugin {
shapes.Plugin(
name: "my_tool",
description: "Does something useful",
plugin_type: shapes.ToolType(my_tool()),
supervised: False,
start: fn() { Ok(Nil) },
stop: fn() { Ok(Nil) },
health: fn() { Ok("ok") },
)
}
fn my_tool() -> shapes.Tool {
shapes.Tool(
name: "my_tool",
description: "Does something useful",
parameters: shapes.empty_schema(),
execute: fn(_params) { Ok("Hello from my_tool!") },
parallel_safe: True,
)
}The plugin registry auto-discovers any .gleam file in ~/.agent/tools/ on startup. Your tool appears in the agent's tool list immediately.
Custom Hook
Same pattern — create a module in ~/.agent/hooks/ that implements the Hook shape. Hooks can be BeforeTool (fire before tool execution), AfterTool (after tool execution), or AfterTurn (after each conversation turn).
Custom Gateway
Gateways follow the same pattern in ~/.agent/gateways/. A gateway's job is to translate between the agent's internal message format and an external chat platform. See the built-in Telegram gateway as a reference.