agent-working-memory 0.9.1 → 0.10.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/dist/cli.js +324 -223
- package/dist/cli.js.map +1 -1
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +10 -1
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +5 -1
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/storage/factory.d.ts +1 -1
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +16 -2
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +8 -0
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +228 -0
- package/dist/storage/postgres.d.ts.map +1 -0
- package/dist/storage/postgres.js +1221 -0
- package/dist/storage/postgres.js.map +1 -0
- package/package.json +3 -1
- package/src/cli.ts +266 -272
- package/src/core/salience.ts +10 -1
- package/src/core/write-pipeline.ts +5 -1
- package/src/storage/factory.ts +15 -3
- package/src/storage/pglite.ts +9 -0
- package/src/storage/postgres.ts +1475 -0
package/src/core/salience.ts
CHANGED
|
@@ -493,12 +493,21 @@ export async function computeNoveltyWithMatch(
|
|
|
493
493
|
: typeof created === 'number' ? created : Date.parse(created);
|
|
494
494
|
return Number.isFinite(createdMs) && createdMs >= cutoffMs;
|
|
495
495
|
};
|
|
496
|
+
// Novelty PENALTY: an exact-concept recent match on EITHER channel (including cross-agent workspace
|
|
497
|
+
// results) is a near-duplicate for novelty-scoring purposes.
|
|
496
498
|
const exactConceptRecent = allBm25.some(r => checkExactConcept(r.engram))
|
|
497
499
|
|| (topCosine ? checkExactConcept(topCosine.engram) : false);
|
|
500
|
+
// REINFORCE redirect: prefer an exact same-concept match as the matched engram so a true duplicate
|
|
501
|
+
// REINFORCES it (R1) instead of creating a new one even when a different-concept engram out-scores it.
|
|
502
|
+
// CRUCIALLY, only consider AGENT-SCOPED candidates — bm25Results and the cosine channel are scoped to
|
|
503
|
+
// this agent, but `wsResults` are OTHER agents' engrams; redirecting to one would make the write
|
|
504
|
+
// pipeline reinforce/supersede a foreign agent's memory (cross-agent contamination).
|
|
505
|
+
const exactMatch = bm25Results.find(r => checkExactConcept(r.engram))?.engram
|
|
506
|
+
?? (topCosine && checkExactConcept(topCosine.engram) ? topCosine.engram : undefined);
|
|
498
507
|
const conceptPenalty = exactConceptRecent ? 0.3 : 0;
|
|
499
508
|
|
|
500
509
|
const novelty = Math.max(0.05, Math.min(0.95, baseNovelty - conceptPenalty));
|
|
501
|
-
return { novelty, matchedEngramId: combinedTop.engramId, matchScore: topScore };
|
|
510
|
+
return { novelty, matchedEngramId: exactMatch?.id ?? combinedTop.engramId, matchScore: topScore };
|
|
502
511
|
} catch {
|
|
503
512
|
return { novelty: 0.8, matchedEngramId: null, matchScore: 0 };
|
|
504
513
|
}
|
|
@@ -220,7 +220,11 @@ export async function performWrite(
|
|
|
220
220
|
let result: WriteResult | null = null;
|
|
221
221
|
if (enableReinforcement && noveltyResult.matchedEngramId) {
|
|
222
222
|
const matched = await store.getEngram(noveltyResult.matchedEngramId);
|
|
223
|
-
|
|
223
|
+
// Agent-scope guard: NEVER reinforce/supersede an engram that belongs to a DIFFERENT agent. A
|
|
224
|
+
// workspace/hive recall can surface another agent's same-concept engram as the match; mutating it
|
|
225
|
+
// (merge content, re-embed, bump confidence, or supersede) would corrupt that agent's memory. A
|
|
226
|
+
// cross-agent match falls through to createNewEngram so this agent gets its own copy.
|
|
227
|
+
if (matched && matched.agentId === input.agentId) {
|
|
224
228
|
const newConcept = (input.concept ?? '').toLowerCase().trim();
|
|
225
229
|
const matchedConcept = (matched.concept ?? '').toLowerCase().trim();
|
|
226
230
|
const sameConcept = newConcept === matchedConcept && newConcept.length > 0;
|
package/src/storage/factory.ts
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
import { existsSync, statSync } from 'node:fs';
|
|
40
40
|
import type { IEngramStore } from './store.js';
|
|
41
41
|
|
|
42
|
-
export type StoreBackend = 'sqlite' | 'pglite';
|
|
42
|
+
export type StoreBackend = 'sqlite' | 'pglite' | 'postgres';
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Auto-detect the backend from on-disk state. Used when `AWM_STORE_BACKEND`
|
|
@@ -71,6 +71,7 @@ export function getConfiguredBackend(): StoreBackend {
|
|
|
71
71
|
const normalized = raw.toLowerCase();
|
|
72
72
|
if (normalized === 'pglite') return 'pglite';
|
|
73
73
|
if (normalized === 'sqlite') return 'sqlite';
|
|
74
|
+
if (normalized === 'postgres') return 'postgres';
|
|
74
75
|
console.warn(`Unknown AWM_STORE_BACKEND=${raw}; falling back to sqlite`);
|
|
75
76
|
return 'sqlite';
|
|
76
77
|
}
|
|
@@ -80,8 +81,11 @@ export function getConfiguredBackend(): StoreBackend {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
export function getConfiguredPath(): string {
|
|
84
|
+
const backend = getConfiguredBackend();
|
|
85
|
+
// Postgres uses a connection URL, not an on-disk path.
|
|
86
|
+
if (backend === 'postgres') return process.env.AWM_DATABASE_URL ?? 'postgres://localhost:5432/awm';
|
|
83
87
|
if (process.env.AWM_DB_PATH) return process.env.AWM_DB_PATH;
|
|
84
|
-
return
|
|
88
|
+
return backend === 'pglite' ? 'memory-pglite' : 'memory.db';
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
/**
|
|
@@ -132,7 +136,15 @@ export async function openStore(): Promise<{
|
|
|
132
136
|
const backend = getConfiguredBackend();
|
|
133
137
|
const path = getConfiguredPath();
|
|
134
138
|
|
|
135
|
-
|
|
139
|
+
// The on-disk mismatch warning only applies to file/dir backends.
|
|
140
|
+
if (backend !== 'postgres') warnIfBackendDisagreesWithDisk(backend, path);
|
|
141
|
+
|
|
142
|
+
if (backend === 'postgres') {
|
|
143
|
+
const { PostgresEngramStore } = await import('./postgres.js');
|
|
144
|
+
const store = new PostgresEngramStore(path);
|
|
145
|
+
await store.ready();
|
|
146
|
+
return { store: store as unknown as IEngramStore, backend, path };
|
|
147
|
+
}
|
|
136
148
|
|
|
137
149
|
if (backend === 'pglite') {
|
|
138
150
|
const { PGliteEngramStore } = await import('./pglite.js');
|
package/src/storage/pglite.ts
CHANGED
|
@@ -141,6 +141,8 @@ function extractTagValue(tags: string[], prefix: string): string | null {
|
|
|
141
141
|
return null;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
let warnedNoCoordPglite = false; // one-time warning when workspace/hive recall falls back (coord_agents absent)
|
|
145
|
+
|
|
144
146
|
export class PGliteEngramStore {
|
|
145
147
|
private db!: PGlite;
|
|
146
148
|
private readyPromise: Promise<void>;
|
|
@@ -395,6 +397,13 @@ export class PGliteEngramStore {
|
|
|
395
397
|
if (!names.includes(agentId)) names.push(agentId);
|
|
396
398
|
return names;
|
|
397
399
|
} catch {
|
|
400
|
+
// coord_agents isn't provisioned on PGlite — workspace/hive coordination is currently SQLite-only.
|
|
401
|
+
// Warn ONCE so this degrades VISIBLY (recall scoped to self) instead of silently.
|
|
402
|
+
if (!warnedNoCoordPglite) {
|
|
403
|
+
warnedNoCoordPglite = true;
|
|
404
|
+
console.warn('[awm:pglite] workspace/hive coordination is not available on the PGlite backend ' +
|
|
405
|
+
'(coord_agents not provisioned) — recall is scoped to THIS agent only. Hive coordination is SQLite-only for now.');
|
|
406
|
+
}
|
|
398
407
|
return [agentId];
|
|
399
408
|
}
|
|
400
409
|
}
|