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