Orchestrator API
The OrchestratorAPI extends the Quarry API with agent orchestration capabilities. It’s the primary interface for managing agents, dispatching tasks, and coordinating the orchestration loop.
Initialization
import { createOrchestratorAPI } from '@stoneforge/smithy';import { createStorage, initializeSchema } from '@stoneforge/storage';
const storage = createStorage({ path: '.stoneforge/stoneforge.db' });initializeSchema(storage);
const api = createOrchestratorAPI(storage);The OrchestratorAPI inherits all methods from QuarryAPI — you can use api.create(), api.list(), api.ready(), etc. alongside orchestration-specific methods.
Agent registration
Register a Director
const director = await api.registerDirector({ name: 'MainDirector', createdBy: humanEntityId, maxConcurrentTasks: 1, // Default: 1 provider: 'claude-code', // Optional model: 'sonnet', // Optional executablePath: 'claude', // Optional});Register a Worker
const worker = await api.registerWorker({ name: 'Worker-1', workerMode: 'ephemeral', // 'ephemeral' or 'persistent' createdBy: directorEntityId, reportsTo: directorEntityId, // Optional: management hierarchy maxConcurrentTasks: 2, // Default: 1 roleDefinitionRef: roleDefId, // Optional: link to role definition});Register a Steward
// Merge steward (triggered by events)const steward = await api.registerSteward({ name: 'MergeSteward', stewardFocus: 'merge', // 'merge' | 'docs' | 'recovery' | 'custom' triggers: [ { type: 'event', event: 'task_completed' }, ], createdBy: directorEntityId, maxConcurrentTasks: 1,});
// Custom steward with cron scheduleconst customSteward = await api.registerSteward({ name: 'CleanupSteward', stewardFocus: 'custom', playbook: '## Stale Branch Cleanup\n\n1. List branches older than 14 days\n2. Archive those with no open tasks', triggers: [ { type: 'cron', schedule: '0 2 * * *' }, ], createdBy: directorEntityId,});Agent queries
// Get specific agentconst agent = await api.getAgent(entityId);const agentByName = await api.getAgentByName('Worker-1');
// Get the directorconst director = await api.getDirector();
// List and filter agentsconst allAgents = await api.listAgents();const workers = await api.listAgents({ role: 'worker' });const ephemeralWorkers = await api.listAgents({ role: 'worker', workerMode: 'ephemeral',});
// Role-specific queriesconst stewards = await api.getStewards();const availableWorkers = await api.getAvailableWorkers();const allWorkers = await api.getAgentsByRole('worker');Agent channels
Each agent gets a dedicated channel on registration. Used for dispatch notifications and inter-agent messaging.
// OrchestratorAPI returns just the ChannelIdconst channelId: ChannelId | undefined = await api.getAgentChannel(entityId);
// AgentRegistry returns full Channel objectimport { createAgentRegistry } from '@stoneforge/smithy';
const registry = createAgentRegistry(api);const channel: Channel | undefined = await registry.getAgentChannel(agentId);const channelId2: ChannelId | undefined = await registry.getAgentChannelId(agentId);
// Channel name utilitiesimport { generateAgentChannelName, parseAgentChannelName } from '@stoneforge/smithy';
generateAgentChannelName('Worker-1'); // 'agent-Worker-1'parseAgentChannelName('agent-Worker-1'); // 'Worker-1' or nullTask assignment
Basic assignment
// Auto-generates branch and worktree namesconst task = await api.assignTaskToAgent(taskId, workerId);
// With explicit optionsconst task = await api.assignTaskToAgent(taskId, workerId, { branch: 'agent/worker-1/task-feat-auth', worktree: '.stoneforge/.worktrees/worker-1-feat-auth', sessionId: 'claude-session-123', markAsStarted: true, // Also sets task status to 'in_progress'});Orchestrator task metadata
Each task carries orchestration metadata — branch, worktree, merge status, handoff history, etc.
// Get metadataconst meta = await api.getTaskOrchestratorMeta(taskId);
// Replace entire metadataawait api.setTaskOrchestratorMeta(taskId, fullMeta);
// Partial update (merge)await api.updateTaskOrchestratorMeta(taskId, { mergeStatus: 'pending',});interface OrchestratorTaskMeta { assignedAgent?: EntityId; branch?: string; worktree?: string; targetBranch?: string; // Target branch for merging (inherited from director at dispatch) sessionId?: string; owningDirector?: EntityId; // Director that created/owns this task startedAt?: Timestamp; completedAt?: Timestamp; mergedAt?: Timestamp; mergeStatus?: MergeStatus; mergeFailureReason?: string; testRunCount?: number; lastTestResult?: TestResult; reconciliationCount?: number; stuckMergeRecoveryCount?: number; stewardRecoveryCount?: number; resumeCount?: number; reportedIssues?: readonly string[]; // Handoff context handoffBranch?: string; handoffWorktree?: string; lastSessionId?: string; handoffAt?: Timestamp; handoffFrom?: EntityId; handoffHistory?: HandoffHistoryEntry[]; // Merge request info mergeRequestUrl?: string; mergeRequestId?: number; mergeRequestProvider?: string; completionSummary?: string; lastCommitHash?: string; // Session history sessionHistory?: readonly TaskSessionHistoryEntry[]; // Branch sync lastSyncResult?: SyncResultMeta;}MergeStatus
| Status | Description |
|---|---|
pending | Task completed, awaiting merge |
testing | Steward is running tests |
merging | Tests passed, merge in progress |
merged | Successfully merged |
conflict | Merge conflict detected |
test_failed | Tests failed |
failed | Merge failed (other reason) |
not_applicable | No merge needed |
awaiting_approval | PR created, waiting for human approval/merge |
Session management
// Update agent session stateawait api.updateAgentSession(agentId, 'session-123', 'running');Session states:
type SessionState = 'idle' | 'running' | 'suspended' | 'terminated';Agent types
AgentEntity
A typed wrapper for entities that have agent metadata:
import { getAgentMetadata } from '@stoneforge/smithy';
// AgentEntity extends Entity with typed agent metadatainterface AgentEntity extends Entity { metadata: { agent: AgentMetadata } & Record<string, unknown>;}
// Extract agent metadata from an entity (returns undefined if not an agent)const agentMeta = getAgentMetadata(entity);if (agentMeta) { console.log(agentMeta.agentRole); // 'director' | 'worker' | 'steward'}AgentRole
type AgentRole = 'director' | 'worker' | 'steward';AgentMetadata
Agent metadata is a discriminated union based on agentRole:
interface BaseAgentMetadata { agentRole: AgentRole; channelId?: ChannelId; sessionId?: string; worktree?: string; sessionStatus?: 'idle' | 'running' | 'suspended' | 'terminated'; lastActivityAt?: Timestamp; maxConcurrentTasks?: number; roleDefinitionRef?: ElementId; provider?: string; model?: string; executablePath?: string;}
interface DirectorMetadata extends BaseAgentMetadata { agentRole: 'director'; targetBranch?: string; // Git branch for task merges (falls back to config or auto-detect)}
interface WorkerMetadata extends BaseAgentMetadata { agentRole: 'worker'; workerMode: 'ephemeral' | 'persistent'; branch?: string;}
interface StewardMetadata extends BaseAgentMetadata { agentRole: 'steward'; stewardFocus: 'merge' | 'docs' | 'recovery' | 'custom'; triggers?: StewardTrigger[]; playbook?: string; // @deprecated - use playbookId instead playbookId?: string; lastExecutedAt?: Timestamp; nextScheduledAt?: Timestamp;}StewardTrigger
interface CronTrigger { type: 'cron'; schedule: string; // e.g., '0 2 * * *'}
interface EventTrigger { type: 'event'; event: string; // e.g., 'task_completed' condition?: string;}
type StewardTrigger = CronTrigger | EventTrigger;Type guards
import { isDirectorMetadata, isWorkerMetadata, isStewardMetadata, isCronTrigger, isEventTrigger,} from '@stoneforge/smithy';
if (isWorkerMetadata(agent.metadata.agent)) { console.log(agent.metadata.agent.workerMode);}
if (isStewardMetadata(agent.metadata.agent)) { console.log(agent.metadata.agent.stewardFocus);} Services Reference Detailed docs for orchestrator services — DispatchDaemon, MergeStewardService, WorkerTaskService, and more
Quarry API The data API that OrchestratorAPI extends — CRUD, queries, dependencies, sync