agenr 2.1.0 → 3.1.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.
@@ -34,7 +34,7 @@ type ClaimKeySource = (typeof CLAIM_KEY_SOURCES)[number];
34
34
  */
35
35
  type ClaimSupportMode = (typeof CLAIM_SUPPORT_MODES)[number];
36
36
  /** Ordered list of supported episode sources. */
37
- declare const EPISODE_SOURCES: readonly ["openclaw", "codex", "cli", "synthesis"];
37
+ declare const EPISODE_SOURCES: readonly ["openclaw", "skeln", "codex", "cli", "synthesis"];
38
38
  /** Ordered list of supported episode activity levels. */
39
39
  declare const EPISODE_ACTIVITY_LEVELS: readonly ["substantial", "minimal", "none"];
40
40
  /** Ordered list of supported procedure step kinds. */
@@ -106,6 +106,30 @@ interface Entry extends ClaimKeyLifecycleMetadata {
106
106
  created_at: string;
107
107
  updated_at: string;
108
108
  }
109
+ /**
110
+ * Mutable entry fields supported by direct update paths.
111
+ *
112
+ * Claim-key lifecycle updates are replacement-style, not merge-style. When any
113
+ * lifecycle field is mutated, callers must provide one complete validated
114
+ * lifecycle payload for the target claim key. Partial lifecycle patches are
115
+ * rejected at the persistence boundary.
116
+ */
117
+ interface EntryUpdateInput {
118
+ importance?: Entry["importance"];
119
+ expiry?: Entry["expiry"];
120
+ claim_key?: Entry["claim_key"];
121
+ claim_key_raw?: Entry["claim_key_raw"];
122
+ claim_key_status?: Entry["claim_key_status"];
123
+ claim_key_source?: Entry["claim_key_source"];
124
+ claim_key_confidence?: Entry["claim_key_confidence"];
125
+ claim_key_rationale?: Entry["claim_key_rationale"];
126
+ claim_support_source_kind?: Entry["claim_support_source_kind"];
127
+ claim_support_locator?: Entry["claim_support_locator"];
128
+ claim_support_observed_at?: Entry["claim_support_observed_at"];
129
+ claim_support_mode?: Entry["claim_support_mode"];
130
+ valid_from?: Entry["valid_from"];
131
+ valid_to?: Entry["valid_to"];
132
+ }
109
133
  /**
110
134
  * Canonical stored episodic-memory record.
111
135
  */
@@ -359,6 +383,65 @@ interface ParsedTranscript {
359
383
  warnings: string[];
360
384
  }
361
385
 
386
+ /**
387
+ * Aggregate active corpus counts for one entity prefix.
388
+ */
389
+ interface ClaimKeyEntityPrefixStats {
390
+ entityPrefix: string;
391
+ activeEntryCount: number;
392
+ trustedEntryCount: number;
393
+ tentativeEntryCount: number;
394
+ unresolvedEntryCount: number;
395
+ legacyEntryCount: number;
396
+ deterministicRepairEntryCount: number;
397
+ manualEntryCount: number;
398
+ modelEntryCount: number;
399
+ jsonRetryEntryCount: number;
400
+ surgeonFamilyReuseEntryCount: number;
401
+ }
402
+
403
+ /**
404
+ * Write-time episode payload before adapter-managed identity and lifecycle fields are applied.
405
+ */
406
+ interface EpisodeInput {
407
+ source: EpisodeSource;
408
+ sourceId?: string;
409
+ sourceRef?: string;
410
+ transcriptHash?: string;
411
+ summaryHash?: string;
412
+ agentId?: string;
413
+ surface?: string;
414
+ startedAt: string;
415
+ endedAt?: string;
416
+ summary: string;
417
+ tags?: string[];
418
+ activityLevel?: EpisodeActivityLevel;
419
+ userId?: string;
420
+ project?: string;
421
+ genModel?: string;
422
+ genVersion?: string;
423
+ messageCount?: number;
424
+ embedding?: number[];
425
+ }
426
+ /**
427
+ * Calendar-aware temporal constraint used by future episode retrieval.
428
+ */
429
+ interface TemporalWindow {
430
+ kind: "interval" | "anchor" | "open_start" | "open_end";
431
+ start?: Date;
432
+ end?: Date;
433
+ anchor?: Date;
434
+ radiusDays?: number;
435
+ source: "explicit" | "inferred";
436
+ }
437
+ /**
438
+ * Upsert outcome returned by episode persistence adapters.
439
+ */
440
+ interface EpisodeUpsertResult {
441
+ episode: Episode;
442
+ action: "inserted" | "updated" | "unchanged";
443
+ }
444
+
362
445
  /**
363
446
  * Internal ranking profiles that adjust recall scoring for specific query intents.
364
447
  */
@@ -538,6 +621,7 @@ declare const DEFAULT_STRONG_SEED_SCORE_GAP = 0.05;
538
621
  * trust suppression.
539
622
  */
540
623
  declare const DEFAULT_SEEDED_RERANK_WEIGHT = 0.03;
624
+
541
625
  /** Shared shape used by all seededRerank callers. */
542
626
  interface SeededRerankCandidate {
543
627
  /** Stable identifier used to detect the seed itself and skip rerank. */
@@ -624,6 +708,119 @@ declare function sharesEpisodeLineage(candidate: Episode, seed: Episode): boolea
624
708
  */
625
709
  declare function sharesProcedureLineage(candidate: Procedure, seed: Procedure): boolean;
626
710
 
711
+ /**
712
+ * Storage contract for persisting, updating, and querying knowledge entries.
713
+ */
714
+ interface DatabasePort {
715
+ /** Insert a new entry with its embedding. */
716
+ insertEntry(entry: Entry, embedding: number[], contentHash: string): Promise<string>;
717
+ /** Drop expensive indexes and triggers before a bulk write phase begins. */
718
+ prepareForBulkWrites(): Promise<void>;
719
+ /** Rebuild expensive indexes and triggers after a bulk write phase ends. */
720
+ finalizeBulkWrites(): Promise<void>;
721
+ /** Get entries by IDs. */
722
+ getEntries(ids: string[]): Promise<Entry[]>;
723
+ /** Get a single entry by ID. */
724
+ getEntry(id: string): Promise<Entry | null>;
725
+ /** Check if content hashes already exist. Returns the set of existing hashes. */
726
+ findExistingHashes(hashes: string[]): Promise<Set<string>>;
727
+ /** Check if normalized content hashes already exist. Returns the set of existing hashes. */
728
+ findExistingNormHashes(hashes: string[]): Promise<Set<string>>;
729
+ /** Mark an entry as retired. */
730
+ retireEntry(id: string, reason?: string): Promise<boolean>;
731
+ /** Supersede an active entry, linking it to the new entry that replaces it. */
732
+ supersedeEntry(oldId: string, newId: string, kind?: string, reason?: string): Promise<boolean>;
733
+ /** Find active entries with the given claim key. */
734
+ findActiveEntriesByClaimKey(claimKey: string): Promise<Entry[]>;
735
+ /** Get distinct entity prefixes from existing claim keys. */
736
+ getDistinctClaimKeyPrefixes(): Promise<string[]>;
737
+ /** Get bounded full claim-key examples ordered for extraction hinting. */
738
+ getClaimKeyExamples?(limit?: number): Promise<string[]>;
739
+ /** Get active per-prefix claim-key counts for conservative alias-family handling. */
740
+ getClaimKeyEntityPrefixStats?(): Promise<ClaimKeyEntityPrefixStats[]>;
741
+ /** Update entry fields (importance, expiry, and temporal metadata). */
742
+ updateEntry(id: string, fields: EntryUpdateInput): Promise<boolean>;
743
+ /** Check if a file has been ingested (by path + hash). */
744
+ getIngestLogEntry(filePath: string): Promise<{
745
+ fileHash: string;
746
+ ingestedAt: string;
747
+ } | null>;
748
+ /** Record that a file was ingested. */
749
+ insertIngestLogEntry(filePath: string, fileHash: string, entryCount: number): Promise<void>;
750
+ /** Initialize the database schema. */
751
+ init(): Promise<void>;
752
+ /** Close the database connection. */
753
+ close(): Promise<void>;
754
+ }
755
+ /**
756
+ * Storage contract for persisting and querying episodic memory records.
757
+ */
758
+ interface EpisodeDatabasePort {
759
+ /** Get one episode by its stable `(source, sourceId)` identity. */
760
+ getEpisodeBySourceId(source: EpisodeSource, sourceId: string): Promise<Episode | null>;
761
+ /** Get one episode by fallback `(source, transcriptHash)` identity. */
762
+ getEpisodeByTranscriptHash(source: EpisodeSource, transcriptHash: string): Promise<Episode | null>;
763
+ /** Insert or update an episode using `summaryHash` change detection. */
764
+ upsertEpisode(input: EpisodeInput): Promise<EpisodeUpsertResult>;
765
+ /** List non-retired episodes whose time range overlaps the requested window. */
766
+ listEpisodesByTimeWindow(window: TemporalWindow, limit?: number): Promise<Episode[]>;
767
+ /** Find episodes by vector similarity to a query embedding. */
768
+ episodeVectorSearch(params: {
769
+ embedding: number[];
770
+ limit: number;
771
+ }): Promise<Array<{
772
+ episode: Episode;
773
+ vectorSim: number;
774
+ }>>;
775
+ /** List non-retired episodes that still need embeddings. */
776
+ listEpisodesWithoutEmbeddings(limit?: number): Promise<Episode[]>;
777
+ /** Update only the embedding payload for an existing episode row. */
778
+ updateEpisodeEmbedding(id: string, embedding: number[]): Promise<void>;
779
+ }
780
+ /**
781
+ * Storage contract for persisting and querying procedural-memory revisions.
782
+ */
783
+ interface ProcedureDatabasePort {
784
+ /** Insert or update one procedure revision row. */
785
+ upsertProcedure(procedure: Procedure): Promise<Procedure>;
786
+ /** Get one active procedure by primary key. */
787
+ getProcedure(id: string): Promise<Procedure | null>;
788
+ /** Hydrate active procedures by ID while preserving caller order. */
789
+ hydrateProcedures(ids: string[]): Promise<Procedure[]>;
790
+ /** Get the currently active procedure revision for one stable key. */
791
+ findActiveProcedureByKey(procedureKey: string): Promise<Procedure | null>;
792
+ /** Find procedures by vector similarity to a query embedding. */
793
+ procedureVectorSearch(params: {
794
+ embedding: number[];
795
+ limit: number;
796
+ }): Promise<Array<{
797
+ procedure: Procedure;
798
+ vectorSim: number;
799
+ }>>;
800
+ /** Find procedures by lexical search over the procedure FTS index. */
801
+ procedureFtsSearch(params: {
802
+ text: string;
803
+ limit: number;
804
+ }): Promise<Array<{
805
+ procedure: Procedure;
806
+ rank: number;
807
+ }>>;
808
+ /** List active procedures that still need embeddings. */
809
+ listProceduresWithoutEmbeddings(limit?: number): Promise<Procedure[]>;
810
+ /** Update only the embedding payload for an existing procedure row. */
811
+ updateProcedureEmbedding(id: string, embedding: number[]): Promise<void>;
812
+ /** Mark one active procedure revision as retired. */
813
+ retireProcedure(id: string, reason?: string): Promise<boolean>;
814
+ /** Supersede one active procedure revision with a new revision. */
815
+ supersedeProcedure(oldId: string, newId: string, reason?: string): Promise<boolean>;
816
+ }
817
+ /**
818
+ * Embedding provider contract used by core ranking and storage flows.
819
+ */
820
+ interface EmbeddingPort {
821
+ /** Compute embeddings for one or more texts. */
822
+ embed(texts: string[]): Promise<number[][]>;
823
+ }
627
824
  /**
628
825
  * Query-time retrieval contract used by the v1 recall pipeline.
629
826
  */
@@ -669,6 +866,15 @@ interface RecallPorts {
669
866
  */
670
867
  crossEncoder?: CrossEncoderPort;
671
868
  }
869
+ /**
870
+ * LLM provider contract for free-form and structured completions.
871
+ */
872
+ interface LlmPort {
873
+ /** Generate a completion from a system prompt and user message. */
874
+ complete(systemPrompt: string, userMessage: string): Promise<string>;
875
+ /** Generate a structured completion (JSON output). */
876
+ completeJson<T>(systemPrompt: string, userMessage: string): Promise<T>;
877
+ }
672
878
  /**
673
879
  * One passage handed to the cross-encoder for relevance scoring.
674
880
  */
@@ -716,4 +922,4 @@ interface TranscriptPort {
716
922
  }): Promise<ParsedTranscript>;
717
923
  }
718
924
 
719
- export { type CrossEncoderPort as C, DEFAULT_NEIGHBORHOOD_BUDGET as D, type EntryType as E, type FtsCandidate as F, type NeighborhoodFamily as N, type ParsedTranscript as P, type RecallInput as R, type SeededRerankCandidate as S, type TranscriptPort as T, type VectorCandidate as V, type Expiry as a, type RecallPorts as b, type RecallOutput as c, DEFAULT_SEEDED_RERANK_WEIGHT as d, DEFAULT_STRONG_SEED_SCORE_GAP as e, DEFAULT_STRONG_SEED_TOP_N as f, type EntityAttributeKind as g, type EntityAttributeQueryShape as h, type EntryFilters as i, type EntryNeighborhoodRequest as j, type RecallCandidateEntry as k, type RecallRankingProfile as l, type SeededRerankOptions as m, selectStrongSeeds as n, sharesEntryLineage as o, sharesEpisodeLineage as p, sharesProcedureLineage as q, seededRerank as s };
925
+ export { type CrossEncoderPort as C, DEFAULT_NEIGHBORHOOD_BUDGET as D, type EntryType as E, type FtsCandidate as F, type LlmPort as L, type NeighborhoodFamily as N, type ParsedTranscript as P, type RecallInput as R, type SeededRerankCandidate as S, type TranscriptPort as T, type VectorCandidate as V, type Expiry as a, type RecallPorts as b, type RecallOutput as c, DEFAULT_SEEDED_RERANK_WEIGHT as d, DEFAULT_STRONG_SEED_SCORE_GAP as e, DEFAULT_STRONG_SEED_TOP_N as f, type EntityAttributeKind as g, type EntityAttributeQueryShape as h, type EntryFilters as i, type EntryNeighborhoodRequest as j, type RecallCandidateEntry as k, type RecallRankingProfile as l, type SeededRerankOptions as m, selectStrongSeeds as n, sharesEntryLineage as o, sharesEpisodeLineage as p, sharesProcedureLineage as q, type ProcedureDatabasePort as r, seededRerank as s, type Entry as t, type DatabasePort as u, type EpisodeDatabasePort as v, type EmbeddingPort as w };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenr",
3
- "version": "2.1.0",
3
+ "version": "3.1.0",
4
4
  "description": "Agent memory - local-first knowledge infrastructure for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,8 +15,8 @@
15
15
  "dependencies": {
16
16
  "@clack/prompts": "^1.1.0",
17
17
  "@libsql/client": "^0.17.2",
18
- "@mariozechner/pi-agent-core": "^0.63.1",
19
- "@mariozechner/pi-ai": "^0.63.2",
18
+ "@earendil-works/pi-agent-core": "^0.78.0",
19
+ "@earendil-works/pi-ai": "^0.78.0",
20
20
  "@sinclair/typebox": "^0.34.0",
21
21
  "chalk": "^5.6.2",
22
22
  "commander": "^14.0.3",
@@ -26,6 +26,8 @@
26
26
  "devDependencies": {
27
27
  "@eslint/js": "^10.0.1",
28
28
  "@types/node": "^25.5.0",
29
+ "skeln": "file:../skeln",
30
+ "typebox": "^1.1.38",
29
31
  "eslint": "^10.1.0",
30
32
  "eslint-plugin-jsdoc": "^62.8.1",
31
33
  "globals": "^17.4.0",
@@ -38,14 +40,14 @@
38
40
  "engines": {
39
41
  "node": ">=24"
40
42
  },
41
- "license": "AGPL-3.0",
43
+ "license": "MIT",
42
44
  "scripts": {
43
45
  "build": "pnpm run build:root && pnpm run build:plugin",
44
46
  "build:root": "tsup",
45
- "build:plugin": "pnpm --filter @agenr/agenr-plugin build",
47
+ "build:plugin": "pnpm --filter @agenr/agenr-plugin build && pnpm --filter @agenr/skeln-plugin build",
46
48
  "build:debug": "pnpm run build:root:debug && pnpm run build:plugin:debug",
47
49
  "build:root:debug": "tsup --sourcemap",
48
- "build:plugin:debug": "pnpm --filter @agenr/agenr-plugin build:debug",
50
+ "build:plugin:debug": "pnpm --filter @agenr/agenr-plugin build:debug && pnpm --filter @agenr/skeln-plugin build:debug",
49
51
  "dev": "tsup --watch",
50
52
  "internal:eval-server": "pnpm run build:root && node dist/internal-eval-server.js",
51
53
  "internal:recall-eval-server": "pnpm run internal:eval-server",
File without changes