import type { McpPreflightResult } from "../core/mcp-compatibility.js"; import type { AppState, HarnessAdapter, HarnessKind } from "../shared/types.js"; import { SessionManager, type LaunchOptsBuilder } from "../core/session-manager.js"; import type { HarnessIdentity } from "../cli/auth.js"; import { type McpDevServerCommand } from "../core/inject/mcp-config.js"; export interface HarnessServerOptions { port: number; /** Bind address. Defaults to 127.0.0.1 — the server must never listen on 0.0.0.0. */ host?: string; /** * Per-boot host/browser secret; required on WS upgrades and `/api` headers. * This credential is deliberately never injected into coding-agent PTYs. */ bootToken: string; telemetryOptIn: boolean; /** Shared Sapiom authentication policy. Defaults to enabled. */ authMode?: "enabled" | "disabled"; /** Sapiom identity from CLI auth; omit/null when unauthenticated or --no-auth. */ identity?: HarnessIdentity | null; /** Stable per-install id for analytics. Defaults to a freshly loaded/created one. */ machineId?: string; /** Override the adapter set (tests inject a stubbed claude-code binary). */ adapters?: Partial>; /** Root directory ALL persistent harness state lives under: machine-id, * sessions.json, workflows.json, events.ndjson, settings.json, generated/ * and sample-project/. Defaults to the real HARNESS_HOME * (`~/.sapiom/harness`) — unchanged for CLI users. Tests, e2e scripts and * live checks MUST pass a scratch directory here so a server boot never * registers its temp projects (or anything else) into the developer's * real state. The per-file options below still override individual * locations; each now defaults under this root. */ stateRoot?: string; /** Session registry file. Defaults to `/sessions.json`. */ sessionsPath?: string; buildLaunchOpts?: LaunchOptsBuilder; /** Loads the coding-agent system prompt for each session start. Defaults to the * live fetch of GET /v1/harness/system-prompt (SAP-2810), which resolves to the * bundled DEFAULT_SYSTEM_PROMPT on any failure. Tests and offline hosts inject a * plain function instead of reaching the network. */ loadSystemPrompt?: () => Promise; /** Host-supplied launcher for the local sapiom-dev MCP server, replacing the * default `npx @sapiom/mcp@latest`. The Electron host passes its own binary * (GUI-subsystem — allocates no console window on Windows, where the npx * chain's cmd.exe popped a persistent one that users closed, killing the * server) plus the entry script it installed into the per-user npm prefix. * See core/inject/mcp-config.ts. */ sapiomDevMcp?: McpDevServerCommand; /** Per-create/resume local preflight. Hosts own executable selection/installation. */ prepareSapiomDevMcp?: () => Promise; /** Root directory per-session generated agent configs are written under — * and cleaned up from (exit-time delete + boot-time sweep, see * core/inject/retention.ts). Defaults to `/generated`. */ generatedRoot?: string; /** Root directory archived session records are written under — the copies * that outlive events.ndjson's retention (see core/record-archive.ts). * Defaults to `/records`. */ recordsRoot?: string; collectorUrl?: string; /** Workflow registry file. Defaults to `/workflows.json`. */ workflowsRegistryPath?: string; /** Local analytics ndjson sink. Defaults to `/events.ndjson`. */ eventStorePath?: string; /** Overrides the home directory codex rollout discovery scans * (`/.codex/sessions/...`). Defaults to the real OS home dir; tests * point this at a fixture tree instead of touching `~/.codex` for real. */ codexHomeDir?: string; /** Overrides the home directory Claude Code transcript discovery scans * (`/.claude/projects/...`). Defaults to the real OS home dir; tests * point this at a fixture tree instead of touching `~/.claude` for real. */ claudeHomeDir?: string; /** Directory the built SPA lives in. Defaults to dist/web next to this module. */ webDir?: string; /** The directory the CLI was launched against — scanned for workflows at * boot so the rail isn't empty until a manual "+ Connect", and (unless * autoCreateSession is false) where the boot session is created. */ launchDir?: string; /** The host's default parent directory for NEW agent projects (the * add-workspace template and idea doors), before the user's `projectRoot` * setting overrides it. Defaults to `launchDir` — correct for the CLI, where * the developer chose that directory deliberately. The Electron host passes * `/projects` so user code doesn't land in the state directory's * own listing. Only the host knows which it is, which is why this is an * option rather than something the SPA infers. */ projectRoot?: string; /** Auto-create a session in launchDir once the server is listening, so the * app doesn't open empty. Defaults to true; the CLI's --no-session flag * sets this to false. Uses `defaultHarnessKind` for which agent to launch. */ autoCreateSession?: boolean; /** Harness kind for the auto-created boot session. The CLI resolves this * from doctor() results (claude-code if present, else codex) before * calling startServer; defaults to "claude-code" for callers that don't * run doctor (tests, --dev flows without a real agent). */ defaultHarnessKind?: HarnessKind; /** Harness kinds confirmed available at CLI boot (doctor()), surfaced * as-is in AppState for the SPA — see its doc comment. Omitted (not * defaulted here) when the caller doesn't supply it. */ availableHarnesses?: HarnessKind[]; /** "This boot found no prior harness use on this machine" — computed by * the CLI before it records the launch dir, surfaced verbatim in * AppState.firstRun (see its doc comment). Omitted → absent there too. */ firstRun?: boolean; /** * How telemetry consent was determined — passed through verbatim into * AppState.consentSource. Omitted when the caller didn't run the consent * flow (tests, mocks). */ consentSource?: AppState["consentSource"]; /** * Which env var forced telemetry off (when consentSource === "env-forced-off"). * Passed through into AppState.consentEnvReason. */ consentEnvReason?: string | null; /** Internal deterministic seams for coordinator race tests. */ workflowDiscoveryTestHooks?: { beforeScan?: (input: { root: string; reason: WorkflowScanReason; generation: number; }) => void | Promise; afterScan?: (input: { root: string; reason: WorkflowScanReason; generation: number; }) => void | Promise; beforePublication?: (input: { epoch: number; roots: readonly string[]; }) => void | Promise; afterContextStaging?: (input: { epoch: number; sessionIds: readonly string[]; }) => void | Promise; }; /** Internal deterministic seam for post-listen startup-failure tests. */ startupTestHooks?: { afterListen?: (port: number) => void | Promise; }; } export interface HarnessServer { close(): Promise; port: number; /** Host-only credential used to authorize the initial browser HTML bootstrap. */ uiToken: string; sessionManager: SessionManager; } /** * Why a registry scan is running. Naming every entry path is what * turns "the rail grew rows I did not ask for" into a line in the log: * * boot the directory the studio was launched in * session-create the directory a new session was opened in * workspace-change the session cwd whose marker set the watcher saw change * agent-linked one agent's own directory, after `deploy` wrote its id * agent-created a newly scaffolded agent's containing project * agent-connected one manually connected path, to settle syntax evidence * agent-moved the destination of a rail drag * requested POST /api/workflows/scan — the "Add all" button * * POST /api/workflows/connect registers one path before its reconciliation * scan; every other reason enters through discovery directly. */ export type WorkflowScanReason = "boot" | "session-create" | "workspace-change" | "agent-linked" | "agent-created" | "agent-connected" | "agent-moved" | "requested"; /** * Starts Studio's HTTP server and session lifecycle services. * Call the returned close() method to stop listeners and release resources. */ export declare const startServer: (options: HarnessServerOptions) => Promise; //# sourceMappingURL=index.d.ts.map