oracular 0.0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util.ts","../src/config.ts","../src/math.ts","../src/intent.ts","../src/learning.ts","../src/episode.ts","../src/version.ts","../src/state.ts","../src/explain.ts","../src/observe.ts","../src/predict.ts","../src/serialize.ts"],"names":["record","typeError"],"mappings":"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","file":"index.cjs","sourcesContent":["export function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** Own-property check, so event names like \"constructor\" never hit Object.prototype. */\nexport function hasOwn(record: object, key: string): boolean {\n // biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn is ES2022; this library targets ES2020 runtimes.\n return Object.prototype.hasOwnProperty.call(record, key);\n}\n\nexport function getOwn<T>(record: Record<string, T>, key: string): T | undefined {\n return hasOwn(record, key) ? record[key] : undefined;\n}\n\nexport function mapRecord<T, U>(\n record: Record<string, T>,\n fn: (value: T, key: string) => U,\n): Record<string, U> {\n const out: Record<string, U> = {};\n for (const [key, value] of Object.entries(record)) {\n out[key] = fn(value, key);\n }\n return out;\n}\n","import type { EngineConfig, IntentConfig, LearningConfig, RevealOptions } from './types';\nimport { isRecord } from './util';\n\nexport const DEFAULT_THRESHOLD = 0.5;\nexport const DEFAULT_Z = 1.28;\nexport const DEFAULT_GAIN = 2;\nexport const DEFAULT_PRIOR_EPISODES = 10;\n\nconst CONFIG_KEYS = ['intents', 'episodeTimeout', 'learning', 'reveal'] as const;\nconst INTENT_KEYS = [\n 'prior',\n 'priorStrength',\n 'halfLife',\n 'signals',\n 'outcome',\n 'resetOnOutcome',\n 'reveal',\n] as const;\nconst REVEAL_KEYS = ['threshold', 'z', 'cooldown'] as const;\nconst LEARNING_KEYS = ['halfLife', 'gain', 'priorEpisodes'] as const;\n\nfunction typeError(path: string, message: string): never {\n throw new TypeError(`oracular: ${path} ${message}`);\n}\n\nfunction rangeError(path: string, message: string): never {\n throw new RangeError(`oracular: ${path} ${message}`);\n}\n\nfunction expectRecord(value: unknown, path: string): Record<string, unknown> {\n if (!isRecord(value)) typeError(path, 'must be a plain object');\n return value;\n}\n\nfunction expectKeys(\n record: Record<string, unknown>,\n allowed: readonly string[],\n path: string,\n): void {\n for (const key of Object.keys(record)) {\n if (!allowed.includes(key)) {\n typeError(\n `${path}.${key}`,\n `is not a recognised option (expected one of: ${allowed.join(', ')})`,\n );\n }\n }\n}\n\nfunction expectNumber(value: unknown, path: string): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n typeError(path, 'must be a finite number');\n return value;\n}\n\nfunction expectPositive(value: unknown, path: string): number {\n const n = expectNumber(value, path);\n if (n <= 0) rangeError(path, 'must be greater than 0');\n return n;\n}\n\nfunction expectNonNegative(value: unknown, path: string): number {\n const n = expectNumber(value, path);\n if (n < 0) rangeError(path, 'must be 0 or greater');\n return n;\n}\n\nfunction expectOpenUnit(value: unknown, path: string): number {\n const n = expectNumber(value, path);\n if (n <= 0 || n >= 1) rangeError(path, 'must be strictly between 0 and 1');\n return n;\n}\n\nfunction expectClosedUnit(value: unknown, path: string): number {\n const n = expectNumber(value, path);\n if (n < 0 || n > 1) rangeError(path, 'must be between 0 and 1');\n return n;\n}\n\nexport function expectName(value: unknown, path: string): string {\n if (typeof value !== 'string' || value.length === 0)\n typeError(path, 'must be a non-empty string');\n return value;\n}\n\n/** Keys of user-named records must be usable as own properties of a plain object. */\nfunction expectKeyName(name: string, path: string): string {\n if (name.length === 0) typeError(path, 'names must be non-empty strings');\n if (name === '__proto__') typeError(path, 'must not use the reserved name \"__proto__\"');\n return name;\n}\n\n/** Validates reveal options and returns a copy with only the defined keys. */\nexport function normalizeRevealOptions(value: unknown, path: string): RevealOptions {\n const record = expectRecord(value, path);\n expectKeys(record, REVEAL_KEYS, path);\n const out: RevealOptions = {};\n if (record.threshold !== undefined) {\n out.threshold = expectClosedUnit(record.threshold, `${path}.threshold`);\n }\n if (record.z !== undefined) out.z = expectNonNegative(record.z, `${path}.z`);\n if (record.cooldown !== undefined) {\n out.cooldown = expectNonNegative(record.cooldown, `${path}.cooldown`);\n }\n return out;\n}\n\nfunction normalizeLearning(value: unknown, path: string): LearningConfig {\n const record = expectRecord(value, path);\n expectKeys(record, LEARNING_KEYS, path);\n const out: LearningConfig = {};\n if (record.halfLife !== undefined)\n out.halfLife = expectPositive(record.halfLife, `${path}.halfLife`);\n if (record.gain !== undefined) out.gain = expectPositive(record.gain, `${path}.gain`);\n if (record.priorEpisodes !== undefined) {\n out.priorEpisodes = expectPositive(record.priorEpisodes, `${path}.priorEpisodes`);\n }\n return out;\n}\n\nfunction normalizeIntent(value: unknown, path: string): IntentConfig {\n const record = expectRecord(value, path);\n expectKeys(record, INTENT_KEYS, path);\n const signalsRecord = expectRecord(record.signals, `${path}.signals`);\n const signals: Record<string, number> = {};\n for (const [name, weight] of Object.entries(signalsRecord)) {\n expectKeyName(name, `${path}.signals`);\n const w = expectNumber(weight, `${path}.signals[${JSON.stringify(name)}]`);\n if (w === 0) {\n rangeError(\n `${path}.signals[${JSON.stringify(name)}]`,\n 'must be non-zero (positive is evidence for the intent, negative is evidence against)',\n );\n }\n signals[name] = w;\n }\n const out: IntentConfig = {\n prior: expectOpenUnit(record.prior, `${path}.prior`),\n priorStrength: expectPositive(record.priorStrength, `${path}.priorStrength`),\n halfLife: expectPositive(record.halfLife, `${path}.halfLife`),\n signals,\n };\n if (record.outcome !== undefined) out.outcome = expectName(record.outcome, `${path}.outcome`);\n if (record.resetOnOutcome !== undefined) {\n if (typeof record.resetOnOutcome !== 'boolean') {\n typeError(`${path}.resetOnOutcome`, 'must be a boolean');\n }\n out.resetOnOutcome = record.resetOnOutcome;\n }\n if (record.reveal !== undefined)\n out.reveal = normalizeRevealOptions(record.reveal, `${path}.reveal`);\n return out;\n}\n\n/**\n * Validates a config and returns a fresh copy holding only the defined, recognised keys,\n * so that it survives a JSON round trip unchanged.\n */\nexport function normalizeConfig(value: unknown): EngineConfig {\n const record = expectRecord(value, 'config');\n expectKeys(record, CONFIG_KEYS, 'config');\n const intentsRecord = expectRecord(record.intents, 'config.intents');\n const names = Object.keys(intentsRecord);\n if (names.length === 0) typeError('config.intents', 'must declare at least one intent');\n const intents: Record<string, IntentConfig> = {};\n for (const name of names) {\n expectKeyName(name, 'config.intents');\n intents[name] = normalizeIntent(intentsRecord[name], `config.intents[${JSON.stringify(name)}]`);\n }\n const out: EngineConfig = { intents };\n if (record.episodeTimeout !== undefined) {\n out.episodeTimeout = expectPositive(record.episodeTimeout, 'config.episodeTimeout');\n }\n if (record.learning !== undefined)\n out.learning = normalizeLearning(record.learning, 'config.learning');\n if (record.reveal !== undefined)\n out.reveal = normalizeRevealOptions(record.reveal, 'config.reveal');\n return out;\n}\n\nexport interface ResolvedReveal {\n threshold: number;\n z: number;\n cooldown: number;\n}\n\nexport function resolveReveal(\n config: EngineConfig,\n intent: IntentConfig,\n opts: RevealOptions | undefined,\n): ResolvedReveal {\n const o = opts === undefined ? {} : normalizeRevealOptions(opts, 'options');\n return {\n threshold:\n o.threshold ?? intent.reveal?.threshold ?? config.reveal?.threshold ?? DEFAULT_THRESHOLD,\n z: o.z ?? intent.reveal?.z ?? config.reveal?.z ?? DEFAULT_Z,\n cooldown: o.cooldown ?? intent.reveal?.cooldown ?? config.reveal?.cooldown ?? intent.halfLife,\n };\n}\n\nexport interface ResolvedLearning {\n /** null means learned counts never decay. */\n halfLife: number | null;\n gain: number;\n priorEpisodes: number;\n}\n\nexport function resolveLearning(config: EngineConfig): ResolvedLearning {\n return {\n halfLife: config.learning?.halfLife ?? null,\n gain: config.learning?.gain ?? DEFAULT_GAIN,\n priorEpisodes: config.learning?.priorEpisodes ?? DEFAULT_PRIOR_EPISODES,\n };\n}\n\nexport function expectTimestamp(value: unknown, path: string): number {\n return expectNumber(value, path);\n}\n\nexport function validateEvent(event: unknown): { name: string; at: number } {\n if (!isRecord(event)) typeError('event', 'must be an object like { name, at }');\n const name = expectName(event.name, 'event.name');\n const at = expectTimestamp(event.at, 'event.at');\n if (event.props !== undefined && !isRecord(event.props)) {\n typeError('event.props', 'must be a plain object when present');\n }\n return { name, at };\n}\n","/** 2^(−elapsed / halfLife). Returns 1 for zero, negative or non-finite elapsed time. */\nexport function decayFactor(elapsed: number, halfLife: number): number {\n if (!(elapsed > 0) || !Number.isFinite(elapsed)) return 1;\n return 2 ** (-elapsed / halfLife);\n}\n\nexport function betaMean(alpha: number, beta: number): number {\n return alpha / (alpha + beta);\n}\n\nexport function betaVariance(alpha: number, beta: number): number {\n const n = alpha + beta;\n return (alpha * beta) / (n * n * (n + 1));\n}\n\nexport function betaSd(alpha: number, beta: number): number {\n return Math.sqrt(betaVariance(alpha, beta));\n}\n\nexport function clamp01(x: number): number {\n if (x < 0) return 0;\n if (x > 1) return 1;\n return x;\n}\n\n/** Laplace-smoothed rate: (successes + 1) / (total + 2). Always strictly inside (0, 1). */\nexport function laplace(successes: number, total: number): number {\n return (successes + 1) / (total + 2);\n}\n\n/**\n * How far a rate `q` sits from a base rate `b`, scaled to [−1, 1]:\n * +1 when q is 1, 0 when q equals b, −1 when q is 0.\n */\nexport function lift(q: number, b: number): number {\n if (q >= b) return b >= 1 ? 0 : (q - b) / (1 - b);\n return b <= 0 ? 0 : (q - b) / b;\n}\n","import { decayFactor } from './math';\nimport type { IntentConfig, IntentState, SignalContribution } from './types';\nimport { getOwn, mapRecord } from './util';\n\nexport function priorCounts(intent: IntentConfig): { alpha0: number; beta0: number } {\n return {\n alpha0: intent.prior * intent.priorStrength,\n beta0: (1 - intent.prior) * intent.priorStrength,\n };\n}\n\nexport function initIntent(intent: IntentConfig): IntentState {\n const { alpha0, beta0 } = priorCounts(intent);\n return {\n alpha: alpha0,\n beta: beta0,\n updatedAt: null,\n contributions: {},\n learned: {},\n episodes: { hits: 0, misses: 0 },\n learnedAt: null,\n lastRevealedAt: null,\n lastOutcomeAt: null,\n revealCount: 0,\n };\n}\n\n/**\n * The one place α and β are written: always the prior plus the sum of contributions,\n * so the invariant α = α0 + Σ contributions.alpha (and β likewise) holds by construction.\n */\nexport function withContributions(\n state: IntentState,\n intent: IntentConfig,\n contributions: Record<string, SignalContribution>,\n): IntentState {\n const { alpha0, beta0 } = priorCounts(intent);\n let alpha = alpha0;\n let beta = beta0;\n for (const c of Object.values(contributions)) {\n alpha += c.alpha;\n beta += c.beta;\n }\n return { ...state, alpha, beta, contributions };\n}\n\n/** Decays evidence toward the prior by 2^(−Δt / halfLife) and moves the anchor to `at`. */\nexport function decayIntent(state: IntentState, intent: IntentConfig, at: number): IntentState {\n if (state.updatedAt === null) return { ...state, updatedAt: at };\n const d = decayFactor(at - state.updatedAt, intent.halfLife);\n const updatedAt = Math.max(state.updatedAt, at);\n if (d === 1) return updatedAt === state.updatedAt ? state : { ...state, updatedAt };\n const contributions = mapRecord(state.contributions, (c) => ({\n alpha: c.alpha * d,\n beta: c.beta * d,\n }));\n return withContributions({ ...state, updatedAt }, intent, contributions);\n}\n\n/** Adds `weight` pseudo-counts for the intent (positive → α) or against it (negative → β). */\nexport function applySignal(\n state: IntentState,\n intent: IntentConfig,\n signal: string,\n weight: number,\n): IntentState {\n const prev = getOwn(state.contributions, signal) ?? { alpha: 0, beta: 0 };\n const next: SignalContribution =\n weight >= 0\n ? { alpha: prev.alpha + weight, beta: prev.beta }\n : { alpha: prev.alpha, beta: prev.beta - weight };\n return withContributions(state, intent, { ...state.contributions, [signal]: next });\n}\n\n/** Drops all evidence; α and β return to the prior. Learned data is untouched. */\nexport function resetToPrior(state: IntentState, intent: IntentConfig): IntentState {\n return withContributions(state, intent, {});\n}\n","import type { ResolvedLearning } from './config';\nimport { decayFactor, laplace, lift } from './math';\nimport type { IntentConfig, IntentState, SignalRecord } from './types';\nimport { getOwn, mapRecord } from './util';\n\nconst NONE: SignalRecord = { hits: 0, misses: 0 };\n\n/** Decays learned counts toward 0 by 2^(−Δt / learning.halfLife) and moves the anchor to `at`. */\nexport function decayLearned(\n state: IntentState,\n learning: ResolvedLearning,\n at: number,\n): IntentState {\n if (state.learnedAt === null) return { ...state, learnedAt: at };\n const learnedAt = Math.max(state.learnedAt, at);\n const d = learning.halfLife === null ? 1 : decayFactor(at - state.learnedAt, learning.halfLife);\n if (d === 1) return learnedAt === state.learnedAt ? state : { ...state, learnedAt };\n return {\n ...state,\n learnedAt,\n learned: mapRecord(state.learned, (r) => ({ hits: r.hits * d, misses: r.misses * d })),\n episodes: { hits: state.episodes.hits * d, misses: state.episodes.misses * d },\n };\n}\n\nexport interface SignalWeights {\n initial: number;\n learned: number;\n effective: number;\n hits: number;\n misses: number;\n}\n\n/**\n * initial: the developer's weight w0.\n * learned: gain · |w0| · lift(q, b), where q is the signal's Laplace-smoothed hit rate and\n * b the intent's Laplace-smoothed base rate (episodes ending with the outcome).\n * effective: (k · w0 + n · learned) / (k + n), with k = learning.priorEpisodes and n = hits + misses.\n */\nexport function signalWeights(\n state: IntentState,\n intent: IntentConfig,\n learning: ResolvedLearning,\n signal: string,\n): SignalWeights {\n const initial = getOwn(intent.signals, signal);\n if (initial === undefined) {\n throw new RangeError(`oracular: ${JSON.stringify(signal)} is not a signal of this intent`);\n }\n const record = getOwn(state.learned, signal) ?? NONE;\n const n = record.hits + record.misses;\n const q = laplace(record.hits, n);\n const b = laplace(state.episodes.hits, state.episodes.hits + state.episodes.misses);\n // Nothing has been learned about a signal until it has been seen in a completed episode.\n const learned = n === 0 ? 0 : learning.gain * Math.abs(initial) * lift(q, b);\n const k = learning.priorEpisodes;\n const effective = (k * initial + n * learned) / (k + n);\n return { initial, learned, effective, hits: record.hits, misses: record.misses };\n}\n\n/** Gives each listed signal a hit (outcome arrived) or a miss (episode ended without it), and counts the episode. */\nexport function recordEpisode(\n state: IntentState,\n signals: readonly string[],\n outcome: boolean,\n): IntentState {\n const learned = { ...state.learned };\n for (const signal of signals) {\n const r = getOwn(learned, signal) ?? NONE;\n learned[signal] = outcome\n ? { hits: r.hits + 1, misses: r.misses }\n : { hits: r.hits, misses: r.misses + 1 };\n }\n const episodes = outcome\n ? { hits: state.episodes.hits + 1, misses: state.episodes.misses }\n : { hits: state.episodes.hits, misses: state.episodes.misses + 1 };\n return { ...state, learned, episodes };\n}\n","import type { EpisodeIntentRecord, EpisodeState } from './types';\nimport { getOwn } from './util';\n\nexport function emptyEpisode(): EpisodeState {\n return { startedAt: null, lastEventAt: null, intents: {} };\n}\n\n/** If the episode has been idle longer than `timeout` by `at`, the moment it timed out; otherwise null. */\nexport function timedOutAt(\n episode: EpisodeState,\n timeout: number | undefined,\n at: number,\n): number | null {\n if (timeout === undefined || episode.lastEventAt === null) return null;\n return at - episode.lastEventAt > timeout ? episode.lastEventAt + timeout : null;\n}\n\n/** Opens the episode if needed and records activity at `at`. */\nexport function extendEpisode(episode: EpisodeState, at: number): EpisodeState {\n return {\n startedAt: episode.startedAt ?? at,\n lastEventAt: episode.lastEventAt === null ? at : Math.max(episode.lastEventAt, at),\n intents: episode.intents,\n };\n}\n\nexport function episodeRecord(episode: EpisodeState, intent: string): EpisodeIntentRecord {\n return getOwn(episode.intents, intent) ?? { seen: [], resolved: false };\n}\n\nexport function markSeen(episode: EpisodeState, intent: string, signal: string): EpisodeState {\n const record = episodeRecord(episode, intent);\n if (record.seen.includes(signal)) return episode;\n return {\n ...episode,\n intents: { ...episode.intents, [intent]: { ...record, seen: [...record.seen, signal] } },\n };\n}\n\nexport function markResolved(episode: EpisodeState, intent: string): EpisodeState {\n return { ...episode, intents: { ...episode.intents, [intent]: { seen: [], resolved: true } } };\n}\n","/**\n * Version of the serialized state shape, stored in every state as `state.version`.\n * {@link deserialize} compares it with the stored value and calls your `migrate` function when\n * they differ. Bumped only when `EngineState` changes incompatibly.\n *\n * @example\n * ```ts\n * import { STATE_VERSION, deserialize } from 'oracular';\n *\n * const state = deserialize(saved, {\n * migrate: (stored, version) => {\n * if (version === 0) return upgradeFromV0(stored);\n * throw new Error(`cannot migrate version ${version} to ${STATE_VERSION}`);\n * },\n * });\n * ```\n */\nexport const STATE_VERSION = 1 as const;\n","import { normalizeConfig } from './config';\nimport { emptyEpisode } from './episode';\nimport { initIntent } from './intent';\nimport type { EngineConfig, EngineState, IntentConfig, IntentState } from './types';\nimport { getOwn, isRecord } from './util';\nimport { STATE_VERSION } from './version';\n\n/**\n * Builds a fresh engine state from a config. Every intent starts at its prior and no episode is\n * open. The config is validated and a normalized copy is stored inside the state, so a persisted\n * state remembers the weights it was built with; pass your current config to {@link deserialize}\n * when loading.\n *\n * @param config - Intents with their priors and signals, plus engine-wide options. Validated\n * strictly: unknown keys, non-finite numbers and out-of-range values throw.\n * @returns A new, JSON-serializable state. `config` is not mutated.\n * @throws `TypeError` when a value has the wrong type or a key is not recognised.\n * @throws `RangeError` when a number is out of range, for example a `prior` outside (0, 1) or a\n * signal weight of 0.\n * @example\n * ```ts\n * const state = createState({\n * intents: {\n * upgrade: {\n * prior: 0.05,\n * priorStrength: 10,\n * halfLife: 7 * 24 * 60 * 60 * 1000,\n * signals: { 'pricing.viewed': 3, 'limit.hit': 2, 'plan.downgraded': -4 },\n * outcome: 'upgrade.completed',\n * },\n * },\n * });\n * ```\n */\nexport function createState(config: EngineConfig): EngineState {\n const normalized = normalizeConfig(config);\n const intents: Record<string, IntentState> = {};\n for (const [name, intent] of Object.entries(normalized.intents)) {\n intents[name] = initIntent(intent);\n }\n return { version: STATE_VERSION, config: normalized, intents, episode: emptyEpisode() };\n}\n\nexport function requireState(state: unknown): EngineState {\n if (\n !isRecord(state) ||\n state.version !== STATE_VERSION ||\n !isRecord(state.config) ||\n !isRecord(state.intents) ||\n !isRecord(state.episode)\n ) {\n throw new TypeError('oracular: state must be an EngineState from createState or deserialize');\n }\n return state as unknown as EngineState;\n}\n\nexport function requireIntentConfig(state: EngineState, name: string): IntentConfig {\n const intent = getOwn(state.config.intents, name);\n if (intent === undefined) {\n throw new RangeError(`oracular: unknown intent ${JSON.stringify(name)}`);\n }\n return intent;\n}\n\n/** The stored intent state, or a fresh prior if the intent was added to the config after the state was created. */\nexport function getIntentState(\n state: EngineState,\n name: string,\n intent: IntentConfig,\n): IntentState {\n return getOwn(state.intents, name) ?? initIntent(intent);\n}\n","import { expectName, expectTimestamp, resolveLearning } from './config';\nimport { decayIntent } from './intent';\nimport { decayLearned, signalWeights } from './learning';\nimport { betaMean } from './math';\nimport { getIntentState, requireIntentConfig, requireState } from './state';\nimport type { EngineState, SignalExplanation } from './types';\nimport { getOwn } from './util';\n\n/**\n * Why the intent's p is what it is at time `at`: one entry per declared signal, the ones that\n * matter most first. `contribution` is the signed pseudo-count the signal currently holds after\n * decay, `impact` is how much p would change if that contribution were removed, and the three\n * weights show what the developer said, what this user's history says, and what the next\n * occurrence will actually add. Read-only.\n *\n * @param state - The current state. Not mutated.\n * @param intent - Name of an intent declared in the config.\n * @param at - The time to evaluate at; contributions and learned counts are decayed to it.\n * @returns One {@link SignalExplanation} per declared signal, sorted by |impact| descending,\n * then by name. Empty for an intent that declares no signals.\n * @throws `RangeError` when `intent` is not declared in the config.\n * @throws `TypeError` when `state` is not an engine state or `at` is not finite.\n * @example\n * ```ts\n * for (const row of explain(state, 'upgrade', Date.now())) {\n * console.log(row.signal, row.impact.toFixed(2), row.effectiveWeight.toFixed(2));\n * }\n * ```\n */\nexport function explain(state: EngineState, intent: string, at: number): SignalExplanation[] {\n const s = requireState(state);\n const name = expectName(intent, 'intent');\n const t = expectTimestamp(at, 'at');\n const config = requireIntentConfig(s, name);\n const learning = resolveLearning(s.config);\n\n let is = decayIntent(getIntentState(s, name, config), config, t);\n is = decayLearned(is, learning, t);\n const p = betaMean(is.alpha, is.beta);\n\n const out: SignalExplanation[] = [];\n for (const signal of Object.keys(config.signals)) {\n const c = getOwn(is.contributions, signal) ?? { alpha: 0, beta: 0 };\n const w = signalWeights(is, config, learning, signal);\n const without = betaMean(is.alpha - c.alpha, is.beta - c.beta);\n out.push({\n signal,\n contribution: c.alpha - c.beta,\n impact: p - without,\n initialWeight: w.initial,\n learnedWeight: w.learned,\n effectiveWeight: w.effective,\n hits: w.hits,\n misses: w.misses,\n });\n }\n\n out.sort((a, b) => Math.abs(b.impact) - Math.abs(a.impact) || compare(a.signal, b.signal));\n return out;\n}\n\nfunction compare(a: string, b: string): number {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n","import { expectTimestamp, resolveLearning, validateEvent } from './config';\nimport {\n emptyEpisode,\n episodeRecord,\n extendEpisode,\n markResolved,\n markSeen,\n timedOutAt,\n} from './episode';\nimport { applySignal, decayIntent, resetToPrior } from './intent';\nimport { decayLearned, recordEpisode, signalWeights } from './learning';\nimport { getIntentState, requireState } from './state';\nimport type { EngineState, IntentEvent, IntentState } from './types';\nimport { hasOwn } from './util';\n\n/**\n * Feeds one event to the engine.\n *\n * For every intent the event is a signal of: decay its evidence to `event.at`, then add the\n * signal's effective weight to α (positive) or β (negative) and remember the signal for this\n * episode. For every intent the event is the outcome of: give the remembered signals a hit,\n * count the episode toward the base rate, record `lastOutcomeAt`, and (by default) reset the\n * evidence to the prior. Any event opens or extends the current episode; an idle gap longer\n * than `config.episodeTimeout` closes the previous episode first.\n *\n * @param state - The current state. Not mutated.\n * @param event - `name` is matched against every intent's signals and outcome; `at` is the\n * event's timestamp, in the same unit as your durations; `props` are accepted and ignored,\n * and never stored.\n * @returns The next state. Intents the event does not touch keep their stored numbers; their\n * decay is applied when they are next read or updated.\n * @throws `TypeError` when `state` is not an engine state, or the event has no non-empty\n * `name`, no finite `at`, or a `props` that is not a plain object.\n * @example\n * ```ts\n * let state = createState(config);\n * state = observe(state, { name: 'pricing.viewed', at: Date.now() });\n * state = observe(state, { name: 'upgrade.completed', at: Date.now() }); // outcome\n * ```\n */\nexport function observe(state: EngineState, event: IntentEvent): EngineState {\n let s = requireState(state);\n const { name, at } = validateEvent(event);\n\n const closeAt = timedOutAt(s.episode, s.config.episodeTimeout, at);\n if (closeAt !== null) s = closeEpisode(s, closeAt);\n\n const learning = resolveLearning(s.config);\n const intents: Record<string, IntentState> = { ...s.intents };\n let episode = extendEpisode(s.episode, at);\n\n for (const [intentName, intent] of Object.entries(s.config.intents)) {\n const isSignal = hasOwn(intent.signals, name);\n const isOutcome = intent.outcome === name;\n if (!isSignal && !isOutcome) continue;\n\n let is = getIntentState(s, intentName, intent);\n is = decayIntent(is, intent, at);\n is = decayLearned(is, learning, at);\n\n if (isSignal) {\n const { effective } = signalWeights(is, intent, learning, name);\n is = applySignal(is, intent, name, effective);\n // An event that is also the intent's outcome must not learn from itself.\n if (!isOutcome) episode = markSeen(episode, intentName, name);\n }\n\n if (isOutcome) {\n is = recordEpisode(is, episodeRecord(episode, intentName).seen, true);\n episode = markResolved(episode, intentName);\n is = { ...is, lastOutcomeAt: at };\n if (intent.resetOnOutcome ?? true) is = resetToPrior(is, intent);\n }\n\n intents[intentName] = is;\n }\n\n return { ...s, intents, episode };\n}\n\n/**\n * Ends the current episode at `at`. Every intent whose outcome did not arrive gives a miss to\n * each signal seen this episode and counts the episode toward its base rate. Call it when a\n * session ends: tab closed, app backgrounded, user signed out.\n *\n * @param state - The current state. Not mutated.\n * @param at - When the episode ended. If it had already timed out under\n * `config.episodeTimeout`, it is closed at the moment it timed out instead.\n * @returns The next state with no open episode, or the very same state object when no episode\n * was open.\n * @throws `TypeError` when `state` is not an engine state or `at` is not a finite number.\n * @example\n * ```ts\n * window.addEventListener('pagehide', () => {\n * state = endEpisode(state, Date.now());\n * localStorage.setItem('oracular', serialize(state));\n * });\n * ```\n */\nexport function endEpisode(state: EngineState, at: number): EngineState {\n const s = requireState(state);\n const t = expectTimestamp(at, 'at');\n if (s.episode.startedAt === null) return s;\n const closeAt = timedOutAt(s.episode, s.config.episodeTimeout, t);\n return closeEpisode(s, closeAt ?? t);\n}\n\nfunction closeEpisode(state: EngineState, at: number): EngineState {\n const learning = resolveLearning(state.config);\n const intents: Record<string, IntentState> = { ...state.intents };\n for (const [intentName, intent] of Object.entries(state.config.intents)) {\n const record = episodeRecord(state.episode, intentName);\n if (record.resolved) continue;\n let is = getIntentState(state, intentName, intent);\n is = decayLearned(is, learning, at);\n is = recordEpisode(is, record.seen, false);\n intents[intentName] = is;\n }\n return { ...state, intents, episode: emptyEpisode() };\n}\n","import { expectName, expectTimestamp, resolveReveal } from './config';\nimport { decayIntent } from './intent';\nimport { betaMean, betaSd, clamp01 } from './math';\nimport { getIntentState, requireIntentConfig, requireState } from './state';\nimport type { EngineState, Prediction, RevealOptions } from './types';\n\n/**\n * The intent's current estimate at time `at`: the Beta mean p, its standard deviation, the\n * lower confidence bound that {@link shouldReveal} tests, and the cooldown status. Read-only:\n * the stored state is decayed to `at` for the reading but not changed.\n *\n * @param state - The current state. Not mutated.\n * @param intent - Name of an intent declared in the config.\n * @param at - The time to evaluate at, in the same unit as your durations.\n * @param opts - Overrides for `threshold`, `z` and `cooldown`, resolved as call → intent →\n * engine → defaults. `z` sets `lower`; `cooldown` sets `revealed` and `suppressedUntil`;\n * `threshold` only matters to {@link shouldReveal}.\n * @returns The prediction. `p`, `sd`, `lower` and `confidence` are in [0, 1]; `evidence` is\n * α + β.\n * @throws `RangeError` when `intent` is not declared in the config.\n * @throws `TypeError` when `state` is not an engine state, `at` is not finite, or `opts` is\n * malformed.\n * @example\n * ```ts\n * const { p, lower, suppressedUntil } = predict(state, 'upgrade', Date.now());\n * if (suppressedUntil !== null) scheduleNextCheck(suppressedUntil);\n * ```\n */\nexport function predict(\n state: EngineState,\n intent: string,\n at: number,\n opts?: RevealOptions,\n): Prediction {\n const s = requireState(state);\n const name = expectName(intent, 'intent');\n const t = expectTimestamp(at, 'at');\n const config = requireIntentConfig(s, name);\n const reveal = resolveReveal(s.config, config, opts);\n const is = decayIntent(getIntentState(s, name, config), config, t);\n\n const p = betaMean(is.alpha, is.beta);\n const sd = betaSd(is.alpha, is.beta);\n const revealEnd = cooldownEnd(is.lastRevealedAt, reveal.cooldown, t);\n const outcomeEnd = cooldownEnd(is.lastOutcomeAt, reveal.cooldown, t);\n const suppressedUntil =\n revealEnd === null\n ? outcomeEnd\n : outcomeEnd === null\n ? revealEnd\n : Math.max(revealEnd, outcomeEnd);\n\n return {\n intent: name,\n p,\n sd,\n lower: clamp01(p - reveal.z * sd),\n confidence: clamp01(1 - 2 * sd),\n evidence: is.alpha + is.beta,\n revealed: revealEnd !== null,\n revealedAt: is.lastRevealedAt,\n suppressedUntil,\n };\n}\n\n/** The end of a cooldown that started at `since`, if it is still running at `at`. */\nfunction cooldownEnd(since: number | null, cooldown: number, at: number): number | null {\n if (since === null) return null;\n const end = since + cooldown;\n return at < end ? end : null;\n}\n\n/**\n * Whether to act on the intent now. True when p ≥ `threshold`, the lower confidence bound\n * p − z·sd also clears `threshold`, and no cooldown is running: neither the one started by\n * {@link markRevealed} nor the one started by the intent's outcome event.\n *\n * @param state - The current state. Not mutated.\n * @param intent - Name of an intent declared in the config.\n * @param at - The time to evaluate at.\n * @param opts - Overrides for `threshold` (default 0.5), `z` (default 1.28, a one-sided 90%\n * bound) and `cooldown` (default: the intent's `halfLife`).\n * @returns True when the app should show, offer or reveal whatever this intent gates.\n * @throws `RangeError` when `intent` is not declared in the config.\n * @throws `TypeError` when `state` is not an engine state, `at` is not finite, or `opts` is\n * malformed.\n * @example\n * ```ts\n * if (shouldReveal(state, 'upgrade', Date.now())) {\n * showUpgradeOffer();\n * state = markRevealed(state, 'upgrade', Date.now());\n * }\n * ```\n */\nexport function shouldReveal(\n state: EngineState,\n intent: string,\n at: number,\n opts?: RevealOptions,\n): boolean {\n const s = requireState(state);\n const name = expectName(intent, 'intent');\n const config = requireIntentConfig(s, name);\n const { threshold } = resolveReveal(s.config, config, opts);\n const prediction = predict(s, name, at, opts);\n return (\n prediction.suppressedUntil === null &&\n prediction.p >= threshold &&\n prediction.lower >= threshold\n );\n}\n\n/**\n * Records that the app acted on the intent at `at`. Starts the reveal cooldown, during which\n * `predict().revealed` is true and {@link shouldReveal} is false, and increments\n * `revealCount`. Evidence and learned data are untouched.\n *\n * @param state - The current state. Not mutated.\n * @param intent - Name of an intent declared in the config.\n * @param at - When the reveal happened.\n * @returns The next state.\n * @throws `RangeError` when `intent` is not declared in the config.\n * @throws `TypeError` when `state` is not an engine state or `at` is not finite.\n * @example\n * ```ts\n * state = markRevealed(state, 'upgrade', Date.now());\n * predict(state, 'upgrade', Date.now()).revealed; // true until the cooldown ends\n * ```\n */\nexport function markRevealed(state: EngineState, intent: string, at: number): EngineState {\n const s = requireState(state);\n const name = expectName(intent, 'intent');\n const t = expectTimestamp(at, 'at');\n const config = requireIntentConfig(s, name);\n const is = getIntentState(s, name, config);\n return {\n ...s,\n intents: {\n ...s.intents,\n [name]: { ...is, lastRevealedAt: t, revealCount: is.revealCount + 1 },\n },\n };\n}\n","import { normalizeConfig } from './config';\nimport { initIntent, withContributions } from './intent';\nimport { requireState } from './state';\nimport type {\n DeserializeOptions,\n EngineConfig,\n EngineState,\n EpisodeIntentRecord,\n EpisodeState,\n IntentState,\n SignalContribution,\n SignalRecord,\n} from './types';\nimport { getOwn, hasOwn, isRecord } from './util';\nimport { STATE_VERSION } from './version';\n\n/**\n * The state as a JSON string, for the host app to persist wherever it likes. The state is plain\n * data, so this is `JSON.stringify` after a shape check. The output never contains event props.\n *\n * @param state - The state to persist.\n * @returns A JSON string that {@link deserialize} restores losslessly.\n * @throws `TypeError` when `state` is not an engine state.\n * @example\n * ```ts\n * localStorage.setItem('oracular', serialize(state));\n * ```\n */\nexport function serialize(state: EngineState): string {\n return JSON.stringify(requireState(state));\n}\n\n/**\n * Restores a state saved with {@link serialize}.\n *\n * Always pass the app's current `config`: it is validated and replaces the stored snapshot,\n * intents and signals no longer declared are dropped (α and β are recomputed from the kept\n * contributions), and newly declared intents start at their prior. Pass `migrate` to upgrade a\n * state saved by an older version of this library.\n *\n * @param json - A string produced by {@link serialize}.\n * @param opts - `config`: the app's current config. `migrate`: called as\n * `migrate(stored, version)` when the stored version differs from {@link STATE_VERSION};\n * it must return a state of the current version.\n * @returns A validated state that satisfies the engine's invariants.\n * @throws `SyntaxError` when `json` is not valid JSON.\n * @throws `TypeError` when the stored data, the result of `migrate`, `opts` or `opts.config`\n * has the wrong shape.\n * @throws `RangeError` when the stored version differs and no `migrate` is given, when\n * `migrate` does not return the current version, or when a stored number is out of range.\n * @example\n * ```ts\n * const saved = localStorage.getItem('oracular');\n * let state = saved ? deserialize(saved, { config }) : createState(config);\n * ```\n */\nexport function deserialize(json: string, opts: DeserializeOptions = {}): EngineState {\n if (typeof json !== 'string') throw new TypeError('oracular: deserialize expects a JSON string');\n if (!isRecord(opts)) throw new TypeError('oracular: options must be a plain object');\n for (const key of Object.keys(opts)) {\n if (key !== 'config' && key !== 'migrate') {\n throw new TypeError(\n `oracular: options.${key} is not a recognised option (expected one of: config, migrate)`,\n );\n }\n }\n if (opts.migrate !== undefined && typeof opts.migrate !== 'function') {\n throw new TypeError('oracular: options.migrate must be a function');\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(json);\n } catch (error) {\n throw new SyntaxError(`oracular: stored state is not valid JSON (${(error as Error).message})`);\n }\n if (!isRecord(parsed) || typeof parsed.version !== 'number') {\n throw new TypeError('oracular: stored state has no numeric version field');\n }\n\n let stored: unknown = parsed;\n if (parsed.version !== STATE_VERSION) {\n if (opts.migrate === undefined) {\n throw new RangeError(\n `oracular: stored state is version ${parsed.version} but this library reads version ${STATE_VERSION}; pass a migrate function`,\n );\n }\n stored = opts.migrate(parsed, parsed.version);\n }\n\n const raw = parseState(stored);\n const config = opts.config === undefined ? raw.config : normalizeConfig(opts.config);\n return reconcile(raw, config);\n}\n\n// ---- parsing: field types only; keys are reconciled against the config afterwards ----\n\nfunction typeError(path: string, message: string): never {\n throw new TypeError(`oracular: ${path} ${message}`);\n}\n\nfunction record(value: unknown, path: string): Record<string, unknown> {\n if (!isRecord(value)) typeError(path, 'must be a plain object');\n return value;\n}\n\nfunction finite(value: unknown, path: string): number {\n if (typeof value !== 'number' || !Number.isFinite(value))\n typeError(path, 'must be a finite number');\n return value;\n}\n\nfunction nonNegative(value: unknown, path: string): number {\n const n = finite(value, path);\n if (n < 0) throw new RangeError(`oracular: ${path} must be 0 or greater`);\n return n;\n}\n\nfunction nullOrFinite(value: unknown, path: string): number | null {\n return value === null ? null : finite(value, path);\n}\n\nfunction signalRecord(value: unknown, path: string): SignalRecord {\n const r = record(value, path);\n return {\n hits: nonNegative(r.hits, `${path}.hits`),\n misses: nonNegative(r.misses, `${path}.misses`),\n };\n}\n\nfunction contribution(value: unknown, path: string): SignalContribution {\n const r = record(value, path);\n return {\n alpha: nonNegative(r.alpha, `${path}.alpha`),\n beta: nonNegative(r.beta, `${path}.beta`),\n };\n}\n\nfunction mapOwn<T>(\n value: unknown,\n path: string,\n fn: (item: unknown, itemPath: string) => T,\n): Record<string, T> {\n const r = record(value, path);\n const out: Record<string, T> = {};\n for (const key of Object.keys(r)) {\n if (key === '__proto__') continue;\n out[key] = fn(r[key], `${path}[${JSON.stringify(key)}]`);\n }\n return out;\n}\n\nfunction intentState(value: unknown, path: string): IntentState {\n const r = record(value, path);\n const revealCount = finite(r.revealCount, `${path}.revealCount`);\n if (!Number.isInteger(revealCount) || revealCount < 0) {\n throw new RangeError(`oracular: ${path}.revealCount must be a non-negative integer`);\n }\n return {\n alpha: finite(r.alpha, `${path}.alpha`),\n beta: finite(r.beta, `${path}.beta`),\n updatedAt: nullOrFinite(r.updatedAt, `${path}.updatedAt`),\n contributions: mapOwn(r.contributions, `${path}.contributions`, contribution),\n learned: mapOwn(r.learned, `${path}.learned`, signalRecord),\n episodes: signalRecord(r.episodes, `${path}.episodes`),\n learnedAt: nullOrFinite(r.learnedAt, `${path}.learnedAt`),\n lastRevealedAt: nullOrFinite(r.lastRevealedAt, `${path}.lastRevealedAt`),\n lastOutcomeAt: nullOrFinite(r.lastOutcomeAt, `${path}.lastOutcomeAt`),\n revealCount,\n };\n}\n\nfunction episodeIntent(value: unknown, path: string): EpisodeIntentRecord {\n const r = record(value, path);\n if (!Array.isArray(r.seen) || !r.seen.every((s) => typeof s === 'string')) {\n typeError(`${path}.seen`, 'must be an array of strings');\n }\n if (typeof r.resolved !== 'boolean') typeError(`${path}.resolved`, 'must be a boolean');\n return { seen: r.seen as string[], resolved: r.resolved };\n}\n\nfunction episodeState(value: unknown, path: string): EpisodeState {\n const r = record(value, path);\n return {\n startedAt: nullOrFinite(r.startedAt, `${path}.startedAt`),\n lastEventAt: nullOrFinite(r.lastEventAt, `${path}.lastEventAt`),\n intents: mapOwn(r.intents, `${path}.intents`, episodeIntent),\n };\n}\n\nfunction parseState(value: unknown): EngineState {\n const r = record(value, 'state');\n if (r.version !== STATE_VERSION) {\n throw new RangeError(`oracular: state.version must be ${STATE_VERSION} after migration`);\n }\n return {\n version: STATE_VERSION,\n config: normalizeConfig(r.config),\n intents: mapOwn(r.intents, 'state.intents', intentState),\n episode: episodeState(r.episode, 'state.episode'),\n };\n}\n\n// ---- reconciling: make the state agree with a config ----\n\nfunction keep<T>(source: Record<string, T>, declared: Record<string, unknown>): Record<string, T> {\n const out: Record<string, T> = {};\n for (const [key, value] of Object.entries(source)) {\n if (hasOwn(declared, key)) out[key] = value;\n }\n return out;\n}\n\n/**\n * Drops intents and signals the config no longer declares, starts newly declared intents at\n * their prior, and recomputes α and β as the (possibly new) prior plus the kept contributions.\n */\nfunction reconcile(raw: EngineState, config: EngineConfig): EngineState {\n const intents: Record<string, IntentState> = {};\n const episodeIntents: Record<string, EpisodeIntentRecord> = {};\n\n for (const [name, intent] of Object.entries(config.intents)) {\n const stored = getOwn(raw.intents, name);\n if (stored === undefined) {\n intents[name] = initIntent(intent);\n } else {\n intents[name] = withContributions(\n { ...stored, learned: keep(stored.learned, intent.signals) },\n intent,\n keep(stored.contributions, intent.signals),\n );\n }\n const episode = getOwn(raw.episode.intents, name);\n if (episode !== undefined && raw.episode.startedAt !== null) {\n const seen = episode.seen.filter(\n (s, i) => hasOwn(intent.signals, s) && episode.seen.indexOf(s) === i,\n );\n episodeIntents[name] = { seen, resolved: episode.resolved };\n }\n }\n\n return {\n version: STATE_VERSION,\n config,\n intents,\n episode: {\n startedAt: raw.episode.startedAt,\n lastEventAt: raw.episode.startedAt === null ? null : raw.episode.lastEventAt,\n intents: episodeIntents,\n },\n };\n}\n"]}
@@ -0,0 +1,476 @@
1
+ /**
2
+ * Version of the serialized state shape, stored in every state as `state.version`.
3
+ * {@link deserialize} compares it with the stored value and calls your `migrate` function when
4
+ * they differ. Bumped only when `EngineState` changes incompatibly.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { STATE_VERSION, deserialize } from 'oracular';
9
+ *
10
+ * const state = deserialize(saved, {
11
+ * migrate: (stored, version) => {
12
+ * if (version === 0) return upgradeFromV0(stored);
13
+ * throw new Error(`cannot migrate version ${version} to ${STATE_VERSION}`);
14
+ * },
15
+ * });
16
+ * ```
17
+ */
18
+ declare const STATE_VERSION: 1;
19
+
20
+ /**
21
+ * Options that decide when `shouldReveal` fires and how long cooldowns last. Resolved per call →
22
+ * intent → engine → defaults.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * shouldReveal(state, 'upgrade', Date.now(), { threshold: 0.7, z: 1.64 });
27
+ * ```
28
+ */
29
+ interface RevealOptions {
30
+ /** Probability the intent must reach, in [0, 1]. Default 0.5. */
31
+ threshold?: number;
32
+ /** How many standard deviations below p must still clear the threshold. Default 1.28 (one-sided 90%). */
33
+ z?: number;
34
+ /** How long after `markRevealed` the intent stays revealed and `shouldReveal` is false. Default: the intent's `halfLife`. */
35
+ cooldown?: number;
36
+ }
37
+ /**
38
+ * How the engine learns from the user's own outcomes.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * const config = {
43
+ * intents,
44
+ * learning: { priorEpisodes: 5, gain: 1.5, halfLife: 90 * 24 * 60 * 60 * 1000 },
45
+ * };
46
+ * ```
47
+ */
48
+ interface LearningConfig {
49
+ /** Half-life of learned hit/miss counts, in the same unit as `at`. Omit for no forgetting. */
50
+ halfLife?: number;
51
+ /** A perfectly predictive signal converges to `gain × |initial weight|`. Default 2. */
52
+ gain?: number;
53
+ /** Pseudo-count behind the developer's initial weight: how many observed episodes it takes to weigh as much as the prior. Default 10. */
54
+ priorEpisodes?: number;
55
+ }
56
+ /**
57
+ * One thing the user might be about to do, tracked as a Beta(α, β) distribution with
58
+ * α₀ = prior × priorStrength and β₀ = (1 − prior) × priorStrength.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const churn: IntentConfig = {
63
+ * prior: 0.08,
64
+ * priorStrength: 20,
65
+ * halfLife: 14 * 24 * 60 * 60 * 1000,
66
+ * signals: { 'session.short': 1, 'notifications.disabled': 3, 'feature.new.used': -2 },
67
+ * outcome: 'account.cancelled',
68
+ * reveal: { threshold: 0.5, z: 1.64 },
69
+ * };
70
+ * ```
71
+ */
72
+ interface IntentConfig {
73
+ /** Prior probability, strictly between 0 and 1. */
74
+ prior: number;
75
+ /** Strength of the prior as a pseudo-count, greater than 0. */
76
+ priorStrength: number;
77
+ /** Half-life of evidence, in the same unit as `at`. Evidence decays back toward the prior. */
78
+ halfLife: number;
79
+ /** Event name → initial weight. Positive is evidence for the intent, negative against. Non-zero. */
80
+ signals: Record<string, number>;
81
+ /** Event name that confirms the intent happened, e.g. `'checkout.completed'`. */
82
+ outcome?: string;
83
+ /** Reset α, β and contributions to the prior when the outcome arrives. Default true. */
84
+ resetOnOutcome?: boolean;
85
+ /** Reveal defaults for this intent. */
86
+ reveal?: RevealOptions;
87
+ }
88
+ /**
89
+ * Everything `createState` needs. A normalized copy is stored in the state; pass the current
90
+ * config to `deserialize` on load.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const config: EngineConfig = {
95
+ * episodeTimeout: 30 * 60 * 1000,
96
+ * intents: { upgrade, churn },
97
+ * reveal: { z: 1.28 },
98
+ * };
99
+ * ```
100
+ */
101
+ interface EngineConfig {
102
+ intents: Record<string, IntentConfig>;
103
+ /** Idle gap after which the current episode ends on its own, in the same unit as `at`. Omit to end episodes only via `endEpisode`. */
104
+ episodeTimeout?: number;
105
+ learning?: LearningConfig;
106
+ /** Reveal defaults for every intent. */
107
+ reveal?: RevealOptions;
108
+ }
109
+ /** Pseudo-counts a single signal has added to an intent: `alpha` for, `beta` against. Decays with the intent. */
110
+ interface SignalContribution {
111
+ alpha: number;
112
+ beta: number;
113
+ }
114
+ /** What this user's history says about one signal. Decays with `learning.halfLife`. */
115
+ interface SignalRecord {
116
+ /** Episodes in which the signal was followed by the outcome. */
117
+ hits: number;
118
+ /** Episodes in which it was not. */
119
+ misses: number;
120
+ }
121
+ /**
122
+ * Everything the engine knows about one intent for this user. Plain data; read it for debugging
123
+ * or dashboards, but change it only through the engine's functions.
124
+ */
125
+ interface IntentState {
126
+ /** Beta α. Always equals prior α plus the sum of `contributions[*].alpha`. */
127
+ alpha: number;
128
+ /** Beta β. Always equals prior β plus the sum of `contributions[*].beta`. */
129
+ beta: number;
130
+ /** Time α, β and contributions were last decayed to. */
131
+ updatedAt: number | null;
132
+ contributions: Record<string, SignalContribution>;
133
+ learned: Record<string, SignalRecord>;
134
+ /** Episodes that ended with (`hits`) or without (`misses`) the outcome: the base rate. */
135
+ episodes: SignalRecord;
136
+ /** Time `learned` and `episodes` were last decayed to. */
137
+ learnedAt: number | null;
138
+ lastRevealedAt: number | null;
139
+ /** Time the intent's outcome event last arrived. `shouldReveal` stays false for the cooldown after it. */
140
+ lastOutcomeAt: number | null;
141
+ revealCount: number;
142
+ }
143
+ /** What one intent has seen in the current episode. */
144
+ interface EpisodeIntentRecord {
145
+ /** Signals seen since the episode started (or since this intent's last outcome). */
146
+ seen: string[];
147
+ /** Whether the outcome arrived in this episode. */
148
+ resolved: boolean;
149
+ }
150
+ /** The current episode (session). One episode is open at a time, shared by all intents. */
151
+ interface EpisodeState {
152
+ startedAt: number | null;
153
+ lastEventAt: number | null;
154
+ intents: Record<string, EpisodeIntentRecord>;
155
+ }
156
+ /**
157
+ * The whole engine state: plain JSON with no functions, class instances or hidden fields.
158
+ * Persist it with `serialize`, restore it with `deserialize`.
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const state = createState(config);
163
+ * state.intents.upgrade.alpha; // 0.5 for prior 0.05 × priorStrength 10
164
+ * JSON.stringify(state) === serialize(state); // true
165
+ * ```
166
+ */
167
+ interface EngineState {
168
+ version: typeof STATE_VERSION;
169
+ /** Snapshot of the config this state was built with. Pass your current config to `deserialize` on load. */
170
+ config: EngineConfig;
171
+ intents: Record<string, IntentState>;
172
+ episode: EpisodeState;
173
+ }
174
+ /**
175
+ * What the app feeds to `observe`. Any event name is accepted; only names that appear in some
176
+ * intent's `signals` or as its `outcome` change evidence, every event extends the episode.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * observe(state, { name: 'pricing.viewed', at: Date.now(), props: { plan: 'pro' } });
181
+ * ```
182
+ */
183
+ interface IntentEvent {
184
+ name: string;
185
+ /** Timestamp. Every duration in the config uses the same unit (milliseconds recommended). */
186
+ at: number;
187
+ /** Accepted and ignored in 0.1; never stored. */
188
+ props?: Record<string, unknown>;
189
+ }
190
+ /**
191
+ * What `predict` returns for one intent at one moment.
192
+ *
193
+ * @example
194
+ * ```ts
195
+ * const { p, lower, confidence, revealed, suppressedUntil } = predict(state, 'upgrade', now);
196
+ * ```
197
+ */
198
+ interface Prediction {
199
+ intent: string;
200
+ /** Mean of the Beta distribution: α / (α + β). */
201
+ p: number;
202
+ /** Standard deviation of the Beta distribution. */
203
+ sd: number;
204
+ /** p − z·sd, clamped to [0, 1]. This is what `shouldReveal` compares to the threshold. */
205
+ lower: number;
206
+ /** 1 − 2·sd, clamped to [0, 1]. A heuristic: 1 when the estimate is sharp, 0 when it is as vague as possible. */
207
+ confidence: number;
208
+ /** α + β: total pseudo-observations, prior strength included. */
209
+ evidence: number;
210
+ /** True while inside the cooldown that follows `markRevealed`. */
211
+ revealed: boolean;
212
+ revealedAt: number | null;
213
+ /**
214
+ * When the current suppression ends: the later of the reveal cooldown (after `markRevealed`)
215
+ * and the outcome cooldown (after the outcome event) still running at `at`, or null when
216
+ * neither is. `shouldReveal` is false while this is non-null.
217
+ */
218
+ suppressedUntil: number | null;
219
+ }
220
+ /**
221
+ * One row of `explain`: what a single signal is doing to an intent right now, and what has been
222
+ * learned about it.
223
+ *
224
+ * @example
225
+ * ```ts
226
+ * const [strongest] = explain(state, 'upgrade', now);
227
+ * strongest?.signal; // the signal with the largest |impact|
228
+ * ```
229
+ */
230
+ interface SignalExplanation {
231
+ signal: string;
232
+ /** Signed pseudo-counts this signal currently holds: positive in α, negative in β, after decay. */
233
+ contribution: number;
234
+ /** p minus what p would be without this signal's contribution. */
235
+ impact: number;
236
+ /** The developer's weight. */
237
+ initialWeight: number;
238
+ /** What this user's history alone says the weight should be. */
239
+ learnedWeight: number;
240
+ /** The blend the next occurrence of the signal will actually add. */
241
+ effectiveWeight: number;
242
+ hits: number;
243
+ misses: number;
244
+ }
245
+ /**
246
+ * Turns a stored state of an older version into a current `EngineState`. Receives the parsed
247
+ * JSON as-is and the version it declares.
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * const migrate: Migrate = (stored, version) => {
252
+ * if (version === 0) return { ...(stored as object), version: 1 } as EngineState;
253
+ * throw new Error(`unknown state version ${version}`);
254
+ * };
255
+ * ```
256
+ */
257
+ type Migrate = (stored: unknown, version: number) => EngineState;
258
+ /**
259
+ * Options for `deserialize`.
260
+ *
261
+ * @example
262
+ * ```ts
263
+ * deserialize(saved, { config: currentConfig, migrate });
264
+ * ```
265
+ */
266
+ interface DeserializeOptions {
267
+ /** The app's current config. Validated, swapped in, and used to prune signals and intents no longer declared. */
268
+ config?: EngineConfig;
269
+ /** Called when the stored version differs from `STATE_VERSION`. */
270
+ migrate?: Migrate;
271
+ }
272
+
273
+ /**
274
+ * Why the intent's p is what it is at time `at`: one entry per declared signal, the ones that
275
+ * matter most first. `contribution` is the signed pseudo-count the signal currently holds after
276
+ * decay, `impact` is how much p would change if that contribution were removed, and the three
277
+ * weights show what the developer said, what this user's history says, and what the next
278
+ * occurrence will actually add. Read-only.
279
+ *
280
+ * @param state - The current state. Not mutated.
281
+ * @param intent - Name of an intent declared in the config.
282
+ * @param at - The time to evaluate at; contributions and learned counts are decayed to it.
283
+ * @returns One {@link SignalExplanation} per declared signal, sorted by |impact| descending,
284
+ * then by name. Empty for an intent that declares no signals.
285
+ * @throws `RangeError` when `intent` is not declared in the config.
286
+ * @throws `TypeError` when `state` is not an engine state or `at` is not finite.
287
+ * @example
288
+ * ```ts
289
+ * for (const row of explain(state, 'upgrade', Date.now())) {
290
+ * console.log(row.signal, row.impact.toFixed(2), row.effectiveWeight.toFixed(2));
291
+ * }
292
+ * ```
293
+ */
294
+ declare function explain(state: EngineState, intent: string, at: number): SignalExplanation[];
295
+
296
+ /**
297
+ * Feeds one event to the engine.
298
+ *
299
+ * For every intent the event is a signal of: decay its evidence to `event.at`, then add the
300
+ * signal's effective weight to α (positive) or β (negative) and remember the signal for this
301
+ * episode. For every intent the event is the outcome of: give the remembered signals a hit,
302
+ * count the episode toward the base rate, record `lastOutcomeAt`, and (by default) reset the
303
+ * evidence to the prior. Any event opens or extends the current episode; an idle gap longer
304
+ * than `config.episodeTimeout` closes the previous episode first.
305
+ *
306
+ * @param state - The current state. Not mutated.
307
+ * @param event - `name` is matched against every intent's signals and outcome; `at` is the
308
+ * event's timestamp, in the same unit as your durations; `props` are accepted and ignored,
309
+ * and never stored.
310
+ * @returns The next state. Intents the event does not touch keep their stored numbers; their
311
+ * decay is applied when they are next read or updated.
312
+ * @throws `TypeError` when `state` is not an engine state, or the event has no non-empty
313
+ * `name`, no finite `at`, or a `props` that is not a plain object.
314
+ * @example
315
+ * ```ts
316
+ * let state = createState(config);
317
+ * state = observe(state, { name: 'pricing.viewed', at: Date.now() });
318
+ * state = observe(state, { name: 'upgrade.completed', at: Date.now() }); // outcome
319
+ * ```
320
+ */
321
+ declare function observe(state: EngineState, event: IntentEvent): EngineState;
322
+ /**
323
+ * Ends the current episode at `at`. Every intent whose outcome did not arrive gives a miss to
324
+ * each signal seen this episode and counts the episode toward its base rate. Call it when a
325
+ * session ends: tab closed, app backgrounded, user signed out.
326
+ *
327
+ * @param state - The current state. Not mutated.
328
+ * @param at - When the episode ended. If it had already timed out under
329
+ * `config.episodeTimeout`, it is closed at the moment it timed out instead.
330
+ * @returns The next state with no open episode, or the very same state object when no episode
331
+ * was open.
332
+ * @throws `TypeError` when `state` is not an engine state or `at` is not a finite number.
333
+ * @example
334
+ * ```ts
335
+ * window.addEventListener('pagehide', () => {
336
+ * state = endEpisode(state, Date.now());
337
+ * localStorage.setItem('oracular', serialize(state));
338
+ * });
339
+ * ```
340
+ */
341
+ declare function endEpisode(state: EngineState, at: number): EngineState;
342
+
343
+ /**
344
+ * The intent's current estimate at time `at`: the Beta mean p, its standard deviation, the
345
+ * lower confidence bound that {@link shouldReveal} tests, and the cooldown status. Read-only:
346
+ * the stored state is decayed to `at` for the reading but not changed.
347
+ *
348
+ * @param state - The current state. Not mutated.
349
+ * @param intent - Name of an intent declared in the config.
350
+ * @param at - The time to evaluate at, in the same unit as your durations.
351
+ * @param opts - Overrides for `threshold`, `z` and `cooldown`, resolved as call → intent →
352
+ * engine → defaults. `z` sets `lower`; `cooldown` sets `revealed` and `suppressedUntil`;
353
+ * `threshold` only matters to {@link shouldReveal}.
354
+ * @returns The prediction. `p`, `sd`, `lower` and `confidence` are in [0, 1]; `evidence` is
355
+ * α + β.
356
+ * @throws `RangeError` when `intent` is not declared in the config.
357
+ * @throws `TypeError` when `state` is not an engine state, `at` is not finite, or `opts` is
358
+ * malformed.
359
+ * @example
360
+ * ```ts
361
+ * const { p, lower, suppressedUntil } = predict(state, 'upgrade', Date.now());
362
+ * if (suppressedUntil !== null) scheduleNextCheck(suppressedUntil);
363
+ * ```
364
+ */
365
+ declare function predict(state: EngineState, intent: string, at: number, opts?: RevealOptions): Prediction;
366
+ /**
367
+ * Whether to act on the intent now. True when p ≥ `threshold`, the lower confidence bound
368
+ * p − z·sd also clears `threshold`, and no cooldown is running: neither the one started by
369
+ * {@link markRevealed} nor the one started by the intent's outcome event.
370
+ *
371
+ * @param state - The current state. Not mutated.
372
+ * @param intent - Name of an intent declared in the config.
373
+ * @param at - The time to evaluate at.
374
+ * @param opts - Overrides for `threshold` (default 0.5), `z` (default 1.28, a one-sided 90%
375
+ * bound) and `cooldown` (default: the intent's `halfLife`).
376
+ * @returns True when the app should show, offer or reveal whatever this intent gates.
377
+ * @throws `RangeError` when `intent` is not declared in the config.
378
+ * @throws `TypeError` when `state` is not an engine state, `at` is not finite, or `opts` is
379
+ * malformed.
380
+ * @example
381
+ * ```ts
382
+ * if (shouldReveal(state, 'upgrade', Date.now())) {
383
+ * showUpgradeOffer();
384
+ * state = markRevealed(state, 'upgrade', Date.now());
385
+ * }
386
+ * ```
387
+ */
388
+ declare function shouldReveal(state: EngineState, intent: string, at: number, opts?: RevealOptions): boolean;
389
+ /**
390
+ * Records that the app acted on the intent at `at`. Starts the reveal cooldown, during which
391
+ * `predict().revealed` is true and {@link shouldReveal} is false, and increments
392
+ * `revealCount`. Evidence and learned data are untouched.
393
+ *
394
+ * @param state - The current state. Not mutated.
395
+ * @param intent - Name of an intent declared in the config.
396
+ * @param at - When the reveal happened.
397
+ * @returns The next state.
398
+ * @throws `RangeError` when `intent` is not declared in the config.
399
+ * @throws `TypeError` when `state` is not an engine state or `at` is not finite.
400
+ * @example
401
+ * ```ts
402
+ * state = markRevealed(state, 'upgrade', Date.now());
403
+ * predict(state, 'upgrade', Date.now()).revealed; // true until the cooldown ends
404
+ * ```
405
+ */
406
+ declare function markRevealed(state: EngineState, intent: string, at: number): EngineState;
407
+
408
+ /**
409
+ * The state as a JSON string, for the host app to persist wherever it likes. The state is plain
410
+ * data, so this is `JSON.stringify` after a shape check. The output never contains event props.
411
+ *
412
+ * @param state - The state to persist.
413
+ * @returns A JSON string that {@link deserialize} restores losslessly.
414
+ * @throws `TypeError` when `state` is not an engine state.
415
+ * @example
416
+ * ```ts
417
+ * localStorage.setItem('oracular', serialize(state));
418
+ * ```
419
+ */
420
+ declare function serialize(state: EngineState): string;
421
+ /**
422
+ * Restores a state saved with {@link serialize}.
423
+ *
424
+ * Always pass the app's current `config`: it is validated and replaces the stored snapshot,
425
+ * intents and signals no longer declared are dropped (α and β are recomputed from the kept
426
+ * contributions), and newly declared intents start at their prior. Pass `migrate` to upgrade a
427
+ * state saved by an older version of this library.
428
+ *
429
+ * @param json - A string produced by {@link serialize}.
430
+ * @param opts - `config`: the app's current config. `migrate`: called as
431
+ * `migrate(stored, version)` when the stored version differs from {@link STATE_VERSION};
432
+ * it must return a state of the current version.
433
+ * @returns A validated state that satisfies the engine's invariants.
434
+ * @throws `SyntaxError` when `json` is not valid JSON.
435
+ * @throws `TypeError` when the stored data, the result of `migrate`, `opts` or `opts.config`
436
+ * has the wrong shape.
437
+ * @throws `RangeError` when the stored version differs and no `migrate` is given, when
438
+ * `migrate` does not return the current version, or when a stored number is out of range.
439
+ * @example
440
+ * ```ts
441
+ * const saved = localStorage.getItem('oracular');
442
+ * let state = saved ? deserialize(saved, { config }) : createState(config);
443
+ * ```
444
+ */
445
+ declare function deserialize(json: string, opts?: DeserializeOptions): EngineState;
446
+
447
+ /**
448
+ * Builds a fresh engine state from a config. Every intent starts at its prior and no episode is
449
+ * open. The config is validated and a normalized copy is stored inside the state, so a persisted
450
+ * state remembers the weights it was built with; pass your current config to {@link deserialize}
451
+ * when loading.
452
+ *
453
+ * @param config - Intents with their priors and signals, plus engine-wide options. Validated
454
+ * strictly: unknown keys, non-finite numbers and out-of-range values throw.
455
+ * @returns A new, JSON-serializable state. `config` is not mutated.
456
+ * @throws `TypeError` when a value has the wrong type or a key is not recognised.
457
+ * @throws `RangeError` when a number is out of range, for example a `prior` outside (0, 1) or a
458
+ * signal weight of 0.
459
+ * @example
460
+ * ```ts
461
+ * const state = createState({
462
+ * intents: {
463
+ * upgrade: {
464
+ * prior: 0.05,
465
+ * priorStrength: 10,
466
+ * halfLife: 7 * 24 * 60 * 60 * 1000,
467
+ * signals: { 'pricing.viewed': 3, 'limit.hit': 2, 'plan.downgraded': -4 },
468
+ * outcome: 'upgrade.completed',
469
+ * },
470
+ * },
471
+ * });
472
+ * ```
473
+ */
474
+ declare function createState(config: EngineConfig): EngineState;
475
+
476
+ export { type DeserializeOptions, type EngineConfig, type EngineState, type EpisodeIntentRecord, type EpisodeState, type IntentConfig, type IntentEvent, type IntentState, type LearningConfig, type Migrate, type Prediction, type RevealOptions, STATE_VERSION, type SignalContribution, type SignalExplanation, type SignalRecord, createState, deserialize, endEpisode, explain, markRevealed, observe, predict, serialize, shouldReveal };