This document provides a comprehensive reference for the Loop Agent SDK public APIs.
The main entry point for running PIV (Plan-Implement-Validate) loops.
Coordinates the complete PIV workflow for autonomous software development.
import { PIVOrchestrator } from 'loop-agent';
const orchestrator = new PIVOrchestrator(config, runCommand, fileOps);
const finalState = await orchestrator.run('Add user authentication');new PIVOrchestrator(
config: OrchestratorAgentConfig,
runCommand: (cmd: string) => Promise<CommandResult>,
fileOps: FileOperations
)| Parameter | Type | Description |
|---|---|---|
config |
OrchestratorAgentConfig |
Configuration including working directory and max iterations |
runCommand |
Function |
Shell command executor returning stdout/stderr/exitCode |
fileOps |
FileOperations |
File system operations (read, write, list) |
| Method | Returns | Description |
|---|---|---|
run(userRequest: string) |
Promise<PIVLoopState> |
Execute full PIV loop for a request |
getState() |
PIVLoopState |
Get current immutable state snapshot |
Creates PRDs (Product Requirements Documents) and feature plans (PRPs).
import { createPlannerAgent, createPRD, createFeaturePlan } from 'loop-agent';
const plannerConfig = createPlannerAgent();
const prdResult = await createPRD(plannerConfig, 'Build a REST API');| Function | Returns | Description |
|---|---|---|
createPlannerAgent() |
PlannerAgentConfig |
Create planner with ULTRATHINK enabled |
createDiscoveryAgent() |
PlannerAgentConfig |
Create discovery agent for requirements |
generateDiscoveryQuestions(config, context) |
Promise<AgentResult<DiscoveryQuestions>> |
Generate clarifying questions |
hasEnoughContext(config, context) |
Promise<boolean> |
Check if ready for PRD creation |
updateDiscoveryContext(context, questions, answers) |
DiscoveryContext |
Update context with new Q&A |
createPRD(config, request, discovery?, projectContext?) |
Promise<AgentResult<PRD>> |
Create product requirements |
extractFeatures(config, prd) |
Promise<AgentResult<Feature[]>> |
Extract features from PRD |
createFeaturePlan(config, feature, codebaseContext) |
Promise<AgentResult<FeaturePlan>> |
Create detailed feature plan |
Executes tasks from feature plans.
import { createImplementerAgent, executeTask } from 'loop-agent';
const implementerConfig = createImplementerAgent();
const result = await executeTask(implementerConfig, task, plan, contextFiles);| Function | Returns | Description |
|---|---|---|
createImplementerAgent() |
ImplementerAgentConfig |
Create implementer with ULTRATHINK |
executeTask(config, task, plan, contextFiles) |
Promise<AgentResult<ImplementationResult>> |
Execute a single task |
executeQuickFix(config, description, targetFile, content) |
Promise<AgentResult<ImplementationResult>> |
Apply quick fix (MICRO PIV) |
applyFixes(config, task, errors, currentContent) |
Promise<AgentResult<ImplementationResult>> |
Apply validation fixes |
Runs the 5-level validation pyramid.
import { createValidatorAgent, runValidationPyramid, DEFAULT_VALIDATION_COMMANDS } from 'loop-agent';
const validatorConfig = createValidatorAgent();
const result = await runValidationPyramid(validatorConfig, DEFAULT_VALIDATION_COMMANDS, runCommand);| Function | Returns | Description |
|---|---|---|
createValidatorAgent() |
ValidatorAgentConfig |
Create validator with ULTRATHINK |
runValidationPyramid(config, commands, runCommand) |
Promise<AgentResult<ValidationResult>> |
Run full validation |
runValidationLevel(level, command, runCommand) |
Promise<{passed, output, exitCode}> |
Run single level |
generateValidationSummary(result) |
string |
Format validation result |
const DEFAULT_VALIDATION_COMMANDS: ValidationCommand[] = [
{ level: 1, command: 'npx eslint . --ext .ts,.tsx', description: 'Syntax and style checking', required: true },
{ level: 2, command: 'npx tsc --noEmit', description: 'Type safety validation', required: true },
{ level: 3, command: 'npm test', description: 'Unit test execution', required: true },
{ level: 4, command: 'npm run test:integration', description: 'Integration test execution', required: false }
];Performs code review and system learning.
import { createReviewerAgent, performCodeReview, performSystemReview } from 'loop-agent';
const reviewerConfig = createReviewerAgent();
const review = await performCodeReview(reviewerConfig, changedFiles, originalFiles);| Function | Returns | Description |
|---|---|---|
createReviewerAgent() |
ReviewerAgentConfig |
Create reviewer with ULTRATHINK |
performCodeReview(config, changedFiles, originalFiles) |
Promise<AgentResult<CodeReviewResult>> |
Review code changes |
performSystemReview(config, state) |
Promise<AgentResult<SystemReviewResult>> |
Analyze session for patterns |
generateReviewSummary(result) |
string |
Format review result |
Slash command discovery and execution system.
import { discoverAllCommands, parseCommand, executeCommand } from 'loop-agent';
const commands = discoverAllCommands();
const parsed = parseCommand('/plan Add authentication');
const result = executeCommand('plan', 'Add authentication');| Function | Returns | Description |
|---|---|---|
parseCommand(input) |
ParsedCommand | null |
Parse slash command from user input |
discoverAllCommands(additionalPaths?) |
SlashCommand[] |
Find all available commands |
getCommand(name, commands?) |
SlashCommand | null |
Get specific command by name |
executeCommand(name, args, commands?) |
{prompt, metadata} | null |
Execute command and get prompt |
expandCommandTemplate(template, args, argList) |
string |
Expand template with arguments |
formatCommandList(commands) |
string |
Format commands for display |
clearCommandCache() |
void |
Clear cached commands |
MCP (Model Context Protocol) server management.
import { buildMcpConfig, loadMcpConfig, DEFAULT_MCP_SERVERS } from 'loop-agent';
const servers = buildMcpConfig({ cloudOnly: true });
const health = await checkAllMcpServers(servers);| Function | Returns | Description |
|---|---|---|
loadMcpConfig(projectPath?) |
McpServersConfig | null |
Load from .mcp.json |
buildMcpConfig(options) |
McpServersConfig |
Build final server configuration |
mergeMcpServers(defaults, user) |
McpServersConfig |
Merge configurations |
checkMcpServerHealth(name, config) |
Promise<{connected, error?}> |
Check single server |
checkAllMcpServers(servers) |
Promise<Record<string, {connected, error?}>> |
Check all servers |
formatMcpStatus(results) |
string |
Format status for display |
clearMcpCache() |
void |
Clear cached configuration |
const DEFAULT_MCP_SERVERS: McpServersConfig = {
archon: { type: 'http', url: 'http://localhost:8051/mcp' },
crawl4ai: { type: 'sse', url: 'http://localhost:11235/mcp/sse' },
context7: { type: 'http', url: 'https://mcp.context7.com/mcp' }
};
const CLOUD_MCP_SERVERS: McpServersConfig = {
context7: { type: 'http', url: 'https://mcp.context7.com/mcp' }
};Plugin installation and management.
import { PluginManager, parsePluginSource } from 'loop-agent';
const manager = new PluginManager();
await manager.init();
const result = await manager.install('github:owner/my-plugin');class PluginManager {
async init(): Promise<void>;
async install(source: string): Promise<PluginInstallResult>;
async uninstall(name: string): Promise<{success, error?}>;
async enable(name: string): Promise<boolean>;
async disable(name: string): Promise<boolean>;
list(): LoadedPlugin[];
get(name: string): LoadedPlugin | undefined;
async loadAll(): Promise<void>;
getAllCommands(): SlashCommand[];
getAllMcpServers(): McpServersConfig;
}| Function | Returns | Description |
|---|---|---|
parsePluginSource(source) |
PluginSource |
Parse plugin source string |
loadPluginManifest(pluginPath) |
Promise<PluginManifest | null> |
Load plugin manifest |
loadPlugin(pluginPath, source) |
Promise<LoadedPlugin | null> |
Load plugin from directory |
ensurePluginsDir() |
Promise<void> |
Create plugins directory |
getInstalledPlugins() |
Promise<Record<string, {source, enabled}>> |
Get installed plugins registry |
listInstalledPluginNames() |
Promise<string[]> |
List plugin names from disk |
formatPluginList(plugins) |
string |
Format plugins for display |
interface PIVLoopState {
sessionId: string;
userRequest: string;
complexity: ComplexityLevel;
phase: PIVPhase;
status: 'idle' | 'running' | 'paused' | 'completed' | 'failed';
prd?: PRD;
features: Feature[];
currentFeature?: Feature;
currentPlan?: FeaturePlan;
validationResults: ValidationResult[];
codeReviews: CodeReviewResult[];
totalIterations: number;
startedAt?: Date;
completedAt?: Date;
}
type ComplexityLevel = 'small_task' | 'feature' | 'project';
type PIVPhase =
| 'idle'
| 'discovery'
| 'creating_prd'
| 'extracting_features'
| 'planning_feature'
| 'implementing'
| 'validating'
| 'reviewing'
| 'system_review'
| 'completed'
| 'failed';
type ValidationLevel = 1 | 2 | 3 | 4 | 5;interface AgentResult<T> {
success: boolean;
data?: T;
error?: string;
duration: number;
}
interface ValidationResult {
passed: boolean;
level: ValidationLevel;
failures: ValidationFailure[];
isSystemIssue: boolean;
duration: number;
}
interface CodeReviewResult {
passed: boolean;
filesReviewed: number;
issues: CodeReviewIssue[];
stats: { filesModified: number; filesAdded: number; filesDeleted: number; linesAdded: number; linesDeleted: number };
reviewedAt: Date;
}interface OrchestratorAgentConfig {
workingDirectory: string;
maxIterationsPerTask?: number; // Default: 3
systemPrompt?: string;
model?: string;
}
interface McpOptions {
useDefaultMcpServers?: boolean; // Default: true
cloudOnly?: boolean; // Default: false
mcpConfigPath?: string;
additionalServers?: McpServersConfig;
excludeServers?: string[];
}
type McpServerConfig =
| { type: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }
| { type: 'http'; url: string; headers?: Record<string, string> }
| { type: 'sse'; url: string; headers?: Record<string, string> };Structured logging with OpenTelemetry-compliant format.
import { createLogger, logInfo, logError, logWarning } from 'loop-agent';
const logger = createLogger('my-component');
logger.info('operation.started', { sessionId: 'abc123' });
// Or use direct functions
logInfo('component.action', 'Operation completed', { duration: 150 });
logError('component.action', 'Operation failed', new Error('reason'));Immutable state update functions.
import {
createInitialState,
updatePhase,
updateStatus,
addValidationResult,
addCodeReview
} from 'loop-agent';
let state = createInitialState('session-1', 'Build REST API');
state = updatePhase(state, 'implementing');
state = addValidationResult(state, validationResult);import { assessComplexity } from 'loop-agent';
const complexity = await assessComplexity(config, userRequest, projectContext);
// Returns: 'small_task' | 'feature' | 'project'// Validation pyramid levels
const VALIDATION_LEVELS = {
1: 'Syntax & Style',
2: 'Type Safety',
3: 'Unit Tests',
4: 'Integration Tests',
5: 'Human Review'
};
// Agent models
const INTELLIGENT_MODEL = 'claude-opus-4-5-20251101';
const ULTRATHINK_TOKENS = 128000;
// Cache TTLs
const COMMAND_CACHE_TTL = 30000; // 30 seconds
const MCP_CACHE_TTL = 60000; // 60 seconds
// Timeouts
const MCP_HEALTH_CHECK_TIMEOUT = 5000; // 5 seconds
const GIT_CLONE_TIMEOUT = 30000; // 30 secondsAll agent functions return AgentResult<T> with consistent error handling:
const result = await createPRD(config, request);
if (result.success) {
console.log('PRD created:', result.data.title);
console.log('Duration:', result.duration, 'ms');
} else {
console.error('Failed:', result.error);
}For validation errors, check isSystemIssue to distinguish between code problems and environment issues:
const validation = await runValidationPyramid(config, commands, runCommand);
if (!validation.data?.passed) {
if (validation.data?.isSystemIssue) {
console.log('Environment issue - check dependencies');
} else {
console.log('Code issue - fix and retry');
}
}The PIV Loop SDK provides a typed event system for observability and monitoring.
Type-safe event emitter for PIV loop lifecycle events.
import { pivEvents, PIVEventEmitter } from 'loop-agent';
// Subscribe to specific events
pivEvents.on('lifecycle:started', (event) => {
console.log(`Session ${event.sessionId} started at ${event.timestamp}`);
});
pivEvents.on('validation:completed', (event) => {
console.log(`Validation ${event.result.passed ? 'passed' : 'failed'}`);
});
// Subscribe to all events (for logging/debugging)
pivEvents.onAny((event) => {
console.log(`[${event.type}] ${event.sessionId}`);
});
// Create isolated emitter for testing
const testEmitter = new PIVEventEmitter();| Event | Description | Key Payload Fields |
|---|---|---|
lifecycle:started |
PIV loop started | sessionId, request |
lifecycle:completed |
PIV loop completed successfully | sessionId, state, duration |
lifecycle:failed |
PIV loop failed | sessionId, error, state |
phase:changed |
Phase transition occurred | sessionId, from, to |
status:changed |
Status transition occurred | sessionId, from, to |
validation:started |
Validation level started | sessionId, level, command |
validation:completed |
Validation completed | sessionId, result, duration |
validation:failed |
Validation level failed | sessionId, level, error, command |
iteration:started |
Fix iteration started | sessionId, count, taskId? |
iteration:completed |
Fix iteration completed | sessionId, count, success, taskId? |
task:started |
Task execution started | sessionId, task |
task:completed |
Task execution completed | sessionId, task, success |
error:occurred |
Error occurred | sessionId, error, context, recoverable |
Create event payloads with these helper functions:
| Function | Returns | Description |
|---|---|---|
lifecycleStarted(sessionId, request) |
LifecycleStartedEvent |
Create lifecycle started event |
lifecycleCompleted(sessionId, state, duration) |
LifecycleCompletedEvent |
Create lifecycle completed event |
lifecycleFailed(sessionId, error, state) |
LifecycleFailedEvent |
Create lifecycle failed event |
phaseChanged(sessionId, from, to) |
PhaseChangedEvent |
Create phase change event |
statusChanged(sessionId, from, to) |
StatusChangedEvent |
Create status change event |
validationStarted(sessionId, level, command) |
ValidationStartedEvent |
Create validation started event |
validationCompleted(sessionId, result, duration) |
ValidationCompletedEvent |
Create validation completed event |
validationFailed(sessionId, level, error, command) |
ValidationFailedEvent |
Create validation failed event |
iterationStarted(sessionId, count, taskId?) |
IterationStartedEvent |
Create iteration started event |
iterationCompleted(sessionId, count, success, taskId?) |
IterationCompletedEvent |
Create iteration completed event |
taskStarted(sessionId, task) |
TaskStartedEvent |
Create task started event |
taskCompleted(sessionId, task, success) |
TaskCompletedEvent |
Create task completed event |
errorOccurred(sessionId, error, context, recoverable?) |
ErrorOccurredEvent |
Create error event |
import { pivEvents, lifecycleStarted, validationCompleted } from 'loop-agent';
// Metrics collection
const metrics = {
sessionsStarted: 0,
validationsPassed: 0,
validationsFailed: 0,
};
pivEvents.on('lifecycle:started', () => {
metrics.sessionsStarted++;
});
pivEvents.on('validation:completed', (event) => {
if (event.result.passed) {
metrics.validationsPassed++;
} else {
metrics.validationsFailed++;
}
});
// Emit events programmatically
pivEvents.emit('lifecycle:started', lifecycleStarted('session-123', 'Build REST API'));The hook system allows plugins to intercept and modify PIV loop behavior at key lifecycle points.
import { registerHooks, unregisterHooks, clearAllHooks } from 'loop-agent';
// Register hooks with optional priority (lower = first, default = 100)
registerHooks('my-plugin', {
beforeValidation: async (ctx, state, level) => {
console.log(`Validating at level ${level}`);
return { continue: true };
},
onError: async (ctx, error, errorContext) => {
await logErrorToMonitoring(error);
return { continue: true, message: 'Error logged' };
}
}, 50); // Priority: 50 (runs before default)
// Remove hooks
unregisterHooks('my-plugin');
// Clear all hooks (useful for testing)
clearAllHooks();| Hook | When Called | Can Modify |
|---|---|---|
beforeStart |
Before PIV loop starts | Request string |
afterComplete |
After PIV loop completes | (void) |
beforePlanning |
Before feature planning | Skip planning |
afterPlanning |
After plan created | FeaturePlan |
beforeImplementation |
Before task execution | Task |
afterImplementation |
After task execution | (void) |
beforeValidation |
Before validation runs | Validation level |
afterValidation |
After validation completes | ValidationResult |
onError |
When error occurs | Recovery signal |
Hooks that can modify behavior return HookResult:
interface HookResult<T = void> {
continue: boolean; // false = abort operation
data?: T; // Modified data (optional)
message?: string; // Logging message (optional)
}All hooks receive a HookContext:
interface HookContext {
sessionId: string;
timestamp: Date;
}import { registerHooks, type PIVHooks } from 'loop-agent';
const myPluginHooks: PIVHooks = {
// Modify request before starting
beforeStart: async (ctx, request) => {
console.log(`[${ctx.sessionId}] Starting: ${request}`);
return {
continue: true,
data: request.trim(), // Clean up request
};
},
// Skip planning for small fixes
beforePlanning: async (ctx, state) => {
if (state.complexity === 'small_task') {
return { continue: false, message: 'Skipping planning for small task' };
}
return { continue: true };
},
// Add metadata to plan
afterPlanning: async (ctx, state, plan) => {
return {
continue: true,
data: {
...plan,
metadata: { ...plan.metadata, plugin: 'my-plugin' }
}
};
},
// Log task execution
beforeImplementation: async (ctx, state, task) => {
console.log(`[${ctx.sessionId}] Executing task: ${task.id}`);
return { continue: true };
},
afterImplementation: async (ctx, state, task, success) => {
console.log(`[${ctx.sessionId}] Task ${task.id}: ${success ? 'success' : 'failed'}`);
},
// Skip validation level 4 in dev mode
beforeValidation: async (ctx, state, level) => {
if (level === 4 && process.env.NODE_ENV === 'development') {
return { continue: false, message: 'Skipping integration tests in dev' };
}
return { continue: true };
},
// Force pass certain validations
afterValidation: async (ctx, state, result) => {
if (!result.passed && result.isSystemIssue) {
return {
continue: true,
data: { ...result, passed: true },
message: 'Ignoring system issue'
};
}
return { continue: true };
},
// Handle errors gracefully
onError: async (ctx, error, errorContext) => {
await sendToErrorTracking(error, {
sessionId: ctx.sessionId,
context: errorContext
});
return {
continue: error.message.includes('recoverable'),
message: 'Error reported to monitoring'
};
},
// Cleanup after completion
afterComplete: async (ctx, state) => {
console.log(`[${ctx.sessionId}] Completed with status: ${state.status}`);
await reportSessionMetrics(ctx.sessionId, state);
}
};
// Register with high priority
registerHooks('my-plugin', myPluginHooks, 25);| Function | Returns | Description |
|---|---|---|
registerHooks(name, hooks, priority?) |
void |
Register hook set (priority default: 100) |
unregisterHooks(name) |
boolean |
Remove hooks by name |
clearAllHooks() |
void |
Remove all hooks |
getRegisteredHookNames() |
string[] |
List registered hook names |
getHookCount() |
number |
Count of registered hook sets |
Hooks execute in priority order (lower = first):
registerHooks('first', hooks1, 10); // Executes 1st
registerHooks('second', hooks2, 50); // Executes 2nd
registerHooks('third', hooks3, 100); // Executes 3rd (default)
registerHooks('fourth', hooks4, 200); // Executes 4thIf a hook returns continue: false, subsequent hooks for that checkpoint are skipped