hippo-memory 1.11.2 → 1.11.3

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/dist/api.d.ts CHANGED
@@ -9,9 +9,10 @@
9
9
  import { type DatabaseSyncLike } from './db.js';
10
10
  import { type TaskSnapshot, type SessionEvent } from './store.js';
11
11
  import type { SessionHandoff } from './handoff.js';
12
- import { type MemoryKind } from './memory.js';
12
+ import { type MemoryKind, type MemoryEntry } from './memory.js';
13
13
  import { type AuditEvent, type AuditOp } from './audit.js';
14
14
  import { type ApiKeyListItem } from './auth.js';
15
+ import { type AmbientState } from './ambient.js';
15
16
  export interface Context {
16
17
  hippoRoot: string;
17
18
  tenantId: string;
@@ -527,4 +528,134 @@ export interface AuditListOpts {
527
528
  * A5: cmdAuditList does not record a 'recall'-style read event).
528
529
  */
529
530
  export declare function auditList(ctx: Context, opts: AuditListOpts): AuditEvent[];
531
+ /**
532
+ * Options for `getContext` — assemble a budget-bounded context bundle
533
+ * (recalled memories + active task snapshot + handoff + recent events).
534
+ * Extracted from `cmdContext` in `cli.ts` in Episode A of the api.ts refactor.
535
+ *
536
+ * Named `getContext` (not `context`) to avoid collision with the `Context`
537
+ * interface above and the ubiquitous `ctx: Context` convention. Follows the
538
+ * existing `getEntry` naming pattern in store.ts.
539
+ *
540
+ * Scope narrow (T5 execute decision): rendering opts (`format`, `framing`,
541
+ * `rendered`) and host-side opts (`auto`) are NOT included here. The print
542
+ * helpers (`printContextMarkdown`, `printActiveTaskSnapshot`, `printHandoff`,
543
+ * `printSessionEvents`) are shared with `cmdRecall` / `cmdSnapshot` /
544
+ * `cmdHandoffShow` — moving them into api.ts would expand T5 to also rewire
545
+ * those commands. CLI handles rendering + auto-resolution. Episode B can add
546
+ * `api.renderContext` once a shared rendering need actually materializes.
547
+ */
548
+ export interface ContextOpts {
549
+ q?: string;
550
+ /** Default 1500 tokens. */
551
+ budget?: number;
552
+ limit?: number;
553
+ pinnedOnly?: boolean;
554
+ scope?: string;
555
+ includeRecent?: number;
556
+ }
557
+ export interface ContextResultEntry {
558
+ entry: MemoryEntry;
559
+ score: number;
560
+ tokens: number;
561
+ isGlobal?: boolean;
562
+ isFreshTail?: boolean;
563
+ }
564
+ export interface ContextResult {
565
+ entries: ContextResultEntry[];
566
+ tokens: number;
567
+ activeSnapshot?: TaskSnapshot | null;
568
+ sessionHandoff?: SessionHandoff | null;
569
+ recentEvents?: SessionEvent[];
570
+ }
571
+ /**
572
+ * Assemble a context bundle: recalled memories (pinned-only / strength-sorted
573
+ * fallback / hybrid search) + active task snapshot + session handoff + recent
574
+ * session events. Budget-bounded, tenant-scoped. Mutates `last_retrieval_ids`
575
+ * + emits a 'recall' audit row for non-pinned, non-'*' queries.
576
+ *
577
+ * Behaves like the pre-extraction `cmdContext` data-loading + selection
578
+ * pipeline. CLI presentation (markdown / json / additional-context rendering)
579
+ * stays in `cli.ts`.
580
+ *
581
+ * Tenant scope: all `loadAllEntries` / snapshot / handoff / events reads use
582
+ * `ctx.tenantId`. Cross-tenant rows are filtered out.
583
+ *
584
+ * Returns an empty result (`entries: []`, snapshot/handoff/events undefined)
585
+ * when there's nothing to surface (no memories AND no snapshot AND no handoff
586
+ * AND no recent events).
587
+ */
588
+ export declare function getContext(ctx: Context, opts?: ContextOpts): Promise<ContextResult>;
589
+ /**
590
+ * Options for `sleep` — run the pure-storage consolidation pipeline
591
+ * (consolidate + dedup + audit + share + ambient) and return structured counts.
592
+ *
593
+ * Extracted from `cmdSleepCore` Phase 2-6 in Episode A. NOT covered by api.sleep:
594
+ * the cli-only auto-learn phase (Phase 1: learnFromRepo + learnFromMemoryMd),
595
+ * which is intrinsically host-bound (uses `process.cwd()` / `os.homedir()`).
596
+ * Auto-learn stays in cli.ts cmdSleepCore as a pre-api block.
597
+ *
598
+ * The CLI `cmdSleep` wrapper continues to own the log-file tee + console
599
+ * rendering + `process.exit`; `api.sleep` is pure (no console.log, no IO
600
+ * beyond the store).
601
+ */
602
+ export interface SleepOpts {
603
+ dryRun?: boolean;
604
+ noShare?: boolean;
605
+ }
606
+ export interface SleepResult {
607
+ active: number;
608
+ removed: number;
609
+ mergedEpisodic: number;
610
+ newSemantic: number;
611
+ dryRun: boolean;
612
+ deduped?: {
613
+ removed: number;
614
+ semDups: number;
615
+ epiDups: number;
616
+ crossDups: number;
617
+ };
618
+ audit?: {
619
+ errorsRemoved: number;
620
+ warningCount: number;
621
+ };
622
+ shared?: number;
623
+ ambient?: AmbientState | null;
624
+ details?: string[];
625
+ }
626
+ /**
627
+ * Run the pure-storage consolidation pipeline.
628
+ *
629
+ * Tenant scope note: sleep operates on the WHOLE hippoRoot (all tenants in
630
+ * it), matching the pre-refactor cmdSleepCore behavior. Correct for a CLI
631
+ * maintenance op invoked by the operator. But once Episode B exposes this
632
+ * over HTTP `/v1/sleep`, the route MUST gate to a global-admin actor or
633
+ * scope dedup/audit/delete by ctx.tenantId — otherwise a tenant-A Bearer
634
+ * could dedupe and delete tenant-B's rows. See TODOS.md "Episode A
635
+ * follow-ups" for the Episode B preflight checklist.
636
+ *
637
+ * Audit emission gap: the consolidation phases (dedup, audit-delete) do
638
+ * NOT emit audit_log rows today, matching pre-refactor cmdSleepCore. Same
639
+ * CLI/MCP parity gap that T6 fixed for cmdOutcome, now visible at the api
640
+ * surface. Episode B should decide whether `/v1/sleep` writes a single
641
+ * 'consolidate' audit row per invocation or per-phase rows per deletion.
642
+ */
643
+ export declare function sleep(ctx: Context, opts?: SleepOpts): Promise<SleepResult>;
644
+ /**
645
+ * Apply an outcome to the ids most recently returned by `recall()`.
646
+ *
647
+ * Reads `loadIndex(ctx.hippoRoot).last_retrieval_ids` (per-hippoRoot local
648
+ * state; not tenant-scoped at the index layer) and forwards to `outcome()`,
649
+ * which DOES tenant-filter via `readEntry(..., ctx.tenantId)` — cross-tenant
650
+ * ids in `last_retrieval_ids` are silently skipped, matching the MCP
651
+ * `hippo_outcome` semantics.
652
+ *
653
+ * Do NOT tighten `loadIndex` with `tenantId` inside this helper — doing so
654
+ * would break the (correct) cross-tenant-silent-skip behavior covered by
655
+ * the test in `tests/api-outcome-for-last-recall.test.ts`.
656
+ */
657
+ export declare function outcomeForLastRecall(ctx: Context, good: boolean): {
658
+ applied: number;
659
+ ids: string[];
660
+ };
530
661
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAA6B,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAwBL,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,WAAW,EAEjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,OAAO,EACb,MAAM,YAAY,CAAC;AAGpB,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AAOnB,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtE,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAgB,IAAI,EAChB,gCAAgC,GAChC,uBAAuB,CAAC;gBAE1B,IAAI,EACA,gCAAgC,GAChC,uBAAuB,EAC3B,OAAO,EAAE,MAAM;CAMlB;AAiBD;;;;;;;;;GASG;AACH;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAaT;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAExE;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,GAAG,cAAc,CAczE;AAMD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACrC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,cAAc,EAAE,cAAc,GAAG,IAAI,CAAC;IACtC,mBAAmB,EAAE,YAAY,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,GAAG,YAAY,CA8TnE;AAMD,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;kCAC8B;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB;sCACkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;kCAC8B;IAC9B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;qDACiD;IACjD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;OAOG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,YAAiB,GACtB,cAAc,CA+HhB;AAMD,MAAM,WAAW,aAAa;IAC5B,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IACtH,QAAQ,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnG,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,WAAW,GAAG,eAAe,CAAC;CACxC;AAED,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAElE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,aAAkB,GACvB,gBAAgB,CA0DlB;AAMD;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,EAC1B,IAAI,EAAE,OAAO,GACZ;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAsBrB;AAMD;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAiBzE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CACrB,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,MAAM,GACT;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAuClD;AAMD;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GACjB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAsF5C;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,IAAI,GAAE,cAAmB,GACxB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAsDlC;AAMD,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,GAAG,gBAAgB,CAQ/E;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,GACxB,cAAc,EAAE,CAQlB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CA8CjC;AAMD,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,GAAG,UAAU,EAAE,CAYzE;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACrC,cAAc,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,aAAa,CAAC,CA4RxB;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,KAAK,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,KAAK,CACzB,GAAG,EAAE,OAAO,EACZ,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,WAAW,CAAC,CA+EtB;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,OAAO,GACZ;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAA;CAAE,CAMpC"}
package/dist/api.js CHANGED
@@ -8,13 +8,19 @@
8
8
  */
9
9
  import { createHash } from 'node:crypto';
10
10
  import { openHippoDb, closeHippoDb } from './db.js';
11
- import { writeEntry, writeEntryDbOnly, writeEntryMirrors, readEntry, deleteEntry, loadRecallSearchEntries, loadEntriesByIds, loadChildrenOf, loadFreshRawMemories, loadSessionRawMemories, countSessionRawMemories, DEFAULT_SEARCH_CANDIDATE_LIMIT, RECALL_DEFAULT_DENY_SCOPES, removeEntryMirrors, loadActiveTaskSnapshot, loadLatestHandoff, listSessionEvents, } from './store.js';
12
- import { createMemory, applyOutcome, Layer, } from './memory.js';
13
- import { appendAuditEvent, queryAuditEvents, } from './audit.js';
14
- import { promoteToGlobal, getGlobalRoot } from './shared.js';
11
+ import { writeEntry, writeEntryDbOnly, writeEntryMirrors, readEntry, deleteEntry, loadRecallSearchEntries, loadEntriesByIds, loadChildrenOf, loadFreshRawMemories, loadSessionRawMemories, countSessionRawMemories, DEFAULT_SEARCH_CANDIDATE_LIMIT, RECALL_DEFAULT_DENY_SCOPES, removeEntryMirrors, loadActiveTaskSnapshot, loadLatestHandoff, listSessionEvents, loadIndex, saveIndex, loadAllEntries, updateStats, isInitialized, } from './store.js';
12
+ import { createMemory, applyOutcome, calculateStrength, Layer, } from './memory.js';
13
+ import { appendAuditEvent, queryAuditEvents, auditMemories, } from './audit.js';
14
+ import { promoteToGlobal, getGlobalRoot, autoShare, searchBothHybrid } from './shared.js';
15
15
  import { archiveRawMemory } from './raw-archive.js';
16
16
  import { createApiKey, listApiKeys, revokeApiKey, } from './auth.js';
17
17
  import { applyGoalStackBoost } from './goals.js';
18
+ import { markRetrieved, estimateTokens, hybridSearch, physicsSearch } from './search.js';
19
+ import { scopeMatch } from './scope.js';
20
+ import { consolidate } from './consolidate.js';
21
+ import { loadConfig } from './config.js';
22
+ import { deduplicateStore } from './dedupe.js';
23
+ import { computeAmbientState } from './ambient.js';
18
24
  /**
19
25
  * Thrown by `api.recall` when a caller's options violate a recall contract
20
26
  * that has been opted into via env. Carries a stable `code` field for HTTP /
@@ -998,4 +1004,390 @@ export function auditList(ctx, opts) {
998
1004
  closeHippoDb(db);
999
1005
  }
1000
1006
  }
1007
+ /**
1008
+ * Assemble a context bundle: recalled memories (pinned-only / strength-sorted
1009
+ * fallback / hybrid search) + active task snapshot + session handoff + recent
1010
+ * session events. Budget-bounded, tenant-scoped. Mutates `last_retrieval_ids`
1011
+ * + emits a 'recall' audit row for non-pinned, non-'*' queries.
1012
+ *
1013
+ * Behaves like the pre-extraction `cmdContext` data-loading + selection
1014
+ * pipeline. CLI presentation (markdown / json / additional-context rendering)
1015
+ * stays in `cli.ts`.
1016
+ *
1017
+ * Tenant scope: all `loadAllEntries` / snapshot / handoff / events reads use
1018
+ * `ctx.tenantId`. Cross-tenant rows are filtered out.
1019
+ *
1020
+ * Returns an empty result (`entries: []`, snapshot/handoff/events undefined)
1021
+ * when there's nothing to surface (no memories AND no snapshot AND no handoff
1022
+ * AND no recent events).
1023
+ */
1024
+ export async function getContext(ctx, opts = {}) {
1025
+ const pinnedOnly = opts.pinnedOnly === true;
1026
+ const budget = opts.budget ?? 1500;
1027
+ const limit = opts.limit ?? Number.POSITIVE_INFINITY;
1028
+ const includeRecent = opts.includeRecent ?? 0;
1029
+ const activeScope = opts.scope ?? '';
1030
+ if (budget <= 0) {
1031
+ return { entries: [], tokens: 0 };
1032
+ }
1033
+ // Pinned-only path is allowed against an un-initialised local store (the
1034
+ // UserPromptSubmit hook can run in directories without a .hippo). Non-pinned
1035
+ // path requires an initialised local store; callers should check first.
1036
+ const hasLocal = isInitialized(ctx.hippoRoot);
1037
+ const query = (opts.q ?? '').trim() || '*';
1038
+ const globalRoot = getGlobalRoot();
1039
+ const hasGlobal = isInitialized(globalRoot);
1040
+ // Tenant-scoped loads (v1.11.1 lesson: NEVER resolveTenantId({}) here).
1041
+ let localEntries = hasLocal ? loadAllEntries(ctx.hippoRoot, ctx.tenantId) : [];
1042
+ let globalEntries = hasGlobal ? loadAllEntries(globalRoot, ctx.tenantId) : [];
1043
+ // Filter superseded — context never includes superseded rows.
1044
+ localEntries = localEntries.filter((e) => !e.superseded_by);
1045
+ globalEntries = globalEntries.filter((e) => !e.superseded_by);
1046
+ const activeSnapshot = hasLocal
1047
+ ? loadActiveTaskSnapshot(ctx.hippoRoot, ctx.tenantId)
1048
+ : null;
1049
+ const sessionHandoff = hasLocal && activeSnapshot?.session_id
1050
+ ? loadLatestHandoff(ctx.hippoRoot, ctx.tenantId, activeSnapshot.session_id)
1051
+ : null;
1052
+ const recentSessionEvents = hasLocal && activeSnapshot?.session_id
1053
+ ? listSessionEvents(ctx.hippoRoot, ctx.tenantId, {
1054
+ session_id: activeSnapshot.session_id,
1055
+ limit: 5,
1056
+ })
1057
+ : [];
1058
+ if (localEntries.length === 0 &&
1059
+ globalEntries.length === 0 &&
1060
+ !activeSnapshot &&
1061
+ !sessionHandoff &&
1062
+ recentSessionEvents.length === 0) {
1063
+ return { entries: [], tokens: 0 };
1064
+ }
1065
+ let selectedItems = [];
1066
+ let totalTokens = 0;
1067
+ if (pinnedOnly) {
1068
+ // loadConfig is safe even when local isn't initialised — returns defaults.
1069
+ const pinnedCfg = loadConfig(ctx.hippoRoot);
1070
+ if (!pinnedCfg.pinnedInject.enabled) {
1071
+ return { entries: [], tokens: 0 };
1072
+ }
1073
+ // Effective budget: explicit opts.budget wins over config.
1074
+ const effBudget = opts.budget !== undefined ? budget : pinnedCfg.pinnedInject.budget;
1075
+ const nowP = new Date();
1076
+ const selectedIds = new Set();
1077
+ let usedP = 0;
1078
+ if (includeRecent > 0) {
1079
+ const recent = [
1080
+ ...localEntries.map((entry) => ({ entry, isGlobal: false })),
1081
+ ...globalEntries.map((entry) => ({ entry, isGlobal: true })),
1082
+ ]
1083
+ .sort((a, b) => {
1084
+ const byCreated = Date.parse(b.entry.created) - Date.parse(a.entry.created);
1085
+ return byCreated !== 0 ? byCreated : b.entry.id.localeCompare(a.entry.id);
1086
+ })
1087
+ .slice(0, includeRecent)
1088
+ .map(({ entry, isGlobal }) => ({
1089
+ entry,
1090
+ score: calculateStrength(entry, nowP) * (isGlobal ? 1 / 1.2 : 1),
1091
+ tokens: estimateTokens(entry.content),
1092
+ isGlobal,
1093
+ }));
1094
+ for (const r of recent) {
1095
+ if (selectedIds.has(r.entry.id))
1096
+ continue;
1097
+ if (usedP + r.tokens > effBudget)
1098
+ continue;
1099
+ selectedItems.push(r);
1100
+ selectedIds.add(r.entry.id);
1101
+ usedP += r.tokens;
1102
+ }
1103
+ }
1104
+ const pinnedLocal = localEntries.filter((e) => e.pinned);
1105
+ const pinnedGlobal = globalEntries.filter((e) => e.pinned);
1106
+ if (pinnedLocal.length === 0 &&
1107
+ pinnedGlobal.length === 0 &&
1108
+ selectedItems.length === 0) {
1109
+ return { entries: [], tokens: 0 };
1110
+ }
1111
+ const rankedPinned = [
1112
+ ...pinnedLocal.map((e) => ({ entry: e, isGlobal: false })),
1113
+ ...pinnedGlobal.map((e) => ({ entry: e, isGlobal: true })),
1114
+ ]
1115
+ .map(({ entry, isGlobal }) => {
1116
+ const scopeSig = scopeMatch(entry.tags, activeScope);
1117
+ const sBst = scopeSig === 1 ? 1.5 : scopeSig === -1 ? 0.5 : 1.0;
1118
+ return {
1119
+ entry,
1120
+ score: calculateStrength(entry, nowP) * (isGlobal ? 1 / 1.2 : 1) * sBst,
1121
+ tokens: estimateTokens(entry.content),
1122
+ isGlobal,
1123
+ };
1124
+ })
1125
+ .sort((a, b) => b.score - a.score);
1126
+ for (const r of rankedPinned) {
1127
+ if (selectedIds.has(r.entry.id))
1128
+ continue;
1129
+ if (usedP + r.tokens > effBudget)
1130
+ continue;
1131
+ selectedItems.push(r);
1132
+ selectedIds.add(r.entry.id);
1133
+ usedP += r.tokens;
1134
+ }
1135
+ totalTokens = usedP;
1136
+ }
1137
+ else if (query === '*') {
1138
+ // No query: return strongest memories by strength, up to budget.
1139
+ const now = new Date();
1140
+ const localRanked = localEntries
1141
+ .map((e) => ({
1142
+ entry: e,
1143
+ score: calculateStrength(e, now),
1144
+ tokens: estimateTokens(e.content),
1145
+ isGlobal: false,
1146
+ }))
1147
+ .sort((a, b) => b.score - a.score);
1148
+ const globalRanked = globalEntries
1149
+ .map((e) => ({
1150
+ entry: e,
1151
+ score: calculateStrength(e, now) * (1 / 1.2),
1152
+ tokens: estimateTokens(e.content),
1153
+ isGlobal: true,
1154
+ }))
1155
+ .sort((a, b) => b.score - a.score);
1156
+ const combined = [...localRanked, ...globalRanked].sort((a, b) => b.score - a.score);
1157
+ let used = 0;
1158
+ for (const r of combined) {
1159
+ if (used + r.tokens > budget)
1160
+ continue;
1161
+ selectedItems.push(r);
1162
+ used += r.tokens;
1163
+ }
1164
+ totalTokens = used;
1165
+ }
1166
+ else {
1167
+ // Real query: hybrid search (global + local) or physics+hybrid (local only).
1168
+ let results;
1169
+ if (hasGlobal) {
1170
+ const merged = await searchBothHybrid(query, ctx.hippoRoot, globalRoot, {
1171
+ budget,
1172
+ scope: activeScope,
1173
+ tenantId: ctx.tenantId,
1174
+ });
1175
+ const localIndex = loadIndex(ctx.hippoRoot);
1176
+ results = merged.map((r) => ({
1177
+ entry: r.entry,
1178
+ score: r.score,
1179
+ tokens: r.tokens,
1180
+ isGlobal: !localIndex.entries[r.entry.id],
1181
+ }));
1182
+ }
1183
+ else {
1184
+ const ctxConfig = loadConfig(ctx.hippoRoot);
1185
+ const usePhysicsCtx = ctxConfig.physics?.enabled !== false;
1186
+ const ctxResults = usePhysicsCtx
1187
+ ? await physicsSearch(query, localEntries, {
1188
+ budget,
1189
+ hippoRoot: ctx.hippoRoot,
1190
+ physicsConfig: ctxConfig.physics,
1191
+ scope: activeScope,
1192
+ })
1193
+ : await hybridSearch(query, localEntries, {
1194
+ budget,
1195
+ hippoRoot: ctx.hippoRoot,
1196
+ scope: activeScope,
1197
+ });
1198
+ results = ctxResults.map((r) => ({
1199
+ entry: r.entry,
1200
+ score: r.score,
1201
+ tokens: r.tokens,
1202
+ isGlobal: false,
1203
+ }));
1204
+ }
1205
+ selectedItems = results;
1206
+ totalTokens = results.reduce((sum, r) => sum + r.tokens, 0);
1207
+ // A5 H4: emit recall audit row for context-mode searches (matches the
1208
+ // 'recall' op emitted by api.recall for parity). pinnedOnly + '*' fallback
1209
+ // never hit the search engines, so they don't emit (matches cmdContext).
1210
+ const ctxRecallMetadata = {
1211
+ query: query.slice(0, 200),
1212
+ results: selectedItems.length,
1213
+ mode: 'context',
1214
+ };
1215
+ if (hasLocal) {
1216
+ const localDb = openHippoDb(ctx.hippoRoot);
1217
+ try {
1218
+ appendAuditEvent(localDb, {
1219
+ tenantId: ctx.tenantId,
1220
+ actor: ctx.actor,
1221
+ op: 'recall',
1222
+ metadata: ctxRecallMetadata,
1223
+ });
1224
+ }
1225
+ finally {
1226
+ closeHippoDb(localDb);
1227
+ }
1228
+ }
1229
+ if (hasGlobal) {
1230
+ const globalDb = openHippoDb(globalRoot);
1231
+ try {
1232
+ appendAuditEvent(globalDb, {
1233
+ tenantId: ctx.tenantId,
1234
+ actor: ctx.actor,
1235
+ op: 'recall',
1236
+ metadata: ctxRecallMetadata,
1237
+ });
1238
+ }
1239
+ finally {
1240
+ closeHippoDb(globalDb);
1241
+ }
1242
+ }
1243
+ }
1244
+ if (limit < selectedItems.length) {
1245
+ selectedItems = selectedItems.slice(0, limit);
1246
+ totalTokens = selectedItems.reduce((sum, r) => sum + r.tokens, 0);
1247
+ }
1248
+ if (selectedItems.length === 0 &&
1249
+ !activeSnapshot &&
1250
+ !sessionHandoff &&
1251
+ recentSessionEvents.length === 0) {
1252
+ return { entries: [], tokens: 0 };
1253
+ }
1254
+ // pinnedOnly is the UserPromptSubmit hot path — read-only so pinned
1255
+ // memories don't inflate retrieval_count or extend half_life by 2 days per
1256
+ // turn over a long session.
1257
+ if (!pinnedOnly) {
1258
+ const toUpdate = selectedItems.map((s) => s.entry);
1259
+ const updatedEntries = markRetrieved(toUpdate);
1260
+ const localIndex = loadIndex(ctx.hippoRoot);
1261
+ for (const u of updatedEntries) {
1262
+ const targetRoot = localIndex.entries[u.id]
1263
+ ? ctx.hippoRoot
1264
+ : hasGlobal
1265
+ ? globalRoot
1266
+ : ctx.hippoRoot;
1267
+ writeEntry(targetRoot, u);
1268
+ }
1269
+ localIndex.last_retrieval_ids = updatedEntries.map((u) => u.id);
1270
+ saveIndex(ctx.hippoRoot, localIndex);
1271
+ updateStats(ctx.hippoRoot, { recalled: selectedItems.length });
1272
+ // Replace selectedItems entries with markRetrieved-updated copies so
1273
+ // the returned ContextResult reflects post-recall state.
1274
+ selectedItems = selectedItems.map((s) => ({
1275
+ ...s,
1276
+ entry: updatedEntries.find((u) => u.id === s.entry.id) ?? s.entry,
1277
+ }));
1278
+ }
1279
+ return {
1280
+ entries: selectedItems,
1281
+ tokens: totalTokens,
1282
+ activeSnapshot: activeSnapshot ?? undefined,
1283
+ sessionHandoff: sessionHandoff ?? undefined,
1284
+ recentEvents: recentSessionEvents.length > 0 ? recentSessionEvents : undefined,
1285
+ };
1286
+ }
1287
+ /**
1288
+ * Run the pure-storage consolidation pipeline.
1289
+ *
1290
+ * Tenant scope note: sleep operates on the WHOLE hippoRoot (all tenants in
1291
+ * it), matching the pre-refactor cmdSleepCore behavior. Correct for a CLI
1292
+ * maintenance op invoked by the operator. But once Episode B exposes this
1293
+ * over HTTP `/v1/sleep`, the route MUST gate to a global-admin actor or
1294
+ * scope dedup/audit/delete by ctx.tenantId — otherwise a tenant-A Bearer
1295
+ * could dedupe and delete tenant-B's rows. See TODOS.md "Episode A
1296
+ * follow-ups" for the Episode B preflight checklist.
1297
+ *
1298
+ * Audit emission gap: the consolidation phases (dedup, audit-delete) do
1299
+ * NOT emit audit_log rows today, matching pre-refactor cmdSleepCore. Same
1300
+ * CLI/MCP parity gap that T6 fixed for cmdOutcome, now visible at the api
1301
+ * surface. Episode B should decide whether `/v1/sleep` writes a single
1302
+ * 'consolidate' audit row per invocation or per-phase rows per deletion.
1303
+ */
1304
+ export async function sleep(ctx, opts = {}) {
1305
+ const dryRun = Boolean(opts.dryRun);
1306
+ // Phase 1: Consolidation.
1307
+ const consolidateResult = await consolidate(ctx.hippoRoot, { dryRun });
1308
+ const result = {
1309
+ active: consolidateResult.decayed,
1310
+ removed: consolidateResult.removed,
1311
+ mergedEpisodic: consolidateResult.merged,
1312
+ newSemantic: consolidateResult.semanticCreated,
1313
+ dryRun,
1314
+ details: consolidateResult.details,
1315
+ };
1316
+ if (dryRun)
1317
+ return result;
1318
+ // Phase 2: Dedup (post-consolidate near-duplicate cleanup).
1319
+ const dedupResult = deduplicateStore(ctx.hippoRoot);
1320
+ if (dedupResult.removed > 0) {
1321
+ const semDups = dedupResult.pairs.filter((p) => p.keptLayer === 'semantic' && p.removedLayer === 'semantic').length;
1322
+ const epiDups = dedupResult.pairs.filter((p) => p.keptLayer === 'episodic' && p.removedLayer === 'episodic').length;
1323
+ const crossDups = dedupResult.pairs.filter((p) => p.keptLayer !== p.removedLayer).length;
1324
+ result.deduped = {
1325
+ removed: dedupResult.removed,
1326
+ semDups,
1327
+ epiDups,
1328
+ crossDups,
1329
+ };
1330
+ }
1331
+ // Phase 3: Quality audit (remove junk, report warnings).
1332
+ const allEntries = loadAllEntries(ctx.hippoRoot);
1333
+ const auditOut = auditMemories(allEntries);
1334
+ if (auditOut.issues.length > 0) {
1335
+ const errors = auditOut.issues.filter((i) => i.severity === 'error');
1336
+ const warnings = auditOut.issues.filter((i) => i.severity === 'warning');
1337
+ if (errors.length > 0) {
1338
+ for (const issue of errors) {
1339
+ deleteEntry(ctx.hippoRoot, issue.memoryId);
1340
+ }
1341
+ }
1342
+ if (errors.length > 0 || warnings.length > 0) {
1343
+ result.audit = {
1344
+ errorsRemoved: errors.length,
1345
+ warningCount: warnings.length,
1346
+ };
1347
+ }
1348
+ }
1349
+ // Phase 4: Auto-share high-transfer-score memories to global.
1350
+ if (!opts.noShare) {
1351
+ const sleepConfig = loadConfig(ctx.hippoRoot);
1352
+ if (sleepConfig.autoShareOnSleep) {
1353
+ const shared = autoShare(ctx.hippoRoot, { minScore: 0.6 });
1354
+ if (shared.length > 0) {
1355
+ result.shared = shared.length;
1356
+ }
1357
+ }
1358
+ }
1359
+ // Phase 5: Post-sleep ambient state summary.
1360
+ const postSleepConfig = loadConfig(ctx.hippoRoot);
1361
+ if (postSleepConfig.ambient.enabled) {
1362
+ const postSleepEntries = loadAllEntries(ctx.hippoRoot).filter((e) => !e.superseded_by);
1363
+ if (postSleepEntries.length > 0) {
1364
+ result.ambient = computeAmbientState(postSleepEntries);
1365
+ }
1366
+ }
1367
+ return result;
1368
+ }
1369
+ // ---------------------------------------------------------------------------
1370
+ // outcomeForLastRecall (last-recall wrapper around outcome — Task 3)
1371
+ // ---------------------------------------------------------------------------
1372
+ /**
1373
+ * Apply an outcome to the ids most recently returned by `recall()`.
1374
+ *
1375
+ * Reads `loadIndex(ctx.hippoRoot).last_retrieval_ids` (per-hippoRoot local
1376
+ * state; not tenant-scoped at the index layer) and forwards to `outcome()`,
1377
+ * which DOES tenant-filter via `readEntry(..., ctx.tenantId)` — cross-tenant
1378
+ * ids in `last_retrieval_ids` are silently skipped, matching the MCP
1379
+ * `hippo_outcome` semantics.
1380
+ *
1381
+ * Do NOT tighten `loadIndex` with `tenantId` inside this helper — doing so
1382
+ * would break the (correct) cross-tenant-silent-skip behavior covered by
1383
+ * the test in `tests/api-outcome-for-last-recall.test.ts`.
1384
+ */
1385
+ export function outcomeForLastRecall(ctx, good) {
1386
+ const idx = loadIndex(ctx.hippoRoot);
1387
+ const ids = idx.last_retrieval_ids;
1388
+ if (ids.length === 0)
1389
+ return { applied: 0, ids: [] };
1390
+ const { applied } = outcome(ctx, ids, good);
1391
+ return { applied, ids };
1392
+ }
1001
1393
  //# sourceMappingURL=api.js.map