@dungle-scrubs/hippo 0.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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +439 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +559 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/db.d.ts +45 -0
  8. package/dist/db.d.ts.map +1 -0
  9. package/dist/db.js +80 -0
  10. package/dist/db.js.map +1 -0
  11. package/dist/extractor.d.ts +23 -0
  12. package/dist/extractor.d.ts.map +1 -0
  13. package/dist/extractor.js +121 -0
  14. package/dist/extractor.js.map +1 -0
  15. package/dist/hash.d.ts +11 -0
  16. package/dist/hash.d.ts.map +1 -0
  17. package/dist/hash.js +14 -0
  18. package/dist/hash.js.map +1 -0
  19. package/dist/index.d.ts +19 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +40 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/providers/embedding.d.ts +27 -0
  24. package/dist/providers/embedding.d.ts.map +1 -0
  25. package/dist/providers/embedding.js +41 -0
  26. package/dist/providers/embedding.js.map +1 -0
  27. package/dist/providers/llm.d.ts +29 -0
  28. package/dist/providers/llm.d.ts.map +1 -0
  29. package/dist/providers/llm.js +74 -0
  30. package/dist/providers/llm.js.map +1 -0
  31. package/dist/schema.d.ts +20 -0
  32. package/dist/schema.d.ts.map +1 -0
  33. package/dist/schema.js +78 -0
  34. package/dist/schema.js.map +1 -0
  35. package/dist/server/config.d.ts +38 -0
  36. package/dist/server/config.d.ts.map +1 -0
  37. package/dist/server/config.js +71 -0
  38. package/dist/server/config.js.map +1 -0
  39. package/dist/server/index.d.ts +13 -0
  40. package/dist/server/index.d.ts.map +1 -0
  41. package/dist/server/index.js +372 -0
  42. package/dist/server/index.js.map +1 -0
  43. package/dist/similarity.d.ts +41 -0
  44. package/dist/similarity.d.ts.map +1 -0
  45. package/dist/similarity.js +70 -0
  46. package/dist/similarity.js.map +1 -0
  47. package/dist/strength.d.ts +59 -0
  48. package/dist/strength.d.ts.map +1 -0
  49. package/dist/strength.js +76 -0
  50. package/dist/strength.js.map +1 -0
  51. package/dist/tools/append-memory-block.d.ts +22 -0
  52. package/dist/tools/append-memory-block.d.ts.map +1 -0
  53. package/dist/tools/append-memory-block.js +45 -0
  54. package/dist/tools/append-memory-block.js.map +1 -0
  55. package/dist/tools/forget-memory.d.ts +31 -0
  56. package/dist/tools/forget-memory.d.ts.map +1 -0
  57. package/dist/tools/forget-memory.js +77 -0
  58. package/dist/tools/forget-memory.js.map +1 -0
  59. package/dist/tools/index.d.ts +9 -0
  60. package/dist/tools/index.d.ts.map +1 -0
  61. package/dist/tools/index.js +9 -0
  62. package/dist/tools/index.js.map +1 -0
  63. package/dist/tools/recall-conversation.d.ts +21 -0
  64. package/dist/tools/recall-conversation.d.ts.map +1 -0
  65. package/dist/tools/recall-conversation.js +93 -0
  66. package/dist/tools/recall-conversation.js.map +1 -0
  67. package/dist/tools/recall-memories.d.ts +29 -0
  68. package/dist/tools/recall-memories.d.ts.map +1 -0
  69. package/dist/tools/recall-memories.js +106 -0
  70. package/dist/tools/recall-memories.js.map +1 -0
  71. package/dist/tools/recall-memory-block.d.ts +21 -0
  72. package/dist/tools/recall-memory-block.d.ts.map +1 -0
  73. package/dist/tools/recall-memory-block.js +36 -0
  74. package/dist/tools/recall-memory-block.js.map +1 -0
  75. package/dist/tools/remember-facts.d.ts +30 -0
  76. package/dist/tools/remember-facts.d.ts.map +1 -0
  77. package/dist/tools/remember-facts.js +235 -0
  78. package/dist/tools/remember-facts.js.map +1 -0
  79. package/dist/tools/replace-memory-block.d.ts +25 -0
  80. package/dist/tools/replace-memory-block.d.ts.map +1 -0
  81. package/dist/tools/replace-memory-block.js +69 -0
  82. package/dist/tools/replace-memory-block.js.map +1 -0
  83. package/dist/tools/store-memory.d.ts +27 -0
  84. package/dist/tools/store-memory.d.ts.map +1 -0
  85. package/dist/tools/store-memory.js +129 -0
  86. package/dist/tools/store-memory.js.map +1 -0
  87. package/dist/types.d.ts +86 -0
  88. package/dist/types.d.ts.map +1 -0
  89. package/dist/types.js +2 -0
  90. package/dist/types.js.map +1 -0
  91. package/dist/ulid.d.ts +13 -0
  92. package/dist/ulid.d.ts.map +1 -0
  93. package/dist/ulid.js +39 -0
  94. package/dist/ulid.js.map +1 -0
  95. package/package.json +70 -0
@@ -0,0 +1,59 @@
1
+ /** Minimum effective strength — below this, chunks are excluded from results. */
2
+ export declare const STRENGTH_FLOOR = 0.05;
3
+ /** Scoring weights. */
4
+ export declare const WEIGHTS: {
5
+ readonly recency: 0.1;
6
+ readonly similarity: 0.6;
7
+ readonly strength: 0.3;
8
+ };
9
+ /**
10
+ * Compute decay resistance from access count.
11
+ *
12
+ * Frequently recalled memories decay slower.
13
+ *
14
+ * @param accessCount - Number of times this chunk was accessed
15
+ * @returns Decay resistance multiplier (≥1.0)
16
+ */
17
+ export declare function decayResistance(accessCount: number): number;
18
+ /**
19
+ * Compute effective strength after time decay.
20
+ *
21
+ * @param runningIntensity - Current running intensity [0, 1]
22
+ * @param accessCount - Total access count
23
+ * @param hoursSinceLastAccess - Hours since last access
24
+ * @returns Effective strength [0, 1]
25
+ */
26
+ export declare function effectiveStrength(runningIntensity: number, accessCount: number, hoursSinceLastAccess: number): number;
27
+ /**
28
+ * Compute recency score based on days since creation.
29
+ *
30
+ * @param daysSinceCreation - Days since the chunk was created
31
+ * @returns Recency score [0, 1], exponentially decaying
32
+ */
33
+ export declare function recencyScore(daysSinceCreation: number): number;
34
+ /**
35
+ * Compute composite search score combining similarity, strength, and recency.
36
+ *
37
+ * @param similarity - Cosine similarity [0, 1]
38
+ * @param strength - Effective strength [0, 1]
39
+ * @param recency - Recency score [0, 1]
40
+ * @returns Weighted composite score
41
+ */
42
+ export declare function searchScore(similarity: number, strength: number, recency: number): number;
43
+ /**
44
+ * Update running intensity via moving average after reinforcement.
45
+ *
46
+ * @param oldIntensity - Current running intensity
47
+ * @param encounterCount - Current encounter count (before increment)
48
+ * @param newReading - Intensity of the new encounter
49
+ * @returns Updated running intensity
50
+ */
51
+ export declare function updatedIntensity(oldIntensity: number, encounterCount: number, newReading: number): number;
52
+ /**
53
+ * Apply retrieval boost to running intensity.
54
+ *
55
+ * @param runningIntensity - Current running intensity
56
+ * @returns Boosted intensity, clamped to 1.0
57
+ */
58
+ export declare function retrievalBoost(runningIntensity: number): number;
59
+ //# sourceMappingURL=strength.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strength.d.ts","sourceRoot":"","sources":["../src/strength.ts"],"names":[],"mappings":"AAMA,iFAAiF;AACjF,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC,uBAAuB;AACvB,eAAO,MAAM,OAAO;;;;CAIV,CAAC;AAEX;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAChC,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,oBAAoB,EAAE,MAAM,GAC1B,MAAM,CAGR;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC/B,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,GAChB,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE/D"}
@@ -0,0 +1,76 @@
1
+ /** Default decay constant λ. */
2
+ const LAMBDA = 0.001;
3
+ /** Retrieval boost added to running_intensity on each access. */
4
+ const RETRIEVAL_BOOST = 0.02;
5
+ /** Minimum effective strength — below this, chunks are excluded from results. */
6
+ export const STRENGTH_FLOOR = 0.05;
7
+ /** Scoring weights. */
8
+ export const WEIGHTS = {
9
+ recency: 0.1,
10
+ similarity: 0.6,
11
+ strength: 0.3,
12
+ };
13
+ /**
14
+ * Compute decay resistance from access count.
15
+ *
16
+ * Frequently recalled memories decay slower.
17
+ *
18
+ * @param accessCount - Number of times this chunk was accessed
19
+ * @returns Decay resistance multiplier (≥1.0)
20
+ */
21
+ export function decayResistance(accessCount) {
22
+ return 1 + Math.log(1 + accessCount) * 0.3;
23
+ }
24
+ /**
25
+ * Compute effective strength after time decay.
26
+ *
27
+ * @param runningIntensity - Current running intensity [0, 1]
28
+ * @param accessCount - Total access count
29
+ * @param hoursSinceLastAccess - Hours since last access
30
+ * @returns Effective strength [0, 1]
31
+ */
32
+ export function effectiveStrength(runningIntensity, accessCount, hoursSinceLastAccess) {
33
+ const resistance = decayResistance(accessCount);
34
+ return runningIntensity * Math.exp((-LAMBDA / resistance) * hoursSinceLastAccess);
35
+ }
36
+ /**
37
+ * Compute recency score based on days since creation.
38
+ *
39
+ * @param daysSinceCreation - Days since the chunk was created
40
+ * @returns Recency score [0, 1], exponentially decaying
41
+ */
42
+ export function recencyScore(daysSinceCreation) {
43
+ return Math.exp(-0.01 * daysSinceCreation);
44
+ }
45
+ /**
46
+ * Compute composite search score combining similarity, strength, and recency.
47
+ *
48
+ * @param similarity - Cosine similarity [0, 1]
49
+ * @param strength - Effective strength [0, 1]
50
+ * @param recency - Recency score [0, 1]
51
+ * @returns Weighted composite score
52
+ */
53
+ export function searchScore(similarity, strength, recency) {
54
+ return WEIGHTS.similarity * similarity + WEIGHTS.strength * strength + WEIGHTS.recency * recency;
55
+ }
56
+ /**
57
+ * Update running intensity via moving average after reinforcement.
58
+ *
59
+ * @param oldIntensity - Current running intensity
60
+ * @param encounterCount - Current encounter count (before increment)
61
+ * @param newReading - Intensity of the new encounter
62
+ * @returns Updated running intensity
63
+ */
64
+ export function updatedIntensity(oldIntensity, encounterCount, newReading) {
65
+ return (oldIntensity * encounterCount + newReading) / (encounterCount + 1);
66
+ }
67
+ /**
68
+ * Apply retrieval boost to running intensity.
69
+ *
70
+ * @param runningIntensity - Current running intensity
71
+ * @returns Boosted intensity, clamped to 1.0
72
+ */
73
+ export function retrievalBoost(runningIntensity) {
74
+ return Math.min(1.0, runningIntensity + RETRIEVAL_BOOST);
75
+ }
76
+ //# sourceMappingURL=strength.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strength.js","sourceRoot":"","sources":["../src/strength.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,MAAM,MAAM,GAAG,KAAK,CAAC;AAErB,iEAAiE;AACjE,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,iFAAiF;AACjF,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,uBAAuB;AACvB,MAAM,CAAC,MAAM,OAAO,GAAG;IACtB,OAAO,EAAE,GAAG;IACZ,UAAU,EAAE,GAAG;IACf,QAAQ,EAAE,GAAG;CACJ,CAAC;AAEX;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,WAAmB;IAClD,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAChC,gBAAwB,EACxB,WAAmB,EACnB,oBAA4B;IAE5B,MAAM,UAAU,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAChD,OAAO,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,oBAAoB,CAAC,CAAC;AACnF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,iBAAyB;IACrD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,iBAAiB,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,QAAgB,EAAE,OAAe;IAChF,OAAO,OAAO,CAAC,UAAU,GAAG,UAAU,GAAG,OAAO,CAAC,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC;AAClG,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC/B,YAAoB,EACpB,cAAsB,EACtB,UAAkB;IAElB,OAAO,CAAC,YAAY,GAAG,cAAc,GAAG,UAAU,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,gBAAwB;IACtD,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,gBAAgB,GAAG,eAAe,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
2
+ import type { DbStatements } from "../db.js";
3
+ declare const Params: import("@sinclair/typebox").TObject<{
4
+ content: import("@sinclair/typebox").TString;
5
+ key: import("@sinclair/typebox").TString;
6
+ }>;
7
+ /** Options for creating the append_memory_block tool. */
8
+ export interface AppendMemoryBlockToolOptions {
9
+ readonly agentId: string;
10
+ readonly stmts: DbStatements;
11
+ }
12
+ /**
13
+ * Create the append_memory_block tool.
14
+ *
15
+ * Appends text to a named memory block. Creates the block if it doesn't exist.
16
+ *
17
+ * @param opts - Tool options
18
+ * @returns AgentTool instance
19
+ */
20
+ export declare function createAppendMemoryBlockTool(opts: AppendMemoryBlockToolOptions): AgentTool<typeof Params>;
21
+ export {};
22
+ //# sourceMappingURL=append-memory-block.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-memory-block.d.ts","sourceRoot":"","sources":["../../src/tools/append-memory-block.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAM7C,QAAA,MAAM,MAAM;;;EAGV,CAAC;AAEH,yDAAyD;AACzD,MAAM,WAAW,4BAA4B;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,4BAA4B,GAChC,SAAS,CAAC,OAAO,MAAM,CAAC,CAmC1B"}
@@ -0,0 +1,45 @@
1
+ import { Type } from "@mariozechner/pi-ai";
2
+ /** Threshold in bytes at which the tool warns about block size. */
3
+ const BLOCK_SIZE_WARNING_BYTES = 100_000;
4
+ const Params = Type.Object({
5
+ content: Type.String({ description: "Text to append to the block" }),
6
+ key: Type.String({ description: "Block name (e.g. 'persona', 'human', 'objectives')" }),
7
+ });
8
+ /**
9
+ * Create the append_memory_block tool.
10
+ *
11
+ * Appends text to a named memory block. Creates the block if it doesn't exist.
12
+ *
13
+ * @param opts - Tool options
14
+ * @returns AgentTool instance
15
+ */
16
+ export function createAppendMemoryBlockTool(opts) {
17
+ return {
18
+ description: "Append text to a named memory block. Creates the block if it doesn't exist (upsert).",
19
+ execute: async (_toolCallId, params) => {
20
+ const now = new Date().toISOString();
21
+ const existing = opts.stmts.getBlockByKey.get(opts.agentId, params.key);
22
+ const newValue = existing ? `${existing.value}\n${params.content}` : params.content;
23
+ opts.stmts.upsertBlock.run({
24
+ agent_id: opts.agentId,
25
+ key: params.key,
26
+ updated_at: now,
27
+ value: newValue,
28
+ });
29
+ const action = existing ? "appended to" : "created";
30
+ const sizeBytes = new TextEncoder().encode(newValue).byteLength;
31
+ const warning = sizeBytes > BLOCK_SIZE_WARNING_BYTES
32
+ ? ` (warning: block is ${Math.round(sizeBytes / 1024)}KB — consider using replace_memory_block to trim)`
33
+ : "";
34
+ const result = {
35
+ content: [{ text: `${action} block "${params.key}"${warning}`, type: "text" }],
36
+ details: { action, sizeBytes },
37
+ };
38
+ return result;
39
+ },
40
+ label: "Append Memory Block",
41
+ name: "append_memory_block",
42
+ parameters: Params,
43
+ };
44
+ }
45
+ //# sourceMappingURL=append-memory-block.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"append-memory-block.js","sourceRoot":"","sources":["../../src/tools/append-memory-block.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAI3C,mEAAmE;AACnE,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,6BAA6B,EAAE,CAAC;IACpE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;CACvF,CAAC,CAAC;AAQH;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CAC1C,IAAkC;IAElC,OAAO;QACN,WAAW,EACV,sFAAsF;QACvF,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,CAE1D,CAAC;YAEb,MAAM,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YAEpF,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;gBAC1B,QAAQ,EAAE,IAAI,CAAC,OAAO;gBACtB,GAAG,EAAE,MAAM,CAAC,GAAG;gBACf,UAAU,EAAE,GAAG;gBACf,KAAK,EAAE,QAAQ;aACf,CAAC,CAAC;YAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC;YACpD,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,UAAU,CAAC;YAChE,MAAM,OAAO,GACZ,SAAS,GAAG,wBAAwB;gBACnC,CAAC,CAAC,uBAAuB,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,mDAAmD;gBACxG,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,MAAM,GAA2D;gBACtE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,WAAW,MAAM,CAAC,GAAG,IAAI,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBAC9E,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE;aAC9B,CAAC;YACF,OAAO,MAAM,CAAC;QACf,CAAC;QACD,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,qBAAqB;QAC3B,UAAU,EAAE,MAAM;KAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
2
+ import type { Database } from "better-sqlite3";
3
+ import { type DbStatements } from "../db.js";
4
+ import type { EmbedFn } from "../types.js";
5
+ declare const Params: import("@sinclair/typebox").TObject<{
6
+ description: import("@sinclair/typebox").TString;
7
+ }>;
8
+ /** Options for creating the forget_memory tool. */
9
+ export interface ForgetMemoryToolOptions {
10
+ readonly agentId: string;
11
+ readonly db: Database;
12
+ readonly embed: EmbedFn;
13
+ /** Minimum cosine similarity to consider a match for deletion (default: 0.7). */
14
+ readonly forgetThreshold?: number;
15
+ /** Max chunks to load for brute-force search (default: 10,000). */
16
+ readonly maxSearchChunks?: number;
17
+ readonly stmts: DbStatements;
18
+ }
19
+ /**
20
+ * Create the forget_memory tool.
21
+ *
22
+ * Hard deletes matching chunks from the database. No audit trail.
23
+ * Deletes are wrapped in a transaction so either all matched chunks
24
+ * are forgotten or none are.
25
+ *
26
+ * @param opts - Tool options
27
+ * @returns AgentTool instance
28
+ */
29
+ export declare function createForgetMemoryTool(opts: ForgetMemoryToolOptions): AgentTool<typeof Params>;
30
+ export {};
31
+ //# sourceMappingURL=forget-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget-memory.d.ts","sourceRoot":"","sources":["../../src/tools/forget-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAE9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,UAAU,CAAC;AAEjE,OAAO,KAAK,EAAS,OAAO,EAAE,MAAM,aAAa,CAAC;AAQlD,QAAA,MAAM,MAAM;;EAIV,CAAC;AAEH,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,mEAAmE;IACnE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,CAAC,OAAO,MAAM,CAAC,CA4D9F"}
@@ -0,0 +1,77 @@
1
+ import { Type } from "@mariozechner/pi-ai";
2
+ import { getAllActiveChunks } from "../db.js";
3
+ import { chunkEmbedding, cosineSimilarity } from "../similarity.js";
4
+ /** Default minimum similarity threshold for a chunk to be considered a match for deletion. */
5
+ const DEFAULT_FORGET_THRESHOLD = 0.7;
6
+ /** Default maximum chunks to load for brute-force semantic search. */
7
+ const DEFAULT_MAX_SEARCH_CHUNKS = 10_000;
8
+ const Params = Type.Object({
9
+ description: Type.String({
10
+ description: "Description of what to forget (e.g. 'that I like Redux')",
11
+ }),
12
+ });
13
+ /**
14
+ * Create the forget_memory tool.
15
+ *
16
+ * Hard deletes matching chunks from the database. No audit trail.
17
+ * Deletes are wrapped in a transaction so either all matched chunks
18
+ * are forgotten or none are.
19
+ *
20
+ * @param opts - Tool options
21
+ * @returns AgentTool instance
22
+ */
23
+ export function createForgetMemoryTool(opts) {
24
+ // Wrap deletes in a transaction for atomicity. clearSupersededBy resurrects
25
+ // any chunk whose superseded_by points at a deleted chunk. This handles
26
+ // single-hop chains correctly (A superseded by B → forget B → A active).
27
+ // Multi-hop chains (A→B→C) work because superseded_by is always single-hop:
28
+ // only the directly superseded chunk references the superseder.
29
+ const deleteMatches = opts.db.transaction((matches) => {
30
+ for (const { chunk } of matches) {
31
+ opts.stmts.clearSupersededBy.run(chunk.id, opts.agentId);
32
+ opts.stmts.deleteChunk.run(chunk.id);
33
+ }
34
+ });
35
+ return {
36
+ description: "Forget specific memories or facts. Performs semantic match and hard deletes matching entries. No record of the forget request is stored.",
37
+ execute: async (_toolCallId, params, signal) => {
38
+ const queryEmbedding = await opts.embed(params.description, signal);
39
+ const maxChunks = opts.maxSearchChunks ?? DEFAULT_MAX_SEARCH_CHUNKS;
40
+ const threshold = opts.forgetThreshold ?? DEFAULT_FORGET_THRESHOLD;
41
+ const allChunks = getAllActiveChunks(opts.stmts, opts.agentId, maxChunks);
42
+ // Find matching chunks above threshold
43
+ const matches = [];
44
+ for (const chunk of allChunks) {
45
+ const similarity = cosineSimilarity(queryEmbedding, chunkEmbedding(chunk));
46
+ if (similarity >= threshold) {
47
+ matches.push({ chunk, similarity });
48
+ }
49
+ }
50
+ if (matches.length === 0) {
51
+ const result = {
52
+ content: [{ text: "No matching memories found to forget.", type: "text" }],
53
+ details: { deleted: 0 },
54
+ };
55
+ return result;
56
+ }
57
+ // Hard delete in a transaction — all-or-nothing.
58
+ // Also resurrects any chunks that were superseded by deleted ones.
59
+ deleteMatches(matches);
60
+ const deleted = matches.map((m) => m.chunk.content);
61
+ const result = {
62
+ content: [
63
+ {
64
+ text: `Forgot ${matches.length} memories:\n${deleted.map((d) => `- ${d}`).join("\n")}`,
65
+ type: "text",
66
+ },
67
+ ],
68
+ details: { deleted: matches.length, items: deleted },
69
+ };
70
+ return result;
71
+ },
72
+ label: "Forget Memory",
73
+ name: "forget_memory",
74
+ parameters: Params,
75
+ };
76
+ }
77
+ //# sourceMappingURL=forget-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forget-memory.js","sourceRoot":"","sources":["../../src/tools/forget-memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export { createAppendMemoryBlockTool } from "./append-memory-block.js";
2
+ export { createForgetMemoryTool } from "./forget-memory.js";
3
+ export { createRecallConversationTool } from "./recall-conversation.js";
4
+ export { createRecallMemoriesTool } from "./recall-memories.js";
5
+ export { createRecallMemoryBlockTool } from "./recall-memory-block.js";
6
+ export { createRememberFactsTool } from "./remember-facts.js";
7
+ export { createReplaceMemoryBlockTool } from "./replace-memory-block.js";
8
+ export { createStoreMemoryTool } from "./store-memory.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { createAppendMemoryBlockTool } from "./append-memory-block.js";
2
+ export { createForgetMemoryTool } from "./forget-memory.js";
3
+ export { createRecallConversationTool } from "./recall-conversation.js";
4
+ export { createRecallMemoriesTool } from "./recall-memories.js";
5
+ export { createRecallMemoryBlockTool } from "./recall-memory-block.js";
6
+ export { createRememberFactsTool } from "./remember-facts.js";
7
+ export { createReplaceMemoryBlockTool } from "./replace-memory-block.js";
8
+ export { createStoreMemoryTool } from "./store-memory.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
2
+ import type { Database } from "better-sqlite3";
3
+ declare const Params: import("@sinclair/typebox").TObject<{
4
+ limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
5
+ query: import("@sinclair/typebox").TString;
6
+ }>;
7
+ /**
8
+ * Options for creating the recall_conversation tool.
9
+ *
10
+ * The messages table must have columns: `id` (INTEGER PRIMARY KEY),
11
+ * `role` (TEXT), `content` (TEXT), `created_at` (TEXT).
12
+ * An FTS5 virtual table named `{messagesTable}_fts` must index the
13
+ * `content` column with `content_rowid=id`.
14
+ */
15
+ export interface RecallConversationToolOptions {
16
+ readonly db: Database;
17
+ readonly messagesTable: string;
18
+ }
19
+ export declare function createRecallConversationTool(opts: RecallConversationToolOptions): AgentTool<typeof Params>;
20
+ export {};
21
+ //# sourceMappingURL=recall-conversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall-conversation.d.ts","sourceRoot":"","sources":["../../src/tools/recall-conversation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAE9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,QAAA,MAAM,MAAM;;;EAKV,CAAC;AASH;;;;;;;GAOG;AACH,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IACtB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAwBD,wBAAgB,4BAA4B,CAC3C,IAAI,EAAE,6BAA6B,GACjC,SAAS,CAAC,OAAO,MAAM,CAAC,CA2E1B"}
@@ -0,0 +1,93 @@
1
+ import { Type } from "@mariozechner/pi-ai";
2
+ const Params = Type.Object({
3
+ limit: Type.Optional(Type.Number({ description: "Max results to return (default: 20)", minimum: 1 })),
4
+ query: Type.String({ description: "Search term for past messages" }),
5
+ });
6
+ /**
7
+ * Create the recall_conversation tool.
8
+ *
9
+ * Full-text search over past messages using FTS5.
10
+ * The consumer (marrow) is responsible for creating the messages table
11
+ * and FTS index.
12
+ *
13
+ * @param opts - Tool options
14
+ * @returns AgentTool instance
15
+ */
16
+ /**
17
+ * Validate a SQL identifier to prevent injection via table name interpolation.
18
+ *
19
+ * @param name - Table name to validate
20
+ * @throws If the name contains unsafe characters
21
+ */
22
+ function assertSafeIdentifier(name) {
23
+ if (!/^[a-zA-Z_]\w*$/.test(name)) {
24
+ throw new Error(`Unsafe SQL identifier: "${name}"`);
25
+ }
26
+ }
27
+ export function createRecallConversationTool(opts) {
28
+ assertSafeIdentifier(opts.messagesTable);
29
+ const ftsTable = `${opts.messagesTable}_fts`;
30
+ return {
31
+ description: "Full-text search over past conversation messages. Returns matching messages ranked by relevance.",
32
+ execute: async (_toolCallId, params) => {
33
+ const limit = params.limit ?? 20;
34
+ let rows;
35
+ try {
36
+ // Prepared per-call intentionally: the FTS table may not exist when the
37
+ // tool is created (marrow creates it). better-sqlite3 caches prepared
38
+ // statements internally by SQL text, so runtime overhead is negligible.
39
+ rows = opts.db
40
+ .prepare(`SELECT m.role, m.content, m.created_at
41
+ FROM ${ftsTable} fts
42
+ JOIN ${opts.messagesTable} m ON fts.rowid = m.id
43
+ WHERE ${ftsTable} MATCH ?
44
+ ORDER BY rank
45
+ LIMIT ?`)
46
+ .all(params.query, limit);
47
+ }
48
+ catch (err) {
49
+ // Only handle SQLite operational errors (missing table, bad FTS syntax).
50
+ // Re-throw unexpected errors (I/O, corruption, OOM).
51
+ const isSqliteError = err instanceof Error &&
52
+ "code" in err &&
53
+ err.code === "SQLITE_ERROR";
54
+ if (!isSqliteError) {
55
+ throw err;
56
+ }
57
+ const msg = err.message?.toLowerCase() ?? "";
58
+ const isTableMissing = msg.includes("no such table") || msg.includes("no such module");
59
+ const result = {
60
+ content: [
61
+ {
62
+ text: isTableMissing
63
+ ? "Conversation search unavailable (FTS index may not exist)."
64
+ : `Search query error: ${err.message}`,
65
+ type: "text",
66
+ },
67
+ ],
68
+ details: { error: isTableMissing ? "fts_unavailable" : "query_error" },
69
+ };
70
+ return result;
71
+ }
72
+ if (rows.length === 0) {
73
+ const result = {
74
+ content: [{ text: "No matching messages found.", type: "text" }],
75
+ details: { matches: 0 },
76
+ };
77
+ return result;
78
+ }
79
+ const lines = rows.map((r, i) => {
80
+ return `${i + 1}. [${r.role}] (${r.created_at}) ${r.content}`;
81
+ });
82
+ const result = {
83
+ content: [{ text: lines.join("\n"), type: "text" }],
84
+ details: { matches: rows.length },
85
+ };
86
+ return result;
87
+ },
88
+ label: "Recall Conversation",
89
+ name: "recall_conversation",
90
+ parameters: Params,
91
+ };
92
+ }
93
+ //# sourceMappingURL=recall-conversation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall-conversation.js","sourceRoot":"","sources":["../../src/tools/recall-conversation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAG3C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,IAAI,CAAC,QAAQ,CACnB,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qCAAqC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAC/E;IACD,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;CACpE,CAAC,CAAC;AAsBH;;;;;;;;;GASG;AACH;;;;;GAKG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACzC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,GAAG,CAAC,CAAC;IACrD,CAAC;AACF,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC3C,IAAmC;IAEnC,oBAAoB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,aAAa,MAAM,CAAC;IAE7C,OAAO;QACN,WAAW,EACV,kGAAkG;QACnG,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YAEjC,IAAI,IAAwB,CAAC;YAC7B,IAAI,CAAC;gBACJ,wEAAwE;gBACxE,sEAAsE;gBACtE,wEAAwE;gBACxE,IAAI,GAAG,IAAI,CAAC,EAAE;qBACZ,OAAO,CACP;aACO,QAAQ;aACR,IAAI,CAAC,aAAa;cACjB,QAAQ;;cAER,CACR;qBACA,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAuB,CAAC;YAClD,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACvB,yEAAyE;gBACzE,qDAAqD;gBACrD,MAAM,aAAa,GAClB,GAAG,YAAY,KAAK;oBACpB,MAAM,IAAI,GAAG;oBACZ,GAAgC,CAAC,IAAI,KAAK,cAAc,CAAC;gBAC3D,IAAI,CAAC,aAAa,EAAE,CAAC;oBACpB,MAAM,GAAG,CAAC;gBACX,CAAC;gBAED,MAAM,GAAG,GAAI,GAAa,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;gBACxD,MAAM,cAAc,GAAG,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC;gBAEvF,MAAM,MAAM,GAAuC;oBAClD,OAAO,EAAE;wBACR;4BACC,IAAI,EAAE,cAAc;gCACnB,CAAC,CAAC,4DAA4D;gCAC9D,CAAC,CAAC,uBAAwB,GAAa,CAAC,OAAO,EAAE;4BAClD,IAAI,EAAE,MAAM;yBACZ;qBACD;oBACD,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,EAAE;iBACtE,CAAC;gBACF,OAAO,MAAM,CAAC;YACf,CAAC;YAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAyC;oBACpD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;oBAChE,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE;iBACvB,CAAC;gBACF,OAAO,MAAM,CAAC;YACf,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAC/B,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;YAC/D,CAAC,CAAC,CAAC;YAEH,MAAM,MAAM,GAAyC;gBACpD,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;gBACnD,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE;aACjC,CAAC;YACF,OAAO,MAAM,CAAC;QACf,CAAC;QACD,KAAK,EAAE,qBAAqB;QAC5B,IAAI,EAAE,qBAAqB;QAC3B,UAAU,EAAE,MAAM;KAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { AgentTool } from "@mariozechner/pi-agent-core";
2
+ import { type DbStatements } from "../db.js";
3
+ import type { EmbedFn } from "../types.js";
4
+ declare const Params: import("@sinclair/typebox").TObject<{
5
+ limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
6
+ query: import("@sinclair/typebox").TString;
7
+ }>;
8
+ /** Options for creating the recall_memories tool. */
9
+ export interface RecallMemoriesToolOptions {
10
+ readonly agentId: string;
11
+ readonly embed: EmbedFn;
12
+ /** Max chunks to load for brute-force search (default: 10,000). */
13
+ readonly maxSearchChunks?: number;
14
+ /** Minimum cosine similarity to include in results (default: 0.1). */
15
+ readonly minSimilarity?: number;
16
+ readonly stmts: DbStatements;
17
+ }
18
+ /**
19
+ * Create the recall_memories tool.
20
+ *
21
+ * Performs semantic search over facts and memories, weighted by
22
+ * cosine similarity, effective strength, and recency.
23
+ *
24
+ * @param opts - Tool options
25
+ * @returns AgentTool instance
26
+ */
27
+ export declare function createRecallMemoriesTool(opts: RecallMemoriesToolOptions): AgentTool<typeof Params>;
28
+ export {};
29
+ //# sourceMappingURL=recall-memories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall-memories.d.ts","sourceRoot":"","sources":["../../src/tools/recall-memories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,6BAA6B,CAAC;AAE9E,OAAO,EAAE,KAAK,YAAY,EAAsB,MAAM,UAAU,CAAC;AASjE,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,aAAa,CAAC;AAyBzD,QAAA,MAAM,MAAM;;;EAKV,CAAC;AAEH,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,sEAAsE;IACtE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,yBAAyB,GAC7B,SAAS,CAAC,OAAO,MAAM,CAAC,CAsF1B"}
@@ -0,0 +1,106 @@
1
+ import { Type } from "@mariozechner/pi-ai";
2
+ import { getAllActiveChunks } from "../db.js";
3
+ import { chunkEmbedding, cosineSimilarity } from "../similarity.js";
4
+ import { effectiveStrength, recencyScore, retrievalBoost, STRENGTH_FLOOR, searchScore, } from "../strength.js";
5
+ /** Default maximum chunks to load for brute-force semantic search. */
6
+ const DEFAULT_MAX_SEARCH_CHUNKS = 10_000;
7
+ /** Default minimum cosine similarity to include a result. */
8
+ const DEFAULT_MIN_SIMILARITY = 0.1;
9
+ /** SQLite error codes that are transient (busy/locked) and safe to swallow. */
10
+ const TRANSIENT_SQLITE_CODES = new Set(["SQLITE_BUSY", "SQLITE_LOCKED"]);
11
+ /**
12
+ * Check if an error is a transient SQLite error (busy/locked).
13
+ *
14
+ * @param err - Unknown error value
15
+ * @returns True if this is a transient SQLite error safe to ignore
16
+ */
17
+ function isSqliteTransient(err) {
18
+ return (err instanceof Error &&
19
+ "code" in err &&
20
+ TRANSIENT_SQLITE_CODES.has(err.code));
21
+ }
22
+ const Params = Type.Object({
23
+ limit: Type.Optional(Type.Number({ description: "Max results to return (default: 10)", minimum: 1 })),
24
+ query: Type.String({ description: "What to search for in memory" }),
25
+ });
26
+ /**
27
+ * Create the recall_memories tool.
28
+ *
29
+ * Performs semantic search over facts and memories, weighted by
30
+ * cosine similarity, effective strength, and recency.
31
+ *
32
+ * @param opts - Tool options
33
+ * @returns AgentTool instance
34
+ */
35
+ export function createRecallMemoriesTool(opts) {
36
+ return {
37
+ description: "Semantic search over stored facts and memories. Returns results ranked by relevance, strength, and recency.",
38
+ execute: async (_toolCallId, params, signal) => {
39
+ const limit = params.limit ?? 10;
40
+ const maxChunks = opts.maxSearchChunks ?? DEFAULT_MAX_SEARCH_CHUNKS;
41
+ const minSim = opts.minSimilarity ?? DEFAULT_MIN_SIMILARITY;
42
+ const now = new Date();
43
+ const queryEmbedding = await opts.embed(params.query, signal);
44
+ // Single query for all active chunks (facts + memories),
45
+ // capped to avoid loading unbounded data into memory.
46
+ const allChunks = getAllActiveChunks(opts.stmts, opts.agentId, maxChunks);
47
+ // Score and filter
48
+ const scored = [];
49
+ for (const chunk of allChunks) {
50
+ const similarity = cosineSimilarity(queryEmbedding, chunkEmbedding(chunk));
51
+ if (similarity < minSim) {
52
+ continue;
53
+ }
54
+ const hoursSince = (now.getTime() - new Date(chunk.last_accessed_at).getTime()) / (1000 * 60 * 60);
55
+ const strength = effectiveStrength(chunk.running_intensity, chunk.access_count, hoursSince);
56
+ if (strength < STRENGTH_FLOOR) {
57
+ continue;
58
+ }
59
+ const daysSince = (now.getTime() - new Date(chunk.created_at).getTime()) / (1000 * 60 * 60 * 24);
60
+ const recency = recencyScore(daysSince);
61
+ const score = searchScore(similarity, strength, recency);
62
+ scored.push({ chunk, score });
63
+ }
64
+ // Sort by score descending, take top N
65
+ scored.sort((a, b) => b.score - a.score);
66
+ const topResults = scored.slice(0, limit);
67
+ // Apply retrieval boost to accessed chunks.
68
+ // Boost failures are non-fatal — search results are still valid.
69
+ // Only swallow SQLite busy/locked errors; re-throw corruption/OOM.
70
+ for (const { chunk } of topResults) {
71
+ try {
72
+ const boosted = retrievalBoost(chunk.running_intensity);
73
+ opts.stmts.touchChunk.run({
74
+ id: chunk.id,
75
+ last_accessed_at: now.toISOString(),
76
+ running_intensity: boosted,
77
+ });
78
+ }
79
+ catch (err) {
80
+ if (isSqliteTransient(err)) {
81
+ continue;
82
+ }
83
+ throw err;
84
+ }
85
+ }
86
+ // Format response
87
+ if (topResults.length === 0) {
88
+ const result = {
89
+ content: [{ text: "No memories found.", type: "text" }],
90
+ details: { results: [] },
91
+ };
92
+ return result;
93
+ }
94
+ const lines = topResults.map((r, i) => `${i + 1}. [${r.chunk.kind}] (score: ${r.score.toFixed(3)}) ${r.chunk.content}`);
95
+ const result = {
96
+ content: [{ text: lines.join("\n"), type: "text" }],
97
+ details: { results: topResults },
98
+ };
99
+ return result;
100
+ },
101
+ label: "Recall Memories",
102
+ name: "recall_memories",
103
+ parameters: Params,
104
+ };
105
+ }
106
+ //# sourceMappingURL=recall-memories.js.map