@absolutejs/agent-memory
Advanced tools
+31
-0
@@ -32,2 +32,3 @@ // @bun | ||
| list: async (scope, limit) => [...rows.values()].filter((row) => scopeKey(row.scope) === scopeKey(scope)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)).slice(0, limit).map((row) => structuredClone(row)), | ||
| listRecords: async ({ tenantId, limit }) => [...rows.values()].filter((row) => !tenantId || row.scope.tenantId === tenantId).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, Math.max(1, Math.min(limit, 200))).map((row) => structuredClone(row)), | ||
| delete: async (scope, key) => rows.delete(keyOf(scope, key)), | ||
@@ -104,2 +105,7 @@ eraseSubject: async ({ tenantId, userId }) => { | ||
| list: async (scope, limit) => (await client.query(`SELECT document FROM ${ns}.records WHERE tenant_id=$1 AND namespace=$2 AND user_id=$3 AND agent_id=$4 AND run_id=$5 ORDER BY updated_at DESC LIMIT $6`, [...parts(scope), limit])).rows.map((row) => document(row)), | ||
| listRecords: async ({ tenantId, limit }) => { | ||
| const bounded = Math.max(1, Math.min(limit, 200)); | ||
| const result = tenantId ? await client.query(`SELECT document FROM ${ns}.records WHERE tenant_id=$1 ORDER BY updated_at DESC LIMIT $2`, [tenantId, bounded]) : await client.query(`SELECT document FROM ${ns}.records ORDER BY updated_at DESC LIMIT $1`, [bounded]); | ||
| return result.rows.map((row) => document(row)); | ||
| }, | ||
| delete: async (scope, key) => (await client.query(`DELETE FROM ${ns}.records WHERE tenant_id=$1 AND namespace=$2 AND user_id=$3 AND agent_id=$4 AND run_id=$5 AND memory_key=$6 RETURNING id`, [...parts(scope), key])).rows.length === 1, | ||
@@ -125,5 +131,9 @@ eraseSubject: async ({ tenantId, userId }) => (await client.query(`DELETE FROM ${ns}.records WHERE tenant_id=$1 AND (user_id=$2 OR document->'createdBy'->>'userId'=$2) RETURNING id`, [tenantId, userId])).rows.length, | ||
| validateWrite, | ||
| requireExpiration = false, | ||
| maxTtlMs, | ||
| now = Date.now, | ||
| id = () => crypto.randomUUID() | ||
| }) => { | ||
| if (maxTtlMs !== undefined && (!Number.isSafeInteger(maxTtlMs) || maxTtlMs < 1)) | ||
| throw new Error("Agent memory maxTtlMs must be a positive integer"); | ||
| const decode = async (stored) => ({ | ||
@@ -153,2 +163,6 @@ ...stored, | ||
| throw new Error("Invalid memory expiration"); | ||
| if (requireExpiration && !input.expiresAt) | ||
| throw new Error("Agent memory expiration is required"); | ||
| if (input.expiresAt && maxTtlMs !== undefined && Date.parse(input.expiresAt) > now() + maxTtlMs) | ||
| throw new Error("Agent memory expiration exceeds retention policy"); | ||
| const existing = await store.get(input.scope, input.key); | ||
@@ -213,2 +227,19 @@ const timestamp = new Date(now()).toISOString(); | ||
| }, | ||
| list: async (actor, limit = 100) => { | ||
| const scope = { tenantId: actor.tenantId, namespace: "*" }; | ||
| await permitted("search", actor, scope); | ||
| const output = []; | ||
| const timestamp = new Date(now()).toISOString(); | ||
| for (const stored of await store.listRecords({ | ||
| tenantId: actor.tenantId, | ||
| limit: Math.max(1, Math.min(limit, 200)) | ||
| })) { | ||
| if (stored.expiresAt && stored.expiresAt <= timestamp) | ||
| continue; | ||
| const record = await decode(stored); | ||
| await permitted("read", actor, record.scope, record); | ||
| output.push(record); | ||
| } | ||
| return output; | ||
| }, | ||
| delete: async (actor, scope, key) => { | ||
@@ -215,0 +246,0 @@ await permitted("delete", actor, scope); |
| import type { AgentMemoryActor, AgentMemoryAuthorizer, AgentMemoryCodec, AgentMemoryProvenance, AgentMemoryRecord, AgentMemoryScope, AgentMemorySearchIndex, AgentMemoryStore } from "./types"; | ||
| export declare const createAgentMemory: ({ store, authorize, codec, index, validateWrite, now, id, }: { | ||
| export declare const createAgentMemory: ({ store, authorize, codec, index, validateWrite, requireExpiration, maxTtlMs, now, id, }: { | ||
| store: AgentMemoryStore; | ||
@@ -8,2 +8,4 @@ authorize: AgentMemoryAuthorizer; | ||
| validateWrite?: (record: AgentMemoryRecord) => boolean | Promise<boolean>; | ||
| requireExpiration?: boolean; | ||
| maxTtlMs?: number; | ||
| now?: () => number; | ||
@@ -28,2 +30,3 @@ id?: () => string; | ||
| }[]>; | ||
| list: (actor: AgentMemoryActor, limit?: number) => Promise<AgentMemoryRecord[]>; | ||
| delete: (actor: AgentMemoryActor, scope: AgentMemoryScope, key: string) => Promise<boolean>; | ||
@@ -30,0 +33,0 @@ eraseSubject: (actor: AgentMemoryActor, userId: string) => Promise<number>; |
+4
-0
@@ -65,2 +65,6 @@ export type AgentMemoryScope = { | ||
| list(scope: AgentMemoryScope, limit: number): Promise<StoredAgentMemoryRecord[]>; | ||
| listRecords(input: { | ||
| tenantId?: string; | ||
| limit: number; | ||
| }): Promise<StoredAgentMemoryRecord[]>; | ||
| delete(scope: AgentMemoryScope, key: string): Promise<boolean>; | ||
@@ -67,0 +71,0 @@ eraseSubject(input: { |
+1
-1
| { | ||
| "name": "@absolutejs/agent-memory", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "description": "Scoped durable provenance-aware memory with authorization, retention, erasure, encryption seams, and pluggable retrieval for AI agents.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
23549
9.95%490
8.41%