Aller au contenu

External Adapters

Ce contenu n’est pas encore disponible dans votre langue.

AImetier supports external adapter plugins that can be installed from npm packages or local directories. External adapters work exactly like built-in adapters — they execute agents, parse output, and render transcripts — but they live in their own package and don’t require changes to AImetier’s source code.

Built-in External
Source location Inside aimetier-fork/packages/adapters/ Separate npm package or local directory
Registration Hardcoded in three registries Loaded at startup via plugin system
UI parser Static import at build time Dynamically loaded from API (see UI Parser)
Distribution Ships with AImetier Published to npm or linked via file:
Updates Requires AImetier release Independent versioning
my-adapter/
package.json
tsconfig.json
src/
index.ts # Shared metadata (type, label, models)
server/
index.ts # createServerAdapter() factory
execute.ts # Core execution logic
parse.ts # Output parsing
test.ts # Environment diagnostics
ui-parser.ts # Self-contained UI transcript parser
{
"name": "my-aimetier-adapter",
"version": "1.0.0",
"type": "module",
"license": "MIT",
"aimetier": {
"adapterUiParser": "1.0.0"
},
"exports": {
".": "./dist/index.js",
"./server": "./dist/server/index.js",
"./ui-parser": "./dist/ui-parser.js"
},
"files": ["dist"],
"scripts": {
"build": "tsc"
},
"dependencies": {
"@aimetier/adapter-utils": "^2026.325.0",
"picocolors": "^1.1.0"
},
"devDependencies": {
"@types/node": "^22.0.0",
"typescript": "^5.7.0"
}
}

Key fields:

Field Purpose
exports["."] Entry point — must export createServerAdapter
exports["./ui-parser"] Self-contained UI parser module (optional but recommended)
aimetier.adapterUiParser Contract version for the UI parser ("1.0.0")
files Limits what gets published — only dist/
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"]
}

The plugin loader calls createServerAdapter() from your package root. This function must return a ServerAdapterModule.

export const type = "my_adapter"; // snake_case, globally unique
export const label = "My Agent (local)";
export const models = [
{ id: "model-a", label: "Model A" },
];
export const agentConfigurationDoc = `# my_adapter configuration
Use when: ...
Don't use when: ...
`;
// Required by plugin-loader convention
export { createServerAdapter } from "./server/index.js";
import type { ServerAdapterModule } from "@aimetier/adapter-utils";
import { type, models, agentConfigurationDoc } from "../index.js";
import { execute } from "./execute.js";
import { testEnvironment } from "./test.js";
export function createServerAdapter(): ServerAdapterModule {
return {
type,
execute,
testEnvironment,
models,
agentConfigurationDoc,
};
}

The core execution function. Receives an AdapterExecutionContext and returns an AdapterExecutionResult.

import type {
AdapterExecutionContext,
AdapterExecutionResult,
} from "@aimetier/adapter-utils";
import {
runChildProcess,
buildAImetierEnv,
renderTemplate,
} from "@aimetier/adapter-utils/server-utils";
export async function execute(
ctx: AdapterExecutionContext,
): Promise<AdapterExecutionResult> {
const { config, agent, runtime, context, onLog, onMeta } = ctx;
// 1. Read config with safe helpers
const cwd = String(config.cwd ?? "/tmp");
const command = String(config.command ?? "my-agent");
const timeoutSec = Number(config.timeoutSec ?? 300);
// 2. Build environment with AImetier vars injected
const env = buildAImetierEnv(agent);
// 3. Render prompt template
const prompt = config.promptTemplate
? renderTemplate(String(config.promptTemplate), {
agentId: agent.id,
agentName: agent.name,
companyId: agent.companyId,
runId: ctx.runId,
taskId: context.taskId ?? "",
taskTitle: context.taskTitle ?? "",
})
: "Continue your work.";
// 4. Spawn process
const result = await runChildProcess(command, {
args: [prompt],
cwd,
env,
timeout: timeoutSec * 1000,
graceMs: 10_000,
onStdout: (chunk) => onLog("stdout", chunk),
onStderr: (chunk) => onLog("stderr", chunk),
});
// 5. Return structured result
return {
exitCode: result.exitCode,
timedOut: result.timedOut,
// Include session state for persistence
sessionParams: { /* ... */ },
};
}

Available Helpers from @aimetier/adapter-utils

Section titled “Available Helpers from @aimetier/adapter-utils”
Helper Purpose
runChildProcess(command, opts) Spawn a child process with timeout, grace period, and streaming callbacks
buildAImetierEnv(agent) Inject AIMETIER_* environment variables
renderTemplate(template, data) {{variable}} substitution in prompt templates
asString(v), asNumber(v), asBoolean(v) Safe config value extraction

Validates the adapter configuration before running. Returns structured diagnostics.

import type {
AdapterEnvironmentTestContext,
AdapterEnvironmentTestResult,
} from "@aimetier/adapter-utils";
export async function testEnvironment(
ctx: AdapterEnvironmentTestContext,
): Promise<AdapterEnvironmentTestResult> {
const checks = [];
// Example: check CLI is installed
checks.push({
level: "info",
message: "My Agent CLI v1.2.0 detected",
code: "cli_detected",
});
// Example: check working directory
const cwd = String(ctx.config.cwd ?? "");
if (!cwd.startsWith("/")) {
checks.push({
level: "error",
message: `Working directory must be absolute: "${cwd}"`,
hint: "Use /home/user/project or /workspace",
code: "invalid_cwd",
});
}
return {
adapterType: ctx.adapterType,
status: checks.some(c => c.level === "error") ? "fail" : "pass",
checks,
testedAt: new Date().toISOString(),
};
}

Check levels:

Level Meaning Effect
info Informational Shown in test results
warn Non-blocking issue Shown with yellow indicator
error Blocks execution Prevents agent from running
Terminal window
# Via the AImetier UI
# Settings → Adapters → Install from npm → "my-aimetier-adapter"
# Or via API
curl -X POST http://localhost:3102/api/adapters \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"packageName": "my-aimetier-adapter"}'
Terminal window
curl -X POST http://localhost:3102/api/adapters \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"localPath": "/home/user/my-adapter"}'

Local adapters are symlinked into AImetier’s adapter directory. Changes to the source are picked up on server restart.

For development, you can also edit ~/.aimetier/adapter-plugins.json directly:

[
{
"packageName": "my-aimetier-adapter",
"localPath": "/home/user/my-adapter",
"type": "my_adapter",
"installedAt": "2026-03-30T12:00:00.000Z"
}
]

If your agent runtime supports sessions (conversation continuity across heartbeats), implement a session codec:

import type { AdapterSessionCodec } from "@aimetier/adapter-utils";
export const sessionCodec: AdapterSessionCodec = {
deserialize(raw) {
if (typeof raw !== "object" || raw === null) return null;
const r = raw as Record<string, unknown>;
return r.sessionId ? { sessionId: String(r.sessionId) } : null;
},
serialize(params) {
return params?.sessionId ? { sessionId: String(params.sessionId) } : null;
},
getDisplayId(params) {
return params?.sessionId ? String(params.sessionId) : null;
},
};

Include it in createServerAdapter():

return { type, execute, testEnvironment, sessionCodec, /* ... */ };

If your agent runtime supports skills/plugins, implement listSkills and syncSkills:

return {
type,
execute,
testEnvironment,
async listSkills(ctx) {
return {
adapterType: ctx.adapterType,
supported: true,
mode: "ephemeral",
desiredSkills: [],
entries: [],
warnings: [],
};
},
async syncSkills(ctx, desiredSkills) {
// Install desired skills into the runtime
return { /* same shape as listSkills */ };
},
};

If your runtime has a local config file that specifies the default model:

async function detectModel() {
// Read ~/.my-agent/config.yaml or similar
return {
model: "anthropic/claude-sonnet-4",
provider: "anthropic",
source: "~/.my-agent/config.yaml",
candidates: ["anthropic/claude-sonnet-4", "openai/gpt-4o"],
};
}
return { type, execute, testEnvironment, detectModel: () => detectModel() };
Terminal window
npm run build
npm publish

Other AImetier users can then install your adapter by package name from the UI or API.

  • Treat agent output as untrusted — parse defensively, never eval() agent output
  • Inject secrets via environment variables, not in prompts
  • Configure network access controls if the runtime supports them
  • Always enforce timeout and grace period — don’t let agents run forever
  • The UI parser module runs in a browser sandbox — it must have zero runtime imports and no side effects