@codmir/engine
v0.1.1
Published
Codmir autonomous execution engine - event-driven contract executor
Maintainers
Readme
@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/contractsQuick Start
import { createEngine, InMemoryStorageAdapter, InMemoryEventsAdapter, InMemoryRuntimeAdapter, DefaultPolicyAdapter, SystemClockAdapter, ConsoleLoggerAdapter } from "@codmir/engine";
import { ticketTriageContract } from "@codmir/contracts";
// Create adapters
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();
// Create engine
const engine = createEngine({
events,
storage,
runtime,
policy,
clock,
logger,
});
// Register contracts
await storage.saveContract(ticketTriageContract);
// Start the engine
await engine.start();
// Trigger a run manually
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";
// Create adapter that bridges to @codmir/events
const eventsAdapter = new CodmirEventsAdapter({
emit,
dispatcher: getGlobalDispatcher(),
actorId: "codmir-engine",
});
// Use in engine
const engine = createEngine({
events: eventsAdapter,
// ... other adapters
});Contracts
Contracts define what autonomous agents can do. See @codmir/contracts for the full schema.
Step Types
| Step | Description | Blocking |
|------|-------------|----------|
| 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 }, // 24 hours
}Approve via event:
await events.publish({
name: "engine.approval.granted",
payload: {
runId: "run_123",
stepId: "require-approval",
grantedBy: "[email protected]",
},
});Engine Events
| Event | Description |
|-------|-------------|
| 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";
// Create Railway agent adapter
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,
});
// Create engine with agent runtime
const engine = createEngine({
runtime: agentAdapter,
events: eventsAdapter,
storage: storageAdapter,
policy: policyAdapter,
clock: clockAdapter,
logger: loggerAdapter,
});Supported Agent Tasks
| Task Name | Agent Mode | Description |
|-----------|------------|-------------|
| 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";
// Express endpoint
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) {
// Publish completion event to engine
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
