/** * Pure, daemon-independent helpers and types for mid-run role replacement. * See docs/mastermind/specs/2026-09-07-org-runtime-role-respawn-design.md. */ import type { AgentRuntime, RuntimeKind } from './daemon.js'; import type { OrgDef, OrgRole } from './types.js'; export type RoleSlotPhase = 'pending' | 'starting' | 'running' | 'draining' | 'ended' | 'crashed' | 'stuck' | 'removed'; export interface RetiredUsage { tokens: number; costUsd: number; } /** Only the fields org_respawn_role may override; everything else keeps the * current effective value (see mergeEffectiveRoleConfig). */ export interface RoleOverrides { runtime?: RuntimeKind; model?: string; providerName?: string; } export interface RespawnReceipt { success: boolean; roleId: string; generation: number; respawnCount: number; respawnsRemaining: number; drainTimedOut?: boolean; error?: string; } export interface RoleSlot { generation: number; phase: RoleSlotPhase; runtime?: AgentRuntime; /** The role config actually in effect right now — the org definition's * role, with any accepted overrides folded in. Never the original * org-definition role once at least one replacement has happened. */ effectiveRole: OrgRole; respawnCount: number; /** Accepted org_compact_role / org_clear_role calls for this role this run. */ resetCount?: number; respawnPromise?: Promise; /** Messages that arrived while this slot was draining, not yet delivered * to a live mailbox. */ queuedDuringSwap: string[]; retiredUsage: RetiredUsage; /** The live incarnation's cancellation handle — set whenever * spawnRoleIncarnation is given one, so a later respawn can force-stop it. */ abort?: AbortController; } /** Fold only the SUPPLIED override fields onto `current`; every omitted * field keeps its current effective value (including across repeated * replacements — `current` is always the slot's effectiveRole, never the * original org-definition role, so this naturally persists overrides * through however many replacements have already happened). */ export declare function mergeEffectiveRoleConfig(current: OrgRole, overrides: RoleOverrides): OrgRole; /** The role's per-incarnation token ceiling when org_respawn_role omits * `budgetTokens`: reuse the SAME allocator startup uses (daemon.ts's * perRoleBudget computation), not the older even-split-only * roleTokenBudget() helper. A role with its own budget_tokens override * always gets that value; otherwise the remaining org budget (after * subtracting every override) is split evenly across every role WITHOUT * an override. */ export declare function computeReplacementBudget(def: OrgDef, roleId: string): number; export interface RespawnInput { roleId: string; runtime?: RuntimeKind; model?: string; providerName?: string; budgetTokens?: number; reason: string; briefing: string; } export declare function boundedString(value: unknown, max: number): string | null; /** Validate and bound every field of an org_respawn_role call before any * state change or preflight is attempted. See the design doc's "Validation * rules" — limits chosen there: roleId 128, reason 1000, briefing 20000. */ export declare function validateRespawnInput(input: unknown): { ok: true; value: RespawnInput; } | { ok: false; error: string; }; /** Audit/status-safe view of a role's runtime config — never includes * role.provider (which may carry apiKey/authToken/baseUrl). */ export declare function redactRoleConfig(role: OrgRole): { runtime?: string; model?: string; providerName?: string; }; export declare function buildRespawnReceipt(slot: Pick, maxRespawns: number, success: boolean, extra?: Partial): RespawnReceipt; //# sourceMappingURL=role-slot.d.ts.map