/** * E2 decision first-class object (docs/plans/2026-05-28-e2-decision-object.md). * * `hippo decide` used to write only a tagged memory (tags ['decision'], source * 'decision') with a 90-day half-life, so an in-force decision decayed out of * recall even though it was never reversed. The `decisions` table is now the * source of truth: a decision stays `active` regardless of memory decay, and * `hippo decide list --status active` is authoritative. A memory row still * mirrors the decision for recall surfaces but is NOT canonical — memory_id is * NULLABLE with ON DELETE SET NULL so forget/consolidate/archive gracefully * orphans the decision row. * * Lifecycle: active -> superseded (a newer decision replaces it; superseded_by * points to the successor) or active -> closed (retired with no successor). * * Tenant scoping: every helper requires tenantId. BEFORE INSERT/UPDATE triggers * enforce decisions.tenant_id == the referenced memory's tenant_id, and a * superseded_by same-tenant trigger makes cross-tenant supersession * unrepresentable. Mirrors the v0.31 predictions pattern (src/predictions.ts). * * Dual-write atomicity: `saveDecision` writes the memory + decisions row (and, * when superseding, the old row's UPDATE) inside writeEntry's SAVEPOINT * 'write_entry' (store.ts:1196) via the afterWrite hook, so a failure in any * step rolls all of them back. Pattern matches savePrediction (predictions.ts). */ export type DecisionStatus = 'active' | 'superseded' | 'closed'; export declare const VALID_DECISION_STATES: ReadonlySet; export interface Decision { id: number; /** Nullable: ON DELETE SET NULL lets memory deletion (forget / consolidate / * archive) proceed without breaking the decision row. */ memoryId: string | null; tenantId: string; decisionText: string; context: string | null; status: DecisionStatus; /** Successor decision id; set only when status === 'superseded'. */ supersededBy: number | null; supersededAt: string | null; closedAt: string | null; createdAt: string; } export interface SaveDecisionOpts { decisionText: string; context?: string; /** Table id of an ACTIVE decision this one supersedes. The CLI resolves it * from a `--supersedes ` via resolveActiveDecisionIdByMemory; * HTTP/SDK pass the table id directly. */ supersedesDecisionId?: number; /** Extra memory tags merged after ['decision'] (the CLI passes path-context * tags; HTTP/SDK pass none). */ extraTags?: string[]; } export interface ListDecisionsOpts { status?: DecisionStatus; limit?: number; } /** * Create a decision. Writes the memory mirror + the decisions row atomically * inside writeEntry's SAVEPOINT 'write_entry'. When supersedesDecisionId is * given, the referenced ACTIVE row is UPDATEd -> superseded in the SAME * SAVEPOINT (CAS: WHERE status='active'; throws on changes===0 so a duplicate * supersede aborts the whole write rather than orphaning a successor). * * The memory mirror preserves the legacy `hippo decide` shape: tags * ['decision', ...extraTags], source 'decision', confidence 'verified', * half_life DECISION_HALF_LIFE_DAYS, content = "\n\nContext: " * when context is given (so existing recall output is unchanged). */ export declare function saveDecision(hippoRoot: string, tenantId: string, opts: SaveDecisionOpts, actor?: string): Decision; /** * Close (retire) an active decision with no successor. Updates the decisions * row only; the memory mirror is not mutated. CAS guard mirrors closePrediction * (predictions.ts): WHERE status='active'; 0 changes distinguishes not-found * from not-active so callers surface the right error. */ export declare function closeDecision(hippoRoot: string, tenantId: string, id: number, actor?: string): Decision; export declare function loadDecisionById(hippoRoot: string, tenantId: string, id: number): Decision | null; export declare function loadDecisions(hippoRoot: string, tenantId: string, opts?: ListDecisionsOpts): Decision[]; export declare function loadActiveDecisions(hippoRoot: string, tenantId: string, opts?: { limit?: number; }): Decision[]; /** * Resolve a `--supersedes ` (the legacy CLI contract) to the table id * of the ACTIVE decision backed by that memory, or null when the memory has no * active decision row (a legacy pre-episode decision-tagged memory). Extracted * so the CLI's backward-compat path is unit-testable at the store layer without * exporting cmdDecide. */ export declare function resolveActiveDecisionIdByMemory(hippoRoot: string, tenantId: string, memoryId: string): number | null; //# sourceMappingURL=decisions.d.ts.map