@graphorin/memory 0.5.0
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.
- package/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +401 -0
- package/dist/conflict/index.d.ts +3 -0
- package/dist/conflict/index.js +7 -0
- package/dist/conflict/locale-packs/en.js +262 -0
- package/dist/conflict/locale-packs/en.js.map +1 -0
- package/dist/conflict/locale-packs/index.js +4 -0
- package/dist/conflict/locale-packs/types.d.ts +103 -0
- package/dist/conflict/locale-packs/types.d.ts.map +1 -0
- package/dist/conflict/locale-packs/types.js +55 -0
- package/dist/conflict/locale-packs/types.js.map +1 -0
- package/dist/conflict/pipeline.d.ts +35 -0
- package/dist/conflict/pipeline.d.ts.map +1 -0
- package/dist/conflict/pipeline.js +257 -0
- package/dist/conflict/pipeline.js.map +1 -0
- package/dist/conflict/stages/helpers.js +66 -0
- package/dist/conflict/stages/helpers.js.map +1 -0
- package/dist/conflict/stages/stage1-exact-dedup.js +21 -0
- package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -0
- package/dist/conflict/stages/stage2-embedding-three-zone.js +37 -0
- package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -0
- package/dist/conflict/stages/stage3-heuristic-regex.js +45 -0
- package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -0
- package/dist/conflict/stages/stage4-subject-predicate.js +31 -0
- package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -0
- package/dist/conflict/stages/stage5-defer-to-deep.js +22 -0
- package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -0
- package/dist/conflict/types.d.ts +222 -0
- package/dist/conflict/types.d.ts.map +1 -0
- package/dist/conflict/types.js +50 -0
- package/dist/conflict/types.js.map +1 -0
- package/dist/consolidator/budget.d.ts +1 -0
- package/dist/consolidator/budget.js +214 -0
- package/dist/consolidator/budget.js.map +1 -0
- package/dist/consolidator/decay.d.ts +35 -0
- package/dist/consolidator/decay.d.ts.map +1 -0
- package/dist/consolidator/decay.js +91 -0
- package/dist/consolidator/decay.js.map +1 -0
- package/dist/consolidator/dlq.js +58 -0
- package/dist/consolidator/dlq.js.map +1 -0
- package/dist/consolidator/errors.d.ts +54 -0
- package/dist/consolidator/errors.d.ts.map +1 -0
- package/dist/consolidator/errors.js +67 -0
- package/dist/consolidator/errors.js.map +1 -0
- package/dist/consolidator/idempotency.d.ts +1 -0
- package/dist/consolidator/idempotency.js +22 -0
- package/dist/consolidator/idempotency.js.map +1 -0
- package/dist/consolidator/index.d.ts +34 -0
- package/dist/consolidator/index.d.ts.map +1 -0
- package/dist/consolidator/index.js +163 -0
- package/dist/consolidator/index.js.map +1 -0
- package/dist/consolidator/lock.d.ts +2 -0
- package/dist/consolidator/lock.js +89 -0
- package/dist/consolidator/lock.js.map +1 -0
- package/dist/consolidator/noise-filter.d.ts +2 -0
- package/dist/consolidator/noise-filter.js +173 -0
- package/dist/consolidator/noise-filter.js.map +1 -0
- package/dist/consolidator/phases/deep.d.ts +4 -0
- package/dist/consolidator/phases/deep.js +198 -0
- package/dist/consolidator/phases/deep.js.map +1 -0
- package/dist/consolidator/phases/induce.d.ts +154 -0
- package/dist/consolidator/phases/induce.d.ts.map +1 -0
- package/dist/consolidator/phases/induce.js +253 -0
- package/dist/consolidator/phases/induce.js.map +1 -0
- package/dist/consolidator/phases/light.d.ts +4 -0
- package/dist/consolidator/phases/light.js +99 -0
- package/dist/consolidator/phases/light.js.map +1 -0
- package/dist/consolidator/phases/reflect.js +287 -0
- package/dist/consolidator/phases/reflect.js.map +1 -0
- package/dist/consolidator/phases/standard.d.ts +8 -0
- package/dist/consolidator/phases/standard.js +499 -0
- package/dist/consolidator/phases/standard.js.map +1 -0
- package/dist/consolidator/reconcile.js +192 -0
- package/dist/consolidator/reconcile.js.map +1 -0
- package/dist/consolidator/runtime.d.ts +62 -0
- package/dist/consolidator/runtime.d.ts.map +1 -0
- package/dist/consolidator/runtime.js +580 -0
- package/dist/consolidator/runtime.js.map +1 -0
- package/dist/consolidator/scheduler.d.ts +111 -0
- package/dist/consolidator/scheduler.d.ts.map +1 -0
- package/dist/consolidator/scheduler.js +71 -0
- package/dist/consolidator/scheduler.js.map +1 -0
- package/dist/consolidator/triggers.d.ts +40 -0
- package/dist/consolidator/triggers.d.ts.map +1 -0
- package/dist/consolidator/triggers.js +94 -0
- package/dist/consolidator/triggers.js.map +1 -0
- package/dist/consolidator/types.d.ts +397 -0
- package/dist/consolidator/types.d.ts.map +1 -0
- package/dist/consolidator/types.js +117 -0
- package/dist/consolidator/types.js.map +1 -0
- package/dist/context-engine/annotations.d.ts +118 -0
- package/dist/context-engine/annotations.d.ts.map +1 -0
- package/dist/context-engine/annotations.js +102 -0
- package/dist/context-engine/annotations.js.map +1 -0
- package/dist/context-engine/auto-recall.d.ts +52 -0
- package/dist/context-engine/auto-recall.d.ts.map +1 -0
- package/dist/context-engine/auto-recall.js +41 -0
- package/dist/context-engine/auto-recall.js.map +1 -0
- package/dist/context-engine/compaction/clear-tool-results.d.ts +55 -0
- package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -0
- package/dist/context-engine/compaction/clear-tool-results.js +97 -0
- package/dist/context-engine/compaction/clear-tool-results.js.map +1 -0
- package/dist/context-engine/compaction/compactor.d.ts +52 -0
- package/dist/context-engine/compaction/compactor.d.ts.map +1 -0
- package/dist/context-engine/compaction/compactor.js +196 -0
- package/dist/context-engine/compaction/compactor.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts +15 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +27 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts +18 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +47 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts +15 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +32 -0
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -0
- package/dist/context-engine/compaction/hooks/types.d.ts +42 -0
- package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -0
- package/dist/context-engine/compaction/index.d.ts +9 -0
- package/dist/context-engine/compaction/index.js +9 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts +84 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -0
- package/dist/context-engine/compaction/templates/summary-9-section.js +84 -0
- package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -0
- package/dist/context-engine/compaction/thresholds.d.ts +68 -0
- package/dist/context-engine/compaction/thresholds.d.ts.map +1 -0
- package/dist/context-engine/compaction/thresholds.js +63 -0
- package/dist/context-engine/compaction/thresholds.js.map +1 -0
- package/dist/context-engine/compaction/types.d.ts +202 -0
- package/dist/context-engine/compaction/types.d.ts.map +1 -0
- package/dist/context-engine/engine.d.ts +258 -0
- package/dist/context-engine/engine.d.ts.map +1 -0
- package/dist/context-engine/engine.js +424 -0
- package/dist/context-engine/engine.js.map +1 -0
- package/dist/context-engine/index.d.ts +68 -0
- package/dist/context-engine/index.d.ts.map +1 -0
- package/dist/context-engine/index.js +24 -0
- package/dist/context-engine/locale-packs/en.d.ts +14 -0
- package/dist/context-engine/locale-packs/en.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/en.js +87 -0
- package/dist/context-engine/locale-packs/en.js.map +1 -0
- package/dist/context-engine/locale-packs/index.js +4 -0
- package/dist/context-engine/locale-packs/resolver.d.ts +41 -0
- package/dist/context-engine/locale-packs/resolver.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/resolver.js +119 -0
- package/dist/context-engine/locale-packs/resolver.js.map +1 -0
- package/dist/context-engine/locale-packs/types.d.ts +119 -0
- package/dist/context-engine/locale-packs/types.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/types.js +24 -0
- package/dist/context-engine/locale-packs/types.js.map +1 -0
- package/dist/context-engine/metadata.d.ts +40 -0
- package/dist/context-engine/metadata.d.ts.map +1 -0
- package/dist/context-engine/metadata.js +66 -0
- package/dist/context-engine/metadata.js.map +1 -0
- package/dist/context-engine/preambles/inbound-en.d.ts +20 -0
- package/dist/context-engine/preambles/inbound-en.d.ts.map +1 -0
- package/dist/context-engine/preambles/inbound-en.js +23 -0
- package/dist/context-engine/preambles/inbound-en.js.map +1 -0
- package/dist/context-engine/privacy-filter.d.ts +108 -0
- package/dist/context-engine/privacy-filter.d.ts.map +1 -0
- package/dist/context-engine/privacy-filter.js +113 -0
- package/dist/context-engine/privacy-filter.js.map +1 -0
- package/dist/context-engine/templates/base-en.d.ts +19 -0
- package/dist/context-engine/templates/base-en.d.ts.map +1 -0
- package/dist/context-engine/templates/base-en.js +22 -0
- package/dist/context-engine/templates/base-en.js.map +1 -0
- package/dist/context-engine/templates/composer.d.ts +55 -0
- package/dist/context-engine/templates/composer.d.ts.map +1 -0
- package/dist/context-engine/templates/composer.js +51 -0
- package/dist/context-engine/templates/composer.js.map +1 -0
- package/dist/context-engine/token-budget.d.ts +99 -0
- package/dist/context-engine/token-budget.d.ts.map +1 -0
- package/dist/context-engine/token-budget.js +211 -0
- package/dist/context-engine/token-budget.js.map +1 -0
- package/dist/context-engine/token-counter.d.ts +54 -0
- package/dist/context-engine/token-counter.d.ts.map +1 -0
- package/dist/context-engine/token-counter.js +81 -0
- package/dist/context-engine/token-counter.js.map +1 -0
- package/dist/errors/index.d.ts +171 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +206 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/facade.d.ts +278 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/facade.js +265 -0
- package/dist/facade.js.map +1 -0
- package/dist/graph/entity-resolver.d.ts +135 -0
- package/dist/graph/entity-resolver.d.ts.map +1 -0
- package/dist/graph/entity-resolver.js +202 -0
- package/dist/graph/entity-resolver.js.map +1 -0
- package/dist/graph/index.d.ts +1 -0
- package/dist/index.d.ts +103 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/contextualize.d.ts +14 -0
- package/dist/internal/contextualize.d.ts.map +1 -0
- package/dist/internal/contextualize.js +110 -0
- package/dist/internal/contextualize.js.map +1 -0
- package/dist/internal/embedder-binding.js +50 -0
- package/dist/internal/embedder-binding.js.map +1 -0
- package/dist/internal/id.js +24 -0
- package/dist/internal/id.js.map +1 -0
- package/dist/internal/injection-heuristics.js +64 -0
- package/dist/internal/injection-heuristics.js.map +1 -0
- package/dist/internal/spans.js +35 -0
- package/dist/internal/spans.js.map +1 -0
- package/dist/internal/storage-adapter.d.ts +742 -0
- package/dist/internal/storage-adapter.d.ts.map +1 -0
- package/dist/migration/embedder-migration.d.ts +124 -0
- package/dist/migration/embedder-migration.d.ts.map +1 -0
- package/dist/migration/embedder-migration.js +109 -0
- package/dist/migration/embedder-migration.js.map +1 -0
- package/dist/migration/index.d.ts +2 -0
- package/dist/migration/index.js +3 -0
- package/dist/search/explain.d.ts +74 -0
- package/dist/search/explain.d.ts.map +1 -0
- package/dist/search/explain.js +50 -0
- package/dist/search/explain.js.map +1 -0
- package/dist/search/index.d.ts +6 -0
- package/dist/search/index.js +6 -0
- package/dist/search/iterative.d.ts +223 -0
- package/dist/search/iterative.d.ts.map +1 -0
- package/dist/search/iterative.js +330 -0
- package/dist/search/iterative.js.map +1 -0
- package/dist/search/query-transform.d.ts +112 -0
- package/dist/search/query-transform.d.ts.map +1 -0
- package/dist/search/query-transform.js +184 -0
- package/dist/search/query-transform.js.map +1 -0
- package/dist/search/rrf.d.ts +95 -0
- package/dist/search/rrf.d.ts.map +1 -0
- package/dist/search/rrf.js +166 -0
- package/dist/search/rrf.js.map +1 -0
- package/dist/search/types.d.ts +50 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/tiers/episodic-memory.d.ts +142 -0
- package/dist/tiers/episodic-memory.d.ts.map +1 -0
- package/dist/tiers/episodic-memory.js +254 -0
- package/dist/tiers/episodic-memory.js.map +1 -0
- package/dist/tiers/index.d.ts +8 -0
- package/dist/tiers/index.js +9 -0
- package/dist/tiers/insight-memory.d.ts +71 -0
- package/dist/tiers/insight-memory.d.ts.map +1 -0
- package/dist/tiers/insight-memory.js +133 -0
- package/dist/tiers/insight-memory.js.map +1 -0
- package/dist/tiers/procedural-memory.d.ts +147 -0
- package/dist/tiers/procedural-memory.d.ts.map +1 -0
- package/dist/tiers/procedural-memory.js +267 -0
- package/dist/tiers/procedural-memory.js.map +1 -0
- package/dist/tiers/semantic-memory.d.ts +461 -0
- package/dist/tiers/semantic-memory.d.ts.map +1 -0
- package/dist/tiers/semantic-memory.js +631 -0
- package/dist/tiers/semantic-memory.js.map +1 -0
- package/dist/tiers/session-memory.d.ts +117 -0
- package/dist/tiers/session-memory.d.ts.map +1 -0
- package/dist/tiers/session-memory.js +163 -0
- package/dist/tiers/session-memory.js.map +1 -0
- package/dist/tiers/shared-memory.d.ts +29 -0
- package/dist/tiers/shared-memory.d.ts.map +1 -0
- package/dist/tiers/shared-memory.js +58 -0
- package/dist/tiers/shared-memory.js.map +1 -0
- package/dist/tiers/working-memory.d.ts +129 -0
- package/dist/tiers/working-memory.d.ts.map +1 -0
- package/dist/tiers/working-memory.js +229 -0
- package/dist/tiers/working-memory.js.map +1 -0
- package/dist/tools/block-tools.d.ts +98 -0
- package/dist/tools/block-tools.d.ts.map +1 -0
- package/dist/tools/block-tools.js +114 -0
- package/dist/tools/block-tools.js.map +1 -0
- package/dist/tools/fact-tools.d.ts +294 -0
- package/dist/tools/fact-tools.d.ts.map +1 -0
- package/dist/tools/fact-tools.js +293 -0
- package/dist/tools/fact-tools.js.map +1 -0
- package/dist/tools/index.d.ts +35 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/recall-tools.d.ts +209 -0
- package/dist/tools/recall-tools.d.ts.map +1 -0
- package/dist/tools/recall-tools.js +170 -0
- package/dist/tools/recall-tools.js.map +1 -0
- package/dist/tools/types.d.ts +40 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/package.json +119 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","names":["#semantic","#episodic","#store","#consolidatorStore","#tracer","#now","#randomId","#provider","#cheapProvider","#deepProvider","#defaultScope","#listeners","#lockManager","#budget","#config","#running","#manuallyPaused","lastRunAt: string | undefined","lastPhase: ConsolidatorPhase | undefined","lastRuns: { -readonly [K in keyof ConsolidatorLastRuns]: ConsolidatorLastRuns[K] }","#deferredRunsAdjustment","#deferredRuns","#emptyExtractions","#planPhases","#emit","last: PhaseOutcome | null","#runPhase","replayPhase: ConsolidatorPhase","lastError: unknown","planned: ConsolidatorPhase[]","outcome: PhaseOutcome","#dispatch","#lastDispatchMessageIds","tier: ConsolidatorTier","missing: string[]"],"sources":["../../src/consolidator/runtime.ts"],"sourcesContent":["/**\n * Consolidator runtime — orchestrates the three phases, the lock,\n * the budget, the cursor, and the DLQ. The factory is the public\n * surface; the class lives below it as a private implementation\n * detail.\n *\n * The runtime is **library-mode** by default — it does not start a\n * background scheduler. Cron / idle triggers are realised by\n * registering them with a `@graphorin/triggers` scheduler via\n * `registerWithScheduler(...)` (the standalone server calls this in\n * `beforeStart` whenever a consolidator + a triggers scheduler are both\n * supplied). Turn / event triggers are consumer-emitted — the scheduler\n * cannot fire them on its own, so the consumer must call\n * `trigger({ kind: 'turn' | 'event' }, ...)` from its own loop. Manual\n * `trigger(...)` / `fireNow(...)` calls work in either mode.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, SessionScope, Tracer } from '@graphorin/core';\nimport { NOOP_TRACER } from '@graphorin/core';\nimport type {\n ConsolidatorMemoryStoreExt,\n MemoryStoreAdapter,\n} from '../internal/storage-adapter.js';\nimport type { EpisodicMemory } from '../tiers/episodic-memory.js';\nimport type { SemanticMemory } from '../tiers/semantic-memory.js';\nimport { BudgetTracker } from './budget.js';\nimport { DEFAULT_SALIENCE_WEIGHTS } from './decay.js';\nimport { classifyError, describeError, nextBackoffMs } from './dlq.js';\nimport { CustomTierMisconfiguredError, ProviderNotConfiguredError } from './errors.js';\nimport { tipMessageId } from './idempotency.js';\nimport { LockManager } from './lock.js';\nimport type { NoiseFilterPreset } from './noise-filter.js';\nimport { runDeepPhase } from './phases/deep.js';\nimport { runLightPhase } from './phases/light.js';\nimport { runReflectionPass } from './phases/reflect.js';\nimport { runStandardPhase } from './phases/standard.js';\nimport {\n type RegisterTriggersResult,\n registerConsolidatorTriggers,\n type SchedulerLike,\n} from './scheduler.js';\nimport {\n CONSOLIDATOR_TIER_DEFAULTS,\n type ConsolidatorConfig,\n type ConsolidatorLastRuns,\n type ConsolidatorPhase,\n type ConsolidatorStatus,\n type ConsolidatorTier,\n type ConsolidatorTriggerReason,\n type ConsolidatorTriggerSpec,\n type CreateConsolidatorOptions,\n type PhaseListener,\n type PhaseOutcome,\n} from './types.js';\n\n/**\n * Consolidator runtime surface returned by {@link createConsolidator}.\n * Compatible with the placeholder shape so the facade can swap the\n * implementation without breaking consumers.\n *\n * @stable\n */\nexport interface Consolidator {\n start(): Promise<void>;\n stop(): Promise<void>;\n trigger(reason: ConsolidatorTriggerReason, scope: SessionScope): Promise<PhaseOutcome | null>;\n status(): Promise<ConsolidatorStatus>;\n /**\n * Manual trigger for the requested phase. Skips phase gating + the\n * idle/cron scheduler so admins can flush the queue on demand.\n */\n fireNow(phase: ConsolidatorPhase, scope?: SessionScope): Promise<PhaseOutcome | null>;\n /** Replace the active tier — recomputes ceilings + phase set. */\n setTier(tier: ConsolidatorTier): Promise<void>;\n /** Pause the consolidator until the next budget reset. */\n pause(): Promise<void>;\n /** Resume after `pause()`. */\n resume(): Promise<void>;\n /** Subscribe to phase-finished notifications. Returns an unsubscribe. */\n onPhaseFinished(listener: PhaseListener): () => void;\n /**\n * Record memory-pipeline LLM spend that happened OUTSIDE a phase run\n * (MCON-15 — e.g. workflow induction) so the daily ceilings cover it.\n * Counted under the deep-phase bucket.\n */\n recordExternalSpend(tokens: number, costUsd?: number): void;\n /** Active config — frozen snapshot. */\n config(): ConsolidatorConfig;\n /**\n * Register this consolidator's cron / idle triggers with a\n * `@graphorin/triggers` scheduler so they fire `trigger(...)`\n * automatically (the daemon ↔ triggers bridge — MCON-4). Uses the\n * configured `defaultScope`; throws if none was set. Turn / event\n * triggers are skipped (consumer-emitted). The standalone server calls\n * this in `beforeStart`.\n */\n registerWithScheduler(scheduler: SchedulerLike): Promise<RegisterTriggersResult>;\n /** True when `tier === 'free'`. */\n isFree(): boolean;\n /** Drain DLQ rows whose `nextRetryAt` <= now. */\n drainDlq(scope: SessionScope): Promise<number>;\n}\n\n/**\n * Build the runtime consolidator.\n *\n * @stable\n */\nexport function createConsolidator(opts: CreateConsolidatorOptions): Consolidator {\n return new ConsolidatorImpl(opts);\n}\n\nclass ConsolidatorImpl implements Consolidator {\n readonly #semantic: SemanticMemory;\n readonly #episodic: EpisodicMemory | null;\n readonly #store: MemoryStoreAdapter;\n readonly #consolidatorStore: ConsolidatorMemoryStoreExt | null;\n readonly #tracer: Tracer;\n readonly #now: () => number;\n readonly #randomId: () => string;\n readonly #provider: Provider | null;\n /** Per-phase provider overrides (MCON-7); fall back to `#provider`. */\n readonly #cheapProvider: Provider | null;\n readonly #deepProvider: Provider | null;\n readonly #defaultScope: SessionScope | null;\n readonly #listeners = new Set<PhaseListener>();\n readonly #lockManager: LockManager;\n readonly #budget: BudgetTracker;\n #config: ConsolidatorConfig;\n #running = false;\n #manuallyPaused = false;\n #deferredRuns = 0;\n /**\n * Message ids of the batch the most recent `#dispatch` operated on\n * (MCON-10). Captured so a thrown phase can enqueue its DLQ row with\n * the REAL failed slice instead of `[]` — replays must target the\n * window that failed, not whatever the cursor points at later.\n */\n #lastDispatchMessageIds: ReadonlyArray<string> = [];\n /**\n * Bumped when the runtime cannot persist a deferred run to the\n * audit log (e.g., adapter omits the consolidator surface). The\n * `status()` reader merges this with the persisted count so\n * library-mode callers still observe the deferral.\n */\n #deferredRunsAdjustment = 0;\n #emptyExtractions = 0;\n\n constructor(opts: CreateConsolidatorOptions) {\n this.#semantic = opts.semantic;\n this.#episodic = opts.episodic ?? null;\n this.#store = opts.store;\n this.#consolidatorStore = this.#store.consolidator ?? null;\n this.#tracer = opts.tracer ?? NOOP_TRACER;\n this.#now = opts.now ?? Date.now;\n this.#randomId =\n opts.randomId ??\n ((): string => {\n const a = Math.floor(Math.random() * 1e9).toString(36);\n const b = Math.floor(Math.random() * 1e9).toString(36);\n return `cr_${a}${b}`;\n });\n this.#provider = opts.provider ?? null;\n this.#cheapProvider = opts.cheapProvider ?? null;\n this.#deepProvider = opts.deepProvider ?? null;\n this.#defaultScope = opts.defaultScope ?? null;\n\n this.#config = resolveConfig(opts);\n this.#lockManager = new LockManager({\n store: this.#consolidatorStore,\n waitMs: this.#config.lockWaitMs,\n maxRunDurationMs: this.#config.ceilings.maxRunDurationMs,\n now: this.#now,\n randomId: this.#randomId,\n });\n this.#budget = new BudgetTracker({\n maxTokensPerDay: this.#config.ceilings.maxTokensPerDay,\n maxCostPerDay: this.#config.ceilings.maxCostPerDay,\n onExceed: this.#config.onExceed,\n resetSemantics: this.#config.budgetResetSemantics,\n now: this.#now,\n });\n }\n\n async start(): Promise<void> {\n this.#running = true;\n }\n\n async stop(): Promise<void> {\n this.#running = false;\n }\n\n async pause(): Promise<void> {\n this.#manuallyPaused = true;\n }\n\n async resume(): Promise<void> {\n this.#manuallyPaused = false;\n }\n\n config(): ConsolidatorConfig {\n return this.#config;\n }\n\n async registerWithScheduler(scheduler: SchedulerLike): Promise<RegisterTriggersResult> {\n if (this.#defaultScope === null) {\n throw new Error(\n '[graphorin/memory] Consolidator.registerWithScheduler requires a defaultScope. ' +\n 'Pass `defaultScope` to createConsolidator / createMemory, or call ' +\n 'registerConsolidatorTriggers(consolidator, scheduler, { scope }) directly.',\n );\n }\n return registerConsolidatorTriggers(this, scheduler, { scope: this.#defaultScope });\n }\n\n isFree(): boolean {\n return this.#config.tier === 'free';\n }\n\n onPhaseFinished(listener: PhaseListener): () => void {\n this.#listeners.add(listener);\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n recordExternalSpend(tokens: number, costUsd?: number): void {\n if (!Number.isFinite(tokens) || tokens <= 0) return;\n this.#budget.record({ phase: 'deep', tokens, costUsd: costUsd ?? 0 });\n }\n\n async setTier(tier: ConsolidatorTier): Promise<void> {\n const preset = CONSOLIDATOR_TIER_DEFAULTS[tier];\n if (preset === undefined) {\n throw new Error(`[graphorin/memory] unknown consolidator tier '${tier}'`);\n }\n if (tier === 'custom') {\n throw new Error(\n '[graphorin/memory] consolidator.setTier(\"custom\") requires explicit ceilings — re-create the consolidator with `tier: \"custom\"` + `ceilings`.',\n );\n }\n this.#config = Object.freeze({\n ...this.#config,\n tier,\n phases: preset.phases,\n ceilings: preset.ceilings,\n onExceed: preset.onExceed,\n formEpisodes: preset.formEpisodes,\n importanceScoring: preset.importanceScoring,\n reflection: preset.reflection,\n importanceThreshold: preset.importanceThreshold,\n reflectionMaxQuestions: preset.reflectionMaxQuestions,\n contextualRetrieval: preset.contextualRetrieval,\n });\n this.#budget.reconfigure({\n maxTokensPerDay: preset.ceilings.maxTokensPerDay,\n maxCostPerDay: preset.ceilings.maxCostPerDay,\n });\n }\n\n async status(): Promise<ConsolidatorStatus> {\n let lastRunAt: string | undefined;\n let lastPhase: ConsolidatorPhase | undefined;\n const lastRuns: { -readonly [K in keyof ConsolidatorLastRuns]: ConsolidatorLastRuns[K] } = {};\n let pendingConflicts = 0;\n let dlqSize = 0;\n let deferredFromStore = 0;\n if (this.#consolidatorStore !== null && this.#defaultScope !== null) {\n const state = await this.#consolidatorStore.getState(this.#defaultScope);\n if (state !== null) {\n if (state.lastCompletedAt !== null) {\n lastRunAt = new Date(state.lastCompletedAt).toISOString();\n }\n if (state.lastPhase !== null) {\n lastPhase = state.lastPhase;\n }\n }\n const failed = await this.#consolidatorStore.listFailedBatches(this.#defaultScope, 1000);\n dlqSize = failed.length;\n // Hydrate per-phase `lastRuns` and the persisted deferred_runs\n // counter from the run audit log so process restart preserves\n // both surfaces.\n const recent = await this.#consolidatorStore.listRecentRuns(this.#defaultScope, 500);\n for (const run of recent) {\n if (run.status === 'deferred') {\n deferredFromStore += 1;\n continue;\n }\n if (run.status !== 'completed' && run.status !== 'partial') continue;\n const stamp = new Date(run.finishedAt ?? run.startedAt).toISOString();\n if (run.phase === 'light' && lastRuns.light === undefined) lastRuns.light = stamp;\n else if (run.phase === 'standard' && lastRuns.standard === undefined)\n lastRuns.standard = stamp;\n else if (run.phase === 'deep' && lastRuns.deep === undefined) lastRuns.deep = stamp;\n }\n }\n if (this.#store.conflicts !== undefined && this.#defaultScope !== null) {\n const list = await this.#store.conflicts.listPending(this.#defaultScope, 1000);\n pendingConflicts = list.length;\n }\n const snapshot = this.#budget.snapshot();\n const persistedDeferred = deferredFromStore + this.#deferredRunsAdjustment;\n const totalDeferred = Math.max(persistedDeferred, this.#deferredRuns);\n const out: ConsolidatorStatus = {\n tier: this.#config.tier,\n triggers: this.#config.triggers,\n phases: this.#config.phases,\n running: this.#running,\n paused: this.#manuallyPaused || snapshot.paused,\n pendingConflicts,\n queueDepth: pendingConflicts,\n dlqSize,\n deferredRuns: totalDeferred,\n emptyExtractions: this.#emptyExtractions,\n lastRuns: Object.freeze(lastRuns),\n budget: {\n tokensUsedToday: snapshot.tokensUsedToday,\n costUsedToday: snapshot.costUsedToday,\n tokensRemaining: snapshot.tokensRemaining,\n costRemaining: snapshot.costRemaining,\n resetAt: snapshot.resetAt,\n },\n budgetRemaining: {\n tokens: snapshot.tokensRemaining,\n costUsd: snapshot.costRemaining,\n },\n ...(lastRunAt !== undefined ? { lastRunAt } : {}),\n ...(lastPhase !== undefined ? { lastPhase } : {}),\n };\n return out;\n }\n\n async trigger(\n reason: ConsolidatorTriggerReason,\n scope: SessionScope,\n ): Promise<PhaseOutcome | null> {\n if (!this.#running) return null;\n if (this.#manuallyPaused) return null;\n const phases = this.#planPhases(reason);\n if (phases.length === 0) return null;\n // MCON-8: enforce the cooldown the runtime always PERSISTED but never\n // read — back-to-back triggers used to fire with zero quiet period.\n // Checked ONCE per trigger dispatch (phases within one dispatch chain\n // freely); manual fireNow(...) / DLQ replays bypass it.\n if (reason.kind !== 'manual' && this.#config.ceilings.cooldownMs > 0) {\n const state =\n this.#consolidatorStore !== null ? await this.#consolidatorStore.getState(scope) : null;\n const eligibleAt = state?.nextEligibleAt ?? null;\n const firstPhase = phases[0] ?? 'light';\n if (eligibleAt !== null && this.#now() < eligibleAt) {\n return this.#emit(\n { ...skipOutcome(firstPhase, 'cooldown'), phase: firstPhase },\n scope,\n reason,\n );\n }\n }\n let last: PhaseOutcome | null = null;\n for (const phase of phases) {\n last = await this.#runPhase(phase, scope, reason);\n if (last?.status === 'failed' || last?.status === 'deferred') break;\n }\n return last;\n }\n\n async fireNow(phase: ConsolidatorPhase, scope?: SessionScope): Promise<PhaseOutcome | null> {\n const target = scope ?? this.#defaultScope;\n if (target === null) {\n throw new Error(\n '[graphorin/memory] consolidator.fireNow requires a scope (default scope was not configured).',\n );\n }\n if (!this.#config.phases.includes(phase) && phase !== 'light') {\n // MCON-17: manual flushes bypass phase gating ON PURPOSE — but the\n // operator should know they are running a phase the tier disabled\n // (the old empty branch promised a warn and emitted nothing).\n process.stderr.write(\n `[graphorin/memory] consolidator.fireNow('${phase}') runs a phase not enabled for tier '${this.#config.tier}' — proceeding (manual flushes bypass phase gating).\\n`,\n );\n }\n return this.#runPhase(phase, target, { kind: 'manual', value: phase });\n }\n\n async drainDlq(scope: SessionScope): Promise<number> {\n const store = this.#consolidatorStore;\n if (store === null) return 0;\n const ready = await store.claimReadyBatches(scope, this.#now(), 50);\n let drained = 0;\n for (const row of ready) {\n // MCON-10: replay the phase that actually failed (persisted at\n // enqueue); legacy rows without one fall back to 'standard',\n // matching the old behaviour.\n const replayPhase: ConsolidatorPhase = row.phase ?? 'standard';\n let succeeded = false;\n let deferred = false;\n let lastError: unknown = null;\n try {\n // MCON-10: replays run through the FULL phase envelope — budget\n // precheck, scope lock, and recordRunStart/Finish — so they are\n // visible in `consolidator_runs` and cannot race a live run or\n // spend past a pause.\n const outcome = await this.#runPhase(replayPhase, scope, {\n kind: 'manual',\n value: `dlq-replay:${replayPhase}`,\n });\n if (outcome?.status === 'completed' || outcome?.status === 'partial') {\n succeeded = true;\n } else if (outcome?.status === 'deferred') {\n deferred = true;\n lastError = new Error(outcome.errorMessage ?? 'replay deferred');\n } else if (outcome?.errorMessage != null) {\n lastError = new Error(outcome.errorMessage);\n }\n } catch (err) {\n lastError = err;\n }\n if (succeeded) {\n await store.markBatchSucceeded(row.id);\n drained += 1;\n continue;\n }\n if (deferred) {\n // Lock contention / budget pause is transient — reschedule at the\n // SAME retry count so contention cannot exhaust the row.\n const delayMs = nextBackoffMs({\n retryCount: row.retryCount + 1,\n baseMs: this.#config.dlqBaseBackoffMs,\n maxMs: this.#config.dlqMaxBackoffMs,\n });\n await store.rescheduleBatch(row.id, row.retryCount, this.#now() + delayMs);\n continue;\n }\n const nextRetryCount = row.retryCount + 1;\n if (nextRetryCount >= this.#config.dlqMaxRetries) {\n await store.markBatchExhausted(row.id, describeError(lastError), nextRetryCount);\n } else {\n const delayMs = nextBackoffMs({\n retryCount: nextRetryCount,\n baseMs: this.#config.dlqBaseBackoffMs,\n maxMs: this.#config.dlqMaxBackoffMs,\n });\n await store.rescheduleBatch(row.id, nextRetryCount, this.#now() + delayMs);\n }\n }\n return drained;\n }\n\n // ---------------------------------------------------------------------------\n\n #planPhases(reason: ConsolidatorTriggerReason): ConsolidatorPhase[] {\n const enabled = new Set(this.#config.phases);\n const planned: ConsolidatorPhase[] = [];\n if (reason.kind === 'turn' || reason.kind === 'idle' || reason.kind === 'event') {\n if (enabled.has('light')) planned.push('light');\n if (enabled.has('standard')) planned.push('standard');\n } else if (reason.kind === 'cron' || reason.kind === 'manual' || reason.kind === 'budget') {\n if (enabled.has('light')) planned.push('light');\n if (enabled.has('standard')) planned.push('standard');\n if (enabled.has('deep')) planned.push('deep');\n }\n return planned;\n }\n\n async #runPhase(\n phase: ConsolidatorPhase,\n scope: SessionScope,\n reason: ConsolidatorTriggerReason,\n ): Promise<PhaseOutcome | null> {\n const precheck = this.#budget.precheck(phase);\n if (!precheck.allowed && phase !== 'light') {\n return this.#emit(\n { ...skipOutcome(phase, precheck.reason ?? 'paused'), phase },\n scope,\n reason,\n );\n }\n const acquisition = await this.#lockManager.acquire(scope);\n if (acquisition.kind === 'deferred') {\n this.#deferredRuns += 1;\n const deferredRunId = this.#randomId();\n const deferredAt = this.#now();\n // Persist the deferral as a `consolidator_runs` row so the\n // counter survives process restart (DEC-150 — same code path\n // lib + server). Adapters that do not expose the consolidator\n // surface fall through to the in-memory adjustment counter.\n if (this.#consolidatorStore !== null) {\n try {\n await this.#consolidatorStore.recordRunStart({\n id: deferredRunId,\n scope,\n triggerKind: reason.kind,\n phase,\n startedAt: deferredAt,\n });\n await this.#consolidatorStore.recordRunFinish({\n id: deferredRunId,\n finishedAt: deferredAt,\n status: 'deferred',\n errorMessage: `lock held by ${acquisition.heldBy ?? 'unknown'}`,\n });\n } catch {\n this.#deferredRunsAdjustment += 1;\n }\n } else {\n this.#deferredRunsAdjustment += 1;\n }\n return this.#emit(\n {\n phase,\n status: 'deferred',\n factsCreated: 0,\n factsUpdated: 0,\n conflictsResolved: 0,\n episodesFormed: 0,\n insightsCreated: 0,\n noiseFilteredCount: 0,\n emptyExtractions: 0,\n llmTokensUsed: 0,\n llmCostUsd: null,\n errorMessage: `lock held by ${acquisition.heldBy ?? 'unknown'}`,\n },\n scope,\n reason,\n );\n }\n const runId = acquisition.runId;\n const startedAt = this.#now();\n if (this.#consolidatorStore !== null) {\n await this.#consolidatorStore.recordRunStart({\n id: runId,\n scope,\n triggerKind: reason.kind,\n phase,\n startedAt,\n });\n }\n let outcome: PhaseOutcome;\n try {\n outcome = await this.#dispatch(phase, scope);\n } catch (err) {\n outcome = {\n phase,\n status: 'failed',\n factsCreated: 0,\n factsUpdated: 0,\n conflictsResolved: 0,\n episodesFormed: 0,\n insightsCreated: 0,\n noiseFilteredCount: 0,\n emptyExtractions: 0,\n llmTokensUsed: 0,\n llmCostUsd: null,\n errorMessage: describeError(err),\n };\n // MCON-10: a failed DLQ *replay* must not enqueue a fresh DLQ row —\n // the original row already tracks the failure (drainDlq reschedules\n // or exhausts it); double-tracking multiplies rows per drain.\n const isDlqReplay =\n reason.kind === 'manual' &&\n typeof reason.value === 'string' &&\n reason.value.startsWith('dlq-replay:');\n if (this.#consolidatorStore !== null && !isDlqReplay) {\n await this.#consolidatorStore.enqueueFailedBatch({\n id: this.#randomId(),\n consolidatorRunId: runId,\n scope,\n // MCON-10: capture the slice that actually failed so replays\n // can be audited against it; the cursor may move past this\n // window before the replay fires.\n messageIds: this.#lastDispatchMessageIds,\n errorKind: classifyError(err),\n errorMessage: describeError(err),\n failedAt: this.#now(),\n nextRetryAt:\n this.#now() +\n nextBackoffMs({\n retryCount: 0,\n baseMs: this.#config.dlqBaseBackoffMs,\n maxMs: this.#config.dlqMaxBackoffMs,\n }),\n retryCount: 0,\n // MCON-10: persist the failed phase — replays must re-run the\n // SAME phase, not an inferred 'standard'.\n phase,\n });\n }\n }\n if (outcome.emptyExtractions > 0) {\n this.#emptyExtractions += outcome.emptyExtractions;\n }\n if (this.#consolidatorStore !== null) {\n await this.#consolidatorStore.recordRunFinish({\n id: runId,\n finishedAt: this.#now(),\n status: outcome.status,\n llmTokensUsed: outcome.llmTokensUsed,\n llmCostUsd: outcome.llmCostUsd,\n factsCreated: outcome.factsCreated,\n factsUpdated: outcome.factsUpdated,\n conflictsResolved: outcome.conflictsResolved,\n noiseFilteredCount: outcome.noiseFilteredCount,\n emptyExtractions: outcome.emptyExtractions,\n // MCON-17: the P1-2 / P1-1 counters were computed on the outcome\n // and then dropped — the run audit lost them.\n episodesFormed: outcome.episodesFormed,\n insightsCreated: outcome.insightsCreated,\n errorMessage: outcome.errorMessage,\n });\n await this.#consolidatorStore.upsertState(scope, {\n lastPhase: phase,\n lastCompletedAt: this.#now(),\n nextEligibleAt: this.#now() + this.#config.ceilings.cooldownMs,\n });\n }\n await this.#lockManager.release(scope, runId);\n return this.#emit(outcome, scope, reason);\n }\n\n async #dispatch(phase: ConsolidatorPhase, scope: SessionScope): Promise<PhaseOutcome> {\n // MCON-10: reset the failed-slice capture per dispatch; the standard\n // branch refills it from the batch it actually processes.\n this.#lastDispatchMessageIds = [];\n const state =\n this.#consolidatorStore !== null ? await this.#consolidatorStore.getState(scope) : null;\n const lastProcessedMessageId = state?.lastProcessedMessageId ?? null;\n if (phase === 'light') {\n const out = await runLightPhase({\n store: this.#store,\n consolidatorStore: this.#consolidatorStore,\n tracer: this.#tracer,\n scope,\n now: this.#now,\n decayTauDays: this.#config.decayTauDays,\n decayArchiveThreshold: this.#config.decayArchiveThreshold,\n decayCapacity: this.#config.decayCapacity,\n salienceWeights: this.#config.salienceWeights,\n noiseFilters: this.#config.noiseFilters as ReadonlyArray<NoiseFilterPreset>,\n maxBatchSize: this.#config.maxStandardBatchSize,\n lastProcessedMessageId,\n tier: this.#config.tier,\n });\n return out;\n }\n if (phase === 'standard') {\n const standardProvider = this.#cheapProvider ?? this.#provider;\n if (standardProvider === null) {\n throw new ProviderNotConfiguredError('standard');\n }\n const session = this.#store.session;\n const rawBatch =\n typeof session.listMessagesSince === 'function'\n ? await session.listMessagesSince(\n scope,\n lastProcessedMessageId,\n this.#config.maxStandardBatchSize,\n )\n : [];\n this.#lastDispatchMessageIds = rawBatch.map((r) => r.id);\n const out = await runStandardPhase({\n semantic: this.#semantic,\n episodic: this.#episodic,\n formEpisodes: this.#config.formEpisodes,\n importanceScoring: this.#config.importanceScoring,\n autoPromoteExtraction: this.#config.autoPromoteExtraction,\n contextualRetrieval: this.#config.contextualRetrieval,\n store: this.#store,\n consolidatorStore: this.#consolidatorStore,\n // MCON-7: the standard phase routes to the cheap-tier provider\n // when one is configured.\n provider: standardProvider,\n tracer: this.#tracer,\n scope,\n cheapModel: this.#config.cheapModel,\n noiseFilters: this.#config.noiseFilters as ReadonlyArray<NoiseFilterPreset>,\n maxBatchSize: this.#config.maxStandardBatchSize,\n lastProcessedMessageId,\n budget: this.#budget,\n tier: this.#config.tier === 'free' ? 'cheap' : this.#config.tier,\n now: this.#now,\n batch: rawBatch,\n });\n const cursor = tipMessageId(rawBatch);\n if (cursor !== null && this.#consolidatorStore !== null) {\n await this.#consolidatorStore.upsertState(scope, {\n lastProcessedMessageId: cursor,\n });\n }\n return out;\n }\n const deepProvider = this.#deepProvider ?? this.#provider;\n if (deepProvider === null) {\n throw new ProviderNotConfiguredError('deep');\n }\n const deepOut = await runDeepPhase({\n store: this.#store,\n consolidatorStore: this.#consolidatorStore,\n provider: deepProvider,\n tracer: this.#tracer,\n scope,\n deepModel: this.#config.deepModel,\n maxConflictsPerRun: this.#config.maxDeepConflictsPerRun,\n budget: this.#budget,\n tier:\n this.#config.tier === 'free' || this.#config.tier === 'cheap'\n ? 'standard'\n : this.#config.tier,\n now: this.#now,\n });\n // Reflection pass (P1-1) runs after the conflict drain, reusing the\n // deep run's lock + budget + audit window. Triple-gated: enabled by\n // config, an episodic tier present (importance source), and an\n // insight-capable storage adapter. The accumulated-importance\n // threshold is enforced inside the pass.\n const insightStore = this.#store.insights;\n if (this.#config.reflection && this.#episodic !== null && insightStore !== undefined) {\n // MCON-13: read the persisted reflection watermark so the gate only\n // accumulates importance from episodes newer than the last pass, and\n // persist the advanced value afterwards (a no-op when unchanged).\n const priorWatermark =\n this.#consolidatorStore !== null\n ? ((await this.#consolidatorStore.getState(scope))?.reflectionWatermark ?? null)\n : null;\n const reflection = await runReflectionPass({\n // MCON-7: reflection rides the deep-tier provider.\n provider: deepProvider,\n tracer: this.#tracer,\n scope,\n semantic: this.#semantic,\n episodic: this.#episodic,\n insights: insightStore,\n budget: this.#budget,\n importanceThreshold: this.#config.importanceThreshold,\n reflectionWatermark: priorWatermark,\n maxQuestions: this.#config.reflectionMaxQuestions,\n now: this.#now,\n });\n if (this.#consolidatorStore !== null && reflection.nextWatermark !== priorWatermark) {\n await this.#consolidatorStore.upsertState(scope, {\n reflectionWatermark: reflection.nextWatermark,\n });\n }\n return {\n ...deepOut,\n insightsCreated: reflection.insightsCreated,\n llmTokensUsed: deepOut.llmTokensUsed + reflection.tokens,\n llmCostUsd:\n deepOut.llmCostUsd === null && reflection.costUsd === 0\n ? null\n : (deepOut.llmCostUsd ?? 0) + reflection.costUsd,\n };\n }\n return deepOut;\n }\n\n #emit(\n outcome: PhaseOutcome,\n scope: SessionScope,\n reason: ConsolidatorTriggerReason,\n ): PhaseOutcome {\n for (const listener of this.#listeners) {\n try {\n listener({ ...outcome, scope, trigger: reason });\n } catch {\n // Listeners must never break the runtime.\n }\n }\n return outcome;\n }\n}\n\nfunction skipOutcome(\n phase: ConsolidatorPhase,\n reason: 'tokens-exceeded' | 'cost-exceeded' | 'paused' | 'cooldown',\n): PhaseOutcome {\n return {\n phase,\n status: 'deferred',\n factsCreated: 0,\n factsUpdated: 0,\n conflictsResolved: 0,\n episodesFormed: 0,\n insightsCreated: 0,\n noiseFilteredCount: 0,\n emptyExtractions: 0,\n llmTokensUsed: 0,\n llmCostUsd: null,\n errorMessage: `phase skipped — ${reason}`,\n };\n}\n\n/** Resolve operator-supplied options into a fully-defaulted config. */\nfunction resolveConfig(opts: CreateConsolidatorOptions): ConsolidatorConfig {\n const tier: ConsolidatorTier = opts.tier ?? 'free';\n const preset = CONSOLIDATOR_TIER_DEFAULTS[tier];\n if (preset === undefined) {\n throw new Error(`[graphorin/memory] unknown consolidator tier '${tier}'`);\n }\n const ceilings = {\n ...preset.ceilings,\n ...(opts.ceilings ?? {}),\n };\n const phases = opts.phases ?? preset.phases;\n if (tier === 'custom') {\n const missing: string[] = [];\n if (ceilings.maxTokensPerDay <= 0) missing.push('ceilings.maxTokensPerDay');\n if (ceilings.maxCostPerDay <= 0) missing.push('ceilings.maxCostPerDay');\n if (phases.length === 0) missing.push('phases');\n if (missing.length > 0) {\n throw new CustomTierMisconfiguredError(missing);\n }\n }\n return Object.freeze({\n triggers: Object.freeze([...(opts.triggers ?? defaultTriggers())] as ConsolidatorTriggerSpec[]),\n tier,\n phases: Object.freeze([...phases]) as ReadonlyArray<ConsolidatorPhase>,\n ceilings,\n onExceed: opts.onExceed ?? preset.onExceed,\n cheapModel: opts.cheapModel ?? preset.cheapModel,\n deepModel: opts.deepModel ?? preset.deepModel,\n budgetResetSemantics: opts.budgetResetSemantics ?? 'utc',\n noiseFilters: Object.freeze([...(opts.noiseFilters ?? ['default'])]),\n lockWaitMs: opts.lockWaitMs ?? 30_000,\n decayTauDays: opts.decayTauDays ?? 7,\n decayArchiveThreshold: opts.decayArchiveThreshold ?? 0.05,\n decayCapacity: opts.decayCapacity ?? null,\n salienceWeights: opts.salienceWeights ?? DEFAULT_SALIENCE_WEIGHTS,\n maxStandardBatchSize: opts.maxStandardBatchSize ?? 50,\n maxDeepConflictsPerRun: opts.maxDeepConflictsPerRun ?? 20,\n dlqMaxRetries: opts.dlqMaxRetries ?? 5,\n dlqBaseBackoffMs: opts.dlqBaseBackoffMs ?? 60_000,\n dlqMaxBackoffMs: opts.dlqMaxBackoffMs ?? 60 * 60 * 1000,\n formEpisodes: opts.formEpisodes ?? preset.formEpisodes,\n importanceScoring: opts.importanceScoring ?? preset.importanceScoring,\n // MCON-2: opt-in only — fail-safe (quarantine) stays the default at every tier.\n autoPromoteExtraction: opts.autoPromoteExtraction ?? false,\n reflection: opts.reflection ?? preset.reflection,\n importanceThreshold: opts.importanceThreshold ?? preset.importanceThreshold,\n reflectionMaxQuestions: opts.reflectionMaxQuestions ?? preset.reflectionMaxQuestions,\n contextualRetrieval: opts.contextualRetrieval ?? preset.contextualRetrieval,\n });\n}\n\nfunction defaultTriggers(): ConsolidatorTriggerSpec[] {\n // `idle:5m` drives the light + standard phases between sessions; the daily\n // cron is what makes the **deep** phase reachable (it drains the deferred\n // conflict-check queue + runs reflection — `#planPhases` only schedules deep\n // for cron / manual / budget reasons). A `turn:N` default was dropped: the\n // scheduler cannot count user turns, so it was inert unless a consumer\n // emitted `trigger({ kind: 'turn' })` itself (MCON-4).\n return ['idle:5m', 'cron:0 4 * * *'];\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ConsolidatorTriggerSpec } from "./types.js";
|
|
2
|
+
import { ParsedTrigger } from "./triggers.js";
|
|
3
|
+
import { Consolidator } from "./runtime.js";
|
|
4
|
+
import { SessionScope } from "@graphorin/core";
|
|
5
|
+
|
|
6
|
+
//#region src/consolidator/scheduler.d.ts
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Catch-up policy applied when a trigger missed one or more fires
|
|
10
|
+
* while the scheduler was offline. Mirrors
|
|
11
|
+
* `@graphorin/triggers`'s `CatchupPolicy` — duplicated here so the
|
|
12
|
+
* memory package stays import-free at the type level.
|
|
13
|
+
*
|
|
14
|
+
* @stable
|
|
15
|
+
*/
|
|
16
|
+
type ConsolidatorCatchupPolicy = 'none' | 'last' | 'all';
|
|
17
|
+
/**
|
|
18
|
+
* Subset of the `@graphorin/triggers` Scheduler surface the bridge
|
|
19
|
+
* needs. Defined structurally so consumers can inject mocks in
|
|
20
|
+
* tests without taking the package dependency.
|
|
21
|
+
*
|
|
22
|
+
* @stable
|
|
23
|
+
*/
|
|
24
|
+
interface SchedulerLike {
|
|
25
|
+
register(declaration: TriggerDeclarationLike): Promise<unknown>;
|
|
26
|
+
unregister?(id: string): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Subset of `@graphorin/triggers`'s `TriggerDeclaration` accepted
|
|
30
|
+
* by {@link SchedulerLike.register}. Field names mirror the
|
|
31
|
+
* upstream shape exactly so the structural type is interchangeable
|
|
32
|
+
* with the real export.
|
|
33
|
+
*
|
|
34
|
+
* @stable
|
|
35
|
+
*/
|
|
36
|
+
interface TriggerDeclarationLike {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly kind: 'cron' | 'interval' | 'idle' | 'event';
|
|
39
|
+
readonly spec: string;
|
|
40
|
+
readonly callback: (payload?: unknown) => void | Promise<void>;
|
|
41
|
+
readonly options: {
|
|
42
|
+
readonly catchupPolicy?: ConsolidatorCatchupPolicy;
|
|
43
|
+
readonly maxCatchupRuns?: number;
|
|
44
|
+
readonly catchupWindowMs?: number;
|
|
45
|
+
readonly tags?: ReadonlyArray<string>;
|
|
46
|
+
readonly acknowledgeLibMode?: boolean;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Options accepted by {@link registerConsolidatorTriggers}. */
|
|
50
|
+
interface RegisterTriggersOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Scope passed to `consolidator.trigger(...)` for every fire. The
|
|
53
|
+
* consolidator only operates on a single user/session at a time
|
|
54
|
+
* (DEC-005 single-user-per-process); multi-tenant deployments
|
|
55
|
+
* register one scheduler-trigger pair per scope.
|
|
56
|
+
*/
|
|
57
|
+
readonly scope: SessionScope;
|
|
58
|
+
/**
|
|
59
|
+
* Catch-up policy applied to every registered trigger. Defaults
|
|
60
|
+
* to `'none'` per DEC-150 — safest for personal-assistant
|
|
61
|
+
* scenarios.
|
|
62
|
+
*/
|
|
63
|
+
readonly catchupPolicy?: ConsolidatorCatchupPolicy;
|
|
64
|
+
/** Suppress the per-process library-mode WARN. Defaults to `true`. */
|
|
65
|
+
readonly acknowledgeLibMode?: boolean;
|
|
66
|
+
/** Optional id prefix — useful when multiple scopes share a scheduler. */
|
|
67
|
+
readonly idPrefix?: string;
|
|
68
|
+
/** Optional tags forwarded to the scheduler. */
|
|
69
|
+
readonly tags?: ReadonlyArray<string>;
|
|
70
|
+
/**
|
|
71
|
+
* Filter — only register the supplied subset of triggers. By
|
|
72
|
+
* default every parseable trigger declared on the consolidator is
|
|
73
|
+
* registered (turn / event triggers are skipped because the
|
|
74
|
+
* Scheduler has no way to fire them on its own).
|
|
75
|
+
*/
|
|
76
|
+
readonly include?: ReadonlyArray<ConsolidatorTriggerSpec>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Outcome of {@link registerConsolidatorTriggers}.
|
|
80
|
+
*
|
|
81
|
+
* @stable
|
|
82
|
+
*/
|
|
83
|
+
interface RegisterTriggersResult {
|
|
84
|
+
readonly registered: ReadonlyArray<{
|
|
85
|
+
readonly id: string;
|
|
86
|
+
readonly kind: ParsedTrigger['kind'];
|
|
87
|
+
readonly raw: string;
|
|
88
|
+
}>;
|
|
89
|
+
readonly skipped: ReadonlyArray<{
|
|
90
|
+
readonly raw: string;
|
|
91
|
+
readonly reason: 'unsupported-by-scheduler' | 'filtered-out';
|
|
92
|
+
}>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Register every cron / idle / interval trigger declared on the
|
|
96
|
+
* supplied consolidator with the scheduler. Each trigger fires
|
|
97
|
+
* `consolidator.trigger(reason, scope)` so the lib-mode + server
|
|
98
|
+
* paths converge on the same handler.
|
|
99
|
+
*
|
|
100
|
+
* Turn triggers (`turn:N`) and event triggers (`event:NAME`) are
|
|
101
|
+
* skipped — the scheduler cannot count user turns autonomously, and
|
|
102
|
+
* event triggers fire from the consumer's emit path. The runtime
|
|
103
|
+
* caller is responsible for those (e.g. invoking
|
|
104
|
+
* `consolidator.trigger(...)` from the agent loop).
|
|
105
|
+
*
|
|
106
|
+
* @stable
|
|
107
|
+
*/
|
|
108
|
+
declare function registerConsolidatorTriggers(consolidator: Consolidator, scheduler: SchedulerLike, options: RegisterTriggersOptions): Promise<RegisterTriggersResult>;
|
|
109
|
+
//#endregion
|
|
110
|
+
export { ConsolidatorCatchupPolicy, RegisterTriggersOptions, RegisterTriggersResult, SchedulerLike, TriggerDeclarationLike, registerConsolidatorTriggers };
|
|
111
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":[],"mappings":";;;;;;;AAgEA;;;;;;;AAkCA;AAGmB,KA1EP,yBAAA,GA0EO,MAAA,GAAA,MAAA,GAAA,KAAA;;;;AAuBnB;;;;AAIW,UA5FM,aAAA,CA4FN;EAAR,QAAA,CAAA,WAAA,EA3FqB,sBA2FrB,CAAA,EA3F8C,OA2F9C,CAAA,OAAA,CAAA;EAAO,UAAA,EAAA,EAAA,EAAA,MAAA,CAAA,EA1FiB,OA0FjB,CAAA,IAAA,CAAA;;;;;;;;;;UA/EO,sBAAA;;;;mDAIkC;;6BAEtB;;;oBAGT;;;;;UAMH,uBAAA;;;;;;;kBAOC;;;;;;2BAMS;;;;;;kBAMT;;;;;;;qBAOG,cAAc;;;;;;;UAQlB,sBAAA;uBACM;;mBAEJ;;;oBAGC;;;;;;;;;;;;;;;;;;;iBAoBE,4BAAA,eACN,yBACH,wBACF,0BACR,QAAQ"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { parseTriggerSpec, reasonFromTrigger } from "./triggers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/consolidator/scheduler.ts
|
|
4
|
+
/**
|
|
5
|
+
* Register every cron / idle / interval trigger declared on the
|
|
6
|
+
* supplied consolidator with the scheduler. Each trigger fires
|
|
7
|
+
* `consolidator.trigger(reason, scope)` so the lib-mode + server
|
|
8
|
+
* paths converge on the same handler.
|
|
9
|
+
*
|
|
10
|
+
* Turn triggers (`turn:N`) and event triggers (`event:NAME`) are
|
|
11
|
+
* skipped — the scheduler cannot count user turns autonomously, and
|
|
12
|
+
* event triggers fire from the consumer's emit path. The runtime
|
|
13
|
+
* caller is responsible for those (e.g. invoking
|
|
14
|
+
* `consolidator.trigger(...)` from the agent loop).
|
|
15
|
+
*
|
|
16
|
+
* @stable
|
|
17
|
+
*/
|
|
18
|
+
async function registerConsolidatorTriggers(consolidator, scheduler, options) {
|
|
19
|
+
const config = consolidator.config();
|
|
20
|
+
const include = options.include ?? config.triggers;
|
|
21
|
+
const acknowledgeLibMode = options.acknowledgeLibMode ?? true;
|
|
22
|
+
const catchupPolicy = options.catchupPolicy ?? "none";
|
|
23
|
+
const registered = [];
|
|
24
|
+
const skipped = [];
|
|
25
|
+
const seenSet = new Set(include);
|
|
26
|
+
for (const spec of config.triggers) {
|
|
27
|
+
if (!seenSet.has(spec)) {
|
|
28
|
+
skipped.push({
|
|
29
|
+
raw: spec,
|
|
30
|
+
reason: "filtered-out"
|
|
31
|
+
});
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const parsed = parseTriggerSpec(spec);
|
|
35
|
+
if (parsed.kind === "turn" || parsed.kind === "event" || parsed.kind === "budget") {
|
|
36
|
+
skipped.push({
|
|
37
|
+
raw: parsed.raw,
|
|
38
|
+
reason: "unsupported-by-scheduler"
|
|
39
|
+
});
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const id = `${options.idPrefix ?? "consolidator"}:${parsed.raw}`;
|
|
43
|
+
const declaration = {
|
|
44
|
+
id,
|
|
45
|
+
kind: parsed.kind === "cron" ? "cron" : "idle",
|
|
46
|
+
spec: parsed.kind === "cron" ? parsed.expression : String(parsed.idleMs),
|
|
47
|
+
async callback() {
|
|
48
|
+
await consolidator.trigger(reasonFromTrigger(parsed), options.scope);
|
|
49
|
+
},
|
|
50
|
+
options: {
|
|
51
|
+
catchupPolicy,
|
|
52
|
+
acknowledgeLibMode,
|
|
53
|
+
...options.tags !== void 0 ? { tags: options.tags } : {}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
await scheduler.register(declaration);
|
|
57
|
+
registered.push({
|
|
58
|
+
id,
|
|
59
|
+
kind: parsed.kind,
|
|
60
|
+
raw: parsed.raw
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
registered: Object.freeze(registered),
|
|
65
|
+
skipped: Object.freeze(skipped)
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { registerConsolidatorTriggers };
|
|
71
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":["catchupPolicy: ConsolidatorCatchupPolicy","registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }>","skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }>","declaration: TriggerDeclarationLike"],"sources":["../../src/consolidator/scheduler.ts"],"sourcesContent":["/**\n * Bridge helpers — wire a {@link Consolidator} into the\n * `@graphorin/triggers` Scheduler so trigger declarations fire on\n * the documented schedule (DEC-150 — \"same code path lib + server\").\n *\n * The bridge is structural: any object that exposes the same shape\n * as `@graphorin/triggers`'s `Scheduler` (a `register(declaration)`\n * method) works. The structural import keeps `@graphorin/memory`\n * dependency-free at the type level — the workspace consumer\n * supplies the actual scheduler at runtime.\n *\n * @packageDocumentation\n */\n\nimport type { SessionScope } from '@graphorin/core';\nimport type { Consolidator } from './runtime.js';\nimport { type ParsedTrigger, parseTriggerSpec, reasonFromTrigger } from './triggers.js';\nimport type { ConsolidatorTriggerSpec } from './types.js';\n\n/**\n * Catch-up policy applied when a trigger missed one or more fires\n * while the scheduler was offline. Mirrors\n * `@graphorin/triggers`'s `CatchupPolicy` — duplicated here so the\n * memory package stays import-free at the type level.\n *\n * @stable\n */\nexport type ConsolidatorCatchupPolicy = 'none' | 'last' | 'all';\n\n/**\n * Subset of the `@graphorin/triggers` Scheduler surface the bridge\n * needs. Defined structurally so consumers can inject mocks in\n * tests without taking the package dependency.\n *\n * @stable\n */\nexport interface SchedulerLike {\n register(declaration: TriggerDeclarationLike): Promise<unknown>;\n unregister?(id: string): Promise<void>;\n}\n\n/**\n * Subset of `@graphorin/triggers`'s `TriggerDeclaration` accepted\n * by {@link SchedulerLike.register}. Field names mirror the\n * upstream shape exactly so the structural type is interchangeable\n * with the real export.\n *\n * @stable\n */\nexport interface TriggerDeclarationLike {\n readonly id: string;\n readonly kind: 'cron' | 'interval' | 'idle' | 'event';\n readonly spec: string;\n readonly callback: (payload?: unknown) => void | Promise<void>;\n readonly options: {\n readonly catchupPolicy?: ConsolidatorCatchupPolicy;\n readonly maxCatchupRuns?: number;\n readonly catchupWindowMs?: number;\n readonly tags?: ReadonlyArray<string>;\n readonly acknowledgeLibMode?: boolean;\n };\n}\n\n/** Options accepted by {@link registerConsolidatorTriggers}. */\nexport interface RegisterTriggersOptions {\n /**\n * Scope passed to `consolidator.trigger(...)` for every fire. The\n * consolidator only operates on a single user/session at a time\n * (DEC-005 single-user-per-process); multi-tenant deployments\n * register one scheduler-trigger pair per scope.\n */\n readonly scope: SessionScope;\n /**\n * Catch-up policy applied to every registered trigger. Defaults\n * to `'none'` per DEC-150 — safest for personal-assistant\n * scenarios.\n */\n readonly catchupPolicy?: ConsolidatorCatchupPolicy;\n /** Suppress the per-process library-mode WARN. Defaults to `true`. */\n readonly acknowledgeLibMode?: boolean;\n /** Optional id prefix — useful when multiple scopes share a scheduler. */\n readonly idPrefix?: string;\n /** Optional tags forwarded to the scheduler. */\n readonly tags?: ReadonlyArray<string>;\n /**\n * Filter — only register the supplied subset of triggers. By\n * default every parseable trigger declared on the consolidator is\n * registered (turn / event triggers are skipped because the\n * Scheduler has no way to fire them on its own).\n */\n readonly include?: ReadonlyArray<ConsolidatorTriggerSpec>;\n}\n\n/**\n * Outcome of {@link registerConsolidatorTriggers}.\n *\n * @stable\n */\nexport interface RegisterTriggersResult {\n readonly registered: ReadonlyArray<{\n readonly id: string;\n readonly kind: ParsedTrigger['kind'];\n readonly raw: string;\n }>;\n readonly skipped: ReadonlyArray<{\n readonly raw: string;\n readonly reason: 'unsupported-by-scheduler' | 'filtered-out';\n }>;\n}\n\n/**\n * Register every cron / idle / interval trigger declared on the\n * supplied consolidator with the scheduler. Each trigger fires\n * `consolidator.trigger(reason, scope)` so the lib-mode + server\n * paths converge on the same handler.\n *\n * Turn triggers (`turn:N`) and event triggers (`event:NAME`) are\n * skipped — the scheduler cannot count user turns autonomously, and\n * event triggers fire from the consumer's emit path. The runtime\n * caller is responsible for those (e.g. invoking\n * `consolidator.trigger(...)` from the agent loop).\n *\n * @stable\n */\nexport async function registerConsolidatorTriggers(\n consolidator: Consolidator,\n scheduler: SchedulerLike,\n options: RegisterTriggersOptions,\n): Promise<RegisterTriggersResult> {\n const config = consolidator.config();\n const include = options.include ?? config.triggers;\n const acknowledgeLibMode = options.acknowledgeLibMode ?? true;\n const catchupPolicy: ConsolidatorCatchupPolicy = options.catchupPolicy ?? 'none';\n const registered: Array<{ id: string; kind: ParsedTrigger['kind']; raw: string }> = [];\n const skipped: Array<{ raw: string; reason: 'unsupported-by-scheduler' | 'filtered-out' }> = [];\n const seenSet = new Set(include);\n for (const spec of config.triggers) {\n if (!seenSet.has(spec)) {\n skipped.push({ raw: spec, reason: 'filtered-out' });\n continue;\n }\n const parsed = parseTriggerSpec(spec);\n if (parsed.kind === 'turn' || parsed.kind === 'event' || parsed.kind === 'budget') {\n skipped.push({ raw: parsed.raw, reason: 'unsupported-by-scheduler' });\n continue;\n }\n const id = `${options.idPrefix ?? 'consolidator'}:${parsed.raw}`;\n const kind: 'cron' | 'idle' | 'interval' = parsed.kind === 'cron' ? 'cron' : 'idle';\n const declaration: TriggerDeclarationLike = {\n id,\n kind,\n spec: parsed.kind === 'cron' ? parsed.expression : String(parsed.idleMs),\n async callback() {\n await consolidator.trigger(reasonFromTrigger(parsed), options.scope);\n },\n options: {\n catchupPolicy,\n acknowledgeLibMode,\n ...(options.tags !== undefined ? { tags: options.tags } : {}),\n },\n };\n await scheduler.register(declaration);\n registered.push({ id, kind: parsed.kind, raw: parsed.raw });\n }\n return Object.freeze({\n registered: Object.freeze(registered),\n skipped: Object.freeze(skipped),\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA4HA,eAAsB,6BACpB,cACA,WACA,SACiC;CACjC,MAAM,SAAS,aAAa,QAAQ;CACpC,MAAM,UAAU,QAAQ,WAAW,OAAO;CAC1C,MAAM,qBAAqB,QAAQ,sBAAsB;CACzD,MAAMA,gBAA2C,QAAQ,iBAAiB;CAC1E,MAAMC,aAA8E,EAAE;CACtF,MAAMC,UAAuF,EAAE;CAC/F,MAAM,UAAU,IAAI,IAAI,QAAQ;AAChC,MAAK,MAAM,QAAQ,OAAO,UAAU;AAClC,MAAI,CAAC,QAAQ,IAAI,KAAK,EAAE;AACtB,WAAQ,KAAK;IAAE,KAAK;IAAM,QAAQ;IAAgB,CAAC;AACnD;;EAEF,MAAM,SAAS,iBAAiB,KAAK;AACrC,MAAI,OAAO,SAAS,UAAU,OAAO,SAAS,WAAW,OAAO,SAAS,UAAU;AACjF,WAAQ,KAAK;IAAE,KAAK,OAAO;IAAK,QAAQ;IAA4B,CAAC;AACrE;;EAEF,MAAM,KAAK,GAAG,QAAQ,YAAY,eAAe,GAAG,OAAO;EAE3D,MAAMC,cAAsC;GAC1C;GACA,MAHyC,OAAO,SAAS,SAAS,SAAS;GAI3E,MAAM,OAAO,SAAS,SAAS,OAAO,aAAa,OAAO,OAAO,OAAO;GACxE,MAAM,WAAW;AACf,UAAM,aAAa,QAAQ,kBAAkB,OAAO,EAAE,QAAQ,MAAM;;GAEtE,SAAS;IACP;IACA;IACA,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;IAC7D;GACF;AACD,QAAM,UAAU,SAAS,YAAY;AACrC,aAAW,KAAK;GAAE;GAAI,MAAM,OAAO;GAAM,KAAK,OAAO;GAAK,CAAC;;AAE7D,QAAO,OAAO,OAAO;EACnB,YAAY,OAAO,OAAO,WAAW;EACrC,SAAS,OAAO,OAAO,QAAQ;EAChC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ConsolidatorTriggerReason, ConsolidatorTriggerSpec } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/consolidator/triggers.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parsed trigger declaration. The `kind` discriminator drives the
|
|
7
|
+
* runtime dispatch; the `value` carries the spec-specific argument
|
|
8
|
+
* already converted to a `number` (turns / millis / threshold) or a
|
|
9
|
+
* string (cron expr / event name).
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
type ParsedTrigger = {
|
|
14
|
+
readonly kind: 'turn';
|
|
15
|
+
readonly count: number;
|
|
16
|
+
readonly raw: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly kind: 'idle';
|
|
19
|
+
readonly idleMs: number;
|
|
20
|
+
readonly raw: string;
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: 'cron';
|
|
23
|
+
readonly expression: string;
|
|
24
|
+
readonly raw: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly kind: 'event';
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly raw: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly kind: 'budget';
|
|
31
|
+
readonly threshold: number;
|
|
32
|
+
readonly raw: string;
|
|
33
|
+
};
|
|
34
|
+
/** Parse a {@link ConsolidatorTriggerSpec} into a {@link ParsedTrigger}. */
|
|
35
|
+
declare function parseTriggerSpec(spec: ConsolidatorTriggerSpec): ParsedTrigger;
|
|
36
|
+
/** Build a {@link ConsolidatorTriggerReason} from a parsed trigger. */
|
|
37
|
+
declare function reasonFromTrigger(trigger: ParsedTrigger): ConsolidatorTriggerReason;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { ParsedTrigger, parseTriggerSpec, reasonFromTrigger };
|
|
40
|
+
//# sourceMappingURL=triggers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triggers.d.ts","names":[],"sources":["../../src/consolidator/triggers.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;KAwBY,aAAA;;;;;;;;;;;;;;;;;;;;;;iBAUI,gBAAA,OAAuB,0BAA0B;;iBAuEjD,iBAAA,UAA2B,gBAAgB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
//#region src/consolidator/triggers.ts
|
|
2
|
+
const IDLE_PATTERN = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/u;
|
|
3
|
+
/** Parse a {@link ConsolidatorTriggerSpec} into a {@link ParsedTrigger}. */
|
|
4
|
+
function parseTriggerSpec(spec) {
|
|
5
|
+
if (typeof spec !== "string" || spec.length === 0) throw new TypeError(`[graphorin/memory] consolidator trigger spec must be a non-empty string, got ${typeof spec}`);
|
|
6
|
+
const colon = spec.indexOf(":");
|
|
7
|
+
if (colon < 0) throw new TypeError(`[graphorin/memory] invalid trigger spec '${spec}' — expected 'kind:value'`);
|
|
8
|
+
const kind = spec.slice(0, colon);
|
|
9
|
+
const value = spec.slice(colon + 1);
|
|
10
|
+
switch (kind) {
|
|
11
|
+
case "turn": {
|
|
12
|
+
const count = Number.parseInt(value, 10);
|
|
13
|
+
if (!Number.isFinite(count) || count <= 0) throw new TypeError(`[graphorin/memory] invalid turn trigger '${spec}' — expected positive integer`);
|
|
14
|
+
return {
|
|
15
|
+
kind: "turn",
|
|
16
|
+
count,
|
|
17
|
+
raw: spec
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
case "idle": {
|
|
21
|
+
const match = IDLE_PATTERN.exec(value);
|
|
22
|
+
if (match === null) throw new TypeError(`[graphorin/memory] invalid idle trigger '${spec}' — expected '5m' / '30s' / '1h'`);
|
|
23
|
+
const idleMs = toMs(Number.parseFloat(match[1] ?? "NaN"), match[2] ?? "ms");
|
|
24
|
+
if (!Number.isFinite(idleMs) || idleMs <= 0) throw new TypeError(`[graphorin/memory] invalid idle trigger '${spec}' — non-positive duration`);
|
|
25
|
+
return {
|
|
26
|
+
kind: "idle",
|
|
27
|
+
idleMs,
|
|
28
|
+
raw: spec
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
case "cron":
|
|
32
|
+
if (value.trim().length === 0) throw new TypeError(`[graphorin/memory] invalid cron trigger '${spec}' — empty expression`);
|
|
33
|
+
return {
|
|
34
|
+
kind: "cron",
|
|
35
|
+
expression: value,
|
|
36
|
+
raw: spec
|
|
37
|
+
};
|
|
38
|
+
case "event":
|
|
39
|
+
if (value.length === 0) throw new TypeError(`[graphorin/memory] invalid event trigger '${spec}' — empty event name`);
|
|
40
|
+
return {
|
|
41
|
+
kind: "event",
|
|
42
|
+
name: value,
|
|
43
|
+
raw: spec
|
|
44
|
+
};
|
|
45
|
+
case "budget": {
|
|
46
|
+
const threshold = Number.parseFloat(value);
|
|
47
|
+
if (!Number.isFinite(threshold) || threshold <= 0 || threshold >= 1) throw new TypeError(`[graphorin/memory] invalid budget trigger '${spec}' — expected fraction between 0 and 1`);
|
|
48
|
+
return {
|
|
49
|
+
kind: "budget",
|
|
50
|
+
threshold,
|
|
51
|
+
raw: spec
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
default: throw new TypeError(`[graphorin/memory] unknown consolidator trigger kind '${kind}'`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Build a {@link ConsolidatorTriggerReason} from a parsed trigger. */
|
|
58
|
+
function reasonFromTrigger(trigger) {
|
|
59
|
+
switch (trigger.kind) {
|
|
60
|
+
case "turn": return {
|
|
61
|
+
kind: "turn",
|
|
62
|
+
value: trigger.count
|
|
63
|
+
};
|
|
64
|
+
case "idle": return {
|
|
65
|
+
kind: "idle",
|
|
66
|
+
value: trigger.raw
|
|
67
|
+
};
|
|
68
|
+
case "cron": return {
|
|
69
|
+
kind: "cron",
|
|
70
|
+
value: trigger.expression
|
|
71
|
+
};
|
|
72
|
+
case "event": return {
|
|
73
|
+
kind: "event",
|
|
74
|
+
value: trigger.name
|
|
75
|
+
};
|
|
76
|
+
case "budget": return {
|
|
77
|
+
kind: "budget",
|
|
78
|
+
value: trigger.threshold
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function toMs(value, unit) {
|
|
83
|
+
switch (unit) {
|
|
84
|
+
case "ms": return value;
|
|
85
|
+
case "s": return value * 1e3;
|
|
86
|
+
case "m": return value * 60 * 1e3;
|
|
87
|
+
case "h": return value * 60 * 60 * 1e3;
|
|
88
|
+
default: return NaN;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
export { parseTriggerSpec, reasonFromTrigger };
|
|
94
|
+
//# sourceMappingURL=triggers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triggers.js","names":[],"sources":["../../src/consolidator/triggers.ts"],"sourcesContent":["/**\n * Trigger spec parser. Translates the human-friendly\n * `'turn:N'` / `'idle:Xm'` / `'cron:EXPR'` / `'event:NAME'` /\n * `'budget:N'` declarations into the runtime structure used by the\n * consolidator.\n *\n * The parser is exhaustive: every shape recognised here must be\n * matched in {@link parseTriggerSpec}; unrecognised shapes throw a\n * `TypeError` so misconfigured triggers fail at registration time\n * rather than silently never firing.\n *\n * @packageDocumentation\n */\n\nimport type { ConsolidatorTriggerReason, ConsolidatorTriggerSpec } from './types.js';\n\n/**\n * Parsed trigger declaration. The `kind` discriminator drives the\n * runtime dispatch; the `value` carries the spec-specific argument\n * already converted to a `number` (turns / millis / threshold) or a\n * string (cron expr / event name).\n *\n * @stable\n */\nexport type ParsedTrigger =\n | { readonly kind: 'turn'; readonly count: number; readonly raw: string }\n | { readonly kind: 'idle'; readonly idleMs: number; readonly raw: string }\n | { readonly kind: 'cron'; readonly expression: string; readonly raw: string }\n | { readonly kind: 'event'; readonly name: string; readonly raw: string }\n | { readonly kind: 'budget'; readonly threshold: number; readonly raw: string };\n\nconst IDLE_PATTERN = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h)?$/u;\n\n/** Parse a {@link ConsolidatorTriggerSpec} into a {@link ParsedTrigger}. */\nexport function parseTriggerSpec(spec: ConsolidatorTriggerSpec): ParsedTrigger {\n if (typeof spec !== 'string' || spec.length === 0) {\n throw new TypeError(\n `[graphorin/memory] consolidator trigger spec must be a non-empty string, got ${typeof spec}`,\n );\n }\n const colon = spec.indexOf(':');\n if (colon < 0) {\n throw new TypeError(\n `[graphorin/memory] invalid trigger spec '${spec}' — expected 'kind:value'`,\n );\n }\n const kind = spec.slice(0, colon);\n const value = spec.slice(colon + 1);\n\n switch (kind) {\n case 'turn': {\n const count = Number.parseInt(value, 10);\n if (!Number.isFinite(count) || count <= 0) {\n throw new TypeError(\n `[graphorin/memory] invalid turn trigger '${spec}' — expected positive integer`,\n );\n }\n return { kind: 'turn', count, raw: spec };\n }\n case 'idle': {\n const match = IDLE_PATTERN.exec(value);\n if (match === null) {\n throw new TypeError(\n `[graphorin/memory] invalid idle trigger '${spec}' — expected '5m' / '30s' / '1h'`,\n );\n }\n const num = Number.parseFloat(match[1] ?? 'NaN');\n const unit = match[2] ?? 'ms';\n const idleMs = toMs(num, unit);\n if (!Number.isFinite(idleMs) || idleMs <= 0) {\n throw new TypeError(\n `[graphorin/memory] invalid idle trigger '${spec}' — non-positive duration`,\n );\n }\n return { kind: 'idle', idleMs, raw: spec };\n }\n case 'cron': {\n if (value.trim().length === 0) {\n throw new TypeError(`[graphorin/memory] invalid cron trigger '${spec}' — empty expression`);\n }\n return { kind: 'cron', expression: value, raw: spec };\n }\n case 'event': {\n if (value.length === 0) {\n throw new TypeError(\n `[graphorin/memory] invalid event trigger '${spec}' — empty event name`,\n );\n }\n return { kind: 'event', name: value, raw: spec };\n }\n case 'budget': {\n const threshold = Number.parseFloat(value);\n if (!Number.isFinite(threshold) || threshold <= 0 || threshold >= 1) {\n throw new TypeError(\n `[graphorin/memory] invalid budget trigger '${spec}' — expected fraction between 0 and 1`,\n );\n }\n return { kind: 'budget', threshold, raw: spec };\n }\n default:\n throw new TypeError(`[graphorin/memory] unknown consolidator trigger kind '${kind}'`);\n }\n}\n\n/** Build a {@link ConsolidatorTriggerReason} from a parsed trigger. */\nexport function reasonFromTrigger(trigger: ParsedTrigger): ConsolidatorTriggerReason {\n switch (trigger.kind) {\n case 'turn':\n return { kind: 'turn', value: trigger.count };\n case 'idle':\n return { kind: 'idle', value: trigger.raw };\n case 'cron':\n return { kind: 'cron', value: trigger.expression };\n case 'event':\n return { kind: 'event', value: trigger.name };\n case 'budget':\n return { kind: 'budget', value: trigger.threshold };\n }\n}\n\nfunction toMs(value: number, unit: string): number {\n switch (unit) {\n case 'ms':\n return value;\n case 's':\n return value * 1000;\n case 'm':\n return value * 60 * 1000;\n case 'h':\n return value * 60 * 60 * 1000;\n default:\n return Number.NaN;\n }\n}\n"],"mappings":";AA+BA,MAAM,eAAe;;AAGrB,SAAgB,iBAAiB,MAA8C;AAC7E,KAAI,OAAO,SAAS,YAAY,KAAK,WAAW,EAC9C,OAAM,IAAI,UACR,gFAAgF,OAAO,OACxF;CAEH,MAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,KAAI,QAAQ,EACV,OAAM,IAAI,UACR,4CAA4C,KAAK,2BAClD;CAEH,MAAM,OAAO,KAAK,MAAM,GAAG,MAAM;CACjC,MAAM,QAAQ,KAAK,MAAM,QAAQ,EAAE;AAEnC,SAAQ,MAAR;EACE,KAAK,QAAQ;GACX,MAAM,QAAQ,OAAO,SAAS,OAAO,GAAG;AACxC,OAAI,CAAC,OAAO,SAAS,MAAM,IAAI,SAAS,EACtC,OAAM,IAAI,UACR,4CAA4C,KAAK,+BAClD;AAEH,UAAO;IAAE,MAAM;IAAQ;IAAO,KAAK;IAAM;;EAE3C,KAAK,QAAQ;GACX,MAAM,QAAQ,aAAa,KAAK,MAAM;AACtC,OAAI,UAAU,KACZ,OAAM,IAAI,UACR,4CAA4C,KAAK,kCAClD;GAIH,MAAM,SAAS,KAFH,OAAO,WAAW,MAAM,MAAM,MAAM,EACnC,MAAM,MAAM,KACK;AAC9B,OAAI,CAAC,OAAO,SAAS,OAAO,IAAI,UAAU,EACxC,OAAM,IAAI,UACR,4CAA4C,KAAK,2BAClD;AAEH,UAAO;IAAE,MAAM;IAAQ;IAAQ,KAAK;IAAM;;EAE5C,KAAK;AACH,OAAI,MAAM,MAAM,CAAC,WAAW,EAC1B,OAAM,IAAI,UAAU,4CAA4C,KAAK,sBAAsB;AAE7F,UAAO;IAAE,MAAM;IAAQ,YAAY;IAAO,KAAK;IAAM;EAEvD,KAAK;AACH,OAAI,MAAM,WAAW,EACnB,OAAM,IAAI,UACR,6CAA6C,KAAK,sBACnD;AAEH,UAAO;IAAE,MAAM;IAAS,MAAM;IAAO,KAAK;IAAM;EAElD,KAAK,UAAU;GACb,MAAM,YAAY,OAAO,WAAW,MAAM;AAC1C,OAAI,CAAC,OAAO,SAAS,UAAU,IAAI,aAAa,KAAK,aAAa,EAChE,OAAM,IAAI,UACR,8CAA8C,KAAK,uCACpD;AAEH,UAAO;IAAE,MAAM;IAAU;IAAW,KAAK;IAAM;;EAEjD,QACE,OAAM,IAAI,UAAU,yDAAyD,KAAK,GAAG;;;;AAK3F,SAAgB,kBAAkB,SAAmD;AACnF,SAAQ,QAAQ,MAAhB;EACE,KAAK,OACH,QAAO;GAAE,MAAM;GAAQ,OAAO,QAAQ;GAAO;EAC/C,KAAK,OACH,QAAO;GAAE,MAAM;GAAQ,OAAO,QAAQ;GAAK;EAC7C,KAAK,OACH,QAAO;GAAE,MAAM;GAAQ,OAAO,QAAQ;GAAY;EACpD,KAAK,QACH,QAAO;GAAE,MAAM;GAAS,OAAO,QAAQ;GAAM;EAC/C,KAAK,SACH,QAAO;GAAE,MAAM;GAAU,OAAO,QAAQ;GAAW;;;AAIzD,SAAS,KAAK,OAAe,MAAsB;AACjD,SAAQ,MAAR;EACE,KAAK,KACH,QAAO;EACT,KAAK,IACH,QAAO,QAAQ;EACjB,KAAK,IACH,QAAO,QAAQ,KAAK;EACtB,KAAK,IACH,QAAO,QAAQ,KAAK,KAAK;EAC3B,QACE,QAAO"}
|