@codmir/engine
Codmir Autonomous Execution Engine - Event-driven contract executor for autonomous agent runs.
Overview
The engine is the central orchestrator for autonomous agent execution in Codmir. It:
- Consumes events from the event bus
- Matches triggers to registered contracts
- Creates and manages runs with deterministic state
- Advances runs through step graphs via a pure state machine
All side effects happen through adapters, making the engine deterministic and replayable.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Domain Events │
│ (ticket.created, github.pr.opened, etc.) │
└─────────────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ CodmirEngine │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ Contract │ │ Run │ │ Advance Loop │ │
│ │ Registry │ │ Manager │ │ (State Machine) │ │
│ └─────────────┘ └──────────────┘ └────────────────────┘ │
└─────────────────────────┬───────────────────────────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌───────────┐
│ Runtime │ │ Events │ │ Storage │
│(Railway)│ │ Adapter │ │ Adapter │
└─────────┘ └──────────┘ └───────────┘
Installation
pnpm add @codmir/engine @codmir/contracts
Quick Start
import { createEngine, InMemoryStorageAdapter, InMemoryEventsAdapter, InMemoryRuntimeAdapter, DefaultPolicyAdapter, SystemClockAdapter, ConsoleLoggerAdapter } from "@codmir/engine";
import { ticketTriageContract } from "@codmir/contracts";
const events = new InMemoryEventsAdapter();
const storage = new InMemoryStorageAdapter();
const runtime = new InMemoryRuntimeAdapter({ eventPublisher: events.publish.bind(events) });
const policy = new DefaultPolicyAdapter();
const clock = new SystemClockAdapter();
const logger = new ConsoleLoggerAdapter();
const engine = createEngine({
events,
storage,
runtime,
policy,
clock,
logger,
});
await storage.saveContract(ticketTriageContract);
await engine.start();
const runId = await engine.triggerRun({
contractId: "codmir.ticket-triage",
triggerEvent: {
name: "ticket.created",
payload: {
id: "T-123",
title: "Bug in login flow",
body: "Users can't login...",
},
},
});
console.log("Started run:", runId);
Integration with @codmir/events
import { emit, getGlobalDispatcher } from "@codmir/events";
import { CodmirEventsAdapter } from "@codmir/engine/adapters";
const eventsAdapter = new CodmirEventsAdapter({
emit,
dispatcher: getGlobalDispatcher(),
actorId: "codmir-engine",
});
const engine = createEngine({
events: eventsAdapter,
});
Contracts
Contracts define what autonomous agents can do. See @codmir/contracts for the full schema.
Step Types
task | Execute a task on the runtime | Yes |
llm | Invoke an LLM via intelligence adapter | No* |
emit | Emit an event to the event bus | No |
wait | Wait for an external event | Yes |
branch | Conditional branching | No |
transform | Transform data in context | No |
approval | Human-in-the-loop gate (Overseer) | Yes |
Example Contract
import type { CodmirContract } from "@codmir/contracts";
const myContract: CodmirContract = {
schemaVersion: "codmir.contract.v1",
id: "my-agent",
version: "1.0.0",
title: "My Agent",
triggers: [
{ type: "event", event: "my.trigger" },
],
permissions: [
{ kind: "event:emit", pattern: "my.*" },
{ kind: "llm:invoke" },
],
limits: {
maxRunMs: 60000,
maxSteps: 10,
},
entryStepId: "start",
steps: [
{
id: "start",
name: "Start",
kind: "emit",
event: "my.started",
payload: { triggerId: "{{trigger.id}}" },
next: null,
},
],
};
Adapters
The engine uses adapters for all external interactions:
EngineEventsAdapter
Publish and subscribe to events.
EngineStorageAdapter
Store contracts and run records.
EngineRuntimeAdapter
Execute tasks on remote runtime (Railway).
EngineIntelligenceAdapter
Invoke LLMs (optional).
EnginePolicyAdapter
Enforce permissions and policies.
EngineClockAdapter
Get current time (deterministic for replay).
EngineLoggerAdapter
Log engine activity.
Overseer Pattern (Human-in-the-Loop)
Use approval steps to pause execution until a human approves:
{
id: "require-approval",
name: "Require Approval",
kind: "approval",
prompt: "Deploy to production?",
approvers: ["admin", "devops"],
nextOnApproved: "deploy",
nextOnDenied: "cancel",
timeout: { ms: 86400000 },
}
Approve via event:
await events.publish({
name: "engine.approval.granted",
payload: {
runId: "run_123",
stepId: "require-approval",
grantedBy: "admin@example.com",
},
});
Engine Events
engine.run.requested | Run creation requested |
engine.run.started | Run started executing |
engine.run.completed | Run completed successfully |
engine.run.failed | Run failed |
engine.run.cancelled | Run was cancelled |
engine.run.paused | Run was paused |
engine.run.resumed | Run was resumed |
engine.run.waiting | Run is waiting for event |
engine.step.requested | Step execution requested |
engine.step.completed | Step completed |
engine.step.failed | Step failed |
engine.approval.requested | Approval requested |
engine.approval.granted | Approval granted |
engine.approval.denied | Approval denied |
Integration with apps/agent (Railway Agent)
The engine can delegate heavy task execution to the apps/agent service:
import { createEngine, RailwayAgentAdapter, CodmirEventsAdapter } from "@codmir/engine";
const agentAdapter = new RailwayAgentAdapter({
baseUrl: process.env.AGENT_SERVICE_URL ?? "https://agent.railway.internal",
callbackUrl: process.env.ENGINE_CALLBACK_URL ?? "https://engine.railway.internal/callbacks",
callbackSecret: process.env.CALLBACK_SECRET!,
apiKey: process.env.AGENT_API_KEY,
});
const engine = createEngine({
runtime: agentAdapter,
events: eventsAdapter,
storage: storageAdapter,
policy: policyAdapter,
clock: clockAdapter,
logger: loggerAdapter,
});
Supported Agent Tasks
code-task | task | Code generation/modification |
analyze-code | task | Code analysis |
test-run | task | Run tests |
analyze-ticket | ticket | Ticket analysis |
knowledge-base | task | Knowledge base operations |
Handling Agent Callbacks
Set up a callback endpoint to receive task completion events:
import { handleAgentCallback } from "@codmir/engine/adapters";
app.post('/callbacks', async (req, res) => {
const result = handleAgentCallback(
agentAdapter,
req.body,
req.headers['x-callback-secret'] as string,
process.env.CALLBACK_SECRET
);
if (result) {
await eventsAdapter.publish({
name: result.success ? 'engine.step.completed' : 'engine.step.failed',
payload: {
runId: result.runId,
stepId: result.stepId,
output: result.output,
error: result.error,
},
correlationId: result.runId,
});
}
res.json({ ok: true });
});
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ @codmir/engine │
│ Contract orchestration, triggers, state machine, approvals │
└─────────────────────────┬───────────────────────────────────────┘
│ POST /jobs
▼
┌─────────────────────────────────────────────────────────────────┐
│ apps/agent │
│ Heavy task execution: code gen, tests, analysis, git ops │
└─────────────────────────┬───────────────────────────────────────┘
│ POST /callbacks (on completion)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Engine Callback Handler │
│ Converts to engine.step.completed/failed events │
└─────────────────────────────────────────────────────────────────┘
License
MIT