/** * Sub-agent progress file for the RD/QA sub-agents in `peaks-code`'s * Swarm phase. A sub-agent (or the LLM via the `peaks progress step` * CLI) writes a stable JSON file at * `.peaks/_sub_agents//subagent-progress.json`. The dispatch + * heartbeat flow (slice #009 + #010) reads this file as the source * of truth for "is this sub-agent still alive?". * * Token cost design (the binding constraint of this feature): * - The LLM side (step CLI) writes the file at most once per * phase transition. That is approximately one Bash call per * RD/QA sub-step. In a typical 5-step sub-agent slice the * cost is < 10 output tokens. * - The dispatch side polls the file via `peaks sub-agent * heartbeat`, not the LLM. Zero token cost. * * Slice #014: the legacy `peaks progress start|watch|close` auto-spawn * surface is DELETED. With dispatch + heartbeat (slice #009 + #010), the * same sub-agent now runs in the same IDE/terminal as the main loop, so * a separate watch window is dead weight. The only remaining consumer * of this module is the `peaks progress step` write path + the * dispatcher's read-back of the progress file. * * This module is pure filesystem. It does NOT import the LLM * harness, does NOT spawn terminals, and does NOT talk to any * IPC. The dispatch-side consumption is in * `src/services/dispatch/sub-agent-dispatcher.ts` and reads the * progress file via `subAgentProgressPath`. */ export type SubAgentProgressPhase = 'starting' | 'running' | 'verifying' | 'completing' | 'finished' | 'failed' | 'idle'; export type SubAgentProgressStep = { /** ISO-8601 timestamp at which the sub-agent started. */ startedAt: string; /** ISO-8601 timestamp at which the sub-agent finished (or now, if still running). */ updatedAt: string; /** Free-form human-readable sub-step label, e.g. "running test/ut". */ step: string; /** Current phase bucket. `idle` is the pre-start sentinel. */ phase: SubAgentProgressPhase; /** When set, the sub-agent is finished and reports the verdict here. */ verdict?: 'pass' | 'return-to-rd' | 'blocked'; /** Optional count of in-scope files touched, assertions run, etc. */ counts?: { filesTouched?: number; testsRun?: number; }; }; export type SubAgentProgress = { version: 1; sessionId: string; outerSessionId?: string; /** Outer-agent role that owns the slice (e.g. "rd", "qa"). */ role: string; /** Per-slice identifier. */ requestId: string; /** When the sub-agent entered the first non-idle state. */ startedAt: string; /** When the sub-agent last touched the file. */ updatedAt: string; /** Current step. */ current: SubAgentProgressStep; /** * History of completed steps. Length is unbounded; the watch * tool renders the most recent N. Kept for after-the-fact * forensics ("how long did step 3 take?"). */ history: SubAgentProgressStep[]; }; export type ReadProgressOptions = { projectRoot: string; }; export type ReadProgressResult = { ok: true; data: SubAgentProgress; path: string; } | { ok: false; reason: 'no-binding' | 'no-progress-file' | 'invalid-json'; }; export type WriteProgressOptions = { projectRoot: string; requestId: string; role: string; step: string; phase: SubAgentProgressPhase; verdict?: 'pass' | 'return-to-rd' | 'blocked'; counts?: SubAgentProgressStep['counts']; outerSessionId?: string; }; /** * Read the current progress file. Returns a tagged result so * the CLI can map each failure mode to a distinct nextActions * hint (no-binding → run peaks workspace init; no-progress-file * → sub-agent has not started yet; invalid-json → the LLM wrote * garbage; recover by writing a fresh file). */ export declare function readSubAgentProgress(options: ReadProgressOptions): ReadProgressResult; /** * Append-or-replace the current step. Idempotent on identical * (step, phase) — the file is rewritten with the same payload * (no new history entry) so heartbeats from the same phase do * not pollute the history. Phase transitions append a new step * to the history and replace `current`. */ export declare function writeSubAgentProgress(options: WriteProgressOptions): SubAgentProgress; /** * Resolve the project root for a CLI invocation: --project * override wins, otherwise the canonical git-root promotion * (so the sub-agent's writes land in the same `.peaks/_runtime//` * the user's manual CLI would use). Re-exports the same * helper peaks workspace init / session rotate already use * for symmetry. */ export declare function resolveProgressProjectRoot(override: string | undefined, cwd: string): string; /** * Compute the absolute path to the progress file for a given * project root, for callers that need to display / fs.watch it * (the dispatcher's read-back, the LLM-side step write banner, * etc.). This MUST agree with `progressPath` — the read/write * helpers resolve through `progressPath` and use the session * sub-directory, so the displayed path does too. Without this * agreement the dispatcher's read-back would point at a path * the writer never touches. */ export declare function subAgentProgressPath(projectRoot: string): string;