@stoneforge/smithy
Multi-agent orchestration layer built on top of @stoneforge/quarry. Defines agent roles, manages sessions across multiple providers, dispatches tasks automatically, and isolates work in Git worktrees.
Provider-Agnostic: Supports Claude Code, OpenCode, and OpenAI Codex with a unified interface.
Installation
npm install @stoneforge/smithy
Includes @stoneforge/quarry, @stoneforge/storage, and @stoneforge/core as dependencies.
Optional Providers
# OpenCode provider (optional)
npm install @opencode-ai/sdk
Claude Code provider (@anthropic-ai/claude-agent-sdk) is included by default.
Quick Start
Basic Setup
Register Workers
Assign Task
import { createOrchestratorAPI } from '@stoneforge/smithy' ;
import { createStorage , initializeSchema } from '@stoneforge/storage' ;
const storage = createStorage ({ path: '.stoneforge/db.sqlite' });
initializeSchema ( storage );
// OrchestratorAPI extends QuarryAPI
const api = createOrchestratorAPI ( storage );
// Register a director
const director = await api . registerDirector ({
name: 'lead' ,
createdBy: humanEntityId ,
});
console . log ( `Director registered: ${ director . id } ` );
Agent Roles
Director
Strategic planner that creates tasks, sets priorities, and coordinates workers.
Max tasks this director can manage simultaneously
Register Director
Get Director
const director = await api . registerDirector ({
name: 'MainDirector' ,
createdBy: humanEntityId ,
maxConcurrentTasks: 1 ,
provider: 'claude' ,
model: 'claude-sonnet-4-20250514' ,
});
Worker
Executes assigned tasks. Two modes:
Spawned automatically for a single task, exits on completion. const worker = await api . registerWorker ({
name: 'e-worker-1' ,
workerMode: 'ephemeral' ,
createdBy: director . id ,
reportsTo: director . id ,
});
// Worktree path: agent/{worker-name}/{task-id}-{slug}
Long-lived session for real-time collaboration. const worker = await api . registerWorker ({
name: 'p-worker-1' ,
workerMode: 'persistent' ,
createdBy: director . id ,
});
// Worktree path: agent/{worker-name}/session-{timestamp}
Steward
Handles maintenance workflows.
Merge Steward
Docs Steward
Recovery Steward
Reviews and merges completed work branches. const steward = await api . registerSteward ({
name: 'm-steward-1' ,
stewardFocus: 'merge' ,
triggers: [
{ type: 'event' , event: 'task_completed' },
],
createdBy: director . id ,
});
Scans and fixes documentation. const steward = await api . registerSteward ({
name: 'd-steward-1' ,
stewardFocus: 'docs' ,
triggers: [
{ type: 'schedule' , cron: '0 2 * * *' }, // Daily at 2 AM
],
createdBy: director . id ,
});
Cleans up stuck merges and orphaned tasks. const steward = await api . registerSteward ({
name: 'r-steward-1' ,
stewardFocus: 'recovery' ,
triggers: [
{ type: 'schedule' , cron: '0 * * * *' }, // Hourly
],
createdBy: director . id ,
});
OrchestratorAPI
Extends QuarryAPI with agent-specific operations.
Agent Registration
const director = await api . registerDirector ({
name: string ,
createdBy: EntityId ,
maxConcurrentTasks? : number ,
provider? : 'claude' | 'opencode' | 'codex' ,
model? : string ,
executablePath? : string ,
});
Agent Queries
Get Agents
Get Director
Available Workers
// Get by ID
const agent = await api . getAgent ( entityId );
// Get by name
const agentByName = await api . getAgentByName ( 'dev-1' );
// List all
const allAgents = await api . listAgents ();
// List by role
const workers = await api . listAgents ({ role: 'worker' });
const stewards = await api . getStewards ();
Task Assignment
Auto-Assignment
Explicit Options
Orchestrator Metadata
// Auto-generates branch and worktree paths
const task = await api . assignTaskToAgent ( taskId , workerId );
Session Management
Update Session
Get Agent Channel
await api . updateAgentSession ( agentId , 'session-123' , 'running' );
// States: 'idle' | 'running' | 'suspended' | 'terminated'
Providers
Supported agent providers:
Claude Code
OpenCode
Codex
Default provider, included as a dependency. const director = await api . registerDirector ({
name: 'lead' ,
provider: 'claude' ,
model: 'claude-sonnet-4-20250514' ,
createdBy: humanEntityId ,
});
Optional provider, requires @opencode-ai/sdk. const worker = await api . registerWorker ({
name: 'dev-1' ,
workerMode: 'ephemeral' ,
provider: 'opencode' ,
createdBy: director . id ,
});
Built-in support via JSON-RPC over stdio. const worker = await api . registerWorker ({
name: 'dev-1' ,
workerMode: 'ephemeral' ,
provider: 'codex' ,
createdBy: director . id ,
});
Scaling with Multiple Plans
To run 10+ agents concurrently, split them across multiple Claude MAX/Pro plans:
Create profile wrappers for each plan
Set CLAUDE_CONFIG_DIR to separate directories
Authenticate each profile
Set executablePath per agent:
const worker = await api . registerWorker ({
name: 'dev-1' ,
workerMode: 'ephemeral' ,
provider: 'claude' ,
executablePath: '~/.config/claude-profiles/profile-1/claude-code' ,
createdBy: director . id ,
});
Git Worktree Isolation
Workers operate in isolated Git worktrees to prevent conflicts.
Worktree Paths
agent/{worker-name}/{task-id}-{slug}
Example: agent/e-worker-1/task-123-implement-auth agent/{worker-name}/session-{timestamp}
Example: agent/p-worker-1/session-1672531200
Git Utilities
Create Worktree
List Worktrees
Cleanup
import { createWorktree , listWorktrees } from '@stoneforge/smithy/git' ;
await createWorktree ({
branch: 'agent/worker-1/feat-auth' ,
path: '.stoneforge/.worktrees/worker-1-feat-auth' ,
});
Services
AgentRegistry
import { AgentRegistry } from '@stoneforge/smithy/services' ;
const registry = new AgentRegistry ( storage , api );
// Register
const agent = await registry . register ({
name: 'worker-1' ,
role: 'worker' ,
createdBy: directorId ,
});
// Lookup
const found = await registry . getByName ( 'worker-1' );
const workers = await registry . listByRole ( 'worker' );
DispatchService
import { DispatchService } from '@stoneforge/smithy/services' ;
const dispatch = new DispatchService ( api , registry );
// Match ready tasks to available workers
const assignments = await dispatch . dispatchReadyTasks ();
console . log ( `Assigned ${ assignments . length } tasks` );
DispatchDaemon
import { DispatchDaemon } from '@stoneforge/smithy/services' ;
const daemon = new DispatchDaemon ( api , dispatch );
// Start polling loop
await daemon . start ({
intervalMs: 5000 , // Poll every 5 seconds
});
// Stop
await daemon . stop ();
MergeStewardService
import { MergeStewardService } from '@stoneforge/smithy/services' ;
const mergeSteward = new MergeStewardService ( api , gitOps );
// Review a completed task
const result = await mergeSteward . reviewAndMerge ( taskId , {
testCommand: 'npm test' ,
mergeStrategy: 'squash' ,
autoCleanup: true ,
});
if ( result . merged ) {
console . log ( 'Merge successful' );
} else {
console . log ( `Tests failed: ${ result . reason } ` );
}
Prompts
Customize agent behavior with prompt overrides.
Built-in Prompts
Located in packages/smithy/src/prompts/:
director.md — Director role
worker.md — Ephemeral worker
persistent-worker.md — Persistent worker
steward-base.md — Base steward
steward-merge.md — Merge steward
steward-docs.md — Docs steward
steward-recovery.md — Recovery steward
Override Prompts
Place custom prompts in .stoneforge/prompts/:
.stoneforge/
prompts/
director.md
worker.md
steward-merge.md
Prompts are loaded in order: built-in → project overrides.
import { loadPrompt } from '@stoneforge/smithy' ;
const directorPrompt = await loadPrompt ( 'director' );
Entry Points
Main
Types
Services
Runtime
Git
Providers
import { createOrchestratorAPI } from '@stoneforge/smithy' ;
// All exports
import type { AgentRole , WorkerMode , StewardFocus } from '@stoneforge/smithy/types' ;
import {
AgentRegistry ,
DispatchService ,
MergeStewardService
} from '@stoneforge/smithy/services' ;
import { SessionManager , SpawnerService } from '@stoneforge/smithy/runtime' ;
import { createWorktree , listWorktrees } from '@stoneforge/smithy/git' ;
import { ClaudeProvider , OpenCodeProvider } from '@stoneforge/smithy/providers' ;
API Reference
Agent Types
type AgentRole = 'director' | 'worker' | 'steward' ;
type WorkerMode = 'ephemeral' | 'persistent' ;
type StewardFocus = 'merge' | 'docs' | 'recovery' | 'custom' ;
type SessionState = 'idle' | 'running' | 'suspended' | 'terminated' ;
interface OrchestratorMeta {
branch ?: string ;
worktree ?: string ;
sessionId ?: string ;
mergeStatus ?: 'pending' | 'merged' | 'failed' ;
}
Provider Interface
interface AgentProvider {
headless : HeadlessProvider ;
interactive : InteractiveProvider ;
}
interface HeadlessProvider {
spawn ( options : SpawnOptions ) : Promise < ChildProcess >;
}
interface InteractiveProvider {
start ( options : StartOptions ) : Promise < void >;
}
Multi-Provider: Smithy supports multiple agent providers with a unified interface. Switch providers per-agent without code changes.
Next Steps
Quarry API Learn the underlying data API
UI Components Build a web dashboard for agents