/** * Core data model for Hippo memory entries. * Based on the strength formula from PLAN.md. */ export declare enum Layer { Buffer = "buffer", Episodic = "episodic", Semantic = "semantic", Trace = "trace" } export type EmotionalValence = 'neutral' | 'positive' | 'negative' | 'critical'; export type ConfidenceLevel = 'verified' | 'observed' | 'inferred' | 'stale'; export type TraceOutcome = 'success' | 'failure' | 'partial' | null; export type MemoryKind = 'raw' | 'distilled' | 'superseded' | 'archived'; /** * Timestamp invariant. * * All in-process writes of timestamp fields on `MemoryEntry` (`created`, * `last_retrieved`, `valid_from`) and on session-state types (SessionEvent, * TaskSnapshot, SessionHandoff, AssembledContextItem.createdAt, etc.) emit * canonical `Date.prototype.toISOString()` output: 24 characters, UTC, * milliseconds precision, trailing `Z` (e.g. `2026-05-06T09:55:49.123Z`). * * Caveat — markdown rebuild. `deserializeEntry` / `rebuildIndex` preserve * frontmatter timestamp strings as-is. Legacy markdown that recorded a * non-canonical offset (e.g. `2026-05-06T05:55:49-04:00`) round-trips * through SQLite without normalization, and DAG `earliest_at` / `latest_at` * caches are computed from those strings. Importers SHOULD normalize on * write; rebuild from drifted markdown is a known limitation. * * Byte-comparison sort (`<` / `>`) is chronological for any pair of * canonical UTC ISO strings. ~50× faster than `localeCompare` with no * semantic gain. F4 (v1.6.5) uses byte compare on `assemble`; if a future * import path admits non-canonical timestamps, the F4 sort and any * downstream chronological reasoning will need a normalization pass. */ export interface MemoryEntry { id: string; created: string; last_retrieved: string; retrieval_count: number; strength: number; half_life_days: number; layer: Layer; tags: string[]; emotional_valence: EmotionalValence; schema_fit: number; source: string; outcome_score: number | null; outcome_positive: number; outcome_negative: number; conflicts_with: string[]; pinned: boolean; confidence: ConfidenceLevel; content: string; parents: string[]; starred: boolean; trace_outcome: TraceOutcome; source_session_id: string | null; valid_from: string; superseded_by: string | null; extracted_from: string | null; dag_level: number; dag_parent_id: string | null; descendant_count?: number; earliest_at?: string | null; latest_at?: string | null; /** v28: 1 when this summary row has at least one child invalidated, * superseded, forgotten, or archived since it was last rebuilt. Cleared * by E3's rebuildDirtySummaries during sleep. Always 0 for non-summary * rows (dag_level !== 2; E5 widens to include 3). */ summary_dirty?: 0 | 1; /** v28: ISO 8601 timestamp of the last successful rebuild for this * summary, or null if never rebuilt. */ last_rebuilt_at?: string | null; /** v28: monotonically-increasing counter of successful rebuilds for this * summary. 0 for initial buildDag write; bumped by E3. */ rebuild_count?: number; /** v28 (reserved for E5): ISO 8601 timestamp the level-3 entity profile * was built. Only ever populated on dag_level=3 rows. */ dag_level_3_built_at?: string | null; kind: MemoryKind; scope: string | null; owner: string | null; artifact_ref: string | null; tenantId: string; /** * Memory scope isolation (schema v39): owning project for ambient-context * partitioning. A lowercased project name, '' for user-global (injectable * everywhere), or null for legacy pre-v39 rows - ambient context treats * null as other-project (deny). Stamped from the store's location at write * time (store.ts stampOriginProject); undefined only on entries not yet * written. See docs/plans/2026-07-01-memory-scope-isolation.md. */ origin_project?: string | null; /** * F1 (v1.7.0): raw SQLite FTS5 bm25() score from the FTS path of * `loadSearchEntries`. * * Populated ONLY when ALL of the following hold: * - `loadSearchEntries` was called with a non-empty query, AND * - FTS5 is available (meta `fts5_available = 1`), AND * - the FTS join returned at least one row (path 2 of `loadSearchRows`). * * `undefined` on every other path: empty query, FTS unavailable, LIKE * fallback, full-store fallback, `readEntry`, `loadAllEntries`, manual * upsert, deserializeEntry from markdown. * * SCALE: FTS5 bm25() is negative; lower = better match (ascending order). * NOT a drop-in for the JS-side BM25 in `src/search.ts` — that is a * different scorer (different tokenizer, different params, positive * scale). Treat `bm25_score` as provenance/rank metadata only. */ bm25_score?: number; } export declare const DECISION_HALF_LIFE_DAYS = 90; export declare const INCIDENT_HALF_LIFE_DAYS = 90; export declare const PROCESS_HALF_LIFE_DAYS = 90; export declare const POLICY_HALF_LIFE_DAYS = 90; export declare const SKILL_HALF_LIFE_DAYS = 90; export declare const PROJECT_BRIEF_HALF_LIFE_DAYS = 90; export declare const CUSTOMER_NOTE_HALF_LIFE_DAYS = 90; /** * Test-only helper. Tests that mutate `process.env.HIPPO_LOSS_AVERSION_RATIO` * MUST call this in BOTH `beforeEach` AND `afterEach`: * - beforeEach: clear any stale cache from a previous test before setting * the env var for this test. * - afterEach: clear the cache so the next test (which may not set the env * var) reads the clean default instead of this test's value. * See `tests/emotional-multipliers-j5.test.ts` for the canonical pattern. */ export declare function _resetLossAversionRatioCacheForTests(): void; /** * Compute the reward factor from cumulative outcome counts. * * reward_ratio = (positive - negative) / (positive + negative + 1) * reward_factor = 1 + 0.5 * reward_ratio * * Range: (0.5, 1.5). Neutral (no outcomes) returns 1.0. * Modulates effective half-life: memories with consistent positive outcomes * decay slower; consistent negative outcomes decay faster. */ export declare function calculateRewardFactor(entry: MemoryEntry): number; /** * Options for decay basis. * - clock: wall-clock time (default pre-v0.15) * - session: decay by sleep cycle count (for intermittent agents) * - adaptive: auto-scale half-life by session frequency (default v0.15+) */ export interface DecayOptions { decayBasis?: 'clock' | 'session' | 'adaptive'; /** Average interval between sleep cycles, in days. Used by 'adaptive' and 'session' modes. */ avgSessionIntervalDays?: number; /** Total sleep cycles completed. Used by consolidation tracking. */ sleepCount?: number; } /** * Calculate current strength at a given time. * strength(t) = base_strength * decay * retrieval_boost * emotional_multiplier * * Decay basis modes: * - clock: classic wall-clock decay (daysSince / halfLife) * - session: decay by sleep cycles instead of days (sessionsSince / halfLife) * - adaptive: wall-clock decay with half-life scaled by session frequency * * Pinned memories always return 1.0 (no decay). */ export declare function calculateStrength(entry: MemoryEntry, now?: Date, options?: DecayOptions): number; /** * Derive half-life based on signals, as per PLAN.md table. */ export declare function deriveHalfLife(base: number, entry: Partial): number; /** * Apply outcome feedback to a memory entry. * * Increments outcome_positive or outcome_negative counters. * The reward factor in calculateStrength() uses these counts to * continuously modulate the effective half-life: * reward_ratio = (pos - neg) / (pos + neg + 1) * reward_factor = 1 + 0.5 * reward_ratio // range (0.5, 1.5) * effective_hl = half_life_days * reward_factor * * No fixed half-life delta. Decay rate adjusts proportionally to * cumulative reward signal, inspired by R-STDP in spiking networks. */ export declare function applyOutcome(entry: MemoryEntry, good: boolean): MemoryEntry; /** * Generate a random memory ID using crypto.randomUUID(). */ export declare function generateId(prefix?: string): string; export interface ConfidenceFacets { tier: ConfidenceLevel; agedOut: boolean; } export declare function confidenceFacets(entry: MemoryEntry, now?: Date): ConfidenceFacets; export declare function confidenceLabel(entry: MemoryEntry, now?: Date): { text: string; warn: boolean; }; /** * Resolve the effective confidence for a memory entry. * If the entry has not been retrieved in 30+ days and is not 'verified', * returns 'stale'. Otherwise returns the stored confidence value. */ export declare function resolveConfidence(entry: MemoryEntry, now?: Date): ConfidenceLevel; /** * Create a new memory entry with defaults. */ export declare function createMemory(content: string, options?: { layer?: Layer; tags?: string[]; emotional_valence?: EmotionalValence; pinned?: boolean; schema_fit?: number; source?: string; confidence?: ConfidenceLevel; baseHalfLifeDays?: number; trace_outcome?: TraceOutcome; source_session_id?: string | null; valid_from?: string; extracted_from?: string; dag_level?: number; dag_parent_id?: string; kind?: MemoryKind; scope?: string | null; owner?: string | null; artifact_ref?: string | null; tenantId?: string; }): MemoryEntry; /** * Compute how well new content fits existing knowledge patterns. * Returns 0..1 where: * >0.7 = high fit (consistent with existing knowledge, consolidates faster) * 0.3-0.7 = moderate fit * <0.3 = novel (doesn't match existing patterns, decays faster if unused) * * Uses tag overlap (always available) weighted by how common each tag is. * Rare shared tags signal stronger schema fit than common ones. */ export declare function computeSchemaFit(content: string, tags: string[], existingEntries: MemoryEntry[]): number; //# sourceMappingURL=memory.d.ts.map