/** * KHAELOR * File: src/tools/types.ts * Description: ToolResult envelope, ToolContext, and tool-emitted event shapes (TOOL_PROTOCOL §1). * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import type { FileTimeRegistry, ProcessManager, Workspace } from "../workspace/index.js"; /** The seven V1 tools plus the v2 additions: design (phase gates), remember (memory), symbols/refs (RepoGraph). */ export type ToolName = | "read" | "write" | "edit" | "grep" | "glob" | "bash" | "process" | "design" | "remember" | "symbols" | "refs"; /** * Coarse capability hint used by the executor/permission layer to derive * capability requests. Tools never evaluate permissions themselves — * evaluation happens in the Tool Runtime (ARCHITECTURE.md §5.4). */ export type ToolCapabilityHint = "file.read" | "file.write" | "process.execute"; /** Truncation report attached to results that were cut down (TOOL_PROTOCOL §1.3). */ export interface TruncationInfo { originalBytes: number; originalLines: number; shownHeadLines: number; shownTailLines: number; omittedLines: number; /** Absolute path to the full output, if spilled. */ spillPath?: string; } /** UI/log-facing metadata. Never serialized into model context (TOOL_PROTOCOL §1.1). */ export interface ToolResultMetadata { /** Collapsed one-liner shown in the conversation. */ title: string; /** Unified diff for write/edit — powers instant `d` expansion and /diff. */ diff?: string; additions?: number; deletions?: number; /** grep/glob counts. */ matches?: number; files?: number; /** bash/process. */ exitCode?: number | null; processId?: string; durationMs: number; truncation?: TruncationInfo; /** Free-form extras (path, lines, strategy, status, …). */ extra?: Record; } /** * The result envelope: strict separation of model-facing content from * UI-facing metadata (TOOL_PROTOCOL §1.1). */ export interface ToolResult { /** Exact text the model receives as tool_result content. */ content: string; /** True → tool_result carries is_error: true. The content must be repair prose. */ isError?: boolean; /** UI/log-facing. Never sent to the model. */ metadata?: ToolResultMetadata; } /** * Domain events tools emit. Structurally identical to the corresponding * `DurableEventInput` members in `src/session/events.ts` — kept local so * tools import only `workspace` and `shared` (ARCHITECTURE.md layering). */ export type ToolEmittedEvent = | { type: "file.read"; payload: { /** Relative to workspace cwd. */ path: string; range?: { start: number; end: number }; bytes: number; mtimeMs: number; toolUseId: string; }; } | { type: "file.modified"; payload: { path: string; operation: "write" | "edit"; diffStats: { added: number; removed: number }; diff?: string; toolUseId: string; }; } | { type: "memory.written"; payload: { section: string; entry: string; confidence: "high" | "medium" | "low"; toolUseId: string; }; }; // ─────────────────── v2 service facets (structural seams) ─────────────────── // Defined locally so tools keep importing only `workspace` and `shared` // (ARCHITECTURE.md layering) — the executor injects implementations that are // structurally identical to the phases/repograph services. /** Design artifact shape — structurally identical to session/events DesignArtifact. */ export interface ToolDesignArtifact { goal: string; filesTouched: string[]; approach: string; risks: string[]; verification: string; outOfScope: string[]; } /** Phase-gate facet the `design` tool uses (v2 §1). */ export interface PhaseToolFacet { mode: "strict" | "auto" | "off"; current(): "understand" | "design" | "implement"; submitDesign( artifact: ToolDesignArtifact, ): Promise<{ status: "approved" | "rejected" | "pending"; artifactId: string; reason?: string }>; } /** One symbol row returned by the semantic index (v2 §3). */ export interface SymbolHit { symbol: string; kind: "function" | "class" | "type" | "export" | "variable" | "method"; file: string; line: number; signature: string; docComment?: string; } /** One reference site returned by the semantic index (v2 §3). */ export interface RefHit { file: string; line: number; context: string; } /** RepoGraph facet the `symbols`/`refs` tools use (v2 §3). */ export interface RepoGraphFacet { querySymbols(query: string, kind?: string, scope?: string): Promise; queryRefs(symbol: string, direction: "callers" | "callees" | "importers"): Promise; } /** * Everything a tool may touch during execution (TOOL_PROTOCOL §1.2). * Built by the executor; tools never reach for Node globals (ADR-13). */ export interface ToolContext { readonly sessionId: string; /** Anthropic tool_use id of this call. */ readonly callId: string; /** The ONLY file/exec seam (ADR-13). */ readonly workspace: Workspace; /** Cancellation tree (ADR-11). */ readonly signal: AbortSignal; readonly fileTimes: FileTimeRegistry; readonly processes: ProcessManager; /** Emit durable domain events (FileRead, FileModified). */ emit(event: ToolEmittedEvent): void; /** Stream UI-facing progress metadata mid-execution (ephemeral). */ progress(meta: Record): void; /** Spill oversized output; returns the absolute path (TOOL_PROTOCOL §1.3). */ spill(label: string, content: string): Promise; /** Phase-gate facet (v2 §1); absent when gates are unavailable. */ readonly phases?: PhaseToolFacet; /** Semantic-index facet (v2 §3); absent when the index is unavailable. */ readonly repograph?: RepoGraphFacet; } /** Synthetic content used when a tool call is cancelled (TOOL_PROTOCOL §1.2). */ export const CANCELLED_RESULT_CONTENT = "[Tool execution cancelled by user]";