@ai-craft/agent-loop
v0.1.1
Published
Plan-execute agent loop for @ai-craft/sandbox agents
Readme
@ai-craft/agent-loop
Pure-logic plan-execute harness for agent workloads — no I/O bound to a process model.
Install
npm install @ai-craft/agent-loop@ai-craft/agent-llm is a peer-class dependency.
Quickstart
import { createFake } from '@ai-craft/agent-llm';
import { runLoop } from '@ai-craft/agent-loop';
const llm = createFake();
const ac = new AbortController();
for await (const ev of runLoop(llm, {
systemPrompt: 'You are a build agent.',
task: 'Add a hello function.',
}, ac.signal)) {
if (ev.type === 'plan') console.log('plan:', ev.plan.steps.length, 'steps');
if (ev.type === 'message') process.stdout.write(ev.text);
if (ev.type === 'usage') console.log('usage:', ev.usage);
if (ev.type === 'halt') console.error('halt:', ev.reason);
if (ev.type === 'done') break;
}Architecture
runLoop is an AsyncGenerator<LoopEvent>. The loop:
- Builds an
AgentPlanvia the architect-tier client. - Yields
{ type: 'plan' }. - Streams each step through the routine- or architect-tier client.
- Forwards
textdeltas asmessageevents andusagedeltas asusageevents. - After every
usageevent, asksBudgetGate.check()whether to halt. - Yields
{ type: 'done' }on completion or{ type: 'halt' }on budget/rate-limit.
The generator throws on signal.aborted between steps and inside the inner stream.
Tools
Five built-in tool handlers ship via ToolRegistry:
| Tool | Purpose |
|---|---|
| read_file | Read a path from the working directory |
| write_file | Write text to a path (creates parent dirs) |
| apply_patch | Apply a unified diff to one or more files |
| bash | Spawn a shell command, capture stdout/stderr/exit |
| search | Recursive grep across the working directory |
Register them with a ToolRegistry and dispatch by name; future revisions will route tool_use blocks from the LLM directly into the registry.
Templates
Three workflow templates ship in src/templates/:
| Template | Providers | Purpose |
|---|---|---|
| [email protected] | anthropic | Spec → plan → build → review/debug, TDD discipline |
| [email protected] | anthropic, cursor, openai, azure-foundry | AI-First development lifecycle, advance rules included |
| [email protected] | anthropic, openai, azure-foundry, cursor, fake | Provider-agnostic baseline |
resolveTemplate(provider) picks a sensible default; getTemplate(id) looks up by id; allTemplates enumerates everything.
File memory
FileMemory writes JSONL line-by-line:
interface MemoryEntry {
ts: string; // ISO timestamp, set on append
kind: 'plan' | 'step' | 'done';
data: unknown;
}Append-only, one file per session, parent dir auto-created. Replays trivially with fs.readFile + split('\n').
Budget gate
BudgetGate is subscription-aware:
auth: 'subscription'—check()always returns{ halt: false }(no USD cap).auth: 'api-key'— accumulates input/output tokens, estimates USD against Sonnet-class pricing ($3/$15 per Mtok), halts when the cap is reached.
License
MIT. Part of the @ai-craft monorepo.
