@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.
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +559 -0
- package/dist/cli.js.map +1 -0
- package/dist/db.d.ts +45 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +80 -0
- package/dist/db.js.map +1 -0
- package/dist/extractor.d.ts +23 -0
- package/dist/extractor.d.ts.map +1 -0
- package/dist/extractor.js +121 -0
- package/dist/extractor.js.map +1 -0
- package/dist/hash.d.ts +11 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +14 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/embedding.d.ts +27 -0
- package/dist/providers/embedding.d.ts.map +1 -0
- package/dist/providers/embedding.js +41 -0
- package/dist/providers/embedding.js.map +1 -0
- package/dist/providers/llm.d.ts +29 -0
- package/dist/providers/llm.d.ts.map +1 -0
- package/dist/providers/llm.js +74 -0
- package/dist/providers/llm.js.map +1 -0
- package/dist/schema.d.ts +20 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +78 -0
- package/dist/schema.js.map +1 -0
- package/dist/server/config.d.ts +38 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +71 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/index.d.ts +13 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +372 -0
- package/dist/server/index.js.map +1 -0
- package/dist/similarity.d.ts +41 -0
- package/dist/similarity.d.ts.map +1 -0
- package/dist/similarity.js +70 -0
- package/dist/similarity.js.map +1 -0
- package/dist/strength.d.ts +59 -0
- package/dist/strength.d.ts.map +1 -0
- package/dist/strength.js +76 -0
- package/dist/strength.js.map +1 -0
- package/dist/tools/append-memory-block.d.ts +22 -0
- package/dist/tools/append-memory-block.d.ts.map +1 -0
- package/dist/tools/append-memory-block.js +45 -0
- package/dist/tools/append-memory-block.js.map +1 -0
- package/dist/tools/forget-memory.d.ts +31 -0
- package/dist/tools/forget-memory.d.ts.map +1 -0
- package/dist/tools/forget-memory.js +77 -0
- package/dist/tools/forget-memory.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +9 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/recall-conversation.d.ts +21 -0
- package/dist/tools/recall-conversation.d.ts.map +1 -0
- package/dist/tools/recall-conversation.js +93 -0
- package/dist/tools/recall-conversation.js.map +1 -0
- package/dist/tools/recall-memories.d.ts +29 -0
- package/dist/tools/recall-memories.d.ts.map +1 -0
- package/dist/tools/recall-memories.js +106 -0
- package/dist/tools/recall-memories.js.map +1 -0
- package/dist/tools/recall-memory-block.d.ts +21 -0
- package/dist/tools/recall-memory-block.d.ts.map +1 -0
- package/dist/tools/recall-memory-block.js +36 -0
- package/dist/tools/recall-memory-block.js.map +1 -0
- package/dist/tools/remember-facts.d.ts +30 -0
- package/dist/tools/remember-facts.d.ts.map +1 -0
- package/dist/tools/remember-facts.js +235 -0
- package/dist/tools/remember-facts.js.map +1 -0
- package/dist/tools/replace-memory-block.d.ts +25 -0
- package/dist/tools/replace-memory-block.d.ts.map +1 -0
- package/dist/tools/replace-memory-block.js +69 -0
- package/dist/tools/replace-memory-block.js.map +1 -0
- package/dist/tools/store-memory.d.ts +27 -0
- package/dist/tools/store-memory.d.ts.map +1 -0
- package/dist/tools/store-memory.js +129 -0
- package/dist/tools/store-memory.js.map +1 -0
- package/dist/types.d.ts +86 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ulid.d.ts +13 -0
- package/dist/ulid.d.ts.map +1 -0
- package/dist/ulid.js +39 -0
- package/dist/ulid.js.map +1 -0
- 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"}
|
package/dist/strength.js
ADDED
|
@@ -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":"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"}
|
|
@@ -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
|