/** * KHAELOR * File: src/workspace/workspace.ts * Description: Workspace interface — the single world seam for files and process execution (ADR-13). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ /** * The world seam (ARCHITECTURE.md §workspace, ADR-13). Exactly four methods. * Tools act on the world only through this interface; `src/workspace/` is the * sole module allowed to import `node:fs` / `node:child_process`. */ export interface Workspace { cwd(): string; readFile(path: string): Promise; /** Atomic: temp file + rename, preserving mode bits. */ writeFile(path: string, content: string): Promise; exec(command: Command): Promise; } /** A foreground command for `Workspace.exec` (used by the `bash` tool). */ export interface Command { /** Run via the user shell for the `bash` tool. */ cmd: string; cwd?: string; /** Hard ceiling; ADR-8: long commands redirect to `process`. */ timeoutMs: number; env?: Record; signal?: AbortSignal; } /** Result of a foreground command. */ export interface ProcessResult { /** null = killed by timeout/signal. */ exitCode: number | null; stdout: string; stderr: string; durationMs: number; truncated: boolean; } /** Stable machine-readable error codes for workspace failures. */ export type WorkspaceErrorCode = | "file-not-found" | "file-is-directory" | "file-too-large" | "file-binary" | "write-failed" | "exec-failed" | "process-unknown" | "process-not-running"; /** * Typed workspace failure. Kept module-local (the shared `KhaelorErrorCode` * union stays closed); messages must never contain secrets. */ export class WorkspaceError extends Error { readonly code: WorkspaceErrorCode; readonly details: Readonly>; constructor(code: WorkspaceErrorCode, message: string, details: Record = {}) { super(message); this.name = "WorkspaceError"; this.code = code; this.details = details; } } /** Narrowing helper. */ export function isWorkspaceError(value: unknown): value is WorkspaceError { return value instanceof WorkspaceError; }