Creating an Adapter
A custom Adapter lets you connect SLAW to any AI runtime, coding tool, or execution environment by implementing the ServerAdapterModule interface and packaging it as an npm module.
Prerequisites
- SLAW running locally (
npx slaw onboard --yesor see Local Development) - Node.js 22 or later
- Familiarity with the Adapter UI Parser (optional, for custom transcript rendering)
1. Implement ServerAdapterModule
Create an npm package that exports a ServerAdapterModule-compatible object:
import type { ServerAdapterModule } from "@slaw/adapter-utils";
export const myAdapter: ServerAdapterModule = {
type: "my_adapter", // unique type key
async execute(ctx) {
const { runId, agent, config, onLog } = ctx;
// Start your process or make your API call here.
// Call onLog("stdout", line) for each output line.
await onLog("stdout", "Hello from my adapter\n");
return {
exitCode: 0,
signal: null,
timedOut: false,
};
},
async testEnvironment(ctx) {
// Return a health check result for the Adapters settings page.
return { status: "ok" };
},
};
Key interface fields
| Field | Required | Description |
|---|---|---|
type | Yes | Unique adapter type key string |
execute(ctx) | Yes | Called by SLAW on each Heartbeat; must return AdapterExecutionResult |
testEnvironment(ctx) | Yes | Health check shown in the SLAW Adapters settings page |
models | No | Static list of AdapterModel objects for model picker |
listModels() | No | Dynamic model discovery (overrides models) |
modelProfiles | No | Named model profiles (for example cheap) |
sessionCodec | No | Serialize/deserialize session state between Heartbeats |
listSkills() | No | Enumerate adapter-managed skills |
syncSkills() | No | Sync desired skills into the runtime |
agentConfigurationDoc | No | Markdown string displayed in the agent config UI |
AdapterExecutionContext
Your execute() function receives:
| Field | Type | Description |
|---|---|---|
runId | string | Unique ID for this Heartbeat run |
agent | object | The agent record (ID, squad, role, config) |
config | object | The adapter config for this agent |
context | object | Issue context (identifier, title, description, comments) |
onLog | function | (stream, chunk) => Promise<void> — stream stdout/stderr to SLAW |
onMeta | function | (meta) => Promise<void> — emit structured invocation metadata |
2. Declare a config schema (optional)
Export a JSON Schema to get a generated config form in the SLAW UI:
export const configSchema = {
type: "object",
properties: {
endpoint: { type: "string", description: "API endpoint URL" },
model: { type: "string", description: "Model ID" },
},
required: ["endpoint"],
};
3. Ship a UI parser (optional)
Include a ui-parser.js bundle to control how your agent's stdout renders as transcript entries. See Adapter UI Parser for the protocol and an implementation example.
Export the bundle path in your package.json:
{
"exports": {
".": "./dist/index.js",
"./ui-parser": "./dist/ui-parser.js"
}
}
SLAW automatically discovers and loads ui-parser.js when it is present.
4. Package and install
- Build your package and publish it to an npm registry, or point at a local path.
- Install it as an adapter plugin via the SLAW UI (Settings → Adapters → Install) or the API:
POST /api/squads/{squadId}/adapter-plugins
{
"packageName": "@acme/my-adapter",
"version": "1.0.0"
}
- After installation,
my_adapterappears as an option when creating or editing agents.
5. Verify
Open Settings → Adapters and confirm your adapter appears with a green health check from testEnvironment(). Create a test agent with adapterType: my_adapter, trigger a Heartbeat manually, and inspect the run log.
Notes
- Use
@slaw/adapter-utilsfor shared helpers (buildSlawEnv,runChildProcess,asString, etc.). Import from@slaw/adapter-utils/server-utilsfor execution utilities. - SLAW injects
SLAW_*environment variables into every process run automatically. - For session continuity across Heartbeats, implement
sessionCodecto serialize and deserialize your session state. - An adapter's
typestring must be globally unique within a SLAW instance. Use a namespaced format (@acme/my_runtimestyle) to avoid collisions with other external adapters.