import Fastify from 'fastify'; import { type AuthOptions } from './auth.js'; import { type ChatCitationDeps, type VerifyOptions } from './chat-citations.js'; import { CoreAdapter, type McpEngineGate } from '../core/adapter.js'; import type { PluginSource } from '../skills/plugin-source.js'; import { type VenvProvisioner } from '../skills/venv.js'; /** * The bundled studio SPA lives at `dist/studio` next to the compiled server * (`dist/api/server.js` → `../studio`). See DES-STUDIO-SERVING-001 §2.3/§3.1 * and `scripts/bundle-studio.mjs`, which copies the installed `wicked-studio` * package's `dist/` there (the SPA is its own product since the #98 carve — * github.com/mikeparcewski/wicked-studio — consumed as a dist artifact). */ export declare function defaultStudioRoot(): string; export interface CreateServerOptions { /** Override the studio asset root (tests point this at a temp fixture dir). */ studioRoot?: string; /** * Opt-in governed answering of wicked-interactive first-draft generation (task #86 spike, * Phase 7c). When enabled, a durable subscriber answers `wicked.interactive.doc.created` * (kind:source) with a governed `interactive-draft` run that ends in * `wicked.interactive.draft.completed`; when absent (the default), interactive's own assist * loop remains the answerer and crew never touches that bus traffic. */ interactiveDraftEvents?: { enabled: boolean; /** The bus db; omit for the one the adapter handed its engine (`busDbPath`, core/bus.ts). */ dbPath?: string; /** Poll cadence, ms (tests shorten it). */ pollIntervalMs?: number; /** Heartbeat narration cadence, ms (default 15000). */ heartbeatMs?: number; /** Durable replay-dedup ledger path (default ~/.wicked-crew/interactive-draft-ledger.json). */ ledgerPath?: string; /** Where governed workers write finished drafts (default ~/.wicked-crew/interactive-drafts). */ draftDir?: string; /** Seat roster override (JSON array); omit for the production council roster. */ clisJson?: string; /** Docs-root resolver override (tests); default = per-project `interactiveRoot` setting. Used * to READ the create-time grounding sidecar (F-046). */ resolveDocsRoot?: (projectId: string | undefined) => string; }; /** * Opt-in governed answering of wicked-interactive STRUCTURAL edits (task #86, Phase 7c final * leg). When enabled, a durable subscriber answers `wicked.interactive.feedback.processed` * (awaiting_structural > 0) with a governed `interactive-edit` run that ends in * `wicked.interactive.edit.completed` — after a deterministic INV-2 pre-emit self-check; * when absent (the default), interactive's own assist loop remains the answerer. * Deterministic (content/style/remove) edits never reach this seam at all: the model-free * service applies those instantly and hands off only the structural remainder. */ interactiveEditEvents?: { enabled: boolean; /** The bus db; omit for the one the adapter handed its engine (`busDbPath`, core/bus.ts). */ dbPath?: string; /** Poll cadence, ms (tests shorten it). */ pollIntervalMs?: number; /** Heartbeat narration cadence, ms (default 15000). */ heartbeatMs?: number; /** Durable replay-dedup ledger path (default ~/.wicked-crew/interactive-edit-ledger.json). */ ledgerPath?: string; /** Where handoff files land and workers write edited fragments * (default ~/.wicked-crew/interactive-edits). */ editDir?: string; /** Seat roster override (JSON array); omit for the production council roster. */ clisJson?: string; /** Docs-root resolver override (tests); default = per-project `interactiveRoot` setting. * Used ONLY as the demo-kind gate — a demo doc's step feedback is declined honestly. */ resolveDocsRoot?: (projectId: string | undefined) => string; }; /** * Opt-in governed answering of wicked-interactive's conversational ITERATION asks * (CREW-UX-5, the doc thread's plain send). When enabled, a durable subscriber answers * `wicked.interactive.chat.posted` (role:user, existing kind:source doc, not an in-flight * doc, not a feedback-batch echo) with a governed `interactive-chat` run — * understand-the-ask → revise — that ends in `wicked.interactive.draft.completed` (the * service lands the revised full HTML as a generated version). Asks on a busy doc queue * FIFO per doc. When absent (the default), the topic goes unanswered — the pre-CREW-UX-5 * state. */ /** * Opt-in governed theme learning (DES-artifact-editor-plugins §7.6, EP-C4): a durable subscriber * answers `wicked.interactive.theme.learned` with one `interactive-theme` run that reads the * grabbed render in place and writes design tokens; crew validates shape, grammar and contrast * and writes them THROUGH interactive (`PUT /d/:doc/api/theme/learned {tokens, apply:true}`). * Shares the edit seam's ledger and handoff root. When absent, a grab learns nothing. */ interactiveThemeEvents?: { enabled: boolean; dbPath?: string; pollIntervalMs?: number; heartbeatMs?: number; clisJson?: string; resolveDocsRoot?: (projectId: string | undefined) => string; }; /** * Opt-in governed document reviews (DES-artifact-editor-plugins §7.6, EP-C2): a durable subscriber * answers `wicked.interactive.review.requested` with one read-only `interactive-review` run (the * four reviewers; the document's own authors are kept out of the roster and the judge choice), * records one wicked-ledger verdict row per reviewer and announces `review.completed` from the * record. Shares the edit seam's ledger and handoff root. When absent, a request goes unanswered. */ interactiveReviewEvents?: { enabled: boolean; dbPath?: string; pollIntervalMs?: number; heartbeatMs?: number; clisJson?: string; resolveDocsRoot?: (projectId: string | undefined) => string; }; interactiveChatEvents?: { enabled: boolean; /** The bus db; omit for the one the adapter handed its engine (`busDbPath`, core/bus.ts). */ dbPath?: string; /** Poll cadence, ms (tests shorten it). */ pollIntervalMs?: number; /** Heartbeat narration cadence, ms (default 15000). */ heartbeatMs?: number; /** Durable replay-dedup ledger path (default ~/.wicked-crew/interactive-chat-ledger.json). */ ledgerPath?: string; /** Where head snapshots land and workers write revisions (default ~/.wicked-crew/interactive-chats). */ chatDir?: string; /** Seat roster override (JSON array); omit for the production council roster. */ clisJson?: string; /** Queue-drain sweep cadence, ms (tests shorten it). */ queueSweepMs?: number; /** Post-completion landing-gate timeout, ms (tests shorten it). */ landingGateMs?: number; /** Docs-root resolver override (tests); default = per-project `interactiveRoot` setting. */ resolveDocsRoot?: (projectId: string | undefined) => string; }; /** * The project bus seam (DES-PROJECT-001 §4/§5.2). DEFAULT-ON, unlike the opt-in seams * above: the ADR's event vocabulary and the live activity bridge are part of the surface, not * an integration experiment — but the posture stays LOUD-non-fatal (no wicked-bus / broken db * ⇒ project CRUD works, events don't ride). `disabled: true` turns the whole seam off (tests). */ projectEvents?: { disabled?: boolean; /** The bus db; omit for the one the adapter handed its engine (`busDbPath`, core/bus.ts). */ dbPath?: string; /** Poll cadence for the /ws activity bridge, ms (tests shorten it). */ pollIntervalMs?: number; }; /** * F-042/F-043 — what the spawned wicked-interactive bridge is told about THIS daemon. The pool * always exports `WICKED_CREW_API` (the daemon's bound origin); `busDataDir` is the directory of * the bus db the interactive seams read, exported as `WICKED_BUS_DATA_DIR` so the bridge emits * where the seams read. `null`/omitted = not exported (the CLI passes null only when `--bus-db` * names a file wicked-bus cannot be pointed at through a data dir). */ interactiveBridge?: { busDataDir?: string | null; }; /** * DES-MERGE-001 §5.4/§6.1 (slice 3) — the interactive /ws relay. DEFAULT-ON: every * wicked.interactive.** bus event is bridged onto the /ws stream as an `interactiveEvent` * frame so the studio needs exactly ONE socket. `disabled: true` turns it off (tests). */ /** * DES-TEAMING-002 §4.5 (T8) — the team-row → `/ws` relay (`teamEvent` frames). Armed * when the adapter hands the engine a bus (`busDbPath`); `disabled: true` turns it off. */ teamWsRelay?: { disabled?: boolean; /** Poll cadence, ms (tests shorten it). */ pollIntervalMs?: number; }; /** * The watch registry (DES-TRIGGER-REGISTRY-001, TR-W5a): advisory key-point checks over the * daemon fan-in, relayed as `watchEvent` frames. Arms only where the engine holds a bus; * `disabled: true` turns it off (health then says watching is not configured). */ watch?: { disabled?: boolean; entriesDir?: string; pollIntervalMs?: number; flushMs?: number; tickMs?: number; }; interactiveWsRelay?: { disabled?: boolean; /** The bus db; omit for the one the adapter handed its engine (`busDbPath`, core/bus.ts). */ dbPath?: string; /** Poll cadence, ms (tests shorten it). */ pollIntervalMs?: number; }; /** * The identity/actor seam (task #88). Omit for full env/file resolution: * OFF by default (the local loopback deployment — nothing changes), REQUIRED * under `WICKED_RUNTIME=team` or `WICKED_CREW_AUTH=required`. See * `src/api/auth.ts` + docs/auth.md. */ auth?: AuthOptions; /** Audit-trail path override (tests). Default `~/.wicked-crew/audit.log` / `WICKED_CREW_AUDIT_LOG`. */ auditPath?: string; /** Eval-run-history root override (tests). Default the state home's `evals/` / * `WICKED_CREW_EVAL_STORE`. Symmetric with `auditPath` — a createServer-driven test isolates its * eval history here instead of writing the operator's real `~/.wicked-crew/evals/`. */ evalStoreRoot?: string; /** (crew#720) The final-codebase zip root override (tests). Default `/artifacts/runs`. */ codebaseArchiveRoot?: string; /** * The worker stall watchdog (crew#287 detection + crew#341 escalation). For every run whose * engine status is `executing`, the daemon tracks the last CoreEvent observed on its own * relay (any frame for that run, `unitOutputDelta` included); silence past * `workerStallMinutes` (setting, default 15) broadcasts ONE synthetic * `{ type: "workerStalled", session, ord?, quietForMs }` frame on /ws per quiet period and * logs at warn. Any new event re-arms. Detection never touches the run. * * ESCALATION (crew#341) is ON BY DEFAULT as of perf#4 (`workerStallEscalateMinutes` * defaults to 30; an explicit 0 — setting or `escalateMinutes` override — disarms): a run * still silent past the threshold gets one action per quiet period — `reassign` (default: * recycle the wedged cursor unit via the engine's `reassignUnit`, routed to a DIFFERENT * seat from the run's pool when one is available, budgeted per run by * `workerStallMaxEscalations`) or `notify` (fail-loud `needsYou` frame, run untouched) — * each reported on a `workerStallEscalated` /ws frame and audited as `run.stall.escalated`. * * `enabled` defaults to ON in the daemon and OFF under a test runner (VITEST / * NODE_ENV=test), the seat-health-probe posture. */ /** The seats' standing (tests: a sign-in answer instead of the credential-file heuristic and * the live auth-status probes, so admission is host-independent). */ seats?: { signedIn?: (seatKey: string, workerRoot?: string) => boolean | null; }; /** * TEST SEAM (crew#1155): the engine gate the MCP broker judges calls with. Default: the linked * engine's (`CoreAdapter.mcpEngineGate()`). Only core's carriers mint a capability token, so a * server-level test of a brokered call hands its own gate here. */ mcpEngineGate?: () => McpEngineGate | null; stallWatchdog?: { enabled?: boolean; /** Sweep cadence, ms (default 30 s; tests shorten it). */ sweepIntervalMs?: number; /** Threshold override, minutes — bypasses the settings read (tests). */ stallMinutes?: number; /** Escalation-threshold override, minutes — bypasses the settings read (tests). */ escalateMinutes?: number; /** Escalation-action override — bypasses the settings read (tests). */ escalateAction?: 'reassign' | 'notify'; /** Per-run automatic-reassign budget override — bypasses the settings read (tests). */ maxEscalations?: number; /** Silent-turn rung override, minutes (crew#972) — bypasses the settings read (tests). */ silentTurnMinutes?: number; /** Process-liveness override (crew#629): default = the daemon's own process tree * (`RunLivenessSampler`); tests pass a stub or `null` (frame-only clock). */ busy?: ((runIds: string[]) => Promise>) | null; }; /** * The background delivery-derivation cache's sweep (GET /runs p99): every `sweepIntervalMs` * (default 30 s = `WORKTREE_CLEAN_TTL_MS`) the daemon re-derives every candidate run's * stranded/vacuous/none label through a ≤3-wide git pool, so the run DTOs only ever READ a * cache and the list fan-out never spawns git. `enabled` defaults to ON in the daemon and OFF * under a test runner (VITEST / NODE_ENV=test), the seat-health-probe posture — the * terminal-frame warm is always on (event-driven, not a timer). */ /** DES-learn-workflow §5.3: the learn schedule's tick. ON in the daemon, OFF under a test runner * (the deliveryCache posture); tests drive `LearnScheduler.sweep()` directly. */ learnSchedule?: { enabled?: boolean; sweepIntervalMs?: number; }; /** DES-RUNS-REVIEW-001 §2: the workflow schedules' tick (the run review every 2 days by default). ON in the * daemon, OFF under a test runner; tests drive `WorkflowScheduler.sweep()` directly. */ workflowSchedules?: { enabled?: boolean; sweepIntervalMs?: number; }; deliveryCache?: { enabled?: boolean; /** Sweep cadence, ms (tests shorten it). */ sweepIntervalMs?: number; }; /** * The skills seam (skills keystone): at boot the daemon seeds `/skills` — the ONE * root, not a setting (codex round 5) — from the LIVE installed wicked-garden plugin, publishes a * first immutable snapshot when none exists (its `views/copilot/` generated alongside), and * exports `WICKED_SKILLS_SNAPSHOT` for the engine. It never writes into the user's own CLI * directories (design v3.2 §1): the boot REFUSES (`SkillsRootUnfencedError`) a root whose * canonical path leaves the state home or lands inside one. `disabled: true` registers the * routes without a store (they answer 503) — * the manifest collector and tests that must not touch a plugin cache use it. `source` / * `provisionVenv` aim a test at a fixture plugin root and a provisioner that spawns nothing * (`noVenv`) — a boot test must never run the host's `uv` or download anything; production omits * both (live discovery, `uvSyncBaseline`). */ skills?: { disabled?: boolean; source?: () => PluginSource | null; provisionVenv?: VenvProvisioner; /** * crew#1145: run the boot ladder AFTER `createServer` returns instead of inside it — the daemon * listens first and the skills seam reports `publishing` until the ladder lands. Every engine * launch (run, resume, gate answer, campaign, reassign) waits for it through the adapter's launch * gate, and the `/skills` routes wait too, so nothing reads the engine input before the ladder * decided it. `wicked-crew serve` sets it; tests that read the seam right after `createServer` * leave it off (or await `app.skillsBootSettled()`). */ background?: boolean; }; /** * Chat citation verification (crew#561). DEFAULT-ON: every terminal `chatReply` of a chat with * read roots has its cited paths, `path:line` / `path:symbol` refs and commit SHAs checked * against those roots, and the verdicts ride `/ws` as one `chatCitations` frame. The pass runs * AFTER the reply is broadcast, so it never delays an answer, and it is bounded (see * `chat-citations.ts`). `disabled: true` turns it off; `deps` / `limits` let a test drive it * without a repo, a git or the default budget. */ chatCitations?: { disabled?: boolean; deps?: ChatCitationDeps; limits?: VerifyOptions; }; } /** * Per-stage boot timings, ms (crew#741): where a slow boot's seconds went, instead of one * `startupMs`. EXCLUSIVE stages that sum to the boot: `setup`, `skills`, `venv`, `routes`, * `studio`, `listen` here; `wicked-crew serve` prepends `preflight` and `engine`. The key set is * open — a harness reads the stages it knows. */ export type BootStages = Readonly>; /** * A lap clock for the boot: `lap(name)` closes the stage that ran since the previous lap (or since * construction) and rounds it to whole ms; `set` records (or corrects) a stage measured elsewhere — * a re-`set` key keeps its position. Stages are reported in the order they were first recorded. */ export declare class BootStageClock { private readonly stages; private mark; lap(name: string): number; set(name: string, ms: number): void; snapshot(): BootStages; /** One human line: `setup 12ms · skills 1203ms · venv 0ms · …` */ describe(): string; } declare module 'fastify' { interface FastifyInstance { /** crew#741 — the boot's stage clock; `startServer` adds `listen` and hands the snapshot back. */ bootStages?: BootStageClock; /** crew#1145 — settles once the skills boot ladder landed (at once when it ran inline or the seam is off). */ skillsBootSettled?: () => Promise; } } export declare function createServer(adapter: CoreAdapter, options?: CreateServerOptions): Promise>; export interface StartedServer { app: ReturnType; port: number; host: string; /** crew#741: the boot's exclusive per-stage ms (`setup`, `skills`, `venv`, `routes`, `studio`, `listen`). */ stages: BootStages; } /** The URL a worker on this host reaches the daemon at: a wildcard bind is reached on loopback. */ export declare function brokerUrlFor(host: string, port: number): string; /** Register `run-evals` with the engine. A refusal warns and never fails the boot. */ export declare function registerRunEvals(adapter: CoreAdapter, warn: (m: string) => void, runsDir?: string): Promise; export declare function startServer(adapter: CoreAdapter, port?: number, host?: string, options?: CreateServerOptions): Promise; //# sourceMappingURL=server.d.ts.map