/** * LoopScheduler — moss's self-iteration engine. * * Enables moss to run autonomously in a continuous loop (like an external cron, * but built-in): runs a prompt, waits for completion, sediments findings to * memory + journal, compacts the conversation, then re-schedules. * * Key features: * - Bounded: maxIterations, maxDurationMs, maxTokens — prevents runaway * - Observable: emits LoopEvent stream (iteration N, elapsed, findings, status) * - Resumable: saves state to .moss/loop-state.json for the CLI or SDK to continue * * Usage: * const scheduler = new LoopScheduler(agent, { intervalMs, maxIterations, prompt }); * await scheduler.start(); * // ...later, to resume after crash: * const restored = await LoopScheduler.restore(agent, statePath); * await restored?.start(); * * @public */ import type { MossAgent } from '../agent/moss-agent.js'; export interface LoopSchedulerOptions { /** The prompt to run each iteration. */ prompt: string; /** Interval between iterations (ms). Default 0 (immediately re-run). */ intervalMs?: number; /** Max iterations. 0 = unlimited. Default 0. */ maxIterations?: number; /** Max total duration (ms). 0 = unlimited. Default 0. */ maxDurationMs?: number; /** Session key for the loop. Default 'loop'. */ sessionKey?: string; /** * @deprecated No-op. Each iteration uses an isolated sessionKey so there is * no shared context to compact. Accepted for API compatibility; ignored. * Will be removed in a future major version. */ compactBetweenIterations?: boolean; /** Whether to write a journal to .moss/loop-journal.jsonl. Default true. */ journal?: boolean; /** * Autonomous mode: after each iteration, the scheduler asks the model whether * the goal is complete. If not, the model provides the next sub-task prompt * and the next iteration starts immediately when intervalMs=0 (default for * CLI /goal and /loop). The agent may fan_out_subagents inside an iteration * for independent parallel work. Default false (legacy: re-run the same prompt). */ autonomous?: boolean; /** Consecutive iteration failures before pausing. Default 5. */ maxConsecutiveFailures?: number; /** * Optional callback to receive streaming events from each iteration. * Provides real-time streaming output (text_delta, tool_start/end, etc.) * so TUI / REPL can show the agent working live rather than waiting for * iteration completion. */ onIterationEvent?: (event: import('../index.js').MossAgentEvent) => void; } export interface LoopIterationResult { iteration: number; success: boolean; response: string; durationMs: number; error?: string; startedAt: number; endedAt: number; } export type LoopEvent = { type: 'loop_started'; prompt: string; maxIterations: number; startedAt: number; } | { type: 'iteration_started'; iteration: number; startedAt: number; } | { type: 'iteration_completed'; result: LoopIterationResult; } | { type: 'iteration_failed'; iteration: number; error: string; } | { type: 'loop_paused'; reason: string; iteration: number; } | { type: 'loop_completed'; totalIterations: number; totalDurationMs: number; startedAt: number; endedAt: number; } | { type: 'loop_aborted'; reason: string; iteration: number; }; export interface LoopState { prompt: string; currentPrompt?: string; intervalMs: number; maxIterations: number; maxDurationMs: number; maxConsecutiveFailures?: number; sessionKey: string; /** @deprecated No-op; persisted for resume compatibility only. */ compactBetweenIterations?: boolean; journal?: boolean; autonomous?: boolean; currentIteration: number; startedAt: number; totalDurationMs: number; lastResult?: LoopIterationResult; paused: boolean; pauseReason?: string; status?: 'running' | 'paused' | 'completed'; } export interface LoopRestoreOptions { onIterationEvent?: LoopSchedulerOptions['onIterationEvent']; maxIterations?: number; } export declare class LoopScheduler { private readonly agent; private readonly options; private state; private listeners; private running; private consecutiveFailures; private abortController?; /** * In autonomous mode, the prompt for the current iteration. Starts as the * original goal; after each iteration, `checkCompletion` may replace it with * a model-generated continuation prompt for the next sub-task. */ private currentPrompt; private activeSessionKey?; private steeringRevision; private workspaceDir; private resumePending; constructor(agent: MossAgent, options: LoopSchedulerOptions); /** Subscribe to loop events (for TUI / observability). */ on(listener: (event: LoopEvent) => void): () => void; private emit; /** Start the loop. Runs until maxIterations/maxDurationMs or abort. */ start(): Promise; /** Abort the active agent run and preserve the last completed iteration. */ abort(): void; /** Get the current loop state (for observability). */ getState(): LoopState; getActiveSessionKey(): string | undefined; /** Update the active loop now, or its next iteration at the next safe boundary. */ steer(prompt: string): boolean; private runOneIteration; private buildIterationPrompt; /** * Ask the model whether the overall goal is complete. In autonomous mode this * runs after each iteration; if the model says the goal is NOT done, it also * provides a continuation prompt for the next sub-task. * * Returns `{ done: true }` when the goal is achieved, or `{ done: false, * nextPrompt }` with the model-suggested next step. */ private checkCompletion; private recordCompletionUsage; private sleep; private saveState; private appendJournal; static restore(agent: MossAgent, workspaceDir?: string, options?: LoopRestoreOptions): Promise; } //# sourceMappingURL=loop-scheduler.d.ts.map