/** * @fileoverview CLI adapter for `roy-agent tasks get --operations --json`. * * Design goals: * - Spawn the host CLI as a discrete arg array — never via shell string. * - Enforce a wall-clock timeout and a maximum stdout size. * - Locate the leading `{` in stdout to skip INFO/log lines the real * CLI emits before its JSON envelope. * - Validate the parsed envelope against a stable schema. * - Sort operations ascending by timestamp and assign `sequence` numbers. * - Truncate over-long descriptions defensively. * - Surface typed errors so callers can map them to HTTP status codes. * * The adapter is intentionally narrow: it knows nothing about HTTP, the * collector, or the frontend. The `runner` is injectable so tests never * need a real subprocess. */ import type { TaskShowConfig } from "./types.js"; import { type TaskContextValue } from "./task-metadata.js"; export type CanonicalMilestoneType = "create" | "progress" | "milestone" | "problem" | "solution" | "decision" | "review" | "completed" | "unknown"; /** Subset of the CLI's `task` payload that we surface to the UI. */ export interface TaskMetadata { id: number; title: string; status: string; priority: string; type: string; progress?: number; createdAt: string; updatedAt: string; tags: string[]; /** Arbitrary JSON context, or its original ordinary/malformed string form. */ context?: TaskContextValue; /** Full task goals text (not just the operations summary DTO). */ goals_and_expected_deliverables?: string; projectPath?: string; } /** One row in the operations pipeline. */ export interface OperationRecord { id: number; /** 1-based sequence number, ascending by timestamp. */ sequence: number; /** Canonical milestone type (or 'unknown' if the CLI emits an unknown value). */ milestoneType: CanonicalMilestoneType; title: string; description: string; processDescription: string; timestamp: string; /** Truncated session id for display; the full id is never exposed. */ sessionShort: string; } export interface TaskOperationsEnvelope { task: TaskMetadata; operations: OperationRecord[]; } export interface CachedEnvelope { task: TaskMetadata; operations: OperationRecord[]; /** When the source data was last fetched (ISO 8601). */ fetchedAt: string; /** True if the cache entry is past TTL but still served. */ stale: boolean; } export interface AdapterRunnerResult { stdout: string; stderr: string; exitCode: number; } export type AdapterRunner = (args: string[]) => Promise; export interface AdapterOptions { /** Absolute path to the `roy-agent` executable. */ cliPath: string; /** Mockable subprocess runner (defaults to `defaultRunner`). */ runner?: AdapterRunner; /** Wall-clock timeout (default 5000 ms). */ timeoutMs?: number; /** Maximum stdout bytes to keep (default 1 MiB). */ maxBytes?: number; /** TaskShowConfig for maxOperations / maxDescriptionChars. */ cfg: TaskShowConfig; } /** Source interface used by OperationsCache and the HTTP layer. */ export interface TaskOperationsSource { getTaskOperations(taskId: number): Promise; } export declare class AdapterError extends Error { readonly code: string; readonly cause?: unknown; readonly exitCode?: number; constructor(message: string, opts?: { code?: string; exitCode?: number; cause?: unknown; }); } export declare class TaskNotFoundError extends AdapterError { readonly taskId: number; constructor(message: string, taskId: number); } export declare class ParseError extends AdapterError { constructor(message: string, cause?: unknown); } export declare class TimeoutError extends AdapterError { constructor(message: string, cause?: unknown); } export declare class SchemaError extends AdapterError { constructor(message: string); } export declare function assertValidTaskId(id: unknown): asserts id is number; /** * Default subprocess runner. We deliberately avoid `shell: true` to keep * argv as a literal array — no shell metacharacter interpretation. The * `AbortController` enforces the wall-clock timeout by killing the child. */ export declare const defaultRunner: AdapterRunner; /** Build the argv array. Pure function — easy to unit-test. */ export declare function buildTasksGetArgs(cliPath: string, taskId: number): string[]; /** * Locate the first top-level `{` in stdout and return everything from * there to the end. Tolerant of: * - INFO / log lines printed before the JSON envelope * - mixed CRLF / LF * - `✗ Task not found: N` lines (handled separately by exit code) */ export declare function extractJsonEnvelope(stdout: string): string; /** * Map any string to a CanonicalMilestoneType. Unknown values fall back * to 'unknown' (with the original string preserved under `title` if we * ever want to display it). */ export declare function canonicalMilestoneType(raw: unknown): CanonicalMilestoneType; /** Shorten a session id like `session_392d70e0-07bc-4a41-8669-ece72f325aad` * to `s_392d70e0` (12 chars max, never leaks the full UUID). */ export declare function shortSessionId(raw: unknown): string; /** * Run `roy-agent tasks get --operations --json` and return a parsed * envelope. Errors are typed so the HTTP layer can map them to status codes. * * Pass `runner: defaultRunner` in production. Tests pass a `fixedRunner`. */ export declare function runTasksGetOperations(taskId: number, options: AdapterOptions): Promise; /** * Convenience wrapper used by OperationsCache: a `TaskOperationsSource` * whose `getTaskOperations(taskId)` returns the parsed envelope or throws. */ export declare function makeTaskOperationsSource(opts: AdapterOptions): TaskOperationsSource; //# sourceMappingURL=cli-tasks-adapter.d.ts.map