import type { AssistantStateSnapshot } from "../shared/assistant-state.js"; /** * REST surface under /api — see src/shared/types.ts for the full contract * table. This router covers the session-lifecycle endpoints (W1); workflows, * macros and settings are mounted by other workstreams alongside this one. */ import { Router } from "express"; import type { AppState, BackgroundTask, HarnessAdapter, HarnessKind, HarnessSession, MacroDef, WorkflowInfo, SessionInputSubmissionResult } from "../shared/types.js"; import type { WorkspaceScopeSummary } from "../shared/workspace-scope.js"; import type { StudioProjectSummary } from "@sapiom/agent-map"; import { type SessionManager } from "../core/session-manager.js"; import type { SessionRecordReader } from "../core/session-record.js"; export interface RestRouterOptions { getAssistantState?: () => AssistantStateSnapshot; sessionManager: SessionManager; adapters: Partial>; version: string; /** Sapiom identity from CLI auth; null when unauthenticated / --no-auth. */ identity: { userId: string; tenantId: string; organizationName: string; } | null; listWorkflows: () => Promise; /** Scope identities joining visible folders to durable Studio projects. */ listWorkspaceScopes?: () => WorkspaceScopeSummary[] | Promise; /** Path-free durable Agent Map project identities. */ listStudioProjects?: () => StudioProjectSummary[] | Promise; listMacros: () => MacroDef[]; /** Look up a registered workflow by its path; null when not found. Backs * PATCH /sessions/:id/workflow's validation (a bind target must already * be a known workflow — scan/connect it first). */ findWorkflow: (workflowPath: string) => WorkflowInfo | null; /** Mirrors a session's workspace state (its binding plus the full workflow * registry) into HARNESS_CONTEXT_FILE in its cwd — the caller (server/ * index.ts) resolves `session.boundWorkflowPath` against the live * registry, so this only needs the session itself. Called on every * successful bind/unbind. Never throws — a write failure is logged by the * implementation, not surfaced as a request error. */ writeWorkspaceContext: (session: HarnessSession) => Promise; /** Called after a settings PATCH persists a changed telemetryOptIn, so the * live collector batcher can be gated without a server restart. */ onTelemetryOptInChange?: (optIn: boolean) => void; /** Called only for roots newly added to the user's durable project list. */ onRecentDirAdded?: (root: string) => Promise | void; /** Called (fire-and-forget) after a session is created, with its cwd and id * — lets the integrator scan that directory for workflows (so opening a * session in a new project discovers them without a manual "+ Connect") * and, when the scan discovers one, render the new session's canvas. */ onSessionCreated?: (cwd: string, harnessSessionId: string) => void; /** * Optional input boundary shared with the rolling alias; without it the * route falls back to SessionManager.submitInput. */ submitSessionInput?: (sessionId: string, text: string, submit: boolean) => Promise; /** The directory the CLI was launched against — surfaced in AppState so the * SPA can prefill the new-session modal with it. */ launchDir: string; /** The host's default parent directory for NEW agent projects. Omitted (the * CLI case), AppState.defaultProjectRoot is absent and the SPA falls back to * `launchDir` — see the field's doc on AppState. */ defaultProjectRoot?: string; /** The Agents API base URL (env-configurable) — surfaced in AppState so the * snippet panel's executions host matches where the server resolves slugs. * Omitted by tests, leaving AppState.agentsBaseUrl absent (the SPA then uses * the SDK's default host). */ agentsBaseUrl?: string; /** Background tasks known to this boot (TaskManager.list) — surfaced in * AppState so a page load mid-run shows the canvas activity state without * waiting for the next task.status frame. Optional: omitted by callers * without a TaskManager (tests), leaving AppState.tasks absent. */ listTasks?: () => BackgroundTask[]; /** Harness kinds confirmed available at CLI boot (doctor()), in * default-preference order. Omitted (rather than defaulted here) when the * caller doesn't supply it, so AppState.availableHarnesses stays absent in * that case too — see its doc comment for how consumers should treat that. */ availableHarnesses?: HarnessKind[]; /** "This boot found no prior harness use" — computed by the CLI before it * mutated any state, surfaced verbatim in AppState.firstRun. Omitted when * the caller doesn't supply it (tests), leaving AppState.firstRun absent. */ firstRun?: boolean; /** How telemetry consent was determined at boot — surfaced in AppState so the * UI can show the first-run notice when the user never explicitly answered. * Omitted when the caller didn't run the consent flow (tests). */ consentSource?: AppState["consentSource"]; /** Which env var forced telemetry off — surfaced in AppState for the * tracking indicator's "off (env)" label. Null/absent otherwise. */ consentEnvReason?: string | null; /** Where GET/PATCH /settings (and /state's settings read) persist to. * Omitted, the real `~/.sapiom/harness/settings.json` — the integrator * (server/index.ts) passes the path under its resolved state root so an * isolated boot never reads or writes the developer's real settings. */ settingsPath?: string; /** * Reader for session records rebuilt from our own analytics events (see * core/session-record.ts). Backs GET /sessions/:id/record and the * `turnCount` on history rows. * * Optional: when omitted, the record route returns 501 and history rows * carry no turnCount (tests that don't care about records don't need to stub * an event store). Never a hard dependency of the history list itself — a * failing reader degrades those rows to what the adapters knew, it never * fails the request. */ sessionRecords?: SessionRecordReader; /** * Dependencies for POST /api/track (UI-interaction analytics). * Optional: when omitted, /api/track returns 501 (tests that don't care * about UI analytics don't need to stub these). */ uiTrack?: { store: { append(event: import("../shared/types.js").AnalyticsEvent): Promise; }; batcher: { enqueue(event: import("../shared/types.js").AnalyticsEvent): void; }; /** Per-boot seq counter shared with the ingest pipeline. */ nextSeq: (sessionId: string) => number; machineId: string; userId: string | null; tenantId: string | null; }; } export declare function createRestRouter(options: RestRouterOptions): Router; //# sourceMappingURL=rest.d.ts.map