@caupulican/pi-adaptative 0.80.62 → 0.80.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.80.63] - 2026-06-27
2
+
3
+ ### Changed
4
+
5
+ - Persistent memory no longer accumulates duplicate facts: when a remembered fact restates an existing one, it supersedes that line in place instead of piling up (anti append-rot).
6
+
1
7
  ## [0.80.62] - 2026-06-27
2
8
 
3
9
  ### Added
@@ -1,5 +1,11 @@
1
1
  import type { ToolDefinition } from "../../extensions/types.ts";
2
2
  import type { MemoryLifecycleContext, MemoryProvider } from "../memory-provider.ts";
3
+ /**
4
+ * R5 confront-before-write (anti append-rot): if `content` is a near-duplicate of an existing
5
+ * non-empty line (token Jaccard ≥ threshold — i.e. the same fact reworded), supersede that line in
6
+ * place and return the rewritten file; otherwise return null (the caller appends normally).
7
+ */
8
+ export declare function supersedeNearDuplicateLine(existing: string, content: string): string | null;
3
9
  export declare class FileStoreProvider implements MemoryProvider {
4
10
  readonly name = "file-store";
5
11
  private ctx?;
@@ -1 +1 @@
1
- {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../../../src/core/memory/providers/file-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAiBpF,qBAAa,iBAAkB,YAAW,cAAc;IACvD,SAAgB,IAAI,gBAAgB;IAEpC,OAAO,CAAC,GAAG,CAAC,CAAyB;IACrC,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,YAAY,CAAM;IAE1B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,eAAe,CAAM;IAG7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAQ;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,WAAW,IAAI,OAAO,CAE5B;IAEM,eAAe;;MAErB;IAEY,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtF;IAEM,iBAAiB,IAAI,MAAM,CA6BjC;IAEY,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrD;IAEY,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAErC;IAEM,iBAAiB,IAAI,MAAM,EAAE,CAEnC;IAEM,kBAAkB,IAAI,cAAc,EAAE,CA8H5C;CACD","sourcesContent":["import { existsSync, promises as fs, mkdirSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { scanContextFileThreats } from \"../../resource-loader.ts\";\nimport type { MemoryLifecycleContext, MemoryProvider } from \"../memory-provider.ts\";\n\nconst memorySchema = Type.Object({\n\taction: Type.Union([Type.Literal(\"add\"), Type.Literal(\"replace\"), Type.Literal(\"remove\")], {\n\t\tdescription: \"Action to perform: add new content, replace existing content, or remove content\",\n\t}),\n\ttarget: Type.Union([Type.Literal(\"memory\"), Type.Literal(\"user\")], {\n\t\tdescription: \"Target file: 'memory' for MEMORY.md, 'user' for USER.md\",\n\t}),\n\tcontent: Type.Optional(Type.String({ description: \"Content to write (required for 'add' or 'replace')\" })),\n\toldContent: Type.Optional(\n\t\tType.String({ description: \"Exact substring to replace or remove (required for 'replace' or 'remove')\" }),\n\t),\n});\n\ntype MemoryParams = Static<typeof memorySchema>;\n\nexport class FileStoreProvider implements MemoryProvider {\n\tpublic readonly name = \"file-store\";\n\n\tprivate ctx?: MemoryLifecycleContext;\n\tprivate memoryFilePath = \"\";\n\tprivate userFilePath = \"\";\n\n\tprivate lastWrittenMemory = \"\";\n\tprivate lastWrittenUser = \"\";\n\n\t// Character budgets\n\tprivate static readonly BUDGET_MEMORY = 2200;\n\tprivate static readonly BUDGET_USER = 1375;\n\n\tpublic isAvailable(): boolean {\n\t\treturn true;\n\t}\n\n\tpublic getCapabilities() {\n\t\treturn { surfaces: [\"context\" as const] };\n\t}\n\n\tpublic async initialize(_sessionId: string, ctx: MemoryLifecycleContext): Promise<void> {\n\t\tthis.ctx = ctx;\n\t\tthis.memoryFilePath = join(ctx.agentDir, \"MEMORY.md\");\n\t\tthis.userFilePath = join(ctx.agentDir, \"USER.md\");\n\n\t\t// Ensure agentDir exists\n\t\tif (!existsSync(ctx.agentDir)) {\n\t\t\tmkdirSync(ctx.agentDir, { recursive: true });\n\t\t}\n\n\t\t// Initialize files if they do not exist\n\t\tif (!existsSync(this.memoryFilePath)) {\n\t\t\twriteFileSync(this.memoryFilePath, \"\", \"utf-8\");\n\t\t}\n\t\tif (!existsSync(this.userFilePath)) {\n\t\t\twriteFileSync(this.userFilePath, \"\", \"utf-8\");\n\t\t}\n\n\t\t// Load initial contents\n\t\tthis.lastWrittenMemory = await fs.readFile(this.memoryFilePath, \"utf-8\");\n\t\tthis.lastWrittenUser = await fs.readFile(this.userFilePath, \"utf-8\");\n\t}\n\n\tpublic systemPromptBlock(): string {\n\t\tconst sanitize = (content: string) => {\n\t\t\tconst lines = content.split(\"\\n\");\n\t\t\tconst sanitizedLines = lines.map((line) => {\n\t\t\t\tconst threats = scanContextFileThreats(line);\n\t\t\t\tif (threats.length > 0) {\n\t\t\t\t\treturn `[BLOCKED: potential threat detected (${threats.join(\", \")})]`;\n\t\t\t\t}\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\treturn sanitizedLines.join(\"\\n\");\n\t\t};\n\n\t\tconst mem = sanitize(this.lastWrittenMemory);\n\t\tconst usr = sanitize(this.lastWrittenUser);\n\n\t\tconst blocks: string[] = [];\n\t\tif (mem.trim()) {\n\t\t\tblocks.push(`## MEMORY.md:\\n${mem}`);\n\t\t}\n\t\tif (usr.trim()) {\n\t\t\tblocks.push(`## USER.md:\\n${usr}`);\n\t\t}\n\n\t\tif (blocks.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\treturn `=== Persistent Memory (file-store) ===\\n[System Note: Below is a snapshot of your persistent memory. You can update these using the 'memory' tool.]\\n\\n${blocks.join(\"\\n\\n\")}`;\n\t}\n\n\tpublic async prefetch(_query: string): Promise<string> {\n\t\t// static system prompt block is sufficient for file-store default; no-op prefetch\n\t\treturn \"\";\n\t}\n\n\tpublic async shutdown(): Promise<void> {\n\t\t// no-op\n\t}\n\n\tpublic getContextMarkers(): string[] {\n\t\treturn [];\n\t}\n\n\tpublic getToolDefinitions(): ToolDefinition[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"memory\",\n\t\t\t\tlabel: \"Persistent Memory Manager\",\n\t\t\t\tdescription: \"Add, replace, or remove contents in persistent memory files (MEMORY.md/USER.md).\",\n\t\t\t\tparameters: memorySchema,\n\t\t\t\texecute: async (_toolCallId, params: MemoryParams, _signal, _onUpdate, _execCtx) => {\n\t\t\t\t\tif (this.ctx?.isChildSession) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: \"Error: Writes to persistent memory are not allowed in child sessions (subagents).\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: \"Child session write-gated\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { action, target, content, oldContent } = params;\n\t\t\t\t\tconst filePath = target === \"memory\" ? this.memoryFilePath : this.userFilePath;\n\t\t\t\t\tconst budget = target === \"memory\" ? FileStoreProvider.BUDGET_MEMORY : FileStoreProvider.BUDGET_USER;\n\n\t\t\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// File lock\n\t\t\t\t\t\trelease = await lockfile.lock(filePath, { realpath: false, retries: 5 });\n\n\t\t\t\t\t\tconst lastWritten = target === \"memory\" ? this.lastWrittenMemory : this.lastWrittenUser;\n\t\t\t\t\t\t// Read current file content on disk for drift detection\n\t\t\t\t\t\tconst currentOnDisk = await fs.readFile(filePath, \"utf-8\");\n\t\t\t\t\t\tif (currentOnDisk !== lastWritten) {\n\t\t\t\t\t\t\t// Drift detected. Backup current file and refuse write.\n\t\t\t\t\t\t\tconst backupPath = `${filePath}.bak.${Date.now()}`;\n\t\t\t\t\t\t\tawait fs.writeFile(backupPath, currentOnDisk, \"utf-8\");\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Drift detected. The memory file has been modified out-of-band by an external process. A backup was created at ${backupPath}. Operation aborted.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Drift detected\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet newContent = currentOnDisk;\n\t\t\t\t\t\tif (action === \"add\") {\n\t\t\t\t\t\t\tif (content === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'content' is required for action 'add'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent =\n\t\t\t\t\t\t\t\tnewContent.endsWith(\"\\n\") || newContent === \"\"\n\t\t\t\t\t\t\t\t\t? `${newContent}${content}\\n`\n\t\t\t\t\t\t\t\t\t: `${newContent}\\n${content}\\n`;\n\t\t\t\t\t\t} else if (action === \"replace\") {\n\t\t\t\t\t\t\tif (content === undefined || oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameters 'content' and 'oldContent' are required for action 'replace'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to replace ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, content);\n\t\t\t\t\t\t} else if (action === \"remove\") {\n\t\t\t\t\t\t\tif (oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'oldContent' is required for action 'remove'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to remove ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, \"\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Budget check\n\t\t\t\t\t\tif (newContent.length > budget) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Memory budget exceeded. ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"} limit is ${budget} characters. Current operation would result in ${newContent.length} characters.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Memory budget exceeded\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic write\n\t\t\t\t\t\tconst tmpPath = `${filePath}.tmp`;\n\t\t\t\t\t\tawait fs.writeFile(tmpPath, newContent, \"utf-8\");\n\t\t\t\t\t\tawait fs.rename(tmpPath, filePath);\n\n\t\t\t\t\t\t// Update in-memory tracker\n\t\t\t\t\t\tif (target === \"memory\") {\n\t\t\t\t\t\t\tthis.lastWrittenMemory = newContent;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.lastWrittenUser = newContent;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Successfully updated ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"}.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: true },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Error: Failed to perform memory operation: ${String(err)}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: String(err) },\n\t\t\t\t\t\t};\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (release) {\n\t\t\t\t\t\t\tawait release();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n"]}
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../../../src/core/memory/providers/file-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEpF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmB3F;AAiBD,qBAAa,iBAAkB,YAAW,cAAc;IACvD,SAAgB,IAAI,gBAAgB;IAEpC,OAAO,CAAC,GAAG,CAAC,CAAyB;IACrC,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,YAAY,CAAM;IAE1B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,eAAe,CAAM;IAG7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAQ;IAC7C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAQ;IAEpC,WAAW,IAAI,OAAO,CAE5B;IAEM,eAAe;;MAErB;IAEY,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBtF;IAEM,iBAAiB,IAAI,MAAM,CA6BjC;IAEY,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrD;IAEY,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAErC;IAEM,iBAAiB,IAAI,MAAM,EAAE,CAEnC;IAEM,kBAAkB,IAAI,cAAc,EAAE,CAqI5C;CACD","sourcesContent":["import { existsSync, promises as fs, mkdirSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { scanContextFileThreats } from \"../../resource-loader.ts\";\nimport { jaccard, tokenize } from \"../../tools/skill-audit.ts\";\nimport type { MemoryLifecycleContext, MemoryProvider } from \"../memory-provider.ts\";\n\n/**\n * R5 confront-before-write (anti append-rot): if `content` is a near-duplicate of an existing\n * non-empty line (token Jaccard ≥ threshold — i.e. the same fact reworded), supersede that line in\n * place and return the rewritten file; otherwise return null (the caller appends normally).\n */\nexport function supersedeNearDuplicateLine(existing: string, content: string): string | null {\n\tconst NEAR_DUP_THRESHOLD = 0.6;\n\tconst contentTokens = tokenize(content);\n\tif (contentTokens.length === 0) return null;\n\tconst lines = existing.split(\"\\n\");\n\tlet bestIdx = -1;\n\tlet bestScore = NEAR_DUP_THRESHOLD;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i].trim();\n\t\tif (!line) continue;\n\t\tconst score = jaccard(contentTokens, tokenize(line));\n\t\tif (score >= bestScore) {\n\t\t\tbestScore = score;\n\t\t\tbestIdx = i;\n\t\t}\n\t}\n\tif (bestIdx === -1) return null;\n\tlines[bestIdx] = content;\n\treturn lines.join(\"\\n\");\n}\n\nconst memorySchema = Type.Object({\n\taction: Type.Union([Type.Literal(\"add\"), Type.Literal(\"replace\"), Type.Literal(\"remove\")], {\n\t\tdescription: \"Action to perform: add new content, replace existing content, or remove content\",\n\t}),\n\ttarget: Type.Union([Type.Literal(\"memory\"), Type.Literal(\"user\")], {\n\t\tdescription: \"Target file: 'memory' for MEMORY.md, 'user' for USER.md\",\n\t}),\n\tcontent: Type.Optional(Type.String({ description: \"Content to write (required for 'add' or 'replace')\" })),\n\toldContent: Type.Optional(\n\t\tType.String({ description: \"Exact substring to replace or remove (required for 'replace' or 'remove')\" }),\n\t),\n});\n\ntype MemoryParams = Static<typeof memorySchema>;\n\nexport class FileStoreProvider implements MemoryProvider {\n\tpublic readonly name = \"file-store\";\n\n\tprivate ctx?: MemoryLifecycleContext;\n\tprivate memoryFilePath = \"\";\n\tprivate userFilePath = \"\";\n\n\tprivate lastWrittenMemory = \"\";\n\tprivate lastWrittenUser = \"\";\n\n\t// Character budgets\n\tprivate static readonly BUDGET_MEMORY = 2200;\n\tprivate static readonly BUDGET_USER = 1375;\n\n\tpublic isAvailable(): boolean {\n\t\treturn true;\n\t}\n\n\tpublic getCapabilities() {\n\t\treturn { surfaces: [\"context\" as const] };\n\t}\n\n\tpublic async initialize(_sessionId: string, ctx: MemoryLifecycleContext): Promise<void> {\n\t\tthis.ctx = ctx;\n\t\tthis.memoryFilePath = join(ctx.agentDir, \"MEMORY.md\");\n\t\tthis.userFilePath = join(ctx.agentDir, \"USER.md\");\n\n\t\t// Ensure agentDir exists\n\t\tif (!existsSync(ctx.agentDir)) {\n\t\t\tmkdirSync(ctx.agentDir, { recursive: true });\n\t\t}\n\n\t\t// Initialize files if they do not exist\n\t\tif (!existsSync(this.memoryFilePath)) {\n\t\t\twriteFileSync(this.memoryFilePath, \"\", \"utf-8\");\n\t\t}\n\t\tif (!existsSync(this.userFilePath)) {\n\t\t\twriteFileSync(this.userFilePath, \"\", \"utf-8\");\n\t\t}\n\n\t\t// Load initial contents\n\t\tthis.lastWrittenMemory = await fs.readFile(this.memoryFilePath, \"utf-8\");\n\t\tthis.lastWrittenUser = await fs.readFile(this.userFilePath, \"utf-8\");\n\t}\n\n\tpublic systemPromptBlock(): string {\n\t\tconst sanitize = (content: string) => {\n\t\t\tconst lines = content.split(\"\\n\");\n\t\t\tconst sanitizedLines = lines.map((line) => {\n\t\t\t\tconst threats = scanContextFileThreats(line);\n\t\t\t\tif (threats.length > 0) {\n\t\t\t\t\treturn `[BLOCKED: potential threat detected (${threats.join(\", \")})]`;\n\t\t\t\t}\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\treturn sanitizedLines.join(\"\\n\");\n\t\t};\n\n\t\tconst mem = sanitize(this.lastWrittenMemory);\n\t\tconst usr = sanitize(this.lastWrittenUser);\n\n\t\tconst blocks: string[] = [];\n\t\tif (mem.trim()) {\n\t\t\tblocks.push(`## MEMORY.md:\\n${mem}`);\n\t\t}\n\t\tif (usr.trim()) {\n\t\t\tblocks.push(`## USER.md:\\n${usr}`);\n\t\t}\n\n\t\tif (blocks.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\treturn `=== Persistent Memory (file-store) ===\\n[System Note: Below is a snapshot of your persistent memory. You can update these using the 'memory' tool.]\\n\\n${blocks.join(\"\\n\\n\")}`;\n\t}\n\n\tpublic async prefetch(_query: string): Promise<string> {\n\t\t// static system prompt block is sufficient for file-store default; no-op prefetch\n\t\treturn \"\";\n\t}\n\n\tpublic async shutdown(): Promise<void> {\n\t\t// no-op\n\t}\n\n\tpublic getContextMarkers(): string[] {\n\t\treturn [];\n\t}\n\n\tpublic getToolDefinitions(): ToolDefinition[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"memory\",\n\t\t\t\tlabel: \"Persistent Memory Manager\",\n\t\t\t\tdescription: \"Add, replace, or remove contents in persistent memory files (MEMORY.md/USER.md).\",\n\t\t\t\tparameters: memorySchema,\n\t\t\t\texecute: async (_toolCallId, params: MemoryParams, _signal, _onUpdate, _execCtx) => {\n\t\t\t\t\tif (this.ctx?.isChildSession) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: \"Error: Writes to persistent memory are not allowed in child sessions (subagents).\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: \"Child session write-gated\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { action, target, content, oldContent } = params;\n\t\t\t\t\tconst filePath = target === \"memory\" ? this.memoryFilePath : this.userFilePath;\n\t\t\t\t\tconst budget = target === \"memory\" ? FileStoreProvider.BUDGET_MEMORY : FileStoreProvider.BUDGET_USER;\n\n\t\t\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// File lock\n\t\t\t\t\t\trelease = await lockfile.lock(filePath, { realpath: false, retries: 5 });\n\n\t\t\t\t\t\tconst lastWritten = target === \"memory\" ? this.lastWrittenMemory : this.lastWrittenUser;\n\t\t\t\t\t\t// Read current file content on disk for drift detection\n\t\t\t\t\t\tconst currentOnDisk = await fs.readFile(filePath, \"utf-8\");\n\t\t\t\t\t\tif (currentOnDisk !== lastWritten) {\n\t\t\t\t\t\t\t// Drift detected. Backup current file and refuse write.\n\t\t\t\t\t\t\tconst backupPath = `${filePath}.bak.${Date.now()}`;\n\t\t\t\t\t\t\tawait fs.writeFile(backupPath, currentOnDisk, \"utf-8\");\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Drift detected. The memory file has been modified out-of-band by an external process. A backup was created at ${backupPath}. Operation aborted.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Drift detected\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet newContent = currentOnDisk;\n\t\t\t\t\t\tif (action === \"add\") {\n\t\t\t\t\t\t\tif (content === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'content' is required for action 'add'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// R5: confront before write. If this fact is a near-duplicate of an existing line,\n\t\t\t\t\t\t\t// supersede it in place instead of appending a redundant copy (prevents append-rot).\n\t\t\t\t\t\t\tconst superseded = supersedeNearDuplicateLine(currentOnDisk, content);\n\t\t\t\t\t\t\tif (superseded !== null) {\n\t\t\t\t\t\t\t\tnewContent = superseded;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnewContent =\n\t\t\t\t\t\t\t\t\tnewContent.endsWith(\"\\n\") || newContent === \"\"\n\t\t\t\t\t\t\t\t\t\t? `${newContent}${content}\\n`\n\t\t\t\t\t\t\t\t\t\t: `${newContent}\\n${content}\\n`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (action === \"replace\") {\n\t\t\t\t\t\t\tif (content === undefined || oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameters 'content' and 'oldContent' are required for action 'replace'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to replace ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, content);\n\t\t\t\t\t\t} else if (action === \"remove\") {\n\t\t\t\t\t\t\tif (oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'oldContent' is required for action 'remove'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to remove ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, \"\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Budget check\n\t\t\t\t\t\tif (newContent.length > budget) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Memory budget exceeded. ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"} limit is ${budget} characters. Current operation would result in ${newContent.length} characters.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Memory budget exceeded\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic write\n\t\t\t\t\t\tconst tmpPath = `${filePath}.tmp`;\n\t\t\t\t\t\tawait fs.writeFile(tmpPath, newContent, \"utf-8\");\n\t\t\t\t\t\tawait fs.rename(tmpPath, filePath);\n\n\t\t\t\t\t\t// Update in-memory tracker\n\t\t\t\t\t\tif (target === \"memory\") {\n\t\t\t\t\t\t\tthis.lastWrittenMemory = newContent;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.lastWrittenUser = newContent;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Successfully updated ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"}.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: true },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Error: Failed to perform memory operation: ${String(err)}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: String(err) },\n\t\t\t\t\t\t};\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (release) {\n\t\t\t\t\t\t\tawait release();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n"]}
@@ -3,6 +3,35 @@ import { join } from "path";
3
3
  import lockfile from "proper-lockfile";
4
4
  import { Type } from "typebox";
5
5
  import { scanContextFileThreats } from "../../resource-loader.js";
6
+ import { jaccard, tokenize } from "../../tools/skill-audit.js";
7
+ /**
8
+ * R5 confront-before-write (anti append-rot): if `content` is a near-duplicate of an existing
9
+ * non-empty line (token Jaccard ≥ threshold — i.e. the same fact reworded), supersede that line in
10
+ * place and return the rewritten file; otherwise return null (the caller appends normally).
11
+ */
12
+ export function supersedeNearDuplicateLine(existing, content) {
13
+ const NEAR_DUP_THRESHOLD = 0.6;
14
+ const contentTokens = tokenize(content);
15
+ if (contentTokens.length === 0)
16
+ return null;
17
+ const lines = existing.split("\n");
18
+ let bestIdx = -1;
19
+ let bestScore = NEAR_DUP_THRESHOLD;
20
+ for (let i = 0; i < lines.length; i++) {
21
+ const line = lines[i].trim();
22
+ if (!line)
23
+ continue;
24
+ const score = jaccard(contentTokens, tokenize(line));
25
+ if (score >= bestScore) {
26
+ bestScore = score;
27
+ bestIdx = i;
28
+ }
29
+ }
30
+ if (bestIdx === -1)
31
+ return null;
32
+ lines[bestIdx] = content;
33
+ return lines.join("\n");
34
+ }
6
35
  const memorySchema = Type.Object({
7
36
  action: Type.Union([Type.Literal("add"), Type.Literal("replace"), Type.Literal("remove")], {
8
37
  description: "Action to perform: add new content, replace existing content, or remove content",
@@ -132,10 +161,18 @@ export class FileStoreProvider {
132
161
  if (content === undefined) {
133
162
  throw new Error("Parameter 'content' is required for action 'add'.");
134
163
  }
135
- newContent =
136
- newContent.endsWith("\n") || newContent === ""
137
- ? `${newContent}${content}\n`
138
- : `${newContent}\n${content}\n`;
164
+ // R5: confront before write. If this fact is a near-duplicate of an existing line,
165
+ // supersede it in place instead of appending a redundant copy (prevents append-rot).
166
+ const superseded = supersedeNearDuplicateLine(currentOnDisk, content);
167
+ if (superseded !== null) {
168
+ newContent = superseded;
169
+ }
170
+ else {
171
+ newContent =
172
+ newContent.endsWith("\n") || newContent === ""
173
+ ? `${newContent}${content}\n`
174
+ : `${newContent}\n${content}\n`;
175
+ }
139
176
  }
140
177
  else if (action === "replace") {
141
178
  if (content === undefined || oldContent === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"file-store.js","sourceRoot":"","sources":["../../../../src/core/memory/providers/file-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, promises as fs, mkdirSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { scanContextFileThreats } from \"../../resource-loader.ts\";\nimport type { MemoryLifecycleContext, MemoryProvider } from \"../memory-provider.ts\";\n\nconst memorySchema = Type.Object({\n\taction: Type.Union([Type.Literal(\"add\"), Type.Literal(\"replace\"), Type.Literal(\"remove\")], {\n\t\tdescription: \"Action to perform: add new content, replace existing content, or remove content\",\n\t}),\n\ttarget: Type.Union([Type.Literal(\"memory\"), Type.Literal(\"user\")], {\n\t\tdescription: \"Target file: 'memory' for MEMORY.md, 'user' for USER.md\",\n\t}),\n\tcontent: Type.Optional(Type.String({ description: \"Content to write (required for 'add' or 'replace')\" })),\n\toldContent: Type.Optional(\n\t\tType.String({ description: \"Exact substring to replace or remove (required for 'replace' or 'remove')\" }),\n\t),\n});\n\ntype MemoryParams = Static<typeof memorySchema>;\n\nexport class FileStoreProvider implements MemoryProvider {\n\tpublic readonly name = \"file-store\";\n\n\tprivate ctx?: MemoryLifecycleContext;\n\tprivate memoryFilePath = \"\";\n\tprivate userFilePath = \"\";\n\n\tprivate lastWrittenMemory = \"\";\n\tprivate lastWrittenUser = \"\";\n\n\t// Character budgets\n\tprivate static readonly BUDGET_MEMORY = 2200;\n\tprivate static readonly BUDGET_USER = 1375;\n\n\tpublic isAvailable(): boolean {\n\t\treturn true;\n\t}\n\n\tpublic getCapabilities() {\n\t\treturn { surfaces: [\"context\" as const] };\n\t}\n\n\tpublic async initialize(_sessionId: string, ctx: MemoryLifecycleContext): Promise<void> {\n\t\tthis.ctx = ctx;\n\t\tthis.memoryFilePath = join(ctx.agentDir, \"MEMORY.md\");\n\t\tthis.userFilePath = join(ctx.agentDir, \"USER.md\");\n\n\t\t// Ensure agentDir exists\n\t\tif (!existsSync(ctx.agentDir)) {\n\t\t\tmkdirSync(ctx.agentDir, { recursive: true });\n\t\t}\n\n\t\t// Initialize files if they do not exist\n\t\tif (!existsSync(this.memoryFilePath)) {\n\t\t\twriteFileSync(this.memoryFilePath, \"\", \"utf-8\");\n\t\t}\n\t\tif (!existsSync(this.userFilePath)) {\n\t\t\twriteFileSync(this.userFilePath, \"\", \"utf-8\");\n\t\t}\n\n\t\t// Load initial contents\n\t\tthis.lastWrittenMemory = await fs.readFile(this.memoryFilePath, \"utf-8\");\n\t\tthis.lastWrittenUser = await fs.readFile(this.userFilePath, \"utf-8\");\n\t}\n\n\tpublic systemPromptBlock(): string {\n\t\tconst sanitize = (content: string) => {\n\t\t\tconst lines = content.split(\"\\n\");\n\t\t\tconst sanitizedLines = lines.map((line) => {\n\t\t\t\tconst threats = scanContextFileThreats(line);\n\t\t\t\tif (threats.length > 0) {\n\t\t\t\t\treturn `[BLOCKED: potential threat detected (${threats.join(\", \")})]`;\n\t\t\t\t}\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\treturn sanitizedLines.join(\"\\n\");\n\t\t};\n\n\t\tconst mem = sanitize(this.lastWrittenMemory);\n\t\tconst usr = sanitize(this.lastWrittenUser);\n\n\t\tconst blocks: string[] = [];\n\t\tif (mem.trim()) {\n\t\t\tblocks.push(`## MEMORY.md:\\n${mem}`);\n\t\t}\n\t\tif (usr.trim()) {\n\t\t\tblocks.push(`## USER.md:\\n${usr}`);\n\t\t}\n\n\t\tif (blocks.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\treturn `=== Persistent Memory (file-store) ===\\n[System Note: Below is a snapshot of your persistent memory. You can update these using the 'memory' tool.]\\n\\n${blocks.join(\"\\n\\n\")}`;\n\t}\n\n\tpublic async prefetch(_query: string): Promise<string> {\n\t\t// static system prompt block is sufficient for file-store default; no-op prefetch\n\t\treturn \"\";\n\t}\n\n\tpublic async shutdown(): Promise<void> {\n\t\t// no-op\n\t}\n\n\tpublic getContextMarkers(): string[] {\n\t\treturn [];\n\t}\n\n\tpublic getToolDefinitions(): ToolDefinition[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"memory\",\n\t\t\t\tlabel: \"Persistent Memory Manager\",\n\t\t\t\tdescription: \"Add, replace, or remove contents in persistent memory files (MEMORY.md/USER.md).\",\n\t\t\t\tparameters: memorySchema,\n\t\t\t\texecute: async (_toolCallId, params: MemoryParams, _signal, _onUpdate, _execCtx) => {\n\t\t\t\t\tif (this.ctx?.isChildSession) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: \"Error: Writes to persistent memory are not allowed in child sessions (subagents).\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: \"Child session write-gated\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { action, target, content, oldContent } = params;\n\t\t\t\t\tconst filePath = target === \"memory\" ? this.memoryFilePath : this.userFilePath;\n\t\t\t\t\tconst budget = target === \"memory\" ? FileStoreProvider.BUDGET_MEMORY : FileStoreProvider.BUDGET_USER;\n\n\t\t\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// File lock\n\t\t\t\t\t\trelease = await lockfile.lock(filePath, { realpath: false, retries: 5 });\n\n\t\t\t\t\t\tconst lastWritten = target === \"memory\" ? this.lastWrittenMemory : this.lastWrittenUser;\n\t\t\t\t\t\t// Read current file content on disk for drift detection\n\t\t\t\t\t\tconst currentOnDisk = await fs.readFile(filePath, \"utf-8\");\n\t\t\t\t\t\tif (currentOnDisk !== lastWritten) {\n\t\t\t\t\t\t\t// Drift detected. Backup current file and refuse write.\n\t\t\t\t\t\t\tconst backupPath = `${filePath}.bak.${Date.now()}`;\n\t\t\t\t\t\t\tawait fs.writeFile(backupPath, currentOnDisk, \"utf-8\");\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Drift detected. The memory file has been modified out-of-band by an external process. A backup was created at ${backupPath}. Operation aborted.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Drift detected\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet newContent = currentOnDisk;\n\t\t\t\t\t\tif (action === \"add\") {\n\t\t\t\t\t\t\tif (content === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'content' is required for action 'add'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent =\n\t\t\t\t\t\t\t\tnewContent.endsWith(\"\\n\") || newContent === \"\"\n\t\t\t\t\t\t\t\t\t? `${newContent}${content}\\n`\n\t\t\t\t\t\t\t\t\t: `${newContent}\\n${content}\\n`;\n\t\t\t\t\t\t} else if (action === \"replace\") {\n\t\t\t\t\t\t\tif (content === undefined || oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameters 'content' and 'oldContent' are required for action 'replace'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to replace ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, content);\n\t\t\t\t\t\t} else if (action === \"remove\") {\n\t\t\t\t\t\t\tif (oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'oldContent' is required for action 'remove'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to remove ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, \"\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Budget check\n\t\t\t\t\t\tif (newContent.length > budget) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Memory budget exceeded. ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"} limit is ${budget} characters. Current operation would result in ${newContent.length} characters.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Memory budget exceeded\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic write\n\t\t\t\t\t\tconst tmpPath = `${filePath}.tmp`;\n\t\t\t\t\t\tawait fs.writeFile(tmpPath, newContent, \"utf-8\");\n\t\t\t\t\t\tawait fs.rename(tmpPath, filePath);\n\n\t\t\t\t\t\t// Update in-memory tracker\n\t\t\t\t\t\tif (target === \"memory\") {\n\t\t\t\t\t\t\tthis.lastWrittenMemory = newContent;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.lastWrittenUser = newContent;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Successfully updated ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"}.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: true },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Error: Failed to perform memory operation: ${String(err)}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: String(err) },\n\t\t\t\t\t\t};\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (release) {\n\t\t\t\t\t\t\tawait release();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n"]}
1
+ {"version":3,"file":"file-store.js","sourceRoot":"","sources":["../../../../src/core/memory/providers/file-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, promises as fs, mkdirSync, writeFileSync } from \"fs\";\nimport { join } from \"path\";\nimport lockfile from \"proper-lockfile\";\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"../../extensions/types.ts\";\nimport { scanContextFileThreats } from \"../../resource-loader.ts\";\nimport { jaccard, tokenize } from \"../../tools/skill-audit.ts\";\nimport type { MemoryLifecycleContext, MemoryProvider } from \"../memory-provider.ts\";\n\n/**\n * R5 confront-before-write (anti append-rot): if `content` is a near-duplicate of an existing\n * non-empty line (token Jaccard ≥ threshold — i.e. the same fact reworded), supersede that line in\n * place and return the rewritten file; otherwise return null (the caller appends normally).\n */\nexport function supersedeNearDuplicateLine(existing: string, content: string): string | null {\n\tconst NEAR_DUP_THRESHOLD = 0.6;\n\tconst contentTokens = tokenize(content);\n\tif (contentTokens.length === 0) return null;\n\tconst lines = existing.split(\"\\n\");\n\tlet bestIdx = -1;\n\tlet bestScore = NEAR_DUP_THRESHOLD;\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst line = lines[i].trim();\n\t\tif (!line) continue;\n\t\tconst score = jaccard(contentTokens, tokenize(line));\n\t\tif (score >= bestScore) {\n\t\t\tbestScore = score;\n\t\t\tbestIdx = i;\n\t\t}\n\t}\n\tif (bestIdx === -1) return null;\n\tlines[bestIdx] = content;\n\treturn lines.join(\"\\n\");\n}\n\nconst memorySchema = Type.Object({\n\taction: Type.Union([Type.Literal(\"add\"), Type.Literal(\"replace\"), Type.Literal(\"remove\")], {\n\t\tdescription: \"Action to perform: add new content, replace existing content, or remove content\",\n\t}),\n\ttarget: Type.Union([Type.Literal(\"memory\"), Type.Literal(\"user\")], {\n\t\tdescription: \"Target file: 'memory' for MEMORY.md, 'user' for USER.md\",\n\t}),\n\tcontent: Type.Optional(Type.String({ description: \"Content to write (required for 'add' or 'replace')\" })),\n\toldContent: Type.Optional(\n\t\tType.String({ description: \"Exact substring to replace or remove (required for 'replace' or 'remove')\" }),\n\t),\n});\n\ntype MemoryParams = Static<typeof memorySchema>;\n\nexport class FileStoreProvider implements MemoryProvider {\n\tpublic readonly name = \"file-store\";\n\n\tprivate ctx?: MemoryLifecycleContext;\n\tprivate memoryFilePath = \"\";\n\tprivate userFilePath = \"\";\n\n\tprivate lastWrittenMemory = \"\";\n\tprivate lastWrittenUser = \"\";\n\n\t// Character budgets\n\tprivate static readonly BUDGET_MEMORY = 2200;\n\tprivate static readonly BUDGET_USER = 1375;\n\n\tpublic isAvailable(): boolean {\n\t\treturn true;\n\t}\n\n\tpublic getCapabilities() {\n\t\treturn { surfaces: [\"context\" as const] };\n\t}\n\n\tpublic async initialize(_sessionId: string, ctx: MemoryLifecycleContext): Promise<void> {\n\t\tthis.ctx = ctx;\n\t\tthis.memoryFilePath = join(ctx.agentDir, \"MEMORY.md\");\n\t\tthis.userFilePath = join(ctx.agentDir, \"USER.md\");\n\n\t\t// Ensure agentDir exists\n\t\tif (!existsSync(ctx.agentDir)) {\n\t\t\tmkdirSync(ctx.agentDir, { recursive: true });\n\t\t}\n\n\t\t// Initialize files if they do not exist\n\t\tif (!existsSync(this.memoryFilePath)) {\n\t\t\twriteFileSync(this.memoryFilePath, \"\", \"utf-8\");\n\t\t}\n\t\tif (!existsSync(this.userFilePath)) {\n\t\t\twriteFileSync(this.userFilePath, \"\", \"utf-8\");\n\t\t}\n\n\t\t// Load initial contents\n\t\tthis.lastWrittenMemory = await fs.readFile(this.memoryFilePath, \"utf-8\");\n\t\tthis.lastWrittenUser = await fs.readFile(this.userFilePath, \"utf-8\");\n\t}\n\n\tpublic systemPromptBlock(): string {\n\t\tconst sanitize = (content: string) => {\n\t\t\tconst lines = content.split(\"\\n\");\n\t\t\tconst sanitizedLines = lines.map((line) => {\n\t\t\t\tconst threats = scanContextFileThreats(line);\n\t\t\t\tif (threats.length > 0) {\n\t\t\t\t\treturn `[BLOCKED: potential threat detected (${threats.join(\", \")})]`;\n\t\t\t\t}\n\t\t\t\treturn line;\n\t\t\t});\n\t\t\treturn sanitizedLines.join(\"\\n\");\n\t\t};\n\n\t\tconst mem = sanitize(this.lastWrittenMemory);\n\t\tconst usr = sanitize(this.lastWrittenUser);\n\n\t\tconst blocks: string[] = [];\n\t\tif (mem.trim()) {\n\t\t\tblocks.push(`## MEMORY.md:\\n${mem}`);\n\t\t}\n\t\tif (usr.trim()) {\n\t\t\tblocks.push(`## USER.md:\\n${usr}`);\n\t\t}\n\n\t\tif (blocks.length === 0) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\treturn `=== Persistent Memory (file-store) ===\\n[System Note: Below is a snapshot of your persistent memory. You can update these using the 'memory' tool.]\\n\\n${blocks.join(\"\\n\\n\")}`;\n\t}\n\n\tpublic async prefetch(_query: string): Promise<string> {\n\t\t// static system prompt block is sufficient for file-store default; no-op prefetch\n\t\treturn \"\";\n\t}\n\n\tpublic async shutdown(): Promise<void> {\n\t\t// no-op\n\t}\n\n\tpublic getContextMarkers(): string[] {\n\t\treturn [];\n\t}\n\n\tpublic getToolDefinitions(): ToolDefinition[] {\n\t\treturn [\n\t\t\t{\n\t\t\t\tname: \"memory\",\n\t\t\t\tlabel: \"Persistent Memory Manager\",\n\t\t\t\tdescription: \"Add, replace, or remove contents in persistent memory files (MEMORY.md/USER.md).\",\n\t\t\t\tparameters: memorySchema,\n\t\t\t\texecute: async (_toolCallId, params: MemoryParams, _signal, _onUpdate, _execCtx) => {\n\t\t\t\t\tif (this.ctx?.isChildSession) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: \"Error: Writes to persistent memory are not allowed in child sessions (subagents).\",\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: \"Child session write-gated\" },\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { action, target, content, oldContent } = params;\n\t\t\t\t\tconst filePath = target === \"memory\" ? this.memoryFilePath : this.userFilePath;\n\t\t\t\t\tconst budget = target === \"memory\" ? FileStoreProvider.BUDGET_MEMORY : FileStoreProvider.BUDGET_USER;\n\n\t\t\t\t\tlet release: (() => Promise<void>) | undefined;\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// File lock\n\t\t\t\t\t\trelease = await lockfile.lock(filePath, { realpath: false, retries: 5 });\n\n\t\t\t\t\t\tconst lastWritten = target === \"memory\" ? this.lastWrittenMemory : this.lastWrittenUser;\n\t\t\t\t\t\t// Read current file content on disk for drift detection\n\t\t\t\t\t\tconst currentOnDisk = await fs.readFile(filePath, \"utf-8\");\n\t\t\t\t\t\tif (currentOnDisk !== lastWritten) {\n\t\t\t\t\t\t\t// Drift detected. Backup current file and refuse write.\n\t\t\t\t\t\t\tconst backupPath = `${filePath}.bak.${Date.now()}`;\n\t\t\t\t\t\t\tawait fs.writeFile(backupPath, currentOnDisk, \"utf-8\");\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Drift detected. The memory file has been modified out-of-band by an external process. A backup was created at ${backupPath}. Operation aborted.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Drift detected\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tlet newContent = currentOnDisk;\n\t\t\t\t\t\tif (action === \"add\") {\n\t\t\t\t\t\t\tif (content === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'content' is required for action 'add'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// R5: confront before write. If this fact is a near-duplicate of an existing line,\n\t\t\t\t\t\t\t// supersede it in place instead of appending a redundant copy (prevents append-rot).\n\t\t\t\t\t\t\tconst superseded = supersedeNearDuplicateLine(currentOnDisk, content);\n\t\t\t\t\t\t\tif (superseded !== null) {\n\t\t\t\t\t\t\t\tnewContent = superseded;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tnewContent =\n\t\t\t\t\t\t\t\t\tnewContent.endsWith(\"\\n\") || newContent === \"\"\n\t\t\t\t\t\t\t\t\t\t? `${newContent}${content}\\n`\n\t\t\t\t\t\t\t\t\t\t: `${newContent}\\n${content}\\n`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (action === \"replace\") {\n\t\t\t\t\t\t\tif (content === undefined || oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameters 'content' and 'oldContent' are required for action 'replace'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to replace ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, content);\n\t\t\t\t\t\t} else if (action === \"remove\") {\n\t\t\t\t\t\t\tif (oldContent === undefined) {\n\t\t\t\t\t\t\t\tthrow new Error(\"Parameter 'oldContent' is required for action 'remove'.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!currentOnDisk.includes(oldContent)) {\n\t\t\t\t\t\t\t\tthrow new Error(`The content to remove ('oldContent') was not found in the file.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tnewContent = currentOnDisk.replace(oldContent, \"\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Budget check\n\t\t\t\t\t\tif (newContent.length > budget) {\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\t\ttext: `Error: Memory budget exceeded. ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"} limit is ${budget} characters. Current operation would result in ${newContent.length} characters.`,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tdetails: { success: false, error: \"Memory budget exceeded\" },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic write\n\t\t\t\t\t\tconst tmpPath = `${filePath}.tmp`;\n\t\t\t\t\t\tawait fs.writeFile(tmpPath, newContent, \"utf-8\");\n\t\t\t\t\t\tawait fs.rename(tmpPath, filePath);\n\n\t\t\t\t\t\t// Update in-memory tracker\n\t\t\t\t\t\tif (target === \"memory\") {\n\t\t\t\t\t\t\tthis.lastWrittenMemory = newContent;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.lastWrittenUser = newContent;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Successfully updated ${target === \"memory\" ? \"MEMORY.md\" : \"USER.md\"}.`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: true },\n\t\t\t\t\t\t};\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\t\t\ttext: `Error: Failed to perform memory operation: ${String(err)}`,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\tdetails: { success: false, error: String(err) },\n\t\t\t\t\t\t};\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (release) {\n\t\t\t\t\t\t\tawait release();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t];\n\t}\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider",
3
- "version": "0.80.59",
3
+ "version": "0.80.60",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-custom-provider",
9
- "version": "0.80.59",
9
+ "version": "0.80.60",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.52.0"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-anthropic",
3
3
  "private": true,
4
- "version": "0.80.59",
4
+ "version": "0.80.60",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-custom-provider-gitlab-duo",
3
3
  "private": true,
4
- "version": "0.80.59",
4
+ "version": "0.80.60",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
- "version": "0.80.59",
3
+ "version": "0.80.60",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-sandbox",
9
- "version": "0.80.59",
9
+ "version": "0.80.60",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sandbox-runtime": "^0.0.26"
12
12
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-sandbox",
3
3
  "private": true,
4
- "version": "0.80.59",
4
+ "version": "0.80.60",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "0.80.59",
3
+ "version": "0.80.60",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "0.80.59",
9
+ "version": "0.80.60",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "0.80.59",
4
+ "version": "0.80.60",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@caupulican/pi-adaptative",
3
- "version": "0.80.62",
3
+ "version": "0.80.63",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@caupulican/pi-adaptative",
9
- "version": "0.80.62",
9
+ "version": "0.80.63",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
- "@caupulican/pi-agent-core": "^0.80.62",
13
- "@caupulican/pi-ai": "^0.80.62",
14
- "@caupulican/pi-tui": "^0.80.62",
12
+ "@caupulican/pi-agent-core": "^0.80.63",
13
+ "@caupulican/pi-ai": "^0.80.63",
14
+ "@caupulican/pi-tui": "^0.80.63",
15
15
  "@silvia-odwyer/photon-node": "0.3.4",
16
16
  "chalk": "5.6.2",
17
17
  "cross-spawn": "7.0.6",
@@ -474,11 +474,11 @@
474
474
  }
475
475
  },
476
476
  "node_modules/@caupulican/pi-agent-core": {
477
- "version": "0.80.62",
478
- "resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.80.62.tgz",
477
+ "version": "0.80.63",
478
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-agent-core/-/pi-agent-core-0.80.63.tgz",
479
479
  "license": "MIT",
480
480
  "dependencies": {
481
- "@caupulican/pi-ai": "^0.80.62",
481
+ "@caupulican/pi-ai": "^0.80.63",
482
482
  "ignore": "7.0.5",
483
483
  "typebox": "1.1.38",
484
484
  "yaml": "2.9.0"
@@ -488,8 +488,8 @@
488
488
  }
489
489
  },
490
490
  "node_modules/@caupulican/pi-ai": {
491
- "version": "0.80.62",
492
- "resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.80.62.tgz",
491
+ "version": "0.80.63",
492
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-ai/-/pi-ai-0.80.63.tgz",
493
493
  "license": "MIT",
494
494
  "dependencies": {
495
495
  "@anthropic-ai/sdk": "0.91.1",
@@ -511,8 +511,8 @@
511
511
  }
512
512
  },
513
513
  "node_modules/@caupulican/pi-tui": {
514
- "version": "0.80.62",
515
- "resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.80.62.tgz",
514
+ "version": "0.80.63",
515
+ "resolved": "https://registry.npmjs.org/@caupulican/pi-tui/-/pi-tui-0.80.63.tgz",
516
516
  "license": "MIT",
517
517
  "dependencies": {
518
518
  "get-east-asian-width": "1.6.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caupulican/pi-adaptative",
3
- "version": "0.80.62",
3
+ "version": "0.80.63",
4
4
  "description": "Adaptive fork of Pi coding agent for self-evolving agent harness experiments",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -41,9 +41,9 @@
41
41
  "prepublishOnly": "npm run clean && npm run build && npm run shrinkwrap"
42
42
  },
43
43
  "dependencies": {
44
- "@caupulican/pi-agent-core": "^0.80.62",
45
- "@caupulican/pi-ai": "^0.80.62",
46
- "@caupulican/pi-tui": "^0.80.62",
44
+ "@caupulican/pi-agent-core": "^0.80.63",
45
+ "@caupulican/pi-ai": "^0.80.63",
46
+ "@caupulican/pi-tui": "^0.80.63",
47
47
  "@silvia-odwyer/photon-node": "0.3.4",
48
48
  "chalk": "5.6.2",
49
49
  "cross-spawn": "7.0.6",