import type { Cursor, IsoTime, NodeIdDTO, NodeStatusDTO } from './common.js'; import type { NodeSummaryDTO } from './nodes.js'; /** `GET /v1/nodes` + `GET /v1/status` composed for the dashboard view. */ export interface DashboardQuery { /** Restrict to the subtree under this node. */ under?: NodeIdDTO; } export interface DashboardDTO { nodes: NodeSummaryDTO[]; /** Node counts keyed by status. */ counts: Partial>; generated_at: IsoTime; } /** A node needing attention (tickets across the canvas). Projected from the * `TicketEntry` reader — one entry per node with open human tickets. */ export interface AttentionItemDTO { node_id: NodeIdDTO; name: string; cwd: string; /** Number of open tickets on this node. */ count: number; } /** `GET /v1/canvas/attention` result. */ export interface AttentionDTO { items: AttentionItemDTO[]; } /** `POST /v1/canvas/attention/counts` request. Restrict the synchronous inbox * scan to the nodes a viewer is actually rendering instead of enriching the * entire canvas roster. */ export interface AttentionCountsRequest { node_ids: NodeIdDTO[]; } /** `POST /v1/canvas/attention/counts` result, keyed by requested node id. */ export interface AttentionCountsDTO { counts: Record; } /** `POST /v1/canvas/history/search` body — the ranked/filtered search over the * per-cwd episodic corpus, backing `crtr canvas history search` (optional * query: ranked when present, recency browse when omitted). The CLI parses + * validates flags (`--type` set, `--since`/`--until` → epoch ms) and sends * this normalized query; the server runs `buildCorpus` + ranking + pagination * (closures like `loadBody` cannot cross the boundary, so the whole search * executes server-side). Sent as POST — the query carries arrays and * free-text that do not serialize cleanly as GET params. */ export interface HistorySearchQuery { /** Whitespace-separated terms, ranked; omit to browse by recency. */ query?: string; cwd?: string; all_cwds?: boolean; under?: string; /** Restrict to specific node ids. */ nodes?: string[]; /** Corpus types: report | doc | roadmap | meta | inbox. */ types?: string[]; report_kind?: string; kinds?: string[]; statuses?: string[]; /** Lower bound on artifact timestamp (epoch ms), parsed CLI-side. */ since_ms?: number; /** Upper bound on artifact timestamp (epoch ms), parsed CLI-side. */ until_ms?: number; /** Weigh full body text in ranking (`--body`). */ weigh_body?: boolean; /** relevance | recency | oldest — already resolved to the effective sort. */ sort?: string; snippet_lines?: number; full?: boolean; limit?: number; cursor?: Cursor; } /** `POST /v1/canvas/history/search` result. `hits` is heterogeneous by mode * (with/without a relevance score, snippet vs full body), so it stays a * loose object list — the CLI's renderer already treats hits as * `Record[]`. The static `follow_up` line is appended * CLI-side. */ export interface HistorySearchResultDTO { hits: Record[]; next_cursor: Cursor | null; total: number; } /** `POST /v1/canvas/history/grep` body — the required-pattern line-hit search * over the per-cwd episodic corpus, backing `crtr canvas history grep`. * Shares scope/corpus filters with `HistorySearchQuery` but is a distinct * wire contract: `pattern` is mandatory (an ECMAScript regex) and there is no * ranking/snippet/full-body shape. */ export interface HistoryGrepQuery { /** Required ECMAScript regex, matched per body line (case-insensitive). */ pattern: string; cwd?: string; all_cwds?: boolean; under?: string; /** Restrict to specific node ids. */ nodes?: string[]; /** Corpus types: report | doc | roadmap | meta | inbox. */ types?: string[]; report_kind?: string; kinds?: string[]; statuses?: string[]; /** Lower bound on artifact timestamp (epoch ms), parsed CLI-side. */ since_ms?: number; /** Upper bound on artifact timestamp (epoch ms), parsed CLI-side. */ until_ms?: number; limit?: number; cursor?: Cursor; } /** One matching body line from `crtr canvas history grep`. */ export interface HistoryGrepHitDTO { ref: string; /** "name (id)". */ node: string; ts: IsoTime; line: number; text: string; } /** `POST /v1/canvas/history/grep` result — a stable line-hit schema, distinct * from `HistorySearchResultDTO`'s heterogeneous ranked/browse shape. */ export interface HistoryGrepResultDTO { hits: HistoryGrepHitDTO[]; next_cursor: Cursor | null; total: number; } /** `GET /v1/canvas/history/read` query — resolve one `:` ref. */ export interface HistoryReadQuery { ref: string; /** Keep the artifact's YAML frontmatter (stripped by default). */ frontmatter?: boolean; } /** `GET /v1/canvas/history/read` result — one hit's full body. */ export interface HistoryReadResultDTO { ref: string; /** "name (id)". */ node: string; /** report: | doc | roadmap | meta | inbox. */ source: string; ts: IsoTime; content: string; } /** One node row in the browser canvas roster (`GET /v1/canvas/snapshot`). A * richer projection than `NodeSummaryDTO`: it carries the enriched viewer * fields (`dashboardRowsAll` + `enrichRows`) the browser canvas renders — * attention counts, ctx tokens, streaming/hanging/viewed state, last activity. */ export interface SnapshotNodeDTO { node_id: NodeIdDTO; name: string; kind: string; mode: string; lifecycle?: string; status: NodeStatusDTO; cwd: string; parent: NodeIdDTO | null; created: IsoTime; host_kind: string; /** True when the node hosts an enterable broker (`host_kind === 'broker'`). */ enterable: boolean; attention_count: number; cycles?: number; last_activity?: IsoTime; ctx_tokens?: number; streaming: boolean; /** The node's active fault projection (structurally a `Fault`), or null. Kept * as a loose object to keep this DTO free of a `core` type import. */ hanging: Record | null; viewed: boolean; } /** `GET /v1/canvas/snapshot` result — the machine-readable browser canvas * roster (`crtr canvas snapshot`). Distinct from the per-node `NodeSnapshotDTO` * (nodes.ts). `subscriptions` is the authoritative `subscribes_to` adjacency * map: each node id → its publisher/child node ids in edge order. */ export interface SnapshotDTO { generated_at: IsoTime; nodes: SnapshotNodeDTO[]; subscriptions: Record; } /** One node row in the lean roster (`GET /v1/canvas/roster`). Carries only * columns already native to the `nodes` row — no meta.json read, no * process-liveness probe. `attention_count`/`last_activity`/`ctx_tokens`/ * `streaming`/`hanging`/`viewed` stay on `SnapshotNodeDTO`; a roster consumer * that needs attention counts fetches them separately via the existing * `POST /v1/canvas/attention/counts` (already scoped to exactly the ids it * renders, never the whole canvas). */ export interface RosterNodeDTO { node_id: NodeIdDTO; name: string; kind: string; mode: string; lifecycle?: string; status: NodeStatusDTO; cwd: string; host_kind: string; /** True when the node hosts an enterable broker (`host_kind === 'broker'`). */ enterable: boolean; parent: NodeIdDTO | null; created: IsoTime; } /** One `subscribes_to` edge in the roster's topology half. Every edge is kept * (passive + multiple per node) — never collapsed to one edge per node. */ export interface RosterEdgeDTO { from_id: NodeIdDTO; to_id: NodeIdDTO; active: boolean; created: IsoTime; } /** `GET /v1/canvas/roster` result — the lean, set-based topology read shared by * the attach viewer and the browser canvas: exactly two indexed queries * (`rosterNodes()` + `rosterEdges()`), no `enrichRows`, no per-row * `getNode`/`subscriptionsOf` loop, no synchronous liveness probe. The * recurring poll target that replaces the enriched `/v1/canvas/snapshot` * storm; that endpoint remains for genuinely on-demand rich views. */ export interface RosterDTO { generated_at: IsoTime; nodes: RosterNodeDTO[]; edges: RosterEdgeDTO[]; } /** `POST /v1/canvas/prune` body — the three exclusive prune modes of * `crtr canvas prune`, precedence COUNT (`limit`) > EMPTY (`empty`) > TTL sweep. */ export interface PruneRequest { /** COUNT mode: keep the N most-recently-active nodes, prune the oldest * remaining terminal nodes (protected nodes always survive). */ limit?: number; /** TTL sweep retention window in days (default 14). */ ttl_days?: number; /** TTL sweep: also prune stale active/idle nodes past the TTL whose process * is provably gone. */ include_stale?: boolean; /** EMPTY mode: reap nodes whose engine never produced an assistant message. */ empty?: boolean; /** Report what would be pruned without mutating. */ dry_run?: boolean; } /** One node in a prune result (pruned, or candidate under `dry_run`). */ export interface PrunedNodeDTO { node_id: NodeIdDTO; /** Lifecycle status, or `empty` for EMPTY-mode reaps. */ status: string; /** ISO created timestamp (empty string for EMPTY-mode reaps). */ created: string; } /** `POST /v1/canvas/prune` result. `pruned` is the pruned set (or, under * `dry_run`, the candidate set — nothing deleted). `ttl_days` echoes the * retention window used. */ export interface PruneResultDTO { pruned: PrunedNodeDTO[]; dry_run: boolean; ttl_days: number; } /** `POST /v1/canvas/rebuild-index` result. Fails loud server-side when metas * exist on disk but the rebuild yields zero rows (a corrupt/empty index). */ export interface RebuildIndexResultDTO { /** How many `nodes//meta.json` files were found on disk. */ metas: number; /** How many node rows exist in canvas.db after the rebuild. */ rows: number; }