import type { BusUnavailable } from './engine-bus.js'; import { type AskRequest, type AskTicket, type AskUnit } from './types.js'; import type { EditorGrantsResponse, CoreEvent, LaunchRunInput, LaunchPlan, RepoEntry, SessionView, RecordedEvent, GovernancePolicy, ConformanceRule, GovernanceScoreboard, GovernanceClaim, SteeringImportEntry, SteeringImportResult, GovernanceEvalReport, GovernanceEvalSample, ImportEvalCorpusResponse, CoverageReport, GraphKind, WorkflowDef, CrewSystemSettings, Preset, PresetStep, CatalogEntry, PlanPreviewResponse, PlanProposalResponse, RunTeamView, TeamOutboxReplayReport, Project, ProjectMember, InteractionRequest, Campaign, CampaignDef } from './types.js'; import { type GovernanceStoreLocation } from './governance-store.js'; import { type VaultBinInputs, type VaultBinPosture } from './vault-bin.js'; /** Where the daemon persists system settings. Exported so tests write fixtures to the ONE * spelling of this path (the tests/ tree is audited against spelling core paths itself). * * `WICKED_CREW_SYSTEM_SETTINGS` overrides the file — the same escape hatch * `WICKED_CREW_PROJECT_SETTINGS` gives the project-settings store (projects/settings.ts). The * test harness arms it (tests/setup/hermetic-home.ts) so the suite never reads the operator's * real `~/.config/wicked-core/settings.json` at `createServer` boot — where a real * `worker_config_root` would be exported as `WICKED_WORKER_HOME` into the hermetic env — and a * `PUT /settings` test never rewrites it (crew#396). Read at call time, not import time, so a * test may re-aim it per fixture. */ export declare function settingsFilePath(): string; /** A drop-in workflow definition the ENGINE refused, with the engine's own reason (crew#718). */ export interface RefusedWorkflow { /** The def's id, as the file named it. */ id: string; /** Core's refusal, verbatim — e.g. `gate evaluates nothing: write-a-note — the phase declares * executes_code but pins no validator and has no human gate …`. */ reason: string; } /** The gate-hook binary's file name on this host. */ export declare const WICKED_CORE_EXE_NAME: string; /** * The `wicked-core-ts` platform package for this host — the five names `napi-release.yml` publishes * (`wicked-core-ts-darwin-arm64`, `-darwin-x64`, `-linux-x64-gnu`, `-linux-arm64-gnu`, * `-win32-x64-msvc`); `undefined` for a platform/arch pair no package exists for. */ export declare function wickedCoreTsPlatformPackage(platform?: string, arch?: string): string | undefined; /** * The `wicked-core` hook binary BUNDLED inside this install's platform package (core#405, F-009 — * FIX-IT-ALL L10-9 crew half; core-ts ≥ 0.7.26 ships it beside the `.node`, stamped * `wickedCoreVersion` = the engine semver the addon's gate compares against `--version`). ONE lookup * (review-L10-593 nit 2, D2): the resolver's candidate `node_modules` dirs for this module — the * same sidestep `installedPackageVersion` uses, because a platform package's exports map may not * expose `./package.json` — checked for `//`. The binary found is the one that shipped * WITH this addon, never a stale copy in the operator's home. `undefined` when no platform package * resolves or it carries no binary (a pre-0.7.26 package). */ export declare function bundledWickedCoreExe(exeName?: string, pkg?: string | undefined, resolvePaths?: (id: string) => string[] | null): string | undefined; /** (rejudge) The engine's answer to "can this gate's judge be re-run now?" (`rejudgeReadiness`). */ export interface RejudgeReadiness { /** The open gate is a unit's `judge_unavailable` denial — the gate `rejudge` answers. */ applies: boolean; /** The engine's judge selection names at least one seat now. */ available: boolean; ord: number | null; eligible: string[]; missing: Array<{ seat: string; reason: string; }>; reason: string | null; } /** The engine's two MCP broker calls (`Core.evaluateMcpCall` / `Core.evaluateMcpOutput`). */ export interface McpEngineGate { evaluateCall(requestJson: string): Promise; evaluateOutput(requestJson: string): Promise; } /** The engine's replay report (`Core.replayEmitOutbox`), parsed. */ export interface EmitOutboxReplayReport { /** Non-empty lines read from the outbox. */ read: number; /** Entries written to the store as EVENT nodes. */ replayed: number; /** Entries that did not land — the ORIGINAL line verbatim (so the caller can keep it dead-lettered) and why. */ failed: Array<{ line: string; reason: string; }>; /** Entries already on the store from an earlier replay (deterministic replay ids make a re-replay a * no-op); absent on an engine that predates the field. */ already_present?: number; } /** * Does the linked engine carry DES-TEAMING-002 T3's plan launch (`Core.supportsPlanLaunch`)? A * plan handed to an addon without it would be silently ignored — the run launches as free text, * with no `plan.proposed` and no `plan_approval` gate — so every plan path fails CLOSED on this. * Read at call time (not cached): the addon is resolved once, but tests swap the static. */ export declare function engineSupportsPlanLaunch(): boolean; /** A plan path on an engine without the plan approval gate (maps to 501 at the HTTP boundary). */ export declare class PlanLaunchUnsupportedError extends Error { constructor(what: string); } /** Does the installed addon judge a plan or preset launch's declared deliverables * (`LaunchOptions.deliverables`, wicked-core#858, >= 0.7.48)? Same doctrine: an older addon * ignores the field, and the run would never be held to its deliverable. */ export declare function addonSupportsLaunchDeliverables(): boolean; /** Does the installed addon carry the assurance contract (`LaunchOptions.reducedAssurance`, * wicked-core#850, >= 0.7.46)? Same doctrine. */ export declare function addonSupportsReducedAssurance(): boolean; /** Does the installed addon carry the QE acceptance decision (`LaunchOptions.skipQeAcceptanceReason` * / `forceQeAcceptance`, `assurance.qe`; QE-IN-APP-WORKFLOWS, >= 0.7.49)? Same doctrine. */ export declare function addonSupportsQeOverride(): boolean; /** * Is the installed `wicked-core-ts` at least `maj.min.pat`? * * ONE version reader for every capability gate, because the parsing rule here is load-bearing and * was already got wrong once: `split('.').map(Number)` returns NaN on a pre-release or build suffix * (`0.6.1-beta.1`), which fails a perfectly capable addon closed (Copilot on #263). Matching only * the numeric MAJOR.MINOR.PATCH prefix fixes that, and a second copy of the fix is a second chance * to lose it. No match ⇒ unparseable ⇒ fail closed. */ /** * The wave-3 arms (wicked-core PR-1B `action` / `amendScope`, PR-1D `denial_gate`) need an addon of * at least 0.7.27. ONE fail-closed rule for all of them (review-L1-598 M1): a napi call silently * DROPS a field the addon does not declare — a `request_changes` would run as a plain reject, an * `auto_reject` campaign would `hold` forever exactly when told not to — so a caller that asked for * an arm the installed engine lacks is REFUSED with a named reason (the routes answer 409), never * served a silent no-op. `null` ⇒ the addon carries the arms. `supported` is injectable so the * old-addon path is unit-testable without booting an engine. */ export declare function armsUnsupportedReason(feature: string, remedy: string, supported?: boolean): string | null; /** The recogniser the routes map to 409: the engine-too-old refusal above, by its fixed phrase. */ export declare const ENGINE_TOO_OLD_RE: RegExp; /** * Does the installed engine bench a seat whose ballots fail PERSISTENTLY without a recognised * reason (wicked-core #523's unclassified arm, 3D')? From 0.7.27 only. crew deleted its own * cross-run bench in the same release (BC-15) on the strength of that arm, and the runtime pin * still allows 0.7.26 — so the one place that matters (the daemon's boot log) says which engine it * has. `supported` is injectable so the old-engine path is unit-testable without an addon. */ export declare function engineBenchesUnclassifiedSeats(supported?: boolean): boolean; /** * Chat is not available in this deployment at all — a capability gap, never a bad request. * * It arrives two ways and both mean the same thing to an operator: the addon predates the binding * (no method to call), or the engine was spawned without the ACP runner and says so when called. * Only the first is knowable before the call, which is why this is a thrown type rather than a * capability flag. * * Typed rather than left to the caller to sniff out of the message text: the route used to regex * the message for one of the two phrasings, so the other fell through to `400` and told an operator * to fix a request that was already correct. */ export declare class ChatUnsupportedError extends Error { constructor(message: string); } /** * The project surface (DES-PROJECT-001) is not available in this deployment — the installed * wicked-core-ts predates 0.6.0. Typed for the same reason as `ChatUnsupportedError`: the routes * answer 501 ("upgrade the engine") on this, never 400 ("fix your request"). */ export declare class ProjectsUnsupportedError extends Error { constructor(what: string); } /** * The preset bindings (DES-TEAMING-002 seam C2) are not in the installed `wicked-core-ts`. The * routes answer 501 ("upgrade the engine"), never 400. */ export declare class PresetsUnsupportedError extends Error { constructor(what: string); } /** * A team binding (DES-TEAMING-002 T8) is not in the installed `wicked-core-ts`. The routes answer * 501 ("upgrade the engine"), never 400. */ export declare class TeamUnsupportedError extends Error { constructor(what: string, binding: string); } /** * `resolveElicitation` is not available in this deployment — the installed `wicked-core-ts` * predates the binding (< 0.7.2). Feature-detected by method presence, the * `interactionRequests`/`runEvents` convention (crew#357/#358 wired the live path; this * error is now only the older-addon detect, no longer an always-throw stub). * * Routes map this to HTTP 501 so an operator knows to upgrade rather than to fix a * call that was already correct. */ export declare class ElicitationUnsupportedError extends Error { constructor(message: string); } /** * The campaign surface (DES-CAMPAIGN-001 / TH-9) is not available in this deployment — the * installed wicked-core-ts predates the campaign bindings. Typed for the same reason as * `ChatUnsupportedError`: the routes answer 501 ("upgrade the engine") on this, never 400 * ("fix your request"). Gated on METHOD PRESENCE rather than a version floor because the * bindings ship in the next core-ts release whose number is the release train's to pick — * a presence check is true on whatever version actually carries them. */ export declare class CampaignsUnsupportedError extends Error { constructor(what: string); } /** * The wiki scoreboard (AW-23 / wiki-mgmt) is not available in this deployment — the installed * wicked-core-ts predates the `governanceScoreboard` binding (ships in ≥ 0.7.4). Typed for the * same reason as `ChatUnsupportedError`: the route answers 501 ("upgrade the engine") on this, * never 400 ("fix your request"). Gated on METHOD PRESENCE, the campaigns doctrine — true on * whatever release actually carries the binding. */ /** * The unified steering-rule model (STEERING program) is not available in this deployment — * wicked-core-ts predates the `steeringImport` binding (ships in ≥ 0.7.5, alongside the model * merge). Typed for the same reason as `ChatUnsupportedError`: the routes answer 501 ("upgrade * the engine") on this, never 400 ("fix your request") — and never the WORSE failure of passing * a steering-field write through to an engine that would silently drop the fields. */ export declare class SteeringUnsupportedError extends Error { constructor(what: string); } /** * The installed engine addon predates the governance-evals surface (crew-testing; the * `governanceEvals` + `governanceCorpusImport` bindings ship with wicked-core-ts 0.7.5, which is * unreleased at the time this seam lands). Typed for the same reason as `SteeringUnsupportedError`: * the testing routes answer 501 ("upgrade the engine") on this, never 400 ("fix your request") — * and they must not crash a daemon running against the released 0.7.4 addon. */ export declare class GovernanceEvalsUnsupportedError extends Error { constructor(what: string); } export declare class GovernanceScoreboardUnsupportedError extends Error { constructor(what: string); } /** * Replaying a dead-letter outbox is not available in this deployment — the installed * wicked-core-ts predates the `Core.replayEmitOutbox` static (crew#495's engine companion). Typed * for the same reason as `SteeringUnsupportedError`: the CLI says "upgrade the engine" (exit 2) and * never pretends the entries landed. Gated on METHOD PRESENCE, the campaigns doctrine. */ export declare class GovernanceReplayUnsupportedError extends Error { constructor(what: string); } /** One live chat on the enumerate surface — see {@link CoreAdapter.chatList}. */ export interface ChatSummary { chatId: string; /** The seats currently warm, sorted. */ seats: string[]; /** Seconds since the chat's last open/ensure/turn; `null` when it has no activity stamp. */ idleSecs: number | null; /** The scope the engine recorded at open (wicked-core#410 / crew#502): where the seats run, the * graph their read-only estate MCP is bound to, the roots in scope. ABSENT (not null) on an * engine predating chat scope. */ cwd?: string | null; codeGraphDb?: string | null; readRoots?: string[]; } /** A parsed-CoreEvent listener. */ export type CoreEventListener = (event: CoreEvent) => void; export interface CoreAdapterOptions { /** Estate db path the core actor writes to (single writer). */ dbPath: string; /** `true` → deterministic offline stub engine (tests); `false` → production engine. */ stub?: boolean; /** * Arm the Law 1 EVENT-DRIVEN execution-mediation seam (DES-EXEC-001 §2.3). When `true` (and NOT * stub), the actor PUBLISHES `wicked.task.dispatched` and an off-actor `cli-runner` subscriber * executes the CLI over events, publishing `wicked.task.completed` back — instead of the default * in-process stdin/stdout subprocess path. OFF by default: existing callers keep the in-process path. * * Mechanism: the Rust actor honours `WICKED_BUS_EXEC` + `WICKED_BUS_DB` from the process * environment even on the plain `Core.spawn()` path (wicked-core `actor::run`), so we set those two * env vars BEFORE constructing the Core (the actor thread reads them at spawn time). If the * `cli-runner` cannot initialize (bus db unopenable / cursor unreadable) the engine logs and falls * back to the in-process path — it never silently wedges (wicked-core seam finding #4). */ engineExec?: boolean; /** * The wicked-bus SQLite db handed to the engine as `WICKED_BUS_DB` (DES-TEAMING-002 T0): the * daemon's OWN cross-product bus, the file crew's seams use, on EVERY boot — not only under * `engineExec`. The engine opens it off its actor thread; exec mediation, when armed, runs over * the same file. Required when `engineExec` is on. Absent (a library boot, a unit test, or a * daemon whose bus could not open — see `busUnavailable`) exports nothing. */ busDbPath?: string; /** * Why the daemon hands the engine NO bus (its boot probe could not open the resolved file). The * engine then runs without a bus, and `/health.warnings` carries a `bus.unavailable` notice * naming this reason (T0: a daemon-level notice; a per-run `transport:"none"` lands with P1). */ busUnavailable?: BusUnavailable; /** * The governance store + dead-letter outbox this daemon hands the engine (crew#495 / F-022) — * resolved by the CLI (`core/governance-store.ts`), exported to `process.env` HERE, before the * Core exists, as `WICKED_ESTATE_DB` + `WICKED_APPS_EMIT_DEADLETTER` (the emit seam reads both at * emit time). Absent — a library boot, a unit test — exports nothing: the engine keeps whatever * the process carried (the hermetic test arming included) and `/diagnostics` reports `store: null`. */ governanceStore?: GovernanceStoreLocation; /** * (crew#1068) What the engine's `WICKED_VAULT_BIN` is decided from (`core/vault-bin.ts`): the * operator's boot value, kept when set, else crew's own `wicked-vault` bin. Default — the daemon — * is `BOOT_VAULT_BIN` + the shipped dependency's bin; tests pass both explicitly (a resolver that * fails proves boot survives it) so no case depends on the test process's environment. */ vaultBin?: VaultBinInputs; } /** The ids of a workflow's phases that carry a HUMAN gate (`human_confirm` unconditional, the * conditional `human_confirm_if`, or `consent_before` — crew#888, which pauses BEFORE the phase * runs) — i.e. the phases that will PAUSE for a person. * * FINDING-023 (residual): core#208 made a workflow's phase gate deliberately WIN over a run-level * `humanConfirm: none` (it pauses, with a self-disclosing note), but there was no way to learn a * workflow's gates BEFORE launching it — an operator picking `none` for an unattended run only found * out when it paused. Surfacing this on `GET /workflows/:id` is that missing launch-time signal. * `'auto'` (the string form) is NOT a human gate. */ export declare function humanGatePhaseIds(wf: WorkflowDef): string[]; /** * The single isolation boundary over wicked-core-ts. It holds the ONE `Core` * handle, makes the ONE `subscribe()` call for the whole process, parses each * CoreEvent, and re-emits it to registered in-daemon listeners. Every REST * endpoint and the WS fan-out funnel through this stable API — so when the * in-flight core-ts subscribe/teardown signature lands, only this file changes. */ /** * A launch the daemon hands the engine — or one the engine refused. `handed` is notified BEFORE the * engine call (the skills seam opens a generation pin for the launch, so no worker spawn can read * `WICKED_SKILLS_SNAPSHOT` ahead of the pin — live-generations.ts); `rejected` follows a call that * threw (nothing will ever spawn for it); `accepted` follows a call that RESOLVED (the engine took * it — the only notice a durable per-run record may key on, crew#661). Every path a spawn can originate from goes through here: * `launchRun` (POST /runs, onboarding, testing, steering), `resumeRun`, `confirmGate`, * `launchCampaign`, `resumeCampaign`. */ export interface LaunchNotice { kind: 'run' | 'campaign'; /** The run's session id (`LaunchRunInput.sessionId` / the run id) or the campaign's `CampaignDef.id`. */ id: string; status: 'handed' | 'accepted' | 'rejected'; /** The workflow id a NEW run launches (`LaunchRunInput.workflow` — the base id, before any per-run * composition). Absent on a resume, a gate answer, a campaign, and a launch naming no workflow. */ workflow?: string; } export type LaunchListener = (notice: LaunchNotice) => void; /** * The engine serializes a rule's serde DEFAULTS as absent keys (`steering_type` architecture, * `weight` 1.0, empty `applies_to` / `excludes` — `skip_serializing_if` in wicked-governance's * `conformance.rs`). The import kept those values, but the browse wire then carried no key, and a * client that reads absence as null showed the first entry of an `architecture` import as untyped * and every 1.0 weight / empty scope as missing (#776). The read side materializes the four * defaults so every row says what the engine stored. Values the engine did serialize pass through. */ export declare function withRuleDefaults(rule: ConformanceRule): ConformanceRule; /** * One read of the engine's launch bench (`Core.worktreeBenched`, passed as `predicate`), keeping the * entries whose `governed_only` is `governedOnly` (wicked-core#976: `true` marks the host-floor * bench), keyed by seat with the engine's reason; `null` when the addon has no predicate. */ export declare function readLaunchBench(predicate: ((clisJson: string, bound: boolean) => string) | undefined, seats: readonly unknown[], bound: boolean, governedOnly: boolean): Map | null; export declare class CoreAdapter { private readonly core; private readonly subscription; private readonly listeners; private readonly launchListeners; /** crew#1145: what each engine launch awaits first (`setLaunchGate`); `null` = nothing. */ private launchGate; private closed; /** * The estate db the Core actor was spawned over (`CoreAdapterOptions.dbPath` — the `--db` * flag / `~/.wicked-crew/core.db` default). Kept because some engine bindings take the db * path as an ARG rather than reading the actor's own handle (`governanceEvals` runs its evals * over a read-only connection to this same store); crew resolves it here, the one place the * path is already known, so no route grows a second spelling of where the store lives. */ readonly dbPath: string; /** `true` when this adapter armed the event-driven exec seam (for readiness/reporting). */ readonly engineExec: boolean; /** The bus db handed to the engine (`WICKED_BUS_DB`; exec mediation runs over it when armed), or * `undefined` when this adapter handed none. */ readonly busDbPath: string | undefined; /** Why the engine has no usable bus — the boot probe's failure, or the engine could not arm its * bus bridge on the handed bus within its bound — else `null`. */ readonly busUnavailable: BusUnavailable | null; /** The bus file whose seams are off because the linked engine has no `Core.busEmit`/`busRead` * (wicked-core#631) — `/health.warnings` says so — else `null`. */ readonly busSeamsOff: string | null; /** (crew#1068) The wicked-vault CLI handed to the engine as `WICKED_VAULT_BIN` at construction — * `/health.warnings` and `/diagnostics.vault` report it when the engine cannot vault. */ readonly vaultBin: VaultBinPosture; /** The engine's bus calls, attached for `busDbPath` (core/bus.ts); `null` without a bus. */ private engineBus; /** What crew last handed the engine's `setQeAcceptanceOff` (the engine starts with the boot value). */ private engineQeOff; /** * `true` when this adapter drives the DETERMINISTIC OFFLINE engine (`Core.spawnStub`) rather * than the production one — i.e. the `StubDispatcher` (every seat votes for the first roster * option, no subprocess) plus the `StubStepRunner` (fixed text, no CLI). * * Exposed, not private, because a stub engine is not a quieter production engine — it is a * FABRICATOR. Every phase resolves Ok in under a millisecond, so a run narrates its whole * governed lifecycle, gate approvals included, with no work behind any of it. Anything that * lets this daemon answer OTHER products' traffic has to be able to ask (see * `api/server.ts`, crew#309). */ readonly stub: boolean; /** The governance store this adapter exported to the engine (crew#495), or `null` when none was resolved. */ readonly governanceStore: GovernanceStoreLocation | null; /** * This daemon's own bound origin, resolved LAZILY (crew#524): the adapter exists before the * server listens, so `registerRoutes` hands a getter rather than a value. The deliver phase * composed for a run bakes the origin in, so its script can ask `GET /runs/:id/deliver-text` * for the run-derived PR text at delivery time and the PR body can link the run. `null` (a * CLI-driven adapter with no daemon) ⇒ the script carries only its launch-time text. */ private deliverApiOrigin; /** Hand the adapter the way to learn this daemon's own origin (see `deliverApiOrigin`). */ setDeliverApiOrigin(get: () => string | null): void; /** * Called once per run THIS adapter launches OFF the `POST /runs` path — today the onboarding run * (`_doOnboardingLaunch`, the one site `POST /repos`, `POST /repos/:id/onboard` and the * clone-then-register path all reach; the last is adapter-internal, which is why this cannot live * in the routes) — after the engine accepted the launch. `createServer` wires it to * `recordRunLaunched`, so those runs gain a `run.launched` trail entry and a `created_at` * (crew#496 / studio#230; DES-L8 §5 PR-8B). `null` = a CLI-driven adapter with no daemon: * nothing recorded, the pre-field answer. Best-effort: a recorder that throws never fails the * launch the engine already accepted. */ private onRunLaunched; /** Hand the adapter the daemon's launch recorder (see `onRunLaunched`). */ setOnRunLaunched(record: (runId: string, detail: Record) => void): void; /** * The roster a launch THIS adapter originates should carry — the daemon's roster WITH crew's * standing (`api/roster-standing.ts`) once `createServer` wires it, else the raw registry. * F-RECON-002/003: the onboarding launch (`seatsForWorkflow`) and `wicked-crew start` handed the * engine `CoreAdapter.roster()` undecorated, so `launchRun`'s `engineRosterJson` had no * `council_eligible` to bench on and signed-out seats were convened. Resolved LAZILY, like the * origin above: the adapter exists before the tracker that knows the seats' standing does. */ private rosterProvider; /** Hand the adapter the daemon's standing roster accessor (see `launchRoster`). */ setRosterProvider(get: (() => unknown[]) & { ready?: () => Promise; }): void; /** The seat pool a launch from this adapter carries: standing-decorated when wired, else raw. */ launchRoster(): unknown[]; /** {@link launchRoster} after the seats' login checks answered (bounded, crew#645): an expired * login reads signed out before the launch routes work to it. */ readyLaunchRoster(): Promise; constructor(opts: CoreAdapterOptions); /** * The engine's EVENT-node counter over an estate store (`Core.eventStoreCount`, crew#495's * companion binding), or `null` when the installed addon predates it — `/diagnostics.governance` * then reports `records: { total: null, sinceBoot: null }`, honestly, never a fabricated 0. A * store FILE that does not exist yet counts as 0: nothing has landed, which is a number, not an * unknown (the engine's read-only open refuses a missing file, and that refusal is not "unknown"). */ /** * The engine's MCP broker gate (DES-MCP-TOOLS-001 §6 steps 3 and 8), or `null` on an addon without * BOTH `Core.evaluateMcpCall` and `Core.evaluateMcpOutput`: the broker then refuses every call * (`guard_error`), never runs one unjudged. Read at call time: tests swap the statics. */ static mcpEngineGate(): McpEngineGate | null; /** * The engine's tool list for a token's unit (`Core.listMcpTools`, DES-MCP-TOOLS-001 §8, slice S4), * or `null` on an addon without it: `POST /mcp/tools` then answers 503, never an unjudged list. * Read at call time: tests swap the static. */ static mcpToolLister(): ((requestJson: string) => Promise) | null; /** * The engine's estate scope for a token's unit (`Core.resolveEstateScope`, wicked-core#1018), or * `null` on an addon without it: every `mcp:estate/` call is then refused, never relayed * unscoped. Read at call time: tests swap the static. */ static estateScopeResolver(): ((requestJson: string) => Promise) | null; /** Whether the linked engine follows the one-connection bus rule (`Core.busConnectionStats`, * DES-TEAMING-002 T0) and may therefore be handed the daemon's bus on every boot. */ static engineHoldsBusConnection(): boolean; static eventStoreCounter(): ((dbPath: string) => Promise) | null; /** * The engine's state-home preflight (wicked-core#411 / crew#497), or `null` on an addon without * `Core.preflightStateHome` — crew's `StateHomeWatch` then classifies with its own registry copy * and says so (`source: 'crew'`), never a fabricated clean answer. */ static stateHomePreflighter(): ((snapshotPath: string | null, dbPath: string) => Promise) | null; /** Whether the installed addon can replay a dead-letter outbox (`Core.replayEmitOutbox`, crew#495). */ static replayEmitOutboxSupported(): boolean; /** * Replay the engine's NDJSON spool records at `outboxPath` into the estate store at `dbPath` — * the engine writes each as the EVENT node it should have been (its original `ts` restored where * the record carries one). Throws {@link GovernanceReplayUnsupportedError} on an older addon. */ static replayEmitOutbox(outboxPath: string, dbPath: string): Promise; /** Register a CoreEvent listener. Returns an unsubscribe function. */ onEvent(listener: CoreEventListener): () => void; /** * crew#1145: what every engine launch waits for before it is handed — the daemon's background * skills ladder, so no spawn reads `WICKED_SKILLS_SNAPSHOT` before the ladder decided it. `null` * (the default, and every CLI verb that boots inline) waits for nothing. The gate never rejects. */ setLaunchGate(gate: (() => Promise) | null): void; /** Register a launch listener (`LaunchNotice`). Returns an unsubscribe function. */ onLaunch(listener: LaunchListener): () => void; /** * Deliver a launch notice to EVERY listener; a listener's failure PROPAGATES (codex round 6 on * crew#480 — the notices used to be delivered under a swallowing try/catch). The `handed` notice is * what pins the skills generation the launch is about to read (skills/runtime.ts → * live-generations.ts): a pin that could not be recorded means the generation could be reaped * under the spawn, so the launch must not proceed and the failure is the launch's failure. Every * listener is still notified (a later one is not skipped because an earlier one threw); the FIRST * error is what propagates. */ private notifyLaunch; /** * Hand a launch to the engine with its notices: `handed` BEFORE the call (the pin is open before * any spawn can read the env), `rejected` when the call throws (nothing will spawn — the pin is * released). The result and the error pass through untouched. * * A listener that FAILS on `handed` fails the launch: the engine is never called (nothing spawns * against a generation nobody pinned), the other listeners get `rejected` so whatever they did * record is released, and the listener's error surfaces to the caller as the launch error — * never swallowed (codex round 6). A listener failing on `rejected` cannot un-launch anything; * the primary error keeps precedence and the secondary one is logged, not lost. */ private handedToEngine; /** * Whether this host's skill routing admits `key` to the engine's isolated QE reviewer turn (the * reviewer runs the QE skill; a Claude-only skills root admits claude seats only) — or `null` * when the linked addon predates the predicate. Read at call time. */ static qeReviewerAdmitted(key: string, invocation: string | undefined): boolean | null; /** Deliver `accepted` once the engine TOOK the launch. Never propagates: the launch already happened, * so a listener failure is logged and must not turn an accepted run into a rejected call. */ private announceAccepted; /** Deliver `rejected` while a launch is already failing with `primary`: a secondary listener failure is logged, never masks the primary. */ private releaseAfterFailure; /** The production council roster (static), parsed to seats. */ static roster(): unknown[]; /** * core#920: the seats the engine benches on a run bound to one repository, keyed by seat with the * engine's reason — or `null` when the linked addon predates the predicate. `seats` must be * engine-shaped (the registry roster, or `toEngineRoster` of a served one). Read at call time. */ static worktreeBenched(seats: readonly unknown[]): Map | null; /** * wicked-core#976: the seats the engine benches from GOVERNED work on the interim host floor (any * run, bound or not — `Core.worktreeBenched(seats, false)`'s `governed_only` entries), keyed by * seat with the engine's reason. Empty off the host floor, and with an addon that predates the * host-floor bench (it marks no entry); `null` when the addon predates the predicate itself. Such a * seat still judges, triages and votes: those engine calls are ungoverned. */ static hostFloorBenched(seats: readonly unknown[]): Map | null; private static launchBench; /** Liveness probe → `"ok"` (also proves the event pump). */ ping(): Promise; /** * What THIS daemon's engine addon can do — served on `GET /health.capabilities` so a client * (the studio composer) promises only what the deployment keeps. `deliverGate`: the engine * pauses before the composed `deliver` phase unless the launch opted out (F-E2E-030, * wicked-core-ts ≥ 0.7.24). Version-derived like the other addon probes above — a napi object * silently ignores fields an older addon does not declare, so the version is the only honest * signal until the field lands. */ engineCapabilities(): { deliverGate: boolean; revisesPr: boolean; chatIdOnLaunch: boolean; seatChipOnCreate: boolean; askPath: boolean; }; /** Whether this engine carries the plan approval gate (DES-TEAMING-002 T3) — see * [`engineSupportsPlanLaunch`]. An instance method so a partial-stub adapter can say either. */ /** * (wicked-core#858, X-MIG M9) Hand a plan or preset launch's declared deliverables to the engine * (`LaunchOptions.deliverables`). Fails CLOSED on an addon before the field (napi ignores it, so * the run would never be held to them). */ private passLaunchDeliverables; supportsPlanLaunch(): boolean; /** * (crew#549, crew#737) The CONFIGURED deliver identity for a launch: the repository's own pin * (`deliverIdentityByRepo[repoRef]`) else the daemon-wide `deliverIdentityLogin`. Whether that * login has a gh token here is the credential preflight's question ({@link deliverCredentialsProbe}, * crew#940). RETURNED, never stashed on the adapter: two concurrent delivering launches * on different repositories must not read each other's identity (the F2 origin rule). A LOGIN * only: no token is ever read here. `login: ''` ⇒ nothing configured (`GH_ACCOUNT` decides). */ private resolveDeliverIdentity; /** * The origin the deliver push will actually go to (F2) so the deliver GATE CARD says what will * actually happen. The card used to promise "pushes its branch and opens a pull request" * whatever the origin was — on a local path, an SSH, GitLab, ADO or Gitea remote, the phase * pushed the branch and then died on `gh pr create`, so the operator had consented to a pull * request that could not exist. * * RETURNED, never stashed on the adapter (codex review of the F2 PR, HIGH): two concurrent * delivering launches on different repos would otherwise interleave their `await` with the * synchronous composition that reads it, and one launch's card would name the other repo's * origin. The value is launch-local from here on. * * ONE `git remote get-url --push` per delivering launch, in the registered repo's root (a run * worktree shares its repo's remotes, and the worktree does not exist yet at compose time). * `null` for everything that can go wrong — "could not read it" is not a licence to claim * anything about it either way, and the card keeps its generic sentence. Only git's own "no such * remote" becomes `''`, the one answer that licenses "there is no origin". */ private resolveDeliverOrigin; /** The launcher's `deliver` step for a plan (`name` null) or a preset launch (DES-TEAMING-002 * §8.5): the engine appends it to the plan and puts it in the floor. */ private deliverStep; /** Launch an interactive, resumable run → the run id. */ launchRun(input: LaunchRunInput): Promise; /** * Does the installed addon take `LaunchOptions.evidenceRoot` (WT-C2, wicked-core-ts ≥ 0.7.35)? * Fail closed on version: napi ignores an undeclared field, so an older addon would launch a run * whose walkthrough could never pass while crew believed it had a root. Also `/health.capabilities.walkthroughRoots`. */ evidenceRootsSupported(): boolean; /** Resume a run from its persisted cursor → the status token. */ resumeRun(runId: string): Promise; /** (operator ruling 2026-10-11) The actor answering each run's gate, set by the gate route for its * next {@link confirmGate} call only. */ private readonly gateActors; /** Record who answers `runId`'s gate on the next {@link confirmGate} (the gate API's actor). */ recordGateActor(runId: string, actorLabel: string): void; /** Resolve a human gate: approve (optional amend) / request changes / reject → the status token. * (DES-L1 PR-2) `action` and `amendScope` are the 0.7.27 arms: on an older addon a napi call * would silently DROP them — a `request_changes` would run as a plain reject — so they fail * CLOSED on version (the `extraWriteRoots` / `projectGraph` doctrine); absent, the call is the * three-arg one every engine understands. */ confirmGate(runId: string, approve: boolean, amend?: string, action?: string, amendScope?: string, plan?: LaunchPlan): Promise; /** Cancel a run → the status token. */ cancelRun(runId: string): Promise; /** Whether the installed engine addon carries the campaign bindings. */ campaignsSupported(): boolean; /** Guard: the campaign binding surface, or a typed 501-shaped refusal naming the action. */ private _campaigns; /** Validate + launch a campaign DAG → the campaign id. `def` is the ENGINE wire shape * (snake_case) — `campaigns/plan.ts` is the one producer of it in this daemon. * * `workflows` are the composed per-node Tool workflows the def's run_specs reference; they * are armed FIRST, as system presets (X-MIG M11), so the engine resolves them both now and * after a restart — a campaign is durable, so its node workflows must be too. They never enter * `userWorkflows` or the work-mode catalog. */ launchCampaign(def: CampaignDef, workflows?: WorkflowDef[]): Promise; /** Arm one composed campaign-node workflow as a PRESET (X-MIG M11): the preset store is the * engine's durable record, so a resumed campaign finds its node by name after a restart without * any file overlay. The node's one Tool phase becomes one `run` step carrying its command; the * engine composes it (its step rules apply), and the name keeps it a system preset, off every * picker (`isSystemWorkflow`). */ private _armCampaignWorkflow; /** Resume a campaign from its persisted state → the campaign status token. */ resumeCampaign(id: string): Promise; /** Cancel a campaign (in-flight node Runs cancelled, the rest marked) → the status token. */ cancelCampaign(id: string): Promise; /** A campaign's full persisted state (DAG + node statuses + run ids), or `null` when unknown. */ campaignDetail(id: string): Promise; /** Every campaign on the store. */ campaignList(): Promise; /** * Archive (or unarchive) a TERMINAL run (crew#265) — write-off, not delete. Resolves `true` * when the session existed, `false` for an unknown id (→ 404); REJECTS when the run is * non-terminal (the route answers 409). Fail-closed on an old addon: method-presence guard, * same doctrine as `injectWorkerMessage`. */ archiveRun(runId: string, archived: boolean, note?: string): Promise; /** Inject an operator message into a run's active PTY worker(s). target="all" or a CLI key. */ injectWorkerMessage(runId: string, message: string, target: string): Promise; /** * Recycle the CURSOR unit of an Executing run (crew#341; wicked-core-ts ≥ 0.7.6): the engine * supersedes the stale turn (cancels its epoch — never folded as a step failure), closes the * worker session, bumps the attempt, and re-dispatches. Queued operator injects survive into * the fresh turn. `newCli` set = re-dispatch to that seat (pass the unit's CURRENT seat to * recycle in place); absent/null = the engine re-runs the council and lets it pick. * * REJECTS (engine-validated) when the run is unknown, not Executing, or `ord` is not the * cursor unit — the caller must treat that as "the run moved on", not retry blindly. * Fail-closed on an old addon: method-presence guard, same doctrine as `injectWorkerMessage`. */ reassignUnit(runId: string, ord: number, newCli?: string | null): Promise; /** * A run's recorded event history, oldest first — or `null` when this wicked-core build has no * event-log read binding. * * `null` rather than `[]` on purpose. An empty history is a real, ordinary answer (a run that * emitted nothing, or one predating the log), and collapsing "nothing happened" into "I cannot * tell you what happened" is how a missing capability gets reported to an operator as an absent * gate — the FINDING-050 shape, distinct causes wearing one message. Callers branch on it. */ runEvents(runId: string): Promise; /** Run ids on the store. */ sessions(): Promise; /** * Each run's launch name as the engine RECORDED it — `sessionStarted.workflowId` from its event * log (the preset, the registered def, `:plan-`, or `null` for free text). Immutable * once written, so it is read once per run: a run whose log holds no `sessionStarted` is memoized * only once it is terminal (a live run's frame may not be flushed yet; a finished run's never * will be). Seam X2 — the one input `resolveRunIdentity` needs that the session record lacks. */ private readonly launchedWorkflows; private launchedWorkflowOf; /** * Every run + its ordered units, each with its `run_identity` (seam X2) resolved from the engine's * record — its plan state, else its recorded launch — never from its phase sequence. * * `workflow_id` is still rewritten from the engine's synthetic `wf-` to the resolved NAME * for skins that read it (studio's run-kind list, until it reads `run_identity.system`); a run * with no name (a user plan, free text, unknown) keeps the engine's id. */ sessionsDetail(): Promise; /** The engine's sessions-detail fold running now, and the one every caller that arrived during it shares. */ private detailRunning; private detailNext; /** * The engine's whole-store sessions-detail read, at most one running and one queued (crew#944). * Each call is a fold on the engine's single actor, and the binding holds a libuv worker thread * blocked on the actor's reply until it is served. So N concurrent run reads used to queue N * folds and pin the whole pool, and `/health` (its ping, its state-home readdir) waited behind * all of them. A caller that arrives while a fold runs shares the NEXT fold, which starts after * the running one ends. It never takes the running one's answer, so every caller still reads * a fold that started after it asked (a write it saw committed is in it). Each caller parses its * own copy of the JSON, because callers decorate the views they get in place. */ private engineSessionsDetail; private startSessionsDetail; /** A unit's captured transcript (string, or `null`). */ workOutput(unitId: string): Promise; /** True when the installed addon carries the preset bindings. */ presetsSupported(): boolean; private requirePresets; putPreset(name: string, steps: PresetStep[], projectId?: string, createdBy?: string): Promise; deletePreset(name: string, projectId?: string): Promise; /** The preset `name` resolves to for a launch in `projectId` (the project's row shadows the * global one), or `null` — including on an addon without presets, whose engine resolves every * workflow id as a registered def. */ presetNamed(name: string, projectId?: string): Promise; listPresets(projectId?: string): Promise; private requireTeam; /** The run's team transport and per-unit snapshots, or `null` for a run that is not a team run. */ runTeam(runId: string): Promise; /** Replay `/team-outbox.ndjson` onto the bus. */ replayTeamOutbox(): Promise; /** * The catalog ids whose entry declares `verified_evidence` (seam X2: a plan run's acceptance is * the steps it contains that re-verify evidence). `null` when the engine has no catalog binding * or its catalog does not carry the flag — callers fail closed on it. The catalog is compiled * into the engine, so a known answer is read once. */ verifiedEvidenceCatalog(): Promise | null>; private _verifiedEvidenceCatalog; /** * Every catalog id the engine defines (WT-W2): a plan step naming one outside it fails its * acceptance requirement closed. `null` when the catalog cannot be read. Cached once read. */ catalogIds(): Promise | null>; private _catalogIds; /** The engine's phase catalog. */ catalog(): Promise; /** What a `POST /runs {plan}` launch with the same fields would decide, persisting nothing * (`Core.previewPlan`): `repoRef` is the repo the launch runs on (its graph scores the touch * set), and `deliver` hands the engine the launch's deliver step, as `launchRun` does. */ previewPlan(plan: LaunchPlan, opts?: { projectId?: string; humanConfirm?: string; repoRef?: string; deliver?: boolean; }): Promise; /** A mid-run plan edit (`Core.proposePlan`): the steps to add, held for the run's next step * boundary and approved by its author. Idempotent by `requestId`; rejects with the engine's * refusal (a plan awaiting approval, a started deliver step, a finished or unplanned run, …). */ proposePlan(runId: string, plan: LaunchPlan, requestId: string): Promise; /** True when the installed addon carries the project surface (0.6.0+). */ projectsSupported(): boolean; private requireProjects; projectCreate(name: string, description?: string): Promise; projectUpdate(id: string, patch: { name?: string | undefined; description?: string | undefined; status?: string | undefined; }): Promise; /** Every stored project (all statuses, newest first). The `default` project is NOT here — the * route layer synthesizes it (ADR §7: computed, never stored). */ projectList(): Promise; projectGet(id: string): Promise; projectMembers(projectId: string): Promise; /** Attach a member. `created:false` = the idempotent duplicate hit (emit no event for it). */ projectMemberAttach(projectId: string, kind: string, ref: string, meta?: Record, attachedBy?: string): Promise<{ member: ProjectMember; created: boolean; }>; /** Detach (tombstone). `false` = no such live member on that project (the route's 404). */ projectMemberDetach(projectId: string, memberId: string): Promise; /** The projects holding a live `(kind, ref)` membership — the run→project reverse read. */ memberProjects(kind: string, ref: string): Promise; /** * (rejudge) Whether `runId`'s open gate can RE-RUN ITS JUDGE now: the engine's own judge * selection over the run's roster, bench and the registry as they stand (wicked-core * `rejudge_readiness`). `null` when this addon predates the binding — then the gate offers no * `rejudge`, and the engine would refuse the action anyway. */ rejudgeReadiness(runId: string): Promise; /** * Durable interaction requests (ADR §5.3), newest first — or `null` when this addon predates * the binding (the `runEvents` convention: a missing capability must stay distinguishable from * "no open prompts", or the caches' fallback chain misreports upgrades as empty inboxes). */ interactionRequests(sessionId?: string, status?: string): Promise; /** Capture a memory at a STRICT `kind:id[/…]` scope (the engine refuses malformed segments). */ captureMemory(content: string, scope: string): Promise; /** Memories within `scope`'s subtree, newest first — the ADR's memory.coverage probe. */ listMemories(scope: string, limit: number): Promise<{ content: string; score: number; tier: string; }[]>; /** Ingest a document (title + chunks) into the knowledge store. Returns the chunk count. */ ingestKnowledge(title: string, chunks: string[]): Promise; /** Recall up to `k` knowledge chunks relevant to `query`. */ recallKnowledge(query: string, k: number): Promise<{ content: string; score: number; source: string; }[]>; /** * Every live chat, so an operator can find the ones nothing is going to close (FINDING-027). * * Chat sessions are a warm pool that deliberately outlives the page, and the only client that * knew a chat's id is the tab that minted it. Without this an orphaned seat is unreclaimable * short of restarting the daemon — the leak is real but invisible, which is the worse half. * * `idleSecs` is `number | null`, not `number`. The Rust side uses `u64::MAX` for "no activity * timestamp"; as an f64 that arrives as 18446744073709552000, which no caller can test for by * equality and every caller can accidentally do arithmetic on. The binding maps it to `null`. */ chatList(): Promise; chatClose(chatId: string): Promise; askSupported(): boolean; askUnit(token: string): Promise; openAsk(request: AskRequest): Promise; closeAsk(id: string, reason: 'timeout' | 'teardown'): Promise; private askCall; /** * Forward the operator's elicitation response to the actor (DES-002 §4 P-1; crew#357/#358). * * NAPI flat signature: `resolve_elicitation(run_id, elicitation_id, action, response)`. * `response` is `null` for `decline` and `cancel` actions; a non-empty string for `accept` * (the engine takes any JSON value — crew's route validates a string, so a string is what * crosses the boundary). * * Guarded on METHOD PRESENCE the way `interactionRequests`/`runEvents` are: the binding * ships in wicked-core-ts ≥ 0.7.2, so an older installed addon has no method at runtime. * That case throws `ElicitationUnsupportedError`, which the route maps to HTTP 501 — * "upgrade the engine", never "fix your request". On a binding-bearing build the call is * forwarded and the engine's own rejection ("no matching elicitation", "elicitation not * supported for this runner") propagates as a plain error (route: 500, prompt restored). */ resolveElicitation(runId: string, elicitationId: string, action: string, response: string | null): Promise; /** repo id → onboarding run id (in-memory; graph persists on disk across restarts). */ private readonly repoOnboardRunIds; /** * repo id → the in-flight launch, so a concurrent caller joins it instead of starting a second. * * A `Set` of ids was not enough. The id was added here but the run id was only recorded in * `repoOnboardRunIds` AFTER the launch resolved, so a second caller arriving mid-flight saw * "in flight" with no run id to return, fell through, and launched a DUPLICATE run against the * same repo. Holding the promise makes the second caller await the first and receive its run id — * the dedup the `Set` was named for. */ private readonly onboardingInFlight; /** * Register a local git repo → the persisted `RepoEntry`. The path is resolved to its REAL path * first (crew#778): the engine mints run worktrees under the registered root, and codex's Seatbelt * sandbox refuses a writable root with a symlink component — a repo registered as `/tmp/link-repo` * (or anything under macOS's `/tmp`, itself a symlink) was accepted and then failed every codex * unit. A path that does not resolve is handed on as typed, so the engine's own refusal names it. */ registerRepo(name: string, rootPath: string): Promise; /** * Clone a remote git URL and return the persisted `RepoEntry` from registration. * `checkoutPath` overrides the default clone destination (`~/.wicked/repos/`). */ cloneAndRegister(name: string, gitUrl: string, checkoutPath?: string): Promise; /** * Launch the built-in `onboarding` workflow for a registered repo. * The run's `workdir` = the repo root; Tool phases run estate commands there. * Returns the run id so the UI can navigate directly to it. */ launchOnboardingRun(repoId: string, repoName: string): Promise; private _doOnboardingLaunch; /** * The roster a run of `workflowId` should carry as its seat pool. A workflow or preset whose * every phase (step) runs a TOOL executor convenes no council and dispatches no seat, so its pool * is `[]` — the engine routes each unit `tool` without consulting the pool (verified against the * engine: an onboarding launch with `clis: []` distributes both units to `wicked-estate`). Any * one with an agent phase — or one this daemon cannot read — gets the full roster, as before. * A registered def is read first; otherwise the preset the name launches (the engine resolves a * launch the same way: DES-TEAMING-002 §8.4). */ seatsForWorkflow(workflowId: string): Promise; /** Return the onboarding run id for a repo (undefined if not launched this session). */ getOnboardRunId(repoId: string): string | undefined; /** The repo an onboarding run launched by THIS daemon process was for (undefined for any other run). */ onboardedRepoOf(runId: string): string | undefined; /** List every registered repo. */ listRepos(): Promise; /** All registered governance policies. */ listPolicies(): Promise; /** All conformance rules on the store. */ listConformanceRules(): Promise; /** All recorded conformance claims (governance decisions). */ listConformanceClaims(): Promise; /** Front-half coverage gate report; null when the store has no graph nodes. */ getCoverageReport(): Promise; /** * Coverage for ONE registered repo, computed over that repo's OWN code graph (FINDING-009). Unlike * {@link getCoverageReport} — which reads the daemon store and reports a vacuous `coverage: 1.0` that * names no repo — this resolves `repoRef` in the registry and recomputes over its `code_graph_db`. * The core rejects an unknown repo (never a silent vacuous report), so this throws for a bad ref. */ getCoverageReportForRepo(repoRef: string): Promise; /** * Node-count-by-kind summary of ONE registered repo's code graph, over that repo's OWN store * (#122) — what the estate graph actually holds for the repo, so an operator can see it was * populated. The core rejects an unknown repo, so this throws for a bad ref. */ getGraphKindsForRepo(repoRef: string): Promise; /** Upsert a governance policy via the single-writer actor. */ upsertPolicy(policy: GovernancePolicy): Promise; /** Upsert a conformance rule via the single-writer actor. */ upsertConformanceRule(rule: ConformanceRule): Promise; /** * Withdraw a policy from enforcement. Resolves `true` if a policy with that id existed. * * Retire, not delete (FINDING-038): the node stays readable so a past decision citing this id is * still explicable, but governance stops selecting it. The boolean is what lets the route answer * 404 instead of reporting a success that removed nothing. */ retirePolicy(id: string): Promise; /** Withdraw a conformance rule from recall. Same contract as {@link retirePolicy}. */ retireConformanceRule(id: string): Promise; /** Recall conformance rules matching a facet query (read-only, does not block actor). */ recallRulesPreview(query: Record): Promise; /** Whether the installed engine addon carries the AW-23 scoreboard binding (core-ts ≥ 0.7.4). */ wikiScoreboardSupported(): boolean; /** * The wiki population/connection scoreboard (AW-23 / arch-R23) — typed %, resolving %, * enforcement evidence, and an in-band honesty marker for what cannot be measured. Read-only * on the engine side (`open_store_ro`), so it never blocks the single-writer actor. * * `docsRoot` is the same docs root `rules ingest --dir` used; omitted, the typing half reports * `available: false` with the reason — an honest in-band answer, not an error. * * Presence-gated (the campaigns doctrine): throws {@link GovernanceScoreboardUnsupportedError} * on an addon that predates the binding, which the route maps to 501 ("upgrade the engine"). */ governanceScoreboard(docsRoot?: string): Promise; /** * How many `RuleSet` rows (doctrine domain parents, AW-13) the store holds — or `null` when * this engine build cannot count them (no `listRuleSets` binding yet). * * `null` rather than `0` on purpose (the `runEvents` doctrine): "no rulesets" is a real answer * about an unseeded store, and "I cannot count" must never impersonate it — the wiki meta's * empty state would otherwise report a seeded store as empty and point the operator at a * runbook they already ran. */ countRuleSets(): Promise; /** * Whether the installed engine addon carries the unified steering-rule model (core-ts ≥ 0.7.5). * * The sentinel is the `steeringImport` binding, which ships WITH the model merge — see the * `GovernanceMethods` comment. Consulted by the routes before: filtering rules by * `?type=` (rows on an old engine carry no `steering_type`, so an empty answer would * impersonate "no rules of that type"); accepting a rule write that carries steering fields * (an old engine would silently drop them — the extraWriteRoots doctrine); and folding the * legacy policy write surface (410 only once the unified store exists to point at). */ steeringSupported(): boolean; /** * DC-S3 (DES-decision-capture §5.3): does the engine keep a rule's `targets.project`? The same * presence pattern as {@link steeringSupported}: `considerRules` shipped with the field. Without * it a project-scoped landing fails loud instead of landing everywhere. */ projectRulesSupported(): boolean; /** * DC-S1 "considered · set aside" (DES-decision-capture §4.2.2): the in-force rules for a query * carrying `projects`, and the ones set aside with the reason. Throws on an engine without the * binding — callers probe {@link projectRulesSupported} first (an old `RuleQuery` rejects `projects`). */ considerRules(query: { projects?: string[]; steering_type?: string; }): Promise<{ in_force: ConformanceRule[]; set_aside: Array<{ id: string; statement: string; reason: 'out_of_scope' | 'replaced' | 'retired'; }>; }>; /** One conformance rule as the store holds it now (retired rows included); `null` when absent. */ readConformanceRule(id: string): Promise; /** * The MCP policy preview (DES-MCP-TOOLS-001 S6): `{calls, cells}` → `[{subject, cells}]` JSON, * judged by the same evaluation the broker's `evaluateMcpCall` runs, and recorded nowhere. * Throws `mcp_preview_unsupported` on an addon that predates the binding (the route answers 501). */ previewMcpCalls(requestJson: string): Promise; /** EP-C1: does the engine carry `evaluate_editor_grants` (EP-K1; wicked-core-ts >= 0.7.35)? */ editorGrantsSupported(): boolean; /** * EP-C1 (DES-artifact-editor-plugins §6.2): the engine's decided permission set for one editor — * the EDITOR-GRANTS ledger + the EDITOR-BUILTIN posture over the subject tokens, deny dominates, * recording nothing. Throws on an engine without the binding (the route answers 501). */ evaluateEditorGrants(request: { editorId: string; version: string; sha256: string; permissions: string[]; project?: string; firstParty: boolean; }): Promise; /** * Batch-import steering rules (frontmattered markdown docs and/or ready rule JSON) through the * engine's ingest normalize/validate path — the SAME path `rules ingest --dir` runs, on the * single-writer actor (the daemon must never open the store for writes itself). Fail-closed * PER ENTRY: one bad entry rejects alone with its reason in that entry's result row, the rest * still land. `defaultType` is the page's inferred steering type, applied to entries that * omit one. * * Presence-gated (the campaigns doctrine): throws {@link SteeringUnsupportedError} on an addon * that predates the binding, which the route maps to 501 ("upgrade the engine"). */ importSteeringRules(entries: SteeringImportEntry[], defaultType?: string): Promise; /** * Whether the installed engine addon carries the governance-evals surface (core-ts ≥ 0.7.5, * unreleased at the time this seam lands). The sentinel is the `governanceEvals` binding — * for the WHOLE evals seam, corpus import included: the two bindings ship together, and a * daemon running against the released 0.7.4 addon must answer 501 on both routes, never crash. */ governanceEvalsSupported(): boolean; /** * `WICKED_CREW_KNOWLEDGE_DB` overrides the estate knowledge db the evals seams hand the * engine — the same escape hatch `WICKED_CREW_SYSTEM_SETTINGS` gives {@link settingsFilePath}. * Unset/empty (production) omits `knowledgeDb` from the args so the engine resolves its own * default — which is `~/.wicked-estate/knowledge.db` (evals.rs `default_knowledge_db()`, HOME- * derived), NOT a dbPath-derived sidecar. That home default is exactly why the test harness * arms this seam (tests/setup/hermetic-home.ts): an un-armed corpus-import test writes * `evals:` junk into the OPERATOR's real estate knowledge store — the store the mem/search * domains answer from (crew#396). Read at call time, not construction time, so a test may * re-aim it per fixture. */ private static knowledgeDbOverride; /** * Run the governance evals: every sample of the corpus (`corpus` = an `evals:` estate * scope minted by {@link importGovernanceCorpus}; omitted = the engine's built-in default * corpus) is pushed through the decide path and compared to what its `kind` says SHOULD have * happened. Resolves the engine's serde report — snake_case field names, passed to the wire * verbatim (the pinned crew/studio contract). * * `dbPath` rides the args because the eval run opens its own read-only connection to the * steering store — the SAME store this adapter's actor writes (`this.dbPath`, the one spelling * of where it lives). `knowledgeDb` is normally omitted so the engine resolves its own default * (`~/.wicked-estate/knowledge.db`) — {@link knowledgeDbOverride} is the env escape hatch. * * Presence-gated (the campaigns doctrine): throws {@link GovernanceEvalsUnsupportedError} on * an addon that predates the binding, which the route maps to 501 ("upgrade the engine"). */ runGovernanceEvals(args: { type?: string; corpus?: string; }): Promise; /** * Import a named eval corpus into the knowledge store under the `evals:` scope (the * string a later {@link runGovernanceEvals} names as `corpus`). `knowledgeDb` resolves the * same way as in {@link runGovernanceEvals} — normally omitted (engine default: * `~/.wicked-estate/knowledge.db`), {@link knowledgeDbOverride} wins when set. * * Gated on the `governanceEvals` sentinel as well as the binding itself: the two ship * together, and importing a corpus no engine on this host can ever run would be a trap, not * a feature (the steering author-route doctrine). */ importGovernanceCorpus(name: string, samples: GovernanceEvalSample[]): Promise; /** The built-in presets' defs as crew serves them (`core/builtin-catalog.ts`). Empty until * {@link loadBuiltinCatalog} ran — a directly-driven adapter (a test double, the CLI) serves none. */ private builtinDefs; /** * Read the engine's built-in presets and its phase catalog ONCE and derive the built-in workflows * crew serves (`GET /workflows`, the deliver text, delivery candidacy, acceptance). The built-ins * are compiled into the engine, so one read holds until restart. An engine without presets or the * catalog binding serves none (logged), never a guess. */ loadBuiltinCatalog(warn?: (m: string) => void): Promise; private readonly userWorkflows; /** The workflow catalog `GET /workflows` serves. `unavailable` stays on the wire, always empty: * it named the operator drop-ins the engine refused (crew#718), and drop-ins are retired * (X-MIG M11 — a file in the old overlay dir never runs; the engine names it once at boot). */ workflowCatalog(): Promise<{ workflows: WorkflowDef[]; unavailable: RefusedWorkflow[]; }>; listWorkflows(): WorkflowDef[]; /** The runtime-registered defs only (the user's and crew's seams'), without the built-in presets. */ listRuntimeWorkflows(): WorkflowDef[]; getWorkflow(id: string): WorkflowDef | null; /** * Arm a PER-RUN composed workflow def (crew#293 deliver, crew#311 deliverable floor) with the * engine — hot registration * ONLY. Deliberately not the other path: * - not `registerWorkflow()`: the composed def must not enter `userWorkflows` or the overlay * dir — it is launch input for one run, and persisting it would grow the catalog and the * overlay dir by one entry per delivered run. * The engine's `registerWorkflow` binding validates the def server-side and makes it visible * to the next `launchRun` with no restart — exactly the lifetime a per-run def needs. (The * def is consumed at PLANNING time; a later daemon restart resumes the run from its persisted * units, so the in-memory registration expiring with the process is fine.) */ private _armPerRunWorkflow; /** * Register a RUNTIME workflow def with the engine (X-MIG M11): crew's interactive seams that still * hold a def (review, theme) register it at boot. The engine validates it and holds it in memory * for this process; nothing is written to disk (operator drop-ins are retired, and a user saves a * preset instead). Refused for a built-in preset's name, which a launch would never resolve to it. */ registerWorkflow(def: WorkflowDef): Promise; /** * Save an inline script to `~/.wicked/scripts/.`, make it executable, * and return the absolute path. Tool-executor phases use this path as their command. */ saveScript(name: string, content: string, lang: 'bash' | 'python' | 'sh'): Promise; /** * Open a PTY terminal session in `cwd` running `cmd` (or the login shell when * omitted), sized `cols`x`rows`. `governed:true` keeps tool-calls routed through * the gate-hook (the default); `governed:false` is the loud, opt-in **ungoverned * operator shell** (DES-TERMINAL-001 §7). Resolves the new terminal id. */ openTerminal(cwd: string, cmd: string[] | undefined, cols: number, rows: number, governed: boolean): Promise; /** Write raw input bytes (keystrokes) to a terminal → `"ok"`. Rejects on an unknown id. */ writeTerminal(id: string, bytes: Buffer): Promise; /** Resize a terminal's PTY to `cols`x`rows` → `"ok"`. Rejects on an unknown id. */ resizeTerminal(id: string, cols: number, rows: number): Promise; /** Close a terminal (kill child, join reader) → `"ok"` after a `terminalExited` event. */ closeTerminal(id: string): Promise; /** * Hand the daemon's QE acceptance setting to the ENGINE, for the launches it makes on its own: * campaign nodes (as each dispatches) and `wicked.crew.run.requested` bus requests never pass * through `launchRun`, so crew cannot add the skip to them. With `qeAcceptance: 'off'` the engine * skips such a launch that gives no QE word of its own, with {@link QE_OFF_REASON}, and only when * the run's contract requires QE acceptance (the engine resolves that itself, so a run with nothing * to skip is left alone, never refused). * * Runs after every settings read and write, and reads the file itself, synchronously * (`diskQeAcceptanceMode`): the mirror is the file as it stands at that moment, never the result of * an async read that a later edit or write overtook. `readOk: false` (the settings read failed — * rejected, or could not read, parse or validate the file and served the defaults, which `/health` * and `launchRun` read as `'required'`) clears it: fail closed. An addon without * the method has no such mirror (`engineQeSettingSupported`): there, engine-originated launches * still require QE acceptance. */ private syncEngineQeSetting; /** Whether the installed engine follows the QE acceptance setting on the launches it makes on its * own (campaign nodes, bus requests) — `setQeAcceptanceOff` is present. */ engineQeSettingSupported(): boolean; getSettings(): Promise; /** The settings as read, and whether the read succeeded: a file that could not be read, parsed or * validated serves the defaults (`ok: false`), which the engine's QE mirror reads as a failure. */ private readSettings; updateSettings(patch: Partial): Promise; /** * Tear down the single subscription (stop delivery, release the pump thread + * callback) so the process can exit cleanly. Idempotent. */ close(): void; } //# sourceMappingURL=adapter.d.ts.map