import type { Scope, ScopeConfig, ScopeState, KindConfig, ModelLaddersConfig, ModelProvider, ModelStrength } from '../types.js'; export declare function configPath(scope: Scope): string | null; export declare function statePath(scope: Scope): string | null; export declare function readConfig(scope: Scope): ScopeConfig; /** Read the config that lives at an EXACT scope-root dir, rather than the * resolved nearest root for a scope. Lets a caller iterate each visible * project root's own config when several are in scope (nested `.crouter` dirs * or a profile-widened stack), instead of collapsing them onto the nearest. */ export declare function readConfigAtRoot(root: string): ScopeConfig; export declare function readState(scope: Scope): ScopeState; type RawConfig = Record; export declare function updateRawConfigAtomically(scope: Scope, mutate: (latest: RawConfig) => RawConfig): RawConfig; export declare function writeConfig(scope: Scope, config: ScopeConfig): void; /** Persist a RAW PARTIAL config to a scope's config.json exactly as given. * Unlike `writeConfig`, this makes no promise that every field is present -- * callers pass the sparse partial that should live on disk, so an untouched * key stays absent (and `readConfig`/`mergeConfig` re-derive it live from * whichever build is running). This is the single central primitive every * mutation path funnels through so no writer ever freezes a fully * materialized `modelLadders` (concrete model ids that go stale across * builds -- the live-Hearth freeze bug) onto disk. */ export declare function writeRawConfig(scope: Scope, config: Partial): void; export declare function writeState(scope: Scope, state: ScopeState): void; export declare function ensureScopeInitialized(scope: Scope, root: string): void; /** Raw (un-defaulted) partial config for one scope, or null if the scope has * no root or no config.json. Used by `readMergedLaunchConfig` to layer * scopes onto each other WITHOUT each scope's own default-fill masking a * lower-precedence scope's real customization (see that function's comment * for why `readConfig(scope)`, which already defaults-fills, is unusable * for cross-scope layering). */ export declare function readRawConfigAtRoot(root: string): Partial | null; export declare function readRawScopeConfig(scope: Scope): Partial | null; export interface MergedLaunchConfig { kinds: Record; modelLadders: ModelLaddersConfig; /** The merged `spawnEnv.allow` set (project stack > profile > user > * builtin, additive) — read by `buildOperationalEnvBase` * (`core/runtime/spawn-env.ts`) as source C of the broker spawn-env * boundary. */ spawnEnv: { allow: string[]; }; } /** Merge launch knobs (`kinds`, `modelLadders`) across scopes in * project stack > profile > user > builtin precedence — the same precedence * order used for memory resolution. A kind or ladder cell declared at a * more-specific scope shadows the same key from a less-specific scope; the * project STACK (`findProjectScopeRoots` — every ancestor `.crouter/`, * widened by a selected profile's `projects`) layers nearest-root-strongest; * a key no scope declares falls through to the builtin default registry * (`defaultScopeConfig().kinds` / `.modelLadders`). * * Layers RAW (un-defaulted) partial config per scope, not `readConfig(scope)` * — `readConfig` already fills in every default kind for a scope that * customizes even one, so naively overlaying two already-defaulted * `ScopeConfig.kinds` objects would let an untouched project-scope kind * (silently defaulted) clobber a real user-scope customization of that same * kind. Layering the raw partials avoids that. * * Callers (launch, kind registry) go through this function rather than * `readConfig` directly, so `ScopeConfig.kinds`/`modelLadders`/tools/ * extensions/`availableTo` all honor the same profile + multi-project * precedence. * * `targetCwd`/`targetProfileId` default to this PROCESS's own ambient cwd * and `CRTR_PROFILE_ID` — correct for a caller resolving config for itself * (front-door commands, kind listing). A caller resolving config on behalf * of a DIFFERENT node (the broker spawn-env boundary or cron executor — both * run inside the daemon, whose ambient cwd/profile is the daemon's own, not * the target node's) MUST pass the target's own cwd/profile explicitly, or * `spawnEnv.allow` resolves from the wrong scope entirely (the C-1 * follow-up fix this parameterization exists for). */ export declare function readMergedLaunchConfig(targetCwd?: string, targetProfileId?: string | null): MergedLaunchConfig; /** The effective `KindConfig` for one full kind string (top-level, e.g. * `developer`, or sub-kind, e.g. `plan/reviewers/security`), across * project > user > builtin precedence. Returns `undefined` for a kind no * scope registers — existence is deliberately NOT validated here (kind * existence/launch-menu enumeration is a Phase 3 caller concern); this only * resolves the config for a kind the caller already knows about. */ export declare function resolveKindConfig(kind: string): KindConfig | undefined; /** The sub-kinds available to spawn FROM a given top-level kind — every * registered sub-kind (full path contains `/`) whose `availableTo` (default: * its own top-level ancestor, e.g. `plan/reviewers/security` defaults to * `['plan']`) includes `kind` or `'*'`. The single source both `sys * prompt-review --list`'s `subPersonas` metadata and the live sub-persona * spawn-menu splice (`core/substrate/render.ts`) read, so the two can never * drift apart. Sorted by full kind name for stable rendering. */ export declare function subKindsAvailableTo(kind: string): { kind: string; whenToUse: string; }[]; /** Persist an EXPLICIT sparse `modelLadders` intent at one config root: the * patch's rungs (and `defaultProvider`) are merged over whatever sparse ladder * is already on disk, and every untouched rung stays absent so it keeps * re-deriving from the running build's registry. * * Ladder edits must come through here rather than `updateConfig`, because that * path infers what to persist by diffing the mutated config against the merged * baseline: setting a rung to the value that happens to be today's compiled * default produces an empty diff and silently persists nothing, so the choice * evaporates as soon as the default moves. Explicit intent is recorded as * written. Mirrors `persistDefaultKindModel`. */ export declare function persistModelLadders(root: string, patch: { defaultProvider?: ModelProvider; anthropic?: Partial>; openai?: Partial>; }): void; /** Mutate one exact config root. Setup uses this for profile and explicit * project targets that cannot be represented by the user/project Scope enum. */ export declare function updateConfigAtRoot(root: string, mutate: (cfg: ScopeConfig) => void): ScopeConfig; export declare function updateConfig(scope: Scope, mutate: (cfg: ScopeConfig) => void): ScopeConfig; /** Persist one sparse kind-model field at an exact config root. The sparse * patch keeps every unrelated kind and launch knob inherited from lower * scopes instead of freezing a materialized registry snapshot. */ export declare function persistDefaultKindModel(opts: { root: string; kind: string; model: string; orchestrator?: boolean; }): { path: string; field: 'model' | 'orchestratorModel'; }; export declare function updateState(scope: Scope, mutate: (s: ScopeState) => void): ScopeState; export {};